Compare commits
18 Commits
old-master
...
08e9bf08e7
| Author | SHA1 | Date | |
|---|---|---|---|
| 08e9bf08e7 | |||
| ef29b8abcb | |||
| 59cb4b3c4f | |||
| 55a3aa1e6f | |||
| dc36e719eb | |||
| d2e769ea30 | |||
| 3e56c503e4 | |||
| f68c24d9ea | |||
| 2b76ba96ac | |||
| 98a3fff28f | |||
| c35cea6213 | |||
| 94a8bdca79 | |||
| ae5e4b3419 | |||
| 3ffd84e348 | |||
| 6d7dbbfe74 | |||
| f18610d526 | |||
| 9b245b10e3 | |||
| 359c31a0af |
@@ -65,7 +65,7 @@ PointerAlignment: Left
|
|||||||
ReferenceAlignment: Left # New in v13. int &name ==> int& name
|
ReferenceAlignment: Left # New in v13. int &name ==> int& name
|
||||||
ReflowComments: false
|
ReflowComments: false
|
||||||
SeparateDefinitionBlocks: Always # New in v14.
|
SeparateDefinitionBlocks: Always # New in v14.
|
||||||
SortIncludes: false
|
SortIncludes: true
|
||||||
SortUsingDeclarations: true
|
SortUsingDeclarations: true
|
||||||
SpaceAfterCStyleCast: false
|
SpaceAfterCStyleCast: false
|
||||||
SpaceAfterLogicalNot: false
|
SpaceAfterLogicalNot: false
|
||||||
|
|||||||
98
.github/workflows/busted.yml
vendored
98
.github/workflows/busted.yml
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
---
|
||||||
name: Busted
|
name: Busted
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
@@ -7,13 +8,25 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
lua_version: ["luajit-openresty", "luajit-2.1.0-beta3", "luajit-git"]
|
lua_version:
|
||||||
|
[
|
||||||
|
"luajit-openresty",
|
||||||
|
"luajit-2.1.0-beta3",
|
||||||
|
"luajit-git",
|
||||||
|
"5.4.6",
|
||||||
|
"5.1.5",
|
||||||
|
"5.3.6",
|
||||||
|
]
|
||||||
|
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
- uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
if: ${{ !startsWith(matrix.lua_version, 'luajit-') }}
|
||||||
|
|
||||||
- name: Setup ‘lua’
|
- name: Setup ‘lua’
|
||||||
uses: jkl1337/gh-actions-lua@master
|
uses: jkl1337/gh-actions-lua@master
|
||||||
with:
|
with:
|
||||||
@@ -23,14 +36,75 @@ jobs:
|
|||||||
- name: Setup dependencies
|
- name: Setup dependencies
|
||||||
run: |
|
run: |
|
||||||
luarocks install busted
|
luarocks install busted
|
||||||
luarocks install luacov-coveralls
|
luarocks install luacov-reporter-lcov
|
||||||
- name: Build C library
|
- name: Build C++ library
|
||||||
run: make
|
run: |
|
||||||
- name: Run busted tests
|
luarocks make --no-install
|
||||||
run: busted -c -v
|
|
||||||
- name: Report test coverage
|
|
||||||
if: success()
|
|
||||||
continue-on-error: true
|
|
||||||
run: luacov-coveralls -e .luarocks -e spec
|
|
||||||
env:
|
env:
|
||||||
COVERALLS_REPO_TOKEN: ${{ github.token }}
|
LJKIWI_LUA: ${{ startsWith(matrix.lua_version, 'luajit-') && '0' || '1' }}
|
||||||
|
LJKIWI_CFFI: ${{ startsWith(matrix.lua_version, 'luajit-') && '1' || '0' }}
|
||||||
|
FCOV: ${{ startsWith(matrix.os, 'ubuntu-') && '1' || '' }}
|
||||||
|
# Can't assume so versions, have to update this manually below
|
||||||
|
FSANITIZE: ${{ matrix.os == 'ubuntu-latest' && '1' || '' }}
|
||||||
|
|
||||||
|
- name: Run busted tests
|
||||||
|
run: |
|
||||||
|
busted -c -v
|
||||||
|
env:
|
||||||
|
LD_PRELOAD: |-
|
||||||
|
${{ matrix.os == 'ubuntu-latest' &&
|
||||||
|
'/usr/lib/x86_64-linux-gnu/libasan.so.6:/usr/lib/x86_64-linux-gnu/libstdc++.so.6:/usr/lib/x86_64-linux-gnu/libubsan.so.1'
|
||||||
|
|| '' }}
|
||||||
|
|
||||||
|
- name: Run gcov
|
||||||
|
if: success() && startsWith(matrix.os, 'ubuntu-')
|
||||||
|
run: |
|
||||||
|
gcov -p -b -s"$(pwd)" -r *.gcda
|
||||||
|
rm -f 'kiwi#'*.gcov
|
||||||
|
|
||||||
|
- name: generate Lua lcov test reports
|
||||||
|
if: |-
|
||||||
|
success() && !startsWith(matrix.os, 'windows-')
|
||||||
|
&& startsWith(matrix.lua_version, 'luajit-')
|
||||||
|
run: luacov
|
||||||
|
|
||||||
|
- name: Report test coverage
|
||||||
|
if: |-
|
||||||
|
success() && !startsWith(matrix.os, 'windows-')
|
||||||
|
&& (startsWith(matrix.lua_version, 'luajit-') || startsWith(matrix.os, 'ubuntu-'))
|
||||||
|
continue-on-error: true
|
||||||
|
uses: coverallsapp/github-action@v2
|
||||||
|
with:
|
||||||
|
flag-name: run ${{ join(matrix.*, ' - ') }}
|
||||||
|
|
||||||
|
finish:
|
||||||
|
if: always()
|
||||||
|
needs: busted
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Close coveralls build
|
||||||
|
uses: coverallsapp/github-action@v2
|
||||||
|
with:
|
||||||
|
parallel-finished: true
|
||||||
|
|
||||||
|
publish:
|
||||||
|
if: github.ref_type == 'tag' && startsWith(github.ref_name, 'v')
|
||||||
|
needs: busted
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Setup ‘lua’
|
||||||
|
uses: jkl1337/gh-actions-lua@master
|
||||||
|
with:
|
||||||
|
luaVersion: "5.4.6"
|
||||||
|
- name: Setup ‘luarocks’
|
||||||
|
uses: jkl1337/gh-actions-luarocks@master
|
||||||
|
- name: Build C++ library
|
||||||
|
run: |
|
||||||
|
luarocks make --no-install
|
||||||
|
- name: Build rock
|
||||||
|
run: |
|
||||||
|
luarocks install dkjson
|
||||||
|
luarocks upload --api-key ${{ secrets.LUAROCKS_API_KEY }} \
|
||||||
|
rockspecs/kiwi-${GITHUB_REF_NAME#v}-1.rockspec
|
||||||
|
|||||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -2,9 +2,20 @@
|
|||||||
/lua
|
/lua
|
||||||
/lua_modules
|
/lua_modules
|
||||||
/.luarocks
|
/.luarocks
|
||||||
|
/config.mk
|
||||||
|
*.gcda
|
||||||
|
*.gcno
|
||||||
|
*.gcov
|
||||||
|
*.lcov
|
||||||
|
*.out
|
||||||
*.pch
|
*.pch
|
||||||
*.gch
|
*.gch
|
||||||
|
*.lib
|
||||||
*.so
|
*.so
|
||||||
*.o
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.exp
|
||||||
|
*.dll
|
||||||
|
*.rock
|
||||||
.cache/
|
.cache/
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
|||||||
5
.luacov
Normal file
5
.luacov
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
modules = {
|
||||||
|
kiwi = "kiwi.lua",
|
||||||
|
}
|
||||||
|
reporter = "lcov"
|
||||||
|
reportfile = "luacov.lcov"
|
||||||
164
Makefile
164
Makefile
@@ -1,59 +1,128 @@
|
|||||||
CC := $(CROSS)gcc
|
CXX := $(CROSS)g++
|
||||||
CP := cp
|
CP := cp
|
||||||
RM := rm
|
RM := rm
|
||||||
LIBFLAG := -shared
|
LIBFLAG := -shared
|
||||||
LIB_EXT := so
|
LIB_EXT := $(if $(filter Windows_NT,$(OS)),dll,so)
|
||||||
LUA_INCDIR := /usr/include
|
LUA_INCDIR := /usr/include
|
||||||
|
|
||||||
SRCDIR := .
|
SRCDIR := .
|
||||||
|
|
||||||
OPTFLAG := -O2
|
SANITIZE_FLAGS := -fsanitize=undefined -fsanitize=address -fsanitize=alignment \
|
||||||
CCFLAGS += $(OPTFLAG) -fPIC -Wall -fstrict-flex-arrays -fvisibility=hidden -Wformat=2 -Wconversion -Wimplicit-fallthrough
|
-fsanitize=shift -fsanitize=unreachable -fsanitize=bool -fsanitize=enum
|
||||||
|
|
||||||
SANITIZE_FLAGS := -fsanitize=undefined -fsanitize=address
|
ifdef FDEBUG
|
||||||
|
OPTFLAG := -O2
|
||||||
|
else
|
||||||
|
OPTFLAG := -Og -g
|
||||||
|
endif
|
||||||
|
|
||||||
|
COVERAGE_FLAGS := --coverage
|
||||||
LTO_FLAGS := -flto=auto
|
LTO_FLAGS := -flto=auto
|
||||||
|
|
||||||
ifdef SANITIZE
|
ifeq ($(OS),Windows_NT)
|
||||||
CCFLAGS += $(SANITIZE_FLAGS)
|
is_clang = $(filter %clang++,$(CXX))
|
||||||
endif
|
is_gcc = $(filter %g++,$(CXX))
|
||||||
ifdef LTO
|
|
||||||
CCFLAGS += $(LTO_FLAGS)
|
|
||||||
endif
|
|
||||||
|
|
||||||
override CPPFLAGS += -I$(SRCDIR) -I$(SRCDIR)/kiwi -I$(LUA_INCDIR)
|
ifdef FSANITIZE
|
||||||
override CXXFLAGS += -std=c++14 -fno-rtti $(CCFLAGS)
|
$(error "FSANITIZE is not supported on Windows")
|
||||||
override CFLAGS += -std=c99 $(CCFLAGS)
|
endif
|
||||||
|
|
||||||
ifneq ($(filter %gcc,$(CC)),)
|
|
||||||
CXX := $(patsubst %gcc,%g++,$(CC))
|
|
||||||
PCH := ljkiwi.hpp.gch
|
|
||||||
else
|
else
|
||||||
ifneq ($(filter %clang,$(CC)),)
|
uname_s := $(shell uname -s)
|
||||||
CXX := $(patsubst %clang,%clang++,$(CC))
|
ifeq ($(uname_s),Darwin)
|
||||||
override CXXFLAGS += -pedantic -Wno-c99-extensions
|
is_clang = 1
|
||||||
PCH := ljkiwi.hpp.pch
|
is_gcc =
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifdef LUA
|
CC := env MACOSX_DEPLOYMENT_TARGET=11.0 gcc
|
||||||
LUA_VERSION ?= $(lastword $(shell $(LUA) -e "print(_VERSION)"))
|
CXX := env MACOSX_DEPLOYMENT_TARGET=11.0 g++
|
||||||
endif
|
LIBFLAG := -bundle -undefined dynamic_lookup
|
||||||
|
|
||||||
ifndef LUA_VERSION
|
else
|
||||||
LJKIWI_CKIWI := 1
|
is_clang = $(filter %clang++,$(CXX))
|
||||||
else
|
is_gcc = $(filter %g++,$(CXX))
|
||||||
ifneq ($(LUA_VERSION),5.1)
|
|
||||||
LJKIWI_CKIWI :=
|
SANITIZE_FLAGS += -fsanitize=bounds-strict
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
OBJS := luakiwi.o
|
-include config.mk
|
||||||
ifdef LJKIWI_CKIWI
|
|
||||||
OBJS += ckiwi.o
|
ifeq ($(origin LUAROCKS), command line)
|
||||||
|
ifdef FCOV
|
||||||
|
CCFLAGS := $(patsubst -O%,,$(CFLAGS))
|
||||||
|
else
|
||||||
|
ifdef FDEBUG
|
||||||
|
CCFLAGS := $(patsubst -O%,,$(CFLAGS)) -Og -g
|
||||||
|
else
|
||||||
|
CCFLAGS := $(CFLAGS)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
override CFLAGS := -std=c99 $(CCFLAGS)
|
||||||
|
|
||||||
|
ifneq ($(filter %gcc,$(CC)),)
|
||||||
|
CXX := $(patsubst %gcc,%g++,$(CC))
|
||||||
|
else
|
||||||
|
ifneq ($(filter %clang,$(CC)),)
|
||||||
|
CXX := $(patsubst %clang,%clang++,$(CC))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
else
|
||||||
|
CCFLAGS += -fPIC $(OPTFLAG)
|
||||||
|
override CFLAGS += -std=c99 $(CCFLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
CCFLAGS += -Wall -fvisibility=hidden -Wformat=2 -Wconversion -Wimplicit-fallthrough
|
||||||
|
|
||||||
|
ifdef FCOV
|
||||||
|
CCFLAGS += $(COVERAGE_FLAGS)
|
||||||
|
endif
|
||||||
|
ifdef FSANITIZE
|
||||||
|
CCFLAGS += $(SANITIZE_FLAGS)
|
||||||
|
endif
|
||||||
|
ifdef FLTO
|
||||||
|
CCFLAGS += $(LTO_FLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(is_gcc),)
|
||||||
|
#PCH := ljkiwi.hpp.gch
|
||||||
|
else
|
||||||
|
ifneq ($(is_clang),)
|
||||||
|
override CXXFLAGS += -pedantic -Wno-c99-extensions
|
||||||
|
#PCH := ljkiwi.hpp.pch
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
override CPPFLAGS += -I$(SRCDIR) -I$(SRCDIR)/kiwi -I"$(LUA_INCDIR)"
|
||||||
|
override CXXFLAGS += -std=c++17 -fno-rtti $(CCFLAGS)
|
||||||
|
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
override CPPFLAGS += -DLUA_BUILD_AS_DLL
|
||||||
|
override LIBFLAG += "$(LUA_LIBDIR)/$(LUALIB)"
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef LUA
|
||||||
|
LUA_VERSION ?= $(lastword $(shell "$(LUA)" -e "print(_VERSION)"))
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef LUA_VERSION
|
||||||
|
ifneq ($(LUA_VERSION),5.1)
|
||||||
|
LJKIWI_CFFI ?= 0
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
kiwi_lib_srcs := AssocVector.h constraint.h debug.h errors.h expression.h kiwi.h maptype.h \
|
||||||
|
row.h shareddata.h solver.h solverimpl.h strength.h symbol.h symbolics.h term.h \
|
||||||
|
util.h variable.h version.h
|
||||||
|
|
||||||
|
ifneq ($(LJKIWI_LUA),0)
|
||||||
|
objs += luakiwi.o
|
||||||
|
endif
|
||||||
|
ifneq ($(LJKIWI_CFFI),0)
|
||||||
|
objs += ckiwi.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
vpath %.cpp $(SRCDIR)/ckiwi $(SRCDIR)/luakiwi
|
vpath %.cpp $(SRCDIR)/ckiwi $(SRCDIR)/luakiwi
|
||||||
vpath %.h $(SRCDIR)/ckiwi $(SRCDIR)/luakiwi
|
vpath %.h $(SRCDIR)/ckiwi $(SRCDIR)/luakiwi $(SRCDIR)/kiwi/kiwi
|
||||||
|
|
||||||
all: ljkiwi.$(LIB_EXT)
|
all: ljkiwi.$(LIB_EXT)
|
||||||
|
|
||||||
@@ -61,25 +130,26 @@ install:
|
|||||||
$(CP) -f ljkiwi.$(LIB_EXT) $(INST_LIBDIR)/ljkiwi.$(LIB_EXT)
|
$(CP) -f ljkiwi.$(LIB_EXT) $(INST_LIBDIR)/ljkiwi.$(LIB_EXT)
|
||||||
$(CP) -f kiwi.lua $(INST_LUADIR)/kiwi.lua
|
$(CP) -f kiwi.lua $(INST_LUADIR)/kiwi.lua
|
||||||
|
|
||||||
clean:
|
mostlyclean:
|
||||||
$(RM) -f ljkiwi.$(LIB_EXT) $(OBJS) $(PCH)
|
$(RM) -f ljkiwi.$(LIB_EXT) $(objs) $(objs:.o=.gcda) $(objs:.o=.gcno)
|
||||||
|
|
||||||
ckiwi.o: $(PCH) ckiwi.cpp ckiwi.h
|
clean: mostlyclean
|
||||||
luakiwi.o: $(PCH) luakiwi-int.h luacompat.h
|
$(RM) -f $(PCH)
|
||||||
|
|
||||||
ljkiwi.$(LIB_EXT): $(OBJS)
|
ckiwi.o: $(PCH) ckiwi.cpp ckiwi.h $(kiwi_lib_srcs)
|
||||||
$(CXX) $(CCFLAGS) $(LIBFLAG) -o $@ $(OBJS)
|
luakiwi.o: $(PCH) luakiwi-int.h luacompat.h $(kiwi_lib_srcs)
|
||||||
|
$(PCH): $(kiwi_lib_srcs)
|
||||||
|
|
||||||
|
ljkiwi.$(LIB_EXT): $(objs)
|
||||||
|
$(CXX) $(CCFLAGS) $(LIBFLAG) -o $@ $(objs)
|
||||||
|
|
||||||
%.hpp.gch: %.hpp
|
%.hpp.gch: %.hpp
|
||||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -x c++-header -o $@ $<
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -x c++-header -o $@ $<
|
||||||
|
|
||||||
%.hpp.pch: %.hpp
|
%.hpp.pch: %.hpp
|
||||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -x c++-header -o $@ $<
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -x c++-header $<
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
|
|
||||||
|
|
||||||
%.o: %.cpp
|
%.o: %.cpp
|
||||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
.PHONY: all install clean
|
.PHONY: all install clean mostlyclean
|
||||||
|
|||||||
21
Makefile.win
Normal file
21
Makefile.win
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
T= ljkiwi
|
||||||
|
|
||||||
|
OBJS= luakiwi.obj
|
||||||
|
|
||||||
|
lib: $T.dll
|
||||||
|
|
||||||
|
{luakiwi\}.cpp.obj:
|
||||||
|
$(CC) $(CFLAGS) /W4 /wd4200 /c /D_CRT_SECURE_NO_DEPRECATE /I. /I kiwi /I"$(LUA_INCDIR)" /EHs /Fo$@ $(CFLAGS) $<
|
||||||
|
|
||||||
|
$T.dll: $(OBJS)
|
||||||
|
link $(LIBFLAG) /out:$T.dll "$(LUA_LIBDIR)\$(LUALIB)" $(OBJS)
|
||||||
|
IF EXIST $T.dll.manifest mt -manifest $T.dll.manifest -outputresource:$T.dll;2
|
||||||
|
|
||||||
|
install: $T.dll
|
||||||
|
IF NOT EXIST "$(INST_LIBDIR)" mkdir "$(INST_LIBDIR)"
|
||||||
|
copy $T.dll "$(INST_LIBDIR)"
|
||||||
|
copy kiwi.lua "$(INST_LUADIR)"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
del $T.dll $(OBJS) $T.lib $T.exp
|
||||||
|
IF EXIST $T.dll.manifest del $T.dll.manifest
|
||||||
17
README.md
17
README.md
@@ -1,21 +1,20 @@
|
|||||||
ljkiwi - Free LuaJIT FFI kiwi (Cassowary derived) constraint solver.
|
ljkiwi - Free LuaJIT FFI and Lua C API kiwi (Cassowary derived) constraint solver.
|
||||||
|
|
||||||
[](https://github.com/jkl1337/ljkiwi/actions/workflows/busted.yml)
|
[](https://github.com/jkl1337/ljkiwi/actions/workflows/busted.yml)
|
||||||
[](https://coveralls.io/github/jkl1337/ljkiwi?branch=master)
|
[](https://coveralls.io/github/jkl1337/ljkiwi?branch=master)
|
||||||
[](https://luarocks.org/modules/jkl/ljkiwi)
|
[](https://luarocks.org/modules/jkl/kiwi)
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
Kiwi is a reasonably efficient C++ implementation of the Cassowary constraint solving algorithm. It is an implementation of the algorithm as described in the paper ["The Cassowary Linear Arithmetic Constraint Solving Algorithm"](http://www.cs.washington.edu/research/constraints/cassowary/techreports/cassowaryTR.pdf) by Greg J. Badros and Alan Borning. The Kiwi implementation is not based on the original C++ implementation, but is a ground-up reimplementation with performance 10x to 500x faster in typical use.
|
Kiwi is a reasonably efficient C++ implementation of the Cassowary constraint solving algorithm. It is an implementation of the algorithm as described in the paper ["The Cassowary Linear Arithmetic Constraint Solving Algorithm"](http://www.cs.washington.edu/research/constraints/cassowary/techreports/cassowaryTR.pdf) by Greg J. Badros and Alan Borning. The Kiwi implementation is not based on the original C++ implementation, but is a ground-up reimplementation with performance 10x to 500x faster in typical use.
|
||||||
Cassowary constraint solving is a technique that is particularly well suited to user interface layout. It is the algorithm Apple uses for iOS and OS X Auto Layout.
|
Cassowary constraint solving is a technique that is particularly well suited to user interface layout. It is the algorithm Apple uses for iOS and OS X Auto Layout.
|
||||||
|
|
||||||
There are a few Lua implementations or attempts. The SILE typesetting system has a pure Lua implementation of the original Cassowary code, which appears to be correct but is quite slow. There are two extant Lua ports of Kiwi, one that is based on a C rewrite of Kiwi. However testing of these was not encouraging with either segfaults or incorrect results.
|
There are a few Lua implementations or attempts. The SILE typesetting system has a pure Lua implementation of the original Cassowary code, which appears to be correct but is quite slow. There are two extant Lua ports of Kiwi, one that is based on a C rewrite of Kiwi. However testing of these was not encouraging and they appear mostly unmaintained.
|
||||||
Since the C++ Kiwi library is well tested and widely used it was simpler to provide a LuaJIT FFI wrapper and use that.
|
Since the C++ Kiwi library is well tested, it was simpler to provide a LuaJIT FFI wrapper. Now, there is also a Lua C API binding with support for 5.1 through 5.4, and since
|
||||||
This package has no dependencies other than a C++14 toolchain to compile the included Kiwi library and a small C wrapper.
|
in most common use cases all the heavy lifting is done in the library, there is usually
|
||||||
|
no practical perfomance difference between the two.
|
||||||
|
This package has no dependencies other than a supported C++14 compiler to compile the included Kiwi library and a small C wrapper.
|
||||||
|
|
||||||
The Lua API has a pure Lua expression builder. There is of course some overhead to this, however in most cases expression building is infrequent and the underlying structures can be reused.
|
|
||||||
|
|
||||||
The wrapper is quite close to the Kiwi C++/Python port with a few naming changes.
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
@@ -91,4 +90,4 @@ print(right_edge:value()) -- 500
|
|||||||
In addition to the expression builder there is a convenience constraints submodule with: `pair_ratio`, `pair`, and `single` to allow efficient construction of the most common simple expression types for GUI layout.
|
In addition to the expression builder there is a convenience constraints submodule with: `pair_ratio`, `pair`, and `single` to allow efficient construction of the most common simple expression types for GUI layout.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
WIP - However the API is fully annotated and will work with lua-language-server. Documentation can also be generated with lua-language-server.
|
The API is fully annotated and will work with lua-language-server. Documentation can also be generated with lua-language-server.
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
cl /nologo /O2 /W4 /wd4200 /c /D_CRT_SECURE_NO_DEPRECATE /I "C:\Program Files\luarocks\include" /EHs /I kiwi luakiwi\luakiwi.cpp
|
|
||||||
link /DLL /out:ckiwi.dll /def:luakiwi.def /LIBPATH:"C:\Program Files\luarocks\lib" luakiwi.obj lua54.lib
|
|
||||||
218
ckiwi/ckiwi.cpp
218
ckiwi/ckiwi.cpp
@@ -1,10 +1,12 @@
|
|||||||
#include "ljkiwi.hpp"
|
|
||||||
#include "ckiwi.h"
|
#include "ckiwi.h"
|
||||||
|
|
||||||
#include <kiwi/kiwi.h>
|
#include <kiwi/kiwi.h>
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
|
#include <cstdarg>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#if defined(__GNUC__) && !defined(LJKIWI_NO_BUILTIN)
|
#if defined(__GNUC__) && !defined(LJKIWI_NO_BUILTIN)
|
||||||
#define lk_likely(x) (__builtin_expect(((x) != 0), 1))
|
#define lk_likely(x) (__builtin_expect(((x) != 0), 1))
|
||||||
@@ -36,59 +38,69 @@ const KiwiErr* new_error(const KiwiErr* base, const std::exception& ex) {
|
|||||||
|
|
||||||
static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
|
static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
|
||||||
KiwiErrUnknown,
|
KiwiErrUnknown,
|
||||||
"An unhandled C++ exception occurred."};
|
"An unhandled C++ exception occurred."
|
||||||
|
};
|
||||||
|
|
||||||
static const constexpr KiwiErr kKiwiErrNullObjectArg0 {
|
static const constexpr KiwiErr kKiwiErrNullObjectArg0 {
|
||||||
KiwiErrNullObject,
|
KiwiErrNullObject,
|
||||||
"null object passed as argument #0 (self)"};
|
"null object passed as argument #0 (self)"
|
||||||
|
};
|
||||||
|
|
||||||
static const constexpr KiwiErr kKiwiErrNullObjectArg1 {
|
static const constexpr KiwiErr kKiwiErrNullObjectArg1 {
|
||||||
KiwiErrNullObject,
|
KiwiErrNullObject,
|
||||||
"null object passed as argument #1"};
|
"null object passed as argument #1"
|
||||||
|
};
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
inline const KiwiErr* wrap_err(F&& f) {
|
const KiwiErr* wrap_err(F&& f) {
|
||||||
try {
|
try {
|
||||||
f();
|
f();
|
||||||
} catch (const UnsatisfiableConstraint& ex) {
|
} catch (const UnsatisfiableConstraint& ex) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrUnsatisfiableConstraint,
|
KiwiErrUnsatisfiableConstraint,
|
||||||
"The constraint cannot be satisfied."};
|
"The constraint cannot be satisfied."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
} catch (const UnknownConstraint& ex) {
|
} catch (const UnknownConstraint& ex) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrUnknownConstraint,
|
KiwiErrUnknownConstraint,
|
||||||
"The constraint has not been added to the solver."};
|
"The constraint has not been added to the solver."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const DuplicateConstraint& ex) {
|
} catch (const DuplicateConstraint& ex) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrDuplicateConstraint,
|
KiwiErrDuplicateConstraint,
|
||||||
"The constraint has already been added to the solver."};
|
"The constraint has already been added to the solver."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const UnknownEditVariable& ex) {
|
} catch (const UnknownEditVariable& ex) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrUnknownEditVariable,
|
KiwiErrUnknownEditVariable,
|
||||||
"The edit variable has not been added to the solver."};
|
"The edit variable has not been added to the solver."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const DuplicateEditVariable& ex) {
|
} catch (const DuplicateEditVariable& ex) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrDuplicateEditVariable,
|
KiwiErrDuplicateEditVariable,
|
||||||
"The edit variable has already been added to the solver."};
|
"The edit variable has already been added to the solver."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const BadRequiredStrength& ex) {
|
} catch (const BadRequiredStrength& ex) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrBadRequiredStrength,
|
KiwiErrBadRequiredStrength,
|
||||||
"A required strength cannot be used in this context."};
|
"A required strength cannot be used in this context."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const InternalSolverError& ex) {
|
} catch (const InternalSolverError& ex) {
|
||||||
static const constexpr KiwiErr base {
|
static const constexpr KiwiErr base {
|
||||||
KiwiErrInternalSolverError,
|
KiwiErrInternalSolverError,
|
||||||
"An internal solver error occurred."};
|
"An internal solver error occurred."
|
||||||
|
};
|
||||||
return new_error(&base, ex);
|
return new_error(&base, ex);
|
||||||
} catch (std::bad_alloc&) {
|
} catch (std::bad_alloc&) {
|
||||||
static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."};
|
static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."};
|
||||||
@@ -102,7 +114,7 @@ inline const KiwiErr* wrap_err(F&& f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename P, typename R, typename F>
|
template<typename P, typename R, typename F>
|
||||||
inline const KiwiErr* wrap_err(P self, F&& f) {
|
const KiwiErr* wrap_err(P self, F&& f) {
|
||||||
if (lk_unlikely(!self)) {
|
if (lk_unlikely(!self)) {
|
||||||
return &kKiwiErrNullObjectArg0;
|
return &kKiwiErrNullObjectArg0;
|
||||||
}
|
}
|
||||||
@@ -110,36 +122,51 @@ inline const KiwiErr* wrap_err(P self, F&& f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename P, typename R, typename F>
|
template<typename P, typename R, typename F>
|
||||||
inline const KiwiErr* wrap_err(P self, R item, F&& f) {
|
const KiwiErr* wrap_err(P* self, R* item, F&& f) {
|
||||||
if (lk_unlikely(!self)) {
|
if (lk_unlikely(!self)) {
|
||||||
return &kKiwiErrNullObjectArg0;
|
return &kKiwiErrNullObjectArg0;
|
||||||
} else if (lk_unlikely(!item)) {
|
} else if (lk_unlikely(!item)) {
|
||||||
return &kKiwiErrNullObjectArg1;
|
return &kKiwiErrNullObjectArg1;
|
||||||
}
|
}
|
||||||
return wrap_err([&]() { f(self->solver, *item); });
|
return wrap_err([&]() { f(self->solver, item); });
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
T* make_unmanaged(Args... args) {
|
||||||
|
auto* o = new T(std::forward<Args>(args)...);
|
||||||
|
o->m_refcount = 1;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void release_unmanaged(T* p) {
|
||||||
|
if (lk_likely(p)) {
|
||||||
|
if (--p->m_refcount == 0)
|
||||||
|
delete p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T* retain_unmanaged(T* p) {
|
||||||
|
if (lk_likely(p))
|
||||||
|
p->m_refcount++;
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
KiwiTypeInfo kiwi_ti_KiwiVar = {sizeof(KiwiVar), alignof(KiwiVar)};
|
KiwiVar* kiwi_var_construct(const char* name) {
|
||||||
KiwiTypeInfo kiwi_ti_KiwiConstraint = {sizeof(KiwiConstraint), alignof(KiwiConstraint)};
|
return make_unmanaged<VariableData>(lk_likely(name) ? name : "");
|
||||||
|
|
||||||
void kiwi_var_construct(const char* name, void* mem) {
|
|
||||||
new (mem) KiwiVar {lk_likely(name) ? name : ""};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void kiwi_var_release(KiwiVar* var) {
|
void kiwi_var_release(KiwiVar* var) {
|
||||||
if (lk_likely(var))
|
release_unmanaged(var);
|
||||||
var->~KiwiVar();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void kiwi_var_retain(KiwiVar* var) {
|
void kiwi_var_retain(KiwiVar* var) {
|
||||||
if (lk_likely(var)) {
|
retain_unmanaged(var);
|
||||||
alignas(KiwiVar) unsigned char buf[sizeof(KiwiVar)];
|
|
||||||
new (buf) KiwiVar(*var);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* kiwi_var_name(const KiwiVar* var) {
|
const char* kiwi_var_name(const KiwiVar* var) {
|
||||||
@@ -147,7 +174,7 @@ const char* kiwi_var_name(const KiwiVar* var) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void kiwi_var_set_name(KiwiVar* var, const char* name) {
|
void kiwi_var_set_name(KiwiVar* var, const char* name) {
|
||||||
if (lk_likely(var))
|
if (lk_likely(var && name))
|
||||||
var->setName(name);
|
var->setName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,60 +187,60 @@ void kiwi_var_set_value(KiwiVar* var, double value) {
|
|||||||
var->setValue(value);
|
var->setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool kiwi_var_eq(const KiwiVar* var, const KiwiVar* other) {
|
|
||||||
return lk_likely(var && other) && var->equals(*other);
|
|
||||||
}
|
|
||||||
|
|
||||||
void kiwi_expression_retain(KiwiExpression* expr) {
|
void kiwi_expression_retain(KiwiExpression* expr) {
|
||||||
if (lk_unlikely(!expr))
|
if (lk_unlikely(!expr))
|
||||||
return;
|
return;
|
||||||
alignas(KiwiVar) unsigned char buf[sizeof(KiwiVar)];
|
|
||||||
for (auto* t = expr->terms_; t != expr->terms_ + expr->term_count; ++t) {
|
for (auto* t = expr->terms_; t != expr->terms_ + expr->term_count; ++t) {
|
||||||
new (buf) KiwiVar(*t->var);
|
retain_unmanaged(t->var);
|
||||||
}
|
}
|
||||||
|
expr->owner = expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kiwi_expression_destroy(KiwiExpression* expr) {
|
void kiwi_expression_destroy(KiwiExpression* expr) {
|
||||||
if (lk_unlikely(!expr))
|
if (lk_unlikely(!expr || !expr->owner))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (expr->owner) {
|
if (expr->owner == expr) {
|
||||||
expr->owner->~KiwiConstraint();
|
|
||||||
} else {
|
|
||||||
for (auto* t = expr->terms_; t != expr->terms_ + expr->term_count; ++t) {
|
for (auto* t = expr->terms_; t != expr->terms_ + expr->term_count; ++t) {
|
||||||
t->var->~KiwiVar();
|
release_unmanaged(t->var);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
release_unmanaged(static_cast<ConstraintData*>(expr->owner));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void kiwi_constraint_construct(
|
KiwiConstraint* kiwi_constraint_construct(
|
||||||
const KiwiExpression* lhs,
|
const KiwiExpression* lhs,
|
||||||
const KiwiExpression* rhs,
|
const KiwiExpression* rhs,
|
||||||
enum KiwiRelOp op,
|
enum KiwiRelOp op,
|
||||||
double strength,
|
double strength
|
||||||
void* mem
|
|
||||||
) {
|
) {
|
||||||
if (strength < 0.0) {
|
if (strength < 0.0) {
|
||||||
strength = kiwi::strength::required;
|
strength = kiwi::strength::required;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Term> terms;
|
std::vector<Term> terms;
|
||||||
terms.reserve((lhs ? lhs->term_count : 0) + (rhs ? rhs->term_count : 0));
|
terms.reserve(static_cast<decltype(terms)::size_type>(
|
||||||
|
(lhs && lhs->term_count > 0 ? lhs->term_count : 0)
|
||||||
|
+ (rhs && rhs->term_count > 0 ? rhs->term_count : 0)
|
||||||
|
));
|
||||||
|
|
||||||
if (lhs) {
|
if (lhs) {
|
||||||
for (auto* t = lhs->terms_; t != lhs->terms_ + lhs->term_count; ++t) {
|
for (int i = 0; i < lhs->term_count; ++i) {
|
||||||
if (t->var)
|
const auto& t = lhs->terms_[i];
|
||||||
terms.emplace_back(*t->var, t->coefficient);
|
if (t.var)
|
||||||
|
terms.emplace_back(Variable(t.var), t.coefficient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rhs) {
|
if (rhs) {
|
||||||
for (auto* t = rhs->terms_; t != rhs->terms_ + rhs->term_count; ++t) {
|
for (int i = 0; i < rhs->term_count; ++i) {
|
||||||
if (t->var)
|
const auto& t = rhs->terms_[i];
|
||||||
terms.emplace_back(*t->var, -t->coefficient);
|
if (t.var)
|
||||||
|
terms.emplace_back(Variable(t.var), -t.coefficient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new (mem) Constraint(
|
return make_unmanaged<ConstraintData>(
|
||||||
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
|
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
|
||||||
static_cast<RelationalOperator>(op),
|
static_cast<RelationalOperator>(op),
|
||||||
strength
|
strength
|
||||||
@@ -221,15 +248,11 @@ void kiwi_constraint_construct(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void kiwi_constraint_release(KiwiConstraint* c) {
|
void kiwi_constraint_release(KiwiConstraint* c) {
|
||||||
if (lk_likely(c))
|
release_unmanaged(c);
|
||||||
c->~KiwiConstraint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void kiwi_constraint_retain(KiwiConstraint* c) {
|
void kiwi_constraint_retain(KiwiConstraint* c) {
|
||||||
if (lk_likely(c)) {
|
retain_unmanaged(c);
|
||||||
alignas(KiwiConstraint) unsigned char buf[sizeof(KiwiConstraint)];
|
|
||||||
new (buf) KiwiConstraint(*c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double kiwi_constraint_strength(const KiwiConstraint* c) {
|
double kiwi_constraint_strength(const KiwiConstraint* c) {
|
||||||
@@ -250,20 +273,20 @@ int kiwi_constraint_expression(KiwiConstraint* c, KiwiExpression* out, int out_s
|
|||||||
|
|
||||||
const auto& expr = c->expression();
|
const auto& expr = c->expression();
|
||||||
const auto& terms = expr.terms();
|
const auto& terms = expr.terms();
|
||||||
const auto term_count = static_cast<int>(terms.size());
|
int n = terms.size() < INT_MAX ? static_cast<int>(terms.size()) : INT_MAX;
|
||||||
if (!out || out_size < term_count)
|
if (!out || out_size < n)
|
||||||
return term_count;
|
return n;
|
||||||
|
|
||||||
for (int i = 0; i < term_count; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
out->terms_[i].var = const_cast<KiwiVar*>(&terms[i].variable());
|
const auto& t = terms[static_cast<std::size_t>(i)];
|
||||||
out->terms_[i].coefficient = terms[i].coefficient();
|
out->terms_[i].var = const_cast<Variable&>(t.variable()).ptr();
|
||||||
|
out->terms_[i].coefficient = t.coefficient();
|
||||||
}
|
}
|
||||||
out->constant = expr.constant();
|
out->constant = expr.constant();
|
||||||
out->term_count = term_count;
|
out->term_count = n;
|
||||||
out->owner = c;
|
out->owner = retain_unmanaged(c);
|
||||||
kiwi_constraint_retain(c);
|
|
||||||
|
|
||||||
return term_count;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KiwiSolver {
|
struct KiwiSolver {
|
||||||
@@ -271,15 +294,13 @@ struct KiwiSolver {
|
|||||||
Solver solver;
|
Solver solver;
|
||||||
};
|
};
|
||||||
|
|
||||||
KiwiTypeInfo kiwi_ti_KiwiSolver = {sizeof(KiwiSolver), alignof(KiwiSolver)};
|
KiwiSolver* kiwi_solver_construct(unsigned error_mask) {
|
||||||
|
return new KiwiSolver {error_mask};
|
||||||
void kiwi_solver_construct(unsigned error_mask, void* mem) {
|
|
||||||
new (mem) KiwiSolver {error_mask};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void kiwi_solver_destroy(KiwiSolver* s) {
|
void kiwi_solver_destroy(KiwiSolver* s) {
|
||||||
if (lk_likely(s))
|
if (lk_likely(s))
|
||||||
s->~KiwiSolver();
|
delete s;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned kiwi_solver_get_error_mask(const KiwiSolver* s) {
|
unsigned kiwi_solver_get_error_mask(const KiwiSolver* s) {
|
||||||
@@ -291,38 +312,38 @@ void kiwi_solver_set_error_mask(KiwiSolver* s, unsigned mask) {
|
|||||||
s->error_mask = mask;
|
s->error_mask = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, const KiwiConstraint* constraint) {
|
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, KiwiConstraint* constraint) {
|
||||||
return wrap_err(s, constraint, [](auto& s, const auto& c) { s.addConstraint(c); });
|
return wrap_err(s, constraint, [](auto&& s, auto&& c) { s.addConstraint(Constraint(c)); });
|
||||||
}
|
}
|
||||||
|
|
||||||
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, const KiwiConstraint* constraint) {
|
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, KiwiConstraint* constraint) {
|
||||||
return wrap_err(s, constraint, [](auto& s, const auto& c) { s.removeConstraint(c); });
|
return wrap_err(s, constraint, [](auto&& s, auto&& c) { s.removeConstraint(Constraint(c)); });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool kiwi_solver_has_constraint(const KiwiSolver* s, const KiwiConstraint* constraint) {
|
bool kiwi_solver_has_constraint(const KiwiSolver* s, KiwiConstraint* constraint) {
|
||||||
if (lk_unlikely(!s || !constraint))
|
if (lk_unlikely(!s || !constraint))
|
||||||
return 0;
|
return 0;
|
||||||
return s->solver.hasConstraint(*constraint);
|
return s->solver.hasConstraint(Constraint(constraint));
|
||||||
}
|
}
|
||||||
|
|
||||||
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, const KiwiVar* var, double strength) {
|
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, KiwiVar* var, double strength) {
|
||||||
return wrap_err(s, var, [strength](auto& s, const auto& v) {
|
return wrap_err(s, var, [strength](auto&& s, auto&& v) {
|
||||||
s.addEditVariable(v, strength);
|
s.addEditVariable(Variable(v), strength);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, const KiwiVar* var) {
|
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, KiwiVar* var) {
|
||||||
return wrap_err(s, var, [](auto& s, const auto& v) { s.removeEditVariable(v); });
|
return wrap_err(s, var, [](auto&& s, auto&& v) { s.removeEditVariable(Variable(v)); });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool kiwi_solver_has_edit_var(const KiwiSolver* s, const KiwiVar* var) {
|
bool kiwi_solver_has_edit_var(const KiwiSolver* s, KiwiVar* var) {
|
||||||
if (lk_unlikely(!s || !var))
|
if (lk_unlikely(!s || !var))
|
||||||
return 0;
|
return 0;
|
||||||
return s->solver.hasEditVariable(*var);
|
return s->solver.hasEditVariable(Variable(var));
|
||||||
}
|
}
|
||||||
|
|
||||||
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, const KiwiVar* var, double value) {
|
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, KiwiVar* var, double value) {
|
||||||
return wrap_err(s, var, [value](auto& s, const auto& v) { s.suggestValue(v, value); });
|
return wrap_err(s, var, [value](auto&& s, auto&& v) { s.suggestValue(Variable(v), value); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void kiwi_solver_update_vars(KiwiSolver* s) {
|
void kiwi_solver_update_vars(KiwiSolver* s) {
|
||||||
@@ -353,4 +374,31 @@ char* kiwi_solver_dumps(const KiwiSolver* s) {
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kiwi_tuple_init(KiwiTuple* tuple, int count, ...) {
|
||||||
|
if (lk_unlikely(!tuple))
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, count);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
auto* var = va_arg(args, KiwiVar*);
|
||||||
|
retain_unmanaged(var);
|
||||||
|
tuple->values[i] = &var->m_value;
|
||||||
|
}
|
||||||
|
tuple->count = count;
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kiwi_tuple_destroy(KiwiTuple* tuple) {
|
||||||
|
if (lk_unlikely(!tuple))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < tuple->count; ++i) {
|
||||||
|
auto* value = const_cast<double*>(tuple->values[i]);
|
||||||
|
release_unmanaged(
|
||||||
|
reinterpret_cast<KiwiVar*>(reinterpret_cast<char*>(value) - offsetof(KiwiVar, m_value))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|||||||
112
ckiwi/ckiwi.h
112
ckiwi/ckiwi.h
@@ -10,30 +10,27 @@
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
namespace kiwi {
|
namespace kiwi {
|
||||||
class Variable;
|
class VariableData;
|
||||||
class Constraint;
|
class ConstraintData;
|
||||||
} // namespace kiwi
|
} // namespace kiwi
|
||||||
|
|
||||||
|
typedef kiwi::VariableData KiwiVar;
|
||||||
|
typedef kiwi::ConstraintData KiwiConstraint;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
typedef kiwi::Variable KiwiVar;
|
|
||||||
typedef kiwi::Constraint KiwiConstraint;
|
|
||||||
#else
|
#else
|
||||||
typedef struct KiwiVar KiwiVar;
|
typedef struct KiwiVar KiwiVar;
|
||||||
typedef struct KiwiConstraint KiwiConstraint;
|
typedef struct KiwiConstraint KiwiConstraint;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct KiwiTypeInfo {
|
#if defined __GNUC__ && (!defined _WIN32 || defined __CYGWIN__)
|
||||||
unsigned size;
|
#define LJKIWI_EXP __attribute__((visibility("default")))
|
||||||
unsigned align;
|
#elif defined _WIN32
|
||||||
} KiwiTypeInfo;
|
#define LJKIWI_EXP __declspec(dllexport)
|
||||||
|
|
||||||
#if __GNUC__
|
|
||||||
#pragma GCC visibility push(default)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern KiwiTypeInfo kiwi_ti_KiwiVar, kiwi_ti_KiwiConstraint, kiwi_ti_KiwiSolver;
|
|
||||||
|
|
||||||
// LuaJIT start
|
// LuaJIT start
|
||||||
enum KiwiErrKind {
|
enum KiwiErrKind {
|
||||||
KiwiErrNone,
|
KiwiErrNone,
|
||||||
@@ -59,12 +56,12 @@ typedef struct KiwiTerm {
|
|||||||
typedef struct KiwiExpression {
|
typedef struct KiwiExpression {
|
||||||
double constant;
|
double constant;
|
||||||
int term_count;
|
int term_count;
|
||||||
KiwiConstraint* owner;
|
void* owner;
|
||||||
|
|
||||||
#if defined(LJKIWI_LUAJIT_DEF)
|
#if defined(LJKIWI_LUAJIT_DEF)
|
||||||
KiwiTerm terms_[?];
|
KiwiTerm terms_[?];
|
||||||
#elif defined(LJKIWI_USE_FAM_1)
|
#elif defined(LJKIWI_USE_FAM_1)
|
||||||
KiwiTerm terms_[1]; // LuaJIT: struct KiwiTerm terms_[?];
|
KiwiTerm terms_[1];
|
||||||
#else
|
#else
|
||||||
KiwiTerm terms_[];
|
KiwiTerm terms_[];
|
||||||
#endif
|
#endif
|
||||||
@@ -77,58 +74,67 @@ typedef struct KiwiErr {
|
|||||||
bool must_free;
|
bool must_free;
|
||||||
} KiwiErr;
|
} KiwiErr;
|
||||||
|
|
||||||
|
typedef struct KiwiTuple {
|
||||||
|
int count;
|
||||||
|
#if defined(LJKIWI_LUAJIT_DEF)
|
||||||
|
const double* values[?];
|
||||||
|
#elif defined(LJKIWI_USE_FAM_1)
|
||||||
|
const double* values[1];
|
||||||
|
#else
|
||||||
|
const double* values[];
|
||||||
|
#endif
|
||||||
|
} KiwiTuple;
|
||||||
|
|
||||||
struct KiwiSolver;
|
struct KiwiSolver;
|
||||||
|
|
||||||
void kiwi_var_construct(const char* name, void* mem);
|
LJKIWI_EXP void kiwi_tuple_init(KiwiTuple* tuple, int count, ...);
|
||||||
void kiwi_var_release(KiwiVar* var);
|
LJKIWI_EXP void kiwi_tuple_destroy(KiwiTuple* tuple);
|
||||||
void kiwi_var_retain(KiwiVar* var);
|
|
||||||
|
|
||||||
const char* kiwi_var_name(const KiwiVar* var);
|
LJKIWI_EXP KiwiVar* kiwi_var_construct(const char* name);
|
||||||
void kiwi_var_set_name(KiwiVar* var, const char* name);
|
LJKIWI_EXP void kiwi_var_release(KiwiVar* var);
|
||||||
double kiwi_var_value(const KiwiVar* var);
|
LJKIWI_EXP void kiwi_var_retain(KiwiVar* var);
|
||||||
void kiwi_var_set_value(KiwiVar* var, double value);
|
|
||||||
bool kiwi_var_eq(const KiwiVar* var, const KiwiVar* other);
|
|
||||||
|
|
||||||
void kiwi_expression_retain(KiwiExpression* expr);
|
LJKIWI_EXP const char* kiwi_var_name(const KiwiVar* var);
|
||||||
void kiwi_expression_destroy(KiwiExpression* expr);
|
LJKIWI_EXP void kiwi_var_set_name(KiwiVar* var, const char* name);
|
||||||
|
LJKIWI_EXP double kiwi_var_value(const KiwiVar* var);
|
||||||
|
LJKIWI_EXP void kiwi_var_set_value(KiwiVar* var, double value);
|
||||||
|
|
||||||
void kiwi_constraint_construct(
|
LJKIWI_EXP void kiwi_expression_retain(KiwiExpression* expr);
|
||||||
|
LJKIWI_EXP void kiwi_expression_destroy(KiwiExpression* expr);
|
||||||
|
|
||||||
|
LJKIWI_EXP KiwiConstraint* kiwi_constraint_construct(
|
||||||
const KiwiExpression* lhs,
|
const KiwiExpression* lhs,
|
||||||
const KiwiExpression* rhs,
|
const KiwiExpression* rhs,
|
||||||
enum KiwiRelOp op,
|
enum KiwiRelOp op,
|
||||||
double strength,
|
double strength
|
||||||
void* mem
|
|
||||||
);
|
);
|
||||||
void kiwi_constraint_release(KiwiConstraint* c);
|
LJKIWI_EXP void kiwi_constraint_release(KiwiConstraint* c);
|
||||||
void kiwi_constraint_retain(KiwiConstraint* c);
|
LJKIWI_EXP void kiwi_constraint_retain(KiwiConstraint* c);
|
||||||
|
|
||||||
double kiwi_constraint_strength(const KiwiConstraint* c);
|
LJKIWI_EXP double kiwi_constraint_strength(const KiwiConstraint* c);
|
||||||
enum KiwiRelOp kiwi_constraint_op(const KiwiConstraint* c);
|
LJKIWI_EXP enum KiwiRelOp kiwi_constraint_op(const KiwiConstraint* c);
|
||||||
bool kiwi_constraint_violated(const KiwiConstraint* c);
|
LJKIWI_EXP bool kiwi_constraint_violated(const KiwiConstraint* c);
|
||||||
int kiwi_constraint_expression(KiwiConstraint* c, KiwiExpression* out, int out_size);
|
LJKIWI_EXP int kiwi_constraint_expression(KiwiConstraint* c, KiwiExpression* out, int out_size);
|
||||||
|
|
||||||
void kiwi_solver_construct(unsigned error_mask, void* mem);
|
LJKIWI_EXP KiwiSolver* kiwi_solver_construct(unsigned error_mask);
|
||||||
void kiwi_solver_destroy(KiwiSolver* s);
|
LJKIWI_EXP void kiwi_solver_destroy(KiwiSolver* s);
|
||||||
unsigned kiwi_solver_get_error_mask(const KiwiSolver* s);
|
LJKIWI_EXP unsigned kiwi_solver_get_error_mask(const KiwiSolver* s);
|
||||||
void kiwi_solver_set_error_mask(KiwiSolver* s, unsigned mask);
|
LJKIWI_EXP void kiwi_solver_set_error_mask(KiwiSolver* s, unsigned mask);
|
||||||
|
|
||||||
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, const KiwiConstraint* constraint);
|
LJKIWI_EXP const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, KiwiConstraint* constraint);
|
||||||
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, const KiwiConstraint* constraint);
|
LJKIWI_EXP const KiwiErr*
|
||||||
bool kiwi_solver_has_constraint(const KiwiSolver* s, const KiwiConstraint* constraint);
|
kiwi_solver_remove_constraint(KiwiSolver* s, KiwiConstraint* constraint);
|
||||||
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, const KiwiVar* var, double strength);
|
LJKIWI_EXP bool kiwi_solver_has_constraint(const KiwiSolver* s, KiwiConstraint* constraint);
|
||||||
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, const KiwiVar* var);
|
LJKIWI_EXP const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, KiwiVar* var, double strength);
|
||||||
bool kiwi_solver_has_edit_var(const KiwiSolver* s, const KiwiVar* var);
|
LJKIWI_EXP const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, KiwiVar* var);
|
||||||
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, const KiwiVar* var, double value);
|
LJKIWI_EXP bool kiwi_solver_has_edit_var(const KiwiSolver* s, KiwiVar* var);
|
||||||
void kiwi_solver_update_vars(KiwiSolver* sp);
|
LJKIWI_EXP const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, KiwiVar* var, double value);
|
||||||
void kiwi_solver_reset(KiwiSolver* sp);
|
LJKIWI_EXP void kiwi_solver_update_vars(KiwiSolver* sp);
|
||||||
void kiwi_solver_dump(const KiwiSolver* sp);
|
LJKIWI_EXP void kiwi_solver_reset(KiwiSolver* sp);
|
||||||
char* kiwi_solver_dumps(const KiwiSolver* sp);
|
LJKIWI_EXP void kiwi_solver_dump(const KiwiSolver* sp);
|
||||||
|
LJKIWI_EXP char* kiwi_solver_dumps(const KiwiSolver* sp);
|
||||||
// LuaJIT end
|
// LuaJIT end
|
||||||
|
|
||||||
#if __GNUC__
|
|
||||||
#pragma GCC visibility pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
150
kiwi.lua
150
kiwi.lua
@@ -24,29 +24,9 @@ kiwi.ljkiwi = ljkiwi
|
|||||||
ffi.cdef([[
|
ffi.cdef([[
|
||||||
void free(void *);
|
void free(void *);
|
||||||
|
|
||||||
typedef struct KiwiTypeInfo {
|
typedef struct KiwiVar KiwiVar;
|
||||||
unsigned size;
|
typedef struct KiwiConstraint KiwiConstraint;
|
||||||
unsigned align;
|
typedef struct KiwiSolver KiwiSolver;]])
|
||||||
} KiwiTypeInfo;
|
|
||||||
|
|
||||||
extern KiwiTypeInfo kiwi_ti_KiwiVar, kiwi_ti_KiwiConstraint, kiwi_ti_KiwiSolver;
|
|
||||||
|
|
||||||
]])
|
|
||||||
|
|
||||||
for _, t in ipairs({ "KiwiVar", "KiwiConstraint", "KiwiSolver" }) do
|
|
||||||
local tinfo = ljkiwi[("kiwi_ti_%s"):format(t)] --[[@as any]]
|
|
||||||
ffi.cdef(
|
|
||||||
[[
|
|
||||||
typedef struct $ {
|
|
||||||
unsigned char b_[$];
|
|
||||||
} __attribute__((aligned($))) $;
|
|
||||||
]],
|
|
||||||
t,
|
|
||||||
tinfo.size,
|
|
||||||
tinfo.align,
|
|
||||||
t
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
ffi.cdef([[
|
ffi.cdef([[
|
||||||
enum KiwiErrKind {
|
enum KiwiErrKind {
|
||||||
@@ -73,7 +53,8 @@ typedef struct KiwiTerm {
|
|||||||
typedef struct KiwiExpression {
|
typedef struct KiwiExpression {
|
||||||
double constant;
|
double constant;
|
||||||
int term_count;
|
int term_count;
|
||||||
KiwiConstraint* owner;
|
void* owner;
|
||||||
|
|
||||||
KiwiTerm terms_[?];
|
KiwiTerm terms_[?];
|
||||||
} KiwiExpression;
|
} KiwiExpression;
|
||||||
|
|
||||||
@@ -83,9 +64,17 @@ typedef struct KiwiErr {
|
|||||||
bool must_free;
|
bool must_free;
|
||||||
} KiwiErr;
|
} KiwiErr;
|
||||||
|
|
||||||
|
typedef struct KiwiTuple {
|
||||||
|
int count;
|
||||||
|
const double* values[?];
|
||||||
|
} KiwiTuple;
|
||||||
|
|
||||||
struct KiwiSolver;
|
struct KiwiSolver;
|
||||||
|
|
||||||
void kiwi_var_construct(const char* name, void* mem);
|
void kiwi_tuple_init(KiwiTuple* tuple, int count, ...);
|
||||||
|
void kiwi_tuple_destroy(KiwiTuple* tuple);
|
||||||
|
|
||||||
|
KiwiVar* kiwi_var_construct(const char* name);
|
||||||
void kiwi_var_release(KiwiVar* var);
|
void kiwi_var_release(KiwiVar* var);
|
||||||
void kiwi_var_retain(KiwiVar* var);
|
void kiwi_var_retain(KiwiVar* var);
|
||||||
|
|
||||||
@@ -93,17 +82,15 @@ const char* kiwi_var_name(const KiwiVar* var);
|
|||||||
void kiwi_var_set_name(KiwiVar* var, const char* name);
|
void kiwi_var_set_name(KiwiVar* var, const char* name);
|
||||||
double kiwi_var_value(const KiwiVar* var);
|
double kiwi_var_value(const KiwiVar* var);
|
||||||
void kiwi_var_set_value(KiwiVar* var, double value);
|
void kiwi_var_set_value(KiwiVar* var, double value);
|
||||||
bool kiwi_var_eq(const KiwiVar* var, const KiwiVar* other);
|
|
||||||
|
|
||||||
void kiwi_expression_retain(KiwiExpression* expr);
|
void kiwi_expression_retain(KiwiExpression* expr);
|
||||||
void kiwi_expression_destroy(KiwiExpression* expr);
|
void kiwi_expression_destroy(KiwiExpression* expr);
|
||||||
|
|
||||||
void kiwi_constraint_construct(
|
KiwiConstraint* kiwi_constraint_construct(
|
||||||
const KiwiExpression* lhs,
|
const KiwiExpression* lhs,
|
||||||
const KiwiExpression* rhs,
|
const KiwiExpression* rhs,
|
||||||
enum KiwiRelOp op,
|
enum KiwiRelOp op,
|
||||||
double strength,
|
double strength
|
||||||
void* mem
|
|
||||||
);
|
);
|
||||||
void kiwi_constraint_release(KiwiConstraint* c);
|
void kiwi_constraint_release(KiwiConstraint* c);
|
||||||
void kiwi_constraint_retain(KiwiConstraint* c);
|
void kiwi_constraint_retain(KiwiConstraint* c);
|
||||||
@@ -113,18 +100,18 @@ enum KiwiRelOp kiwi_constraint_op(const KiwiConstraint* c);
|
|||||||
bool kiwi_constraint_violated(const KiwiConstraint* c);
|
bool kiwi_constraint_violated(const KiwiConstraint* c);
|
||||||
int kiwi_constraint_expression(KiwiConstraint* c, KiwiExpression* out, int out_size);
|
int kiwi_constraint_expression(KiwiConstraint* c, KiwiExpression* out, int out_size);
|
||||||
|
|
||||||
void kiwi_solver_construct(unsigned error_mask, void* mem);
|
KiwiSolver* kiwi_solver_construct(unsigned error_mask);
|
||||||
void kiwi_solver_destroy(KiwiSolver* s);
|
void kiwi_solver_destroy(KiwiSolver* s);
|
||||||
unsigned kiwi_solver_get_error_mask(const KiwiSolver* s);
|
unsigned kiwi_solver_get_error_mask(const KiwiSolver* s);
|
||||||
void kiwi_solver_set_error_mask(KiwiSolver* s, unsigned mask);
|
void kiwi_solver_set_error_mask(KiwiSolver* s, unsigned mask);
|
||||||
|
|
||||||
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, const KiwiConstraint* constraint);
|
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, KiwiConstraint* constraint);
|
||||||
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, const KiwiConstraint* constraint);
|
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, KiwiConstraint* constraint);
|
||||||
bool kiwi_solver_has_constraint(const KiwiSolver* s, const KiwiConstraint* constraint);
|
bool kiwi_solver_has_constraint(const KiwiSolver* s, KiwiConstraint* constraint);
|
||||||
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, const KiwiVar* var, double strength);
|
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, KiwiVar* var, double strength);
|
||||||
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, const KiwiVar* var);
|
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, KiwiVar* var);
|
||||||
bool kiwi_solver_has_edit_var(const KiwiSolver* s, const KiwiVar* var);
|
bool kiwi_solver_has_edit_var(const KiwiSolver* s, KiwiVar* var);
|
||||||
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, const KiwiVar* var, double value);
|
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, KiwiVar* var, double value);
|
||||||
void kiwi_solver_update_vars(KiwiSolver* sp);
|
void kiwi_solver_update_vars(KiwiSolver* sp);
|
||||||
void kiwi_solver_reset(KiwiSolver* sp);
|
void kiwi_solver_reset(KiwiSolver* sp);
|
||||||
void kiwi_solver_dump(const KiwiSolver* sp);
|
void kiwi_solver_dump(const KiwiSolver* sp);
|
||||||
@@ -196,38 +183,49 @@ function kiwi.is_var(o)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local Term = ffi.typeof("struct KiwiTerm") --[[@as kiwi.Term]]
|
local Term = ffi.typeof("struct KiwiTerm") --[[@as kiwi.Term]]
|
||||||
|
local SIZEOF_TERM = assert(ffi.sizeof(Term))
|
||||||
kiwi.Term = Term
|
kiwi.Term = Term
|
||||||
|
|
||||||
function kiwi.is_term(o)
|
function kiwi.is_term(o)
|
||||||
return ffi_istype(Term, o)
|
return ffi_istype(Term, o)
|
||||||
end
|
end
|
||||||
|
|
||||||
local Expression = ffi.typeof("KiwiExpression") --[[@as kiwi.Expression]]
|
local Expression = ffi.typeof("struct KiwiExpression") --[[@as kiwi.Expression]]
|
||||||
kiwi.Expression = Expression
|
kiwi.Expression = Expression
|
||||||
|
|
||||||
function kiwi.is_expression(o)
|
function kiwi.is_expression(o)
|
||||||
return ffi_istype(Expression, o)
|
return ffi_istype(Expression, o)
|
||||||
end
|
end
|
||||||
|
|
||||||
local Constraint = ffi.typeof("KiwiConstraint") --[[@as kiwi.Constraint]]
|
local Constraint = ffi.typeof("struct KiwiConstraint") --[[@as kiwi.Constraint]]
|
||||||
kiwi.Constraint = Constraint
|
kiwi.Constraint = Constraint
|
||||||
|
|
||||||
function kiwi.is_constraint(o)
|
function kiwi.is_constraint(o)
|
||||||
return ffi_istype(Constraint, o)
|
return ffi_istype(Constraint, o)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local Tuple = ffi.typeof("struct KiwiTuple") --[[@as kiwi.Tuple]]
|
||||||
|
kiwi.Tuple = Tuple
|
||||||
|
|
||||||
|
---@class kiwi.Tuple: ffi.cdata*
|
||||||
|
---@field values ffi.cdata*
|
||||||
|
---@field count integer
|
||||||
|
---@overload fun(vars: kiwi.Var[]): kiwi.Tuple
|
||||||
|
ffi.metatype(Tuple, {
|
||||||
|
__new = function(self, vars)
|
||||||
|
local t = ffi_new(self, #vars)
|
||||||
|
ljkiwi.kiwi_tuple_init(t, #vars, unpack(vars))
|
||||||
|
return ffi_gc(t, ljkiwi.kiwi_tuple_destroy)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
---@param expr kiwi.Expression
|
---@param expr kiwi.Expression
|
||||||
---@param var kiwi.Var
|
---@param var kiwi.Var
|
||||||
---@param coeff number?
|
---@param coeff number?
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
local function add_expr_term(expr, var, coeff)
|
local function add_expr_term(expr, var, coeff)
|
||||||
local ret = ffi_gc(ffi_new(Expression, expr.term_count + 1), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
|
local ret = ffi_gc(ffi_new(Expression, expr.term_count + 1), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
|
||||||
for i = 0, expr.term_count - 1 do
|
ffi_copy(ret.terms_, expr.terms_, SIZEOF_TERM * expr.term_count)
|
||||||
local st = expr.terms_[i] --[[@as kiwi.Term]]
|
|
||||||
local dt = ret.terms_[i] --[[@as kiwi.Term]]
|
|
||||||
dt.var = st.var
|
|
||||||
dt.coefficient = st.coefficient
|
|
||||||
end
|
|
||||||
local dt = ret.terms_[expr.term_count]
|
local dt = ret.terms_[expr.term_count]
|
||||||
dt.var = var
|
dt.var = var
|
||||||
dt.coefficient = coeff or 1.0
|
dt.coefficient = coeff or 1.0
|
||||||
@@ -248,7 +246,7 @@ local function new_expr_one(constant, var, coeff)
|
|||||||
dt.coefficient = coeff or 1.0
|
dt.coefficient = coeff or 1.0
|
||||||
ret.constant = constant
|
ret.constant = constant
|
||||||
ret.term_count = 1
|
ret.term_count = 1
|
||||||
ljkiwi.kiwi_var_retain(var)
|
ljkiwi.kiwi_expression_retain(ret)
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -303,8 +301,6 @@ local OP_NAMES = {
|
|||||||
EQ = "==",
|
EQ = "==",
|
||||||
}
|
}
|
||||||
|
|
||||||
local SIZEOF_TERM = ffi.sizeof(Term) --[[@as integer]]
|
|
||||||
|
|
||||||
local tmpexpr = ffi_new(Expression, 2) --[[@as kiwi.Expression]]
|
local tmpexpr = ffi_new(Expression, 2) --[[@as kiwi.Expression]]
|
||||||
local tmpexpr_r = ffi_new(Expression, 1) --[[@as kiwi.Expression]]
|
local tmpexpr_r = ffi_new(Expression, 1) --[[@as kiwi.Expression]]
|
||||||
|
|
||||||
@@ -343,9 +339,10 @@ local function rel(lhs, rhs, op, strength)
|
|||||||
op_error(lhs, rhs, OP_NAMES[op])
|
op_error(lhs, rhs, OP_NAMES[op])
|
||||||
end
|
end
|
||||||
|
|
||||||
local c = ffi_new(Constraint)
|
return ffi_gc(
|
||||||
ljkiwi.kiwi_constraint_construct(el, er, op, strength or REQUIRED, c)
|
ljkiwi.kiwi_constraint_construct(el, er, op, strength or REQUIRED),
|
||||||
return ffi_gc(c, ljkiwi.kiwi_constraint_release) --[[@as kiwi.Constraint]]
|
ljkiwi.kiwi_constraint_release
|
||||||
|
) --[[@as kiwi.Constraint]]
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Define a constraint with expressions as `a <= b`.
|
--- Define a constraint with expressions as `a <= b`.
|
||||||
@@ -431,9 +428,7 @@ do
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Var_mt:__new(name)
|
function Var_mt:__new(name)
|
||||||
local v = ffi_new(self)
|
return ffi_gc(ljkiwi.kiwi_var_construct(name), ljkiwi.kiwi_var_release)
|
||||||
ljkiwi.kiwi_var_construct(name, v)
|
|
||||||
return ffi_gc(v, ljkiwi.kiwi_var_release)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Var_mt.__mul(a, b)
|
function Var_mt.__mul(a, b)
|
||||||
@@ -477,10 +472,6 @@ do
|
|||||||
return a + -b
|
return a + -b
|
||||||
end
|
end
|
||||||
|
|
||||||
function Var_mt:__eq(other)
|
|
||||||
return ljkiwi.kiwi_var_eq(self, other)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Var_mt:__tostring()
|
function Var_mt:__tostring()
|
||||||
return self:name() .. "(" .. self:value() .. ")"
|
return self:name() .. "(" .. self:value() .. ")"
|
||||||
end
|
end
|
||||||
@@ -526,8 +517,10 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Term_mt.__new(T, var, coefficient)
|
function Term_mt.__new(T, var, coefficient)
|
||||||
local t = ffi_gc(ffi_new(T, var, coefficient or 1.0), term_gc)
|
local t = ffi_gc(ffi_new(T), term_gc) --[[@as kiwi.Term]]
|
||||||
ljkiwi.kiwi_var_retain(var)
|
ljkiwi.kiwi_var_retain(var)
|
||||||
|
t.var = var
|
||||||
|
t.coefficient = coefficient or 1.0
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -646,13 +639,7 @@ do
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
local function new_expr_constant(expr, constant)
|
local function new_expr_constant(expr, constant)
|
||||||
local ret = ffi_gc(ffi_new(Expression, expr.term_count), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
|
local ret = ffi_gc(ffi_new(Expression, expr.term_count), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
|
||||||
|
ffi_copy(ret.terms_, expr.terms_, SIZEOF_TERM * expr.term_count)
|
||||||
for i = 0, expr.term_count - 1 do
|
|
||||||
local dt = ret.terms_[i] --[[@as kiwi.Term]]
|
|
||||||
local st = expr.terms_[i] --[[@as kiwi.Term]]
|
|
||||||
dt.var = st.var
|
|
||||||
dt.coefficient = st.coefficient
|
|
||||||
end
|
|
||||||
ret.constant = constant
|
ret.constant = constant
|
||||||
ret.term_count = expr.term_count
|
ret.term_count = expr.term_count
|
||||||
ljkiwi.kiwi_expression_retain(ret)
|
ljkiwi.kiwi_expression_retain(ret)
|
||||||
@@ -820,9 +807,10 @@ do
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Constraint_mt:__new(lhs, rhs, op, strength)
|
function Constraint_mt:__new(lhs, rhs, op, strength)
|
||||||
local c = ffi_new(self)
|
return ffi_gc(
|
||||||
ljkiwi.kiwi_constraint_construct(lhs, rhs, op or "EQ", strength or REQUIRED, c)
|
ljkiwi.kiwi_constraint_construct(lhs, rhs, op or "EQ", strength or REQUIRED),
|
||||||
return ffi_gc(c, ljkiwi.kiwi_constraint_release)
|
ljkiwi.kiwi_constraint_release
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
local OPS = { [0] = "<=", ">=", "==" }
|
local OPS = { [0] = "<=", ">=", "==" }
|
||||||
@@ -868,9 +856,10 @@ do
|
|||||||
tmpexpr.constant = constant ~= nil and constant or 0
|
tmpexpr.constant = constant ~= nil and constant or 0
|
||||||
tmpexpr.term_count = 2
|
tmpexpr.term_count = 2
|
||||||
|
|
||||||
local c = ffi_new(Constraint)
|
return ffi_gc(
|
||||||
ljkiwi.kiwi_constraint_construct(tmpexpr, nil, op or "EQ", strength or REQUIRED, c)
|
ljkiwi.kiwi_constraint_construct(tmpexpr, nil, op or "EQ", strength or REQUIRED),
|
||||||
return ffi_gc(c, ljkiwi.kiwi_constraint_release) --[[@as kiwi.Constraint]]
|
ljkiwi.kiwi_constraint_release
|
||||||
|
) --[[@as kiwi.Constraint]]
|
||||||
end
|
end
|
||||||
|
|
||||||
local pair_ratio = constraints.pair_ratio
|
local pair_ratio = constraints.pair_ratio
|
||||||
@@ -904,9 +893,10 @@ do
|
|||||||
t.var = var
|
t.var = var
|
||||||
t.coefficient = 1.0
|
t.coefficient = 1.0
|
||||||
|
|
||||||
local c = ffi_new(Constraint)
|
return ffi_gc(
|
||||||
ljkiwi.kiwi_constraint_construct(tmpexpr, nil, op or "EQ", strength or REQUIRED, c)
|
ljkiwi.kiwi_constraint_construct(tmpexpr, nil, op or "EQ", strength or REQUIRED),
|
||||||
return ffi_gc(c, ljkiwi.kiwi_constraint_release) --[[@as kiwi.Constraint]]
|
ljkiwi.kiwi_constraint_release
|
||||||
|
) --[[@as kiwi.Constraint]]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1182,20 +1172,16 @@ do
|
|||||||
__index = Solver_cls,
|
__index = Solver_cls,
|
||||||
}
|
}
|
||||||
|
|
||||||
local Solver = ffi.typeof("struct KiwiSolver") --[[@as kiwi.Solver]]
|
|
||||||
kiwi.Solver = Solver
|
|
||||||
|
|
||||||
function Solver_mt:__new(error_mask)
|
function Solver_mt:__new(error_mask)
|
||||||
if type(error_mask) == "table" then
|
if type(error_mask) == "table" then
|
||||||
error_mask = kiwi.error_mask(error_mask)
|
error_mask = kiwi.error_mask(error_mask)
|
||||||
end
|
end
|
||||||
|
|
||||||
local s = ffi_new(Solver)
|
return ffi_gc(ljkiwi.kiwi_solver_construct(error_mask or 0), ljkiwi.kiwi_solver_destroy) --[[@as kiwi.Constraint]]
|
||||||
ljkiwi.kiwi_solver_construct(error_mask or 0, s)
|
|
||||||
return ffi_gc(s, ljkiwi.kiwi_solver_destroy) --[[@as kiwi.Constraint]]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
kiwi.Solver = ffi.metatype(Solver, Solver_mt) --[[@as kiwi.Solver]]
|
local Solver = ffi.metatype(ffi.typeof("struct KiwiSolver"), Solver_mt) --[[@as kiwi.Solver]]
|
||||||
|
kiwi.Solver = Solver
|
||||||
|
|
||||||
function kiwi.is_solver(s)
|
function kiwi.is_solver(s)
|
||||||
return ffi_istype(Solver, s)
|
return ffi_istype(Solver, s)
|
||||||
|
|||||||
@@ -26,17 +26,70 @@ enum RelationalOperator
|
|||||||
OP_EQ
|
OP_EQ
|
||||||
};
|
};
|
||||||
|
|
||||||
class Constraint
|
class Constraint;
|
||||||
|
class ConstraintData : public SharedData
|
||||||
{
|
{
|
||||||
|
static Expression reduce(const Expression &expr)
|
||||||
|
{
|
||||||
|
std::map<Variable, double> vars;
|
||||||
|
for (const auto & term : expr.terms())
|
||||||
|
vars[term.variable()] += term.coefficient();
|
||||||
|
|
||||||
|
std::vector<Term> terms(vars.begin(), vars.end());
|
||||||
|
return Expression(std::move(terms), expr.constant());
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
ConstraintData(const Expression &expr,
|
||||||
|
RelationalOperator op,
|
||||||
|
double strength) : SharedData(),
|
||||||
|
m_expression(reduce(expr)),
|
||||||
|
m_strength(strength::clip(strength)),
|
||||||
|
m_op(op) {}
|
||||||
|
|
||||||
|
ConstraintData(const ConstraintData &other, double strength) : SharedData(),
|
||||||
|
m_expression(other.m_expression),
|
||||||
|
m_strength(strength::clip(strength)),
|
||||||
|
m_op(other.m_op) {}
|
||||||
|
|
||||||
|
~ConstraintData() = default;
|
||||||
|
|
||||||
|
const Expression &expression() const { return m_expression; }
|
||||||
|
RelationalOperator op() const { return m_op; }
|
||||||
|
double strength() const { return m_strength; }
|
||||||
|
|
||||||
|
bool violated() const
|
||||||
|
{
|
||||||
|
switch (m_op)
|
||||||
|
{
|
||||||
|
case OP_EQ: return !impl::nearZero(m_expression.value());
|
||||||
|
case OP_GE: return m_expression.value() < 0.0;
|
||||||
|
case OP_LE: return m_expression.value() > 0.0;
|
||||||
|
}
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Expression m_expression;
|
||||||
|
double m_strength;
|
||||||
|
RelationalOperator m_op;
|
||||||
|
|
||||||
|
ConstraintData(const ConstraintData &other) = delete;
|
||||||
|
ConstraintData &operator=(const ConstraintData &other) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Constraint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Constraint(ConstraintData *p) : m_data(p) {}
|
||||||
|
|
||||||
Constraint() = default;
|
Constraint() = default;
|
||||||
|
|
||||||
Constraint(const Expression &expr,
|
Constraint(const Expression &expr,
|
||||||
RelationalOperator op,
|
RelationalOperator op,
|
||||||
double strength = strength::required) : m_data(new ConstraintData(expr, op, strength)) {}
|
double strength = strength::required) : m_data(new ConstraintData(expr, op, strength)) {}
|
||||||
|
|
||||||
Constraint(const Constraint &other, double strength) : m_data(new ConstraintData(other, strength)) {}
|
Constraint(const Constraint &other, double strength) : m_data(new ConstraintData(*other.m_data, strength)) {}
|
||||||
|
|
||||||
Constraint(const Constraint &) = default;
|
Constraint(const Constraint &) = default;
|
||||||
|
|
||||||
@@ -44,32 +97,10 @@ public:
|
|||||||
|
|
||||||
~Constraint() = default;
|
~Constraint() = default;
|
||||||
|
|
||||||
const Expression &expression() const
|
const Expression &expression() const { return m_data->expression(); }
|
||||||
{
|
RelationalOperator op() const { return m_data->op(); }
|
||||||
return m_data->m_expression;
|
double strength() const { return m_data->strength(); }
|
||||||
}
|
bool violated() const { return m_data->violated(); }
|
||||||
|
|
||||||
RelationalOperator op() const
|
|
||||||
{
|
|
||||||
return m_data->m_op;
|
|
||||||
}
|
|
||||||
|
|
||||||
double strength() const
|
|
||||||
{
|
|
||||||
return m_data->m_strength;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool violated() const
|
|
||||||
{
|
|
||||||
switch (m_data->m_op)
|
|
||||||
{
|
|
||||||
case OP_EQ: return !impl::nearZero(m_data->m_expression.value());
|
|
||||||
case OP_GE: return m_data->m_expression.value() < 0.0;
|
|
||||||
case OP_LE: return m_data->m_expression.value() > 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!() const
|
bool operator!() const
|
||||||
{
|
{
|
||||||
@@ -81,46 +112,10 @@ public:
|
|||||||
Constraint& operator=(Constraint &&) noexcept = default;
|
Constraint& operator=(Constraint &&) noexcept = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Expression reduce(const Expression &expr)
|
|
||||||
{
|
|
||||||
std::map<Variable, double> vars;
|
|
||||||
for (const auto & term : expr.terms())
|
|
||||||
vars[term.variable()] += term.coefficient();
|
|
||||||
|
|
||||||
std::vector<Term> terms(vars.begin(), vars.end());
|
|
||||||
return Expression(std::move(terms), expr.constant());
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConstraintData : public SharedData
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
ConstraintData(const Expression &expr,
|
|
||||||
RelationalOperator op,
|
|
||||||
double strength) : SharedData(),
|
|
||||||
m_expression(reduce(expr)),
|
|
||||||
m_strength(strength::clip(strength)),
|
|
||||||
m_op(op) {}
|
|
||||||
|
|
||||||
ConstraintData(const Constraint &other, double strength) : SharedData(),
|
|
||||||
m_expression(other.expression()),
|
|
||||||
m_strength(strength::clip(strength)),
|
|
||||||
m_op(other.op()) {}
|
|
||||||
|
|
||||||
~ConstraintData() = default;
|
|
||||||
|
|
||||||
Expression m_expression;
|
|
||||||
double m_strength;
|
|
||||||
RelationalOperator m_op;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ConstraintData(const ConstraintData &other);
|
|
||||||
|
|
||||||
ConstraintData &operator=(const ConstraintData &other);
|
|
||||||
};
|
|
||||||
|
|
||||||
SharedDataPtr<ConstraintData> m_data;
|
SharedDataPtr<ConstraintData> m_data;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
friend bool operator<(const Constraint &lhs, const Constraint &rhs)
|
friend bool operator<(const Constraint &lhs, const Constraint &rhs)
|
||||||
{
|
{
|
||||||
return lhs.m_data < rhs.m_data;
|
return lhs.m_data < rhs.m_data;
|
||||||
|
|||||||
@@ -13,63 +13,54 @@
|
|||||||
namespace kiwi
|
namespace kiwi
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class VariableData : public SharedData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VariableData(std::string name) : SharedData(),
|
||||||
|
m_name(std::move(name)),
|
||||||
|
m_value(0.0) {}
|
||||||
|
|
||||||
|
VariableData(const char *name) : SharedData(),
|
||||||
|
m_name(name),
|
||||||
|
m_value(0.0) {}
|
||||||
|
|
||||||
|
~VariableData() = default;
|
||||||
|
|
||||||
|
const std::string &name() const { return m_name; }
|
||||||
|
void setName(const char *name) { m_name = name; }
|
||||||
|
void setName(const std::string &name) { m_name = name; }
|
||||||
|
|
||||||
|
double value() const { return m_value; }
|
||||||
|
void setValue(double value) { m_value = value; }
|
||||||
|
|
||||||
|
std::string m_name;
|
||||||
|
double m_value;
|
||||||
|
|
||||||
|
VariableData(const VariableData &other) = delete;
|
||||||
|
VariableData &operator=(const VariableData &other) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
class Variable
|
class Variable
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class Context
|
explicit Variable(VariableData *p) : m_data(p) {}
|
||||||
{
|
VariableData *ptr() { return m_data; }
|
||||||
public:
|
|
||||||
Context() = default;
|
|
||||||
virtual ~Context() {} // LCOV_EXCL_LINE
|
|
||||||
};
|
|
||||||
|
|
||||||
Variable(Context *context = 0) : m_data(new VariableData("", context)) {}
|
Variable() : m_data(new VariableData("")) {}
|
||||||
|
|
||||||
Variable(std::string name, Context *context = 0) : m_data(new VariableData(std::move(name), context)) {}
|
|
||||||
|
|
||||||
Variable(const char *name, Context *context = 0) : m_data(new VariableData(name, context)) {}
|
|
||||||
|
|
||||||
|
Variable(std::string name) : m_data(new VariableData(std::move(name))) {}
|
||||||
|
Variable(const char *name) : m_data(new VariableData(name)) {}
|
||||||
Variable(const Variable&) = default;
|
Variable(const Variable&) = default;
|
||||||
|
|
||||||
Variable(Variable&&) noexcept = default;
|
Variable(Variable&&) noexcept = default;
|
||||||
|
|
||||||
~Variable() = default;
|
~Variable() = default;
|
||||||
|
|
||||||
const std::string &name() const
|
const std::string &name() const { return m_data->name(); }
|
||||||
{
|
void setName(const char *name) { m_data->setName(name); }
|
||||||
return m_data->m_name;
|
void setName(const std::string &name) { m_data->setName(name); }
|
||||||
}
|
|
||||||
|
|
||||||
void setName(const char *name)
|
double value() const { return m_data->value(); }
|
||||||
{
|
void setValue(double value) { m_data->setValue(value); }
|
||||||
m_data->m_name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setName(const std::string &name)
|
|
||||||
{
|
|
||||||
m_data->m_name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
Context *context() const
|
|
||||||
{
|
|
||||||
return m_data->m_context.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setContext(Context *context)
|
|
||||||
{
|
|
||||||
m_data->m_context.reset(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
double value() const
|
|
||||||
{
|
|
||||||
return m_data->m_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setValue(double value)
|
|
||||||
{
|
|
||||||
m_data->m_value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// operator== is used for symbolics
|
// operator== is used for symbolics
|
||||||
bool equals(const Variable &other) const
|
bool equals(const Variable &other) const
|
||||||
@@ -82,32 +73,6 @@ public:
|
|||||||
Variable& operator=(Variable&&) noexcept = default;
|
Variable& operator=(Variable&&) noexcept = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class VariableData : public SharedData
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
VariableData(std::string name, Context *context) : SharedData(),
|
|
||||||
m_name(std::move(name)),
|
|
||||||
m_context(context),
|
|
||||||
m_value(0.0) {}
|
|
||||||
|
|
||||||
VariableData(const char *name, Context *context) : SharedData(),
|
|
||||||
m_name(name),
|
|
||||||
m_context(context),
|
|
||||||
m_value(0.0) {}
|
|
||||||
|
|
||||||
~VariableData() = default;
|
|
||||||
|
|
||||||
std::string m_name;
|
|
||||||
std::unique_ptr<Context> m_context;
|
|
||||||
double m_value;
|
|
||||||
|
|
||||||
private:
|
|
||||||
VariableData(const VariableData &other);
|
|
||||||
|
|
||||||
VariableData &operator=(const VariableData &other);
|
|
||||||
};
|
|
||||||
|
|
||||||
SharedDataPtr<VariableData> m_data;
|
SharedDataPtr<VariableData> m_data;
|
||||||
|
|
||||||
friend bool operator<(const Variable &lhs, const Variable &rhs)
|
friend bool operator<(const Variable &lhs, const Variable &rhs)
|
||||||
|
|||||||
37
ljkiwi.def
37
ljkiwi.def
@@ -1,37 +0,0 @@
|
|||||||
EXPORTS
|
|
||||||
luaopen_ljkiwi
|
|
||||||
|
|
||||||
kiwi_ti_KiwiVar
|
|
||||||
kiwi_ti_KiwiConstraint
|
|
||||||
kiwi_ti_KiwiSolver
|
|
||||||
|
|
||||||
kiwi_constraint_construct
|
|
||||||
kiwi_constraint_expression
|
|
||||||
kiwi_constraint_op
|
|
||||||
kiwi_constraint_release
|
|
||||||
kiwi_constraint_retain
|
|
||||||
kiwi_constraint_strength
|
|
||||||
kiwi_constraint_violated
|
|
||||||
kiwi_expression_destroy
|
|
||||||
kiwi_solver_add_constraint
|
|
||||||
kiwi_solver_add_edit_var
|
|
||||||
kiwi_solver_construct
|
|
||||||
kiwi_solver_destroy
|
|
||||||
kiwi_solver_dump
|
|
||||||
kiwi_solver_dumps
|
|
||||||
kiwi_solver_has_constraint
|
|
||||||
kiwi_solver_has_edit_var
|
|
||||||
kiwi_solver_remove_constraint
|
|
||||||
kiwi_solver_remove_edit_var
|
|
||||||
kiwi_solver_reset
|
|
||||||
kiwi_solver_suggest_value
|
|
||||||
kiwi_solver_update_vars
|
|
||||||
kiwi_var_construct
|
|
||||||
kiwi_var_destroy
|
|
||||||
kiwi_var_eq
|
|
||||||
kiwi_var_name
|
|
||||||
kiwi_var_release
|
|
||||||
kiwi_var_retain
|
|
||||||
kiwi_var_set_name
|
|
||||||
kiwi_var_set_value
|
|
||||||
kiwi_var_value
|
|
||||||
@@ -1,33 +1,29 @@
|
|||||||
#ifndef LKIWI_LUACOMPAT_H_
|
#ifndef LJKIWI_LUACOMPAT_H_
|
||||||
#define LKIWI_LUACOMPAT_H_
|
#define LJKIWI_LUACOMPAT_H_
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
#include <lualib.h>
|
#include <lualib.h>
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 && defined(__GNUC__)
|
||||||
#endif // __cplusplus
|
#define LJKIWI_LJ_COMPAT_ATTR __attribute__((weak, visibility("default")))
|
||||||
|
#else
|
||||||
|
#define LJKIWI_LJ_COMPAT_ATTR static
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501
|
#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501
|
||||||
|
|
||||||
#define LUA_OPADD 0
|
|
||||||
#define LUA_OPSUB 1
|
|
||||||
#define LUA_OPMUL 2
|
|
||||||
#define LUA_OPDIV 3
|
|
||||||
#define LUA_OPMOD 4
|
|
||||||
#define LUA_OPPOW 5
|
|
||||||
#define LUA_OPUNM 6
|
|
||||||
|
|
||||||
static int lua_absindex(lua_State* L, int i) {
|
static int lua_absindex(lua_State* L, int i) {
|
||||||
if (i < 0 && i > LUA_REGISTRYINDEX)
|
if (i < 0 && i > LUA_REGISTRYINDEX)
|
||||||
i += lua_gettop(L) + 1;
|
i += lua_gettop(L) + 1;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static lua_Number lua_tonumberx(lua_State* L, int i, int* isnum) {
|
LJKIWI_LJ_COMPAT_ATTR lua_Number lua_tonumberx(lua_State* L, int i, int* isnum) {
|
||||||
lua_Number n = lua_tonumber(L, i);
|
lua_Number n = lua_tonumber(L, i);
|
||||||
if (isnum != NULL) {
|
if (isnum != NULL) {
|
||||||
*isnum = (n != 0 || lua_isnumber(L, i));
|
*isnum = (n != 0 || lua_isnumber(L, i));
|
||||||
@@ -35,7 +31,7 @@ static lua_Number lua_tonumberx(lua_State* L, int i, int* isnum) {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static lua_Integer lua_tointegerx(lua_State* L, int i, int* isnum) {
|
LJKIWI_LJ_COMPAT_ATTR lua_Integer lua_tointegerx(lua_State* L, int i, int* isnum) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
lua_Number n = lua_tonumberx(L, i, &ok);
|
lua_Number n = lua_tonumberx(L, i, &ok);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
@@ -144,4 +140,8 @@ static int luaL_typeerror(lua_State* L, int arg, const char* tname) {
|
|||||||
#define luaL_checkversion(L) ((void)0)
|
#define luaL_checkversion(L) ((void)0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // LKIWI_LUACOMPAT_H_
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif // LJKIWI_LUACOMPAT_H_
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ using namespace kiwi;
|
|||||||
// Lua 5.1 compatibility for missing lua_arith.
|
// Lua 5.1 compatibility for missing lua_arith.
|
||||||
inline void compat_arith_unm(lua_State* L) {
|
inline void compat_arith_unm(lua_State* L) {
|
||||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
|
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
|
||||||
lua_Number n = lua_tonumber(L, -1);
|
int isnum;
|
||||||
if (n != 0 || lua_isnumber(L, -1)) {
|
lua_Number n = lua_tonumberx(L, -1, &isnum);
|
||||||
|
if (isnum) {
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
lua_pushnumber(L, -n);
|
lua_pushnumber(L, -n);
|
||||||
} else {
|
} else {
|
||||||
@@ -78,26 +79,28 @@ enum KiwiErrKind {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct KiwiTerm {
|
struct KiwiTerm {
|
||||||
Variable* var;
|
VariableData* var;
|
||||||
double coefficient;
|
double coefficient;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KiwiExpression {
|
struct KiwiExpression {
|
||||||
double constant;
|
double constant;
|
||||||
int term_count;
|
int term_count;
|
||||||
Constraint* owner;
|
ConstraintData* owner;
|
||||||
|
|
||||||
#if !defined(_MSC_VER) || _MSC_VER >= 1900
|
#if !defined(_MSC_VER) || _MSC_VER >= 1900
|
||||||
KiwiTerm terms[];
|
KiwiTerm terms[];
|
||||||
|
|
||||||
static constexpr std::size_t sz(int count) {
|
static constexpr std::size_t sz(int count) {
|
||||||
return sizeof(KiwiExpression) + sizeof(KiwiTerm) * (count > 0 ? count : 0);
|
return sizeof(KiwiExpression)
|
||||||
|
+ sizeof(KiwiTerm) * static_cast<std::size_t>(count > 0 ? count : 0);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
KiwiTerm terms[1];
|
KiwiTerm terms[1];
|
||||||
|
|
||||||
static constexpr std::size_t sz(int count) {
|
static constexpr std::size_t sz(int count) {
|
||||||
return sizeof(KiwiExpression) + sizeof(KiwiTerm) * (count > 1 ? count - 1 : 0);
|
return sizeof(KiwiExpression)
|
||||||
|
+ sizeof(KiwiTerm) * static_cast<std::size_t>(count > 0 ? count : 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -138,49 +141,57 @@ template<typename F>
|
|||||||
inline const KiwiErr* wrap_err(F&& f) {
|
inline const KiwiErr* wrap_err(F&& f) {
|
||||||
static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
|
static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
|
||||||
KiwiErrUnknown,
|
KiwiErrUnknown,
|
||||||
"An unhandled C++ exception occurred."};
|
"An unhandled C++ exception occurred."
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
f();
|
f();
|
||||||
} catch (const UnsatisfiableConstraint&) {
|
} catch (const UnsatisfiableConstraint&) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrUnsatisfiableConstraint,
|
KiwiErrUnsatisfiableConstraint,
|
||||||
"The constraint cannot be satisfied."};
|
"The constraint cannot be satisfied."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
} catch (const UnknownConstraint&) {
|
} catch (const UnknownConstraint&) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrUnknownConstraint,
|
KiwiErrUnknownConstraint,
|
||||||
"The constraint has not been added to the solver."};
|
"The constraint has not been added to the solver."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const DuplicateConstraint&) {
|
} catch (const DuplicateConstraint&) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrDuplicateConstraint,
|
KiwiErrDuplicateConstraint,
|
||||||
"The constraint has already been added to the solver."};
|
"The constraint has already been added to the solver."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const UnknownEditVariable&) {
|
} catch (const UnknownEditVariable&) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrUnknownEditVariable,
|
KiwiErrUnknownEditVariable,
|
||||||
"The edit variable has not been added to the solver."};
|
"The edit variable has not been added to the solver."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const DuplicateEditVariable&) {
|
} catch (const DuplicateEditVariable&) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrDuplicateEditVariable,
|
KiwiErrDuplicateEditVariable,
|
||||||
"The edit variable has already been added to the solver."};
|
"The edit variable has already been added to the solver."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const BadRequiredStrength&) {
|
} catch (const BadRequiredStrength&) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrBadRequiredStrength,
|
KiwiErrBadRequiredStrength,
|
||||||
"A required strength cannot be used in this context."};
|
"A required strength cannot be used in this context."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const InternalSolverError& ex) {
|
} catch (const InternalSolverError& ex) {
|
||||||
static const constexpr KiwiErr base {
|
static const constexpr KiwiErr base {
|
||||||
KiwiErrInternalSolverError,
|
KiwiErrInternalSolverError,
|
||||||
"An internal solver error occurred."};
|
"An internal solver error occurred."
|
||||||
|
};
|
||||||
return new_error(&base, ex);
|
return new_error(&base, ex);
|
||||||
} catch (std::bad_alloc&) {
|
} catch (std::bad_alloc&) {
|
||||||
static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."};
|
static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."};
|
||||||
@@ -194,81 +205,108 @@ inline const KiwiErr* wrap_err(F&& f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename P, typename R, typename F>
|
template<typename P, typename R, typename F>
|
||||||
inline const KiwiErr* wrap_err(P& s, F&& f) {
|
inline const KiwiErr* wrap_err(P&& s, F&& f) {
|
||||||
return wrap_err([&]() { f(s); });
|
return wrap_err([&]() { f(s); });
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename P, typename R, typename F>
|
template<typename P, typename R, typename F>
|
||||||
inline const KiwiErr* wrap_err(P& s, R& ref, F&& f) {
|
inline const KiwiErr* wrap_err(P&& s, R&& ref, F&& f) {
|
||||||
return wrap_err([&]() { f(s, ref); });
|
return wrap_err([&]() { f(s, ref); });
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Variable* kiwi_var_retain(Variable* var) {
|
template<typename T, typename... Args>
|
||||||
alignas(Variable) unsigned char buf[sizeof(Variable)];
|
inline T* make_unmanaged(Args... args) {
|
||||||
new (buf) Variable(*var);
|
auto* p = new (std::nothrow) T(std::forward<Args>(args)...);
|
||||||
return var;
|
if (lk_unlikely(!p))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
p->m_refcount = 1;
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Constraint* kiwi_constraint_retain(Constraint* c) {
|
template<typename T>
|
||||||
alignas(Constraint) unsigned char buf[sizeof(Constraint)];
|
inline void release_unmanaged(T* p) {
|
||||||
new (buf) Constraint(*c);
|
if (lk_likely(p)) {
|
||||||
return c;
|
if (--p->m_refcount == 0)
|
||||||
|
delete p;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Constraint* kiwi_constraint_new(
|
template<typename T>
|
||||||
|
inline T* retain_unmanaged(T* p) {
|
||||||
|
if (lk_likely(p))
|
||||||
|
p->m_refcount++;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ConstraintData* kiwi_constraint_new(
|
||||||
const KiwiExpression* lhs,
|
const KiwiExpression* lhs,
|
||||||
const KiwiExpression* rhs,
|
const KiwiExpression* rhs,
|
||||||
RelationalOperator op,
|
RelationalOperator op,
|
||||||
double strength,
|
double strength
|
||||||
void* mem
|
|
||||||
) {
|
) {
|
||||||
if (strength < 0.0) {
|
if (strength < 0.0) {
|
||||||
strength = kiwi::strength::required;
|
strength = kiwi::strength::required;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Term> terms;
|
try {
|
||||||
terms.reserve((lhs ? lhs->term_count : 0) + (rhs ? rhs->term_count : 0));
|
std::vector<Term> terms;
|
||||||
|
|
||||||
if (lhs) {
|
terms.reserve(static_cast<decltype(terms)::size_type>(
|
||||||
for (auto* t = lhs->terms; t != lhs->terms + lhs->term_count; ++t) {
|
(lhs && lhs->term_count > 0 ? lhs->term_count : 0)
|
||||||
terms.emplace_back(*t->var, t->coefficient);
|
+ (rhs && rhs->term_count > 0 ? rhs->term_count : 0)
|
||||||
|
));
|
||||||
|
|
||||||
|
if (lhs) {
|
||||||
|
for (int i = 0; i < lhs->term_count; ++i) {
|
||||||
|
const auto& t = lhs->terms[i];
|
||||||
|
terms.emplace_back(Variable(t.var), t.coefficient);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (rhs) {
|
||||||
if (rhs) {
|
for (int i = 0; i < rhs->term_count; ++i) {
|
||||||
for (auto* t = rhs->terms; t != rhs->terms + rhs->term_count; ++t) {
|
const auto& t = rhs->terms[i];
|
||||||
terms.emplace_back(*t->var, -t->coefficient);
|
terms.emplace_back(Variable(t.var), -t.coefficient);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return make_unmanaged<ConstraintData>(
|
||||||
|
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
|
||||||
|
static_cast<RelationalOperator>(op),
|
||||||
|
strength
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
return new (mem) Constraint(
|
|
||||||
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
|
|
||||||
static_cast<RelationalOperator>(op),
|
|
||||||
strength
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const KiwiErr* kiwi_solver_add_constraint(Solver& s, const Constraint& constraint) {
|
inline const KiwiErr* kiwi_solver_add_constraint(Solver& s, ConstraintData* constraint) {
|
||||||
return wrap_err(s, constraint, [](auto& solver, const auto& c) { solver.addConstraint(c); });
|
return wrap_err(s, constraint, [](auto&& solver, auto&& c) {
|
||||||
}
|
solver.addConstraint(Constraint(c));
|
||||||
|
|
||||||
inline const KiwiErr* kiwi_solver_remove_constraint(Solver& s, const Constraint& constraint) {
|
|
||||||
return wrap_err(s, constraint, [](auto& solver, const auto& c) {
|
|
||||||
solver.removeConstraint(c);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const KiwiErr* kiwi_solver_add_edit_var(Solver& s, const Variable& var, double strength) {
|
inline const KiwiErr* kiwi_solver_remove_constraint(Solver& s, ConstraintData* constraint) {
|
||||||
return wrap_err(s, var, [strength](auto& solver, const auto& v) {
|
return wrap_err(s, constraint, [](auto&& solver, auto&& c) {
|
||||||
solver.addEditVariable(v, strength);
|
solver.removeConstraint(Constraint(c));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const KiwiErr* kiwi_solver_remove_edit_var(Solver& s, const Variable& var) {
|
inline const KiwiErr* kiwi_solver_add_edit_var(Solver& s, VariableData* var, double strength) {
|
||||||
return wrap_err(s, var, [](auto& solver, const auto& v) { solver.removeEditVariable(v); });
|
return wrap_err(s, var, [strength](auto&& solver, auto&& v) {
|
||||||
|
solver.addEditVariable(Variable(v), strength);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const KiwiErr* kiwi_solver_suggest_value(Solver& s, const Variable& var, double value) {
|
inline const KiwiErr* kiwi_solver_remove_edit_var(Solver& s, VariableData* var) {
|
||||||
return wrap_err(s, var, [value](auto& solver, const auto& v) {
|
return wrap_err(s, var, [](auto&& solver, auto&& v) {
|
||||||
solver.suggestValue(v, value);
|
solver.removeEditVariable(Variable(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const KiwiErr* kiwi_solver_suggest_value(Solver& s, VariableData* var, double value) {
|
||||||
|
return wrap_err(s, var, [value](auto&& solver, auto&& v) {
|
||||||
|
solver.suggestValue(Variable(v), value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
#include "ljkiwi.hpp"
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "luacompat.h"
|
|
||||||
#include "luakiwi-int.h"
|
#include "luakiwi-int.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -13,9 +11,7 @@ namespace {
|
|||||||
|
|
||||||
enum TypeId { NOTYPE, VAR = 1, TERM, EXPR, CONSTRAINT, SOLVER, ERROR, NUMBER };
|
enum TypeId { NOTYPE, VAR = 1, TERM, EXPR, CONSTRAINT, SOLVER, ERROR, NUMBER };
|
||||||
|
|
||||||
const int ERR_KIND_TAB = NUMBER + 1;
|
enum { ERR_KIND_TAB = NUMBER + 1, VAR_SUB_FN, MEM_ERR_MSG, CONTEXT_TAB_MAX };
|
||||||
const int VAR_SUB_FN = ERR_KIND_TAB + 1;
|
|
||||||
const int CONTEXT_TAB_MAX = VAR_SUB_FN + 1;
|
|
||||||
|
|
||||||
constexpr const char* const lkiwi_error_kinds[] = {
|
constexpr const char* const lkiwi_error_kinds[] = {
|
||||||
"KiwiErrNone",
|
"KiwiErrNone",
|
||||||
@@ -146,8 +142,8 @@ inline void* try_type(lua_State* L, int idx, TypeId type_id) {
|
|||||||
return lua_rawequal(L, -1, -2) ? p : 0;
|
return lua_rawequal(L, -1, -2) ? p : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Variable* try_var(lua_State* L, int idx) {
|
inline VariableData* try_var(lua_State* L, int idx) {
|
||||||
return static_cast<Variable*>(try_type(L, idx, VAR));
|
return *static_cast<VariableData**>(try_type(L, idx, VAR));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline KiwiTerm* try_term(lua_State* L, int idx) {
|
inline KiwiTerm* try_term(lua_State* L, int idx) {
|
||||||
@@ -191,8 +187,8 @@ inline void* try_arg(lua_State* L, int idx, TypeId* type_id, double* num) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Variable* get_var(lua_State* L, int idx) {
|
inline VariableData* get_var(lua_State* L, int idx) {
|
||||||
return static_cast<Variable*>(check_arg(L, idx, VAR));
|
return *static_cast<VariableData**>(check_arg(L, idx, VAR));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline KiwiTerm* get_term(lua_State* L, int idx) {
|
inline KiwiTerm* get_term(lua_State* L, int idx) {
|
||||||
@@ -210,24 +206,30 @@ inline KiwiExpression* get_expr_opt(lua_State* L, int idx) {
|
|||||||
return static_cast<KiwiExpression*>(check_arg(L, idx, EXPR));
|
return static_cast<KiwiExpression*>(check_arg(L, idx, EXPR));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Constraint* get_constraint(lua_State* L, int idx) {
|
inline ConstraintData* get_constraint(lua_State* L, int idx) {
|
||||||
return static_cast<Constraint*>(check_arg(L, idx, CONSTRAINT));
|
return *static_cast<ConstraintData**>(check_arg(L, idx, CONSTRAINT));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline KiwiSolver* get_solver(lua_State* L, int idx) {
|
inline KiwiSolver* get_solver(lua_State* L, int idx) {
|
||||||
return static_cast<KiwiSolver*>(check_arg(L, idx, SOLVER));
|
return static_cast<KiwiSolver*>(check_arg(L, idx, SOLVER));
|
||||||
}
|
}
|
||||||
|
|
||||||
// note this expects the 2nd upvalue to have the variable weak table
|
VariableData** var_new(lua_State* L) {
|
||||||
template<typename... Args>
|
auto** varp = static_cast<VariableData**>(lua_newuserdata(L, sizeof(VariableData*)));
|
||||||
inline Variable* var_new(lua_State* L, Args&&... args) {
|
|
||||||
auto* var = new (lua_newuserdata(L, sizeof(Variable))) Variable(std::forward<Args>(args)...);
|
|
||||||
push_type(L, VAR);
|
push_type(L, VAR);
|
||||||
lua_setmetatable(L, -2);
|
lua_setmetatable(L, -2);
|
||||||
|
return varp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// note this expects the 2nd upvalue to have the variable weak table
|
||||||
|
VariableData* var_register(lua_State* L, VariableData* var) {
|
||||||
|
if (lk_unlikely(!var)) {
|
||||||
|
lua_rawgeti(L, lua_upvalueindex(1), MEM_ERR_MSG);
|
||||||
|
lua_error(L);
|
||||||
|
}
|
||||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
|
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
|
||||||
// a true compatibility shim has performance implications here
|
// a true compatibility shim has performance implications here
|
||||||
lua_pushlightuserdata(L, p);
|
lua_pushlightuserdata(L, var);
|
||||||
lua_pushvalue(L, -2);
|
lua_pushvalue(L, -2);
|
||||||
lua_rawset(L, lua_upvalueindex(2));
|
lua_rawset(L, lua_upvalueindex(2));
|
||||||
#else
|
#else
|
||||||
@@ -237,7 +239,7 @@ inline Variable* var_new(lua_State* L, Args&&... args) {
|
|||||||
return var;
|
return var;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline KiwiTerm* term_new(lua_State* L) {
|
KiwiTerm* term_new(lua_State* L) {
|
||||||
auto* term = static_cast<KiwiTerm*>(lua_newuserdata(L, sizeof(KiwiTerm)));
|
auto* term = static_cast<KiwiTerm*>(lua_newuserdata(L, sizeof(KiwiTerm)));
|
||||||
push_type(L, TERM);
|
push_type(L, TERM);
|
||||||
lua_setmetatable(L, -2);
|
lua_setmetatable(L, -2);
|
||||||
@@ -246,24 +248,30 @@ inline KiwiTerm* term_new(lua_State* L) {
|
|||||||
|
|
||||||
inline KiwiExpression* expr_new(lua_State* L, int nterms) {
|
inline KiwiExpression* expr_new(lua_State* L, int nterms) {
|
||||||
auto* expr = static_cast<KiwiExpression*>(lua_newuserdata(L, KiwiExpression::sz(nterms)));
|
auto* expr = static_cast<KiwiExpression*>(lua_newuserdata(L, KiwiExpression::sz(nterms)));
|
||||||
|
expr->term_count = 0;
|
||||||
expr->owner = nullptr;
|
expr->owner = nullptr;
|
||||||
push_type(L, EXPR);
|
push_type(L, EXPR);
|
||||||
lua_setmetatable(L, -2);
|
lua_setmetatable(L, -2);
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Constraint* constraint_new(
|
inline ConstraintData* constraint_new(
|
||||||
lua_State* L,
|
lua_State* L,
|
||||||
const KiwiExpression* lhs,
|
const KiwiExpression* lhs,
|
||||||
const KiwiExpression* rhs,
|
const KiwiExpression* rhs,
|
||||||
kiwi::RelationalOperator op,
|
kiwi::RelationalOperator op,
|
||||||
double strength
|
double strength
|
||||||
) {
|
) {
|
||||||
auto* c = kiwi_constraint_new(lhs, rhs, op, strength, lua_newuserdata(L, sizeof(Constraint)));
|
auto** c = static_cast<ConstraintData**>(lua_newuserdata(L, sizeof(ConstraintData*)));
|
||||||
|
|
||||||
push_type(L, CONSTRAINT);
|
push_type(L, CONSTRAINT);
|
||||||
lua_setmetatable(L, -2);
|
lua_setmetatable(L, -2);
|
||||||
return c;
|
|
||||||
|
*c = kiwi_constraint_new(lhs, rhs, op, strength);
|
||||||
|
if (lk_unlikely(!*c)) {
|
||||||
|
lua_rawgeti(L, lua_upvalueindex(1), MEM_ERR_MSG);
|
||||||
|
lua_error(L);
|
||||||
|
}
|
||||||
|
return *c;
|
||||||
}
|
}
|
||||||
|
|
||||||
// stack disposition: dirty
|
// stack disposition: dirty
|
||||||
@@ -288,7 +296,7 @@ KiwiExpression* toexpr(lua_State* L, int idx, KiwiExpression* temp) {
|
|||||||
temp->term_count = 1;
|
temp->term_count = 1;
|
||||||
push_type(L, VAR);
|
push_type(L, VAR);
|
||||||
if (lua_rawequal(L, -1, -3)) {
|
if (lua_rawequal(L, -1, -3)) {
|
||||||
temp->terms[0].var = static_cast<Variable*>(ud);
|
temp->terms[0].var = *static_cast<VariableData**>(ud);
|
||||||
temp->terms[0].coefficient = 1.0;
|
temp->terms[0].coefficient = 1.0;
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
@@ -332,7 +340,7 @@ inline int push_expr_one(lua_State* L, double constant, const KiwiTerm* term) {
|
|||||||
expr->constant = constant;
|
expr->constant = constant;
|
||||||
expr->term_count = 1;
|
expr->term_count = 1;
|
||||||
expr->terms[0].coefficient = term->coefficient;
|
expr->terms[0].coefficient = term->coefficient;
|
||||||
expr->terms[0].var = kiwi_var_retain(term->var);
|
expr->terms[0].var = retain_unmanaged(term->var);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,20 +349,21 @@ inline int push_expr_pair(lua_State* L, double constant, const KiwiTerm* ta, con
|
|||||||
e->constant = constant;
|
e->constant = constant;
|
||||||
e->term_count = 2;
|
e->term_count = 2;
|
||||||
e->terms[0].coefficient = ta->coefficient;
|
e->terms[0].coefficient = ta->coefficient;
|
||||||
e->terms[0].var = kiwi_var_retain(ta->var);
|
e->terms[0].var = retain_unmanaged(ta->var);
|
||||||
e->terms[1].coefficient = tb->coefficient;
|
e->terms[1].coefficient = tb->coefficient;
|
||||||
e->terms[1].var = kiwi_var_retain(tb->var);
|
e->terms[1].var = retain_unmanaged(tb->var);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int push_expr_var_term(lua_State* L, double constant, Variable* var, const KiwiTerm* t) {
|
inline int
|
||||||
|
push_expr_var_term(lua_State* L, double constant, VariableData* var, const KiwiTerm* t) {
|
||||||
auto* e = expr_new(L, 2);
|
auto* e = expr_new(L, 2);
|
||||||
e->constant = constant;
|
e->constant = constant;
|
||||||
e->term_count = 2;
|
e->term_count = 2;
|
||||||
e->terms[0].coefficient = 1.0;
|
e->terms[0].coefficient = 1.0;
|
||||||
e->terms[0].var = kiwi_var_retain(var);
|
e->terms[0].var = retain_unmanaged(var);
|
||||||
e->terms[1].coefficient = t->coefficient;
|
e->terms[1].coefficient = t->coefficient;
|
||||||
e->terms[1].var = kiwi_var_retain(t->var);
|
e->terms[1].var = retain_unmanaged(t->var);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,23 +374,23 @@ int push_add_expr_term(lua_State* L, const KiwiExpression* expr, const KiwiTerm*
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
for (; i < expr->term_count; ++i) {
|
for (; i < expr->term_count; ++i) {
|
||||||
e->terms[i].coefficient = expr->terms[i].coefficient;
|
e->terms[i].coefficient = expr->terms[i].coefficient;
|
||||||
e->terms[i].var = kiwi_var_retain(expr->terms[i].var);
|
e->terms[i].var = retain_unmanaged(expr->terms[i].var);
|
||||||
}
|
}
|
||||||
e->terms[i].coefficient = t->coefficient;
|
e->terms[i].coefficient = t->coefficient;
|
||||||
e->terms[i].var = kiwi_var_retain(t->var);
|
e->terms[i].var = retain_unmanaged(t->var);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_var_m_add(lua_State* L) {
|
int lkiwi_var_m_add(lua_State* L) {
|
||||||
TypeId type_id_b;
|
TypeId type_id_b;
|
||||||
double num;
|
double num = 0.0;
|
||||||
void* arg_b = try_arg(L, 2, &type_id_b, &num);
|
void* arg_b = try_arg(L, 2, &type_id_b, &num);
|
||||||
|
|
||||||
if (type_id_b == VAR) {
|
if (type_id_b == VAR) {
|
||||||
int isnum_a;
|
int isnum_a;
|
||||||
num = lua_tonumberx(L, 1, &isnum_a);
|
num = lua_tonumberx(L, 1, &isnum_a);
|
||||||
if (isnum_a) {
|
if (isnum_a) {
|
||||||
const KiwiTerm t {static_cast<Variable*>(arg_b), 1.0};
|
const KiwiTerm t {*static_cast<VariableData**>(arg_b), 1.0};
|
||||||
return push_expr_one(L, num, &t);
|
return push_expr_one(L, num, &t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -390,7 +399,7 @@ int lkiwi_var_m_add(lua_State* L) {
|
|||||||
if (var_a) {
|
if (var_a) {
|
||||||
switch (type_id_b) {
|
switch (type_id_b) {
|
||||||
case VAR: {
|
case VAR: {
|
||||||
const KiwiTerm ta {var_a, 1.0}, tb {static_cast<Variable*>(arg_b), 1.0};
|
const KiwiTerm ta {var_a, 1.0}, tb {*static_cast<VariableData**>(arg_b), 1.0};
|
||||||
return push_expr_pair(L, 0.0, &ta, &tb);
|
return push_expr_pair(L, 0.0, &ta, &tb);
|
||||||
}
|
}
|
||||||
case TERM:
|
case TERM:
|
||||||
@@ -436,7 +445,7 @@ int lkiwi_var_m_mul(lua_State* L) {
|
|||||||
auto* var = try_var(L, varidx);
|
auto* var = try_var(L, varidx);
|
||||||
if (var) {
|
if (var) {
|
||||||
auto* term = term_new(L);
|
auto* term = term_new(L);
|
||||||
term->var = kiwi_var_retain(var);
|
term->var = retain_unmanaged(var);
|
||||||
term->coefficient = num;
|
term->coefficient = num;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -452,20 +461,20 @@ int lkiwi_var_m_div(lua_State* L) {
|
|||||||
return op_error(L, "/", 1, 2);
|
return op_error(L, "/", 1, 2);
|
||||||
}
|
}
|
||||||
auto* term = term_new(L);
|
auto* term = term_new(L);
|
||||||
term->var = kiwi_var_retain(var);
|
term->var = retain_unmanaged(var);
|
||||||
term->coefficient = 1.0 / num;
|
term->coefficient = 1.0 / num;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_var_m_unm(lua_State* L) {
|
int lkiwi_var_m_unm(lua_State* L) {
|
||||||
auto* term = term_new(L);
|
auto* term = term_new(L);
|
||||||
term->var = kiwi_var_retain(get_var(L, 1));
|
term->var = retain_unmanaged(get_var(L, 1));
|
||||||
term->coefficient = -1.0;
|
term->coefficient = -1.0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_var_m_eq(lua_State* L) {
|
int lkiwi_var_m_eq(lua_State* L) {
|
||||||
lua_pushboolean(L, get_var(L, 1)->equals(*get_var(L, 2)));
|
lua_pushboolean(L, get_var(L, 1) == get_var(L, 2));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,7 +485,7 @@ int lkiwi_var_m_tostring(lua_State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_var_m_gc(lua_State* L) {
|
int lkiwi_var_m_gc(lua_State* L) {
|
||||||
get_var(L, 1)->~Variable();
|
release_unmanaged(get_var(L, 1));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,7 +518,7 @@ int lkiwi_var_toterm(lua_State* L) {
|
|||||||
double coefficient = luaL_optnumber(L, 2, 1.0);
|
double coefficient = luaL_optnumber(L, 2, 1.0);
|
||||||
auto* term = term_new(L);
|
auto* term = term_new(L);
|
||||||
|
|
||||||
term->var = kiwi_var_retain(var);
|
term->var = retain_unmanaged(var);
|
||||||
term->coefficient = coefficient;
|
term->coefficient = coefficient;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -538,59 +547,21 @@ constexpr const struct luaL_Reg kiwi_var_m[] = {
|
|||||||
{"eq", lkiwi_eq},
|
{"eq", lkiwi_eq},
|
||||||
{"le", lkiwi_le},
|
{"le", lkiwi_le},
|
||||||
{"ge", lkiwi_ge},
|
{"ge", lkiwi_ge},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int lkiwi_var_new(lua_State* L) {
|
int lkiwi_var_new(lua_State* L) {
|
||||||
const char* name = luaL_optstring(L, 1, "");
|
const char* name = luaL_optstring(L, 1, "");
|
||||||
var_new(L, name);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lkiwi_term_m_mul(lua_State* L) {
|
auto* varp = var_new(L);
|
||||||
int isnum, termidx = 2;
|
var_register(L, *varp = make_unmanaged<VariableData>(name));
|
||||||
double num = lua_tonumberx(L, 1, &isnum);
|
|
||||||
|
|
||||||
if (!isnum) {
|
|
||||||
termidx = 1;
|
|
||||||
num = lua_tonumberx(L, 2, &isnum);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isnum) {
|
|
||||||
const auto* term = try_term(L, termidx);
|
|
||||||
if (term) {
|
|
||||||
auto* ret = term_new(L);
|
|
||||||
ret->var = kiwi_var_retain(term->var);
|
|
||||||
ret->coefficient = term->coefficient * num;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return op_error(L, "*", 1, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int lkiwi_term_m_div(lua_State* L) {
|
|
||||||
const KiwiTerm* term = try_term(L, 1);
|
|
||||||
int isnum;
|
|
||||||
double num = lua_tonumberx(L, 2, &isnum);
|
|
||||||
if (!term || !isnum) {
|
|
||||||
return op_error(L, "/", 1, 2);
|
|
||||||
}
|
|
||||||
auto* ret = term_new(L);
|
|
||||||
ret->var = kiwi_var_retain(term->var);
|
|
||||||
ret->coefficient = term->coefficient / num;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lkiwi_term_m_unm(lua_State* L) {
|
|
||||||
const auto* term = get_term(L, 1);
|
|
||||||
auto* ret = term_new(L);
|
|
||||||
ret->var = kiwi_var_retain(term->var);
|
|
||||||
ret->coefficient = -term->coefficient;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_term_m_add(lua_State* L) {
|
int lkiwi_term_m_add(lua_State* L) {
|
||||||
TypeId type_id_b;
|
TypeId type_id_b;
|
||||||
double num;
|
double num = 0.0;
|
||||||
void* arg_b = try_arg(L, 2, &type_id_b, &num);
|
void* arg_b = try_arg(L, 2, &type_id_b, &num);
|
||||||
|
|
||||||
if (type_id_b == TERM) {
|
if (type_id_b == TERM) {
|
||||||
@@ -607,7 +578,7 @@ int lkiwi_term_m_add(lua_State* L) {
|
|||||||
case TERM:
|
case TERM:
|
||||||
return push_expr_pair(L, 0.0, term_a, static_cast<KiwiTerm*>(arg_b));
|
return push_expr_pair(L, 0.0, term_a, static_cast<KiwiTerm*>(arg_b));
|
||||||
case VAR: {
|
case VAR: {
|
||||||
const KiwiTerm term_b {static_cast<Variable*>(arg_b), 1.0};
|
const KiwiTerm term_b {*static_cast<VariableData**>(arg_b), 1.0};
|
||||||
return push_expr_pair(L, 0.0, term_a, &term_b);
|
return push_expr_pair(L, 0.0, term_a, &term_b);
|
||||||
}
|
}
|
||||||
case EXPR:
|
case EXPR:
|
||||||
@@ -628,6 +599,48 @@ int lkiwi_term_m_sub(lua_State* L) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lkiwi_term_m_mul(lua_State* L) {
|
||||||
|
int isnum, termidx = 2;
|
||||||
|
double num = lua_tonumberx(L, 1, &isnum);
|
||||||
|
|
||||||
|
if (!isnum) {
|
||||||
|
termidx = 1;
|
||||||
|
num = lua_tonumberx(L, 2, &isnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isnum) {
|
||||||
|
const auto* term = try_term(L, termidx);
|
||||||
|
if (term) {
|
||||||
|
auto* ret = term_new(L);
|
||||||
|
ret->var = retain_unmanaged(term->var);
|
||||||
|
ret->coefficient = term->coefficient * num;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return op_error(L, "*", 1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lkiwi_term_m_div(lua_State* L) {
|
||||||
|
const KiwiTerm* term = try_term(L, 1);
|
||||||
|
int isnum;
|
||||||
|
double num = lua_tonumberx(L, 2, &isnum);
|
||||||
|
if (!term || !isnum) {
|
||||||
|
return op_error(L, "/", 1, 2);
|
||||||
|
}
|
||||||
|
auto* ret = term_new(L);
|
||||||
|
ret->var = retain_unmanaged(term->var);
|
||||||
|
ret->coefficient = term->coefficient / num;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lkiwi_term_m_unm(lua_State* L) {
|
||||||
|
const auto* term = get_term(L, 1);
|
||||||
|
auto* ret = term_new(L);
|
||||||
|
ret->var = retain_unmanaged(term->var);
|
||||||
|
ret->coefficient = -term->coefficient;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int lkiwi_term_toexpr(lua_State* L) {
|
int lkiwi_term_toexpr(lua_State* L) {
|
||||||
return push_expr_one(L, 0.0, get_term(L, 1));
|
return push_expr_one(L, 0.0, get_term(L, 1));
|
||||||
}
|
}
|
||||||
@@ -645,7 +658,7 @@ int lkiwi_term_m_tostring(lua_State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_term_m_gc(lua_State* L) {
|
int lkiwi_term_m_gc(lua_State* L) {
|
||||||
get_term(L, 1)->var->~Variable();
|
release_unmanaged(get_term(L, 1)->var);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -660,8 +673,10 @@ int lkiwi_term_m_index(lua_State* L) {
|
|||||||
#else
|
#else
|
||||||
lua_rawgetp(L, lua_upvalueindex(2), term->var);
|
lua_rawgetp(L, lua_upvalueindex(2), term->var);
|
||||||
#endif
|
#endif
|
||||||
if (lua_isnil(L, -1))
|
if (lua_isnil(L, -1)) {
|
||||||
var_new(L, *term->var);
|
auto* varp = var_new(L);
|
||||||
|
var_register(L, *varp = retain_unmanaged(term->var));
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
} else if (len == 11 && memcmp("coefficient", k, len) == 0) {
|
} else if (len == 11 && memcmp("coefficient", k, len) == 0) {
|
||||||
lua_pushnumber(L, term->coefficient);
|
lua_pushnumber(L, term->coefficient);
|
||||||
@@ -690,13 +705,14 @@ constexpr const struct luaL_Reg kiwi_term_m[] = {
|
|||||||
{"eq", lkiwi_eq},
|
{"eq", lkiwi_eq},
|
||||||
{"le", lkiwi_le},
|
{"le", lkiwi_le},
|
||||||
{"ge", lkiwi_ge},
|
{"ge", lkiwi_ge},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int lkiwi_term_new(lua_State* L) {
|
int lkiwi_term_new(lua_State* L) {
|
||||||
auto* var = get_var(L, 1);
|
auto* var = get_var(L, 1);
|
||||||
double coefficient = luaL_optnumber(L, 2, 1.0);
|
double coefficient = luaL_optnumber(L, 2, 1.0);
|
||||||
auto* term = term_new(L);
|
auto* term = term_new(L);
|
||||||
term->var = kiwi_var_retain(var);
|
term->var = retain_unmanaged(var);
|
||||||
term->coefficient = coefficient;
|
term->coefficient = coefficient;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -704,7 +720,7 @@ int lkiwi_term_new(lua_State* L) {
|
|||||||
int push_expr_constant(lua_State* L, const KiwiExpression* expr, double constant) {
|
int push_expr_constant(lua_State* L, const KiwiExpression* expr, double constant) {
|
||||||
auto* ne = expr_new(L, expr->term_count);
|
auto* ne = expr_new(L, expr->term_count);
|
||||||
for (int i = 0; i < expr->term_count; i++) {
|
for (int i = 0; i < expr->term_count; i++) {
|
||||||
ne->terms[i].var = kiwi_var_retain(expr->terms[i].var);
|
ne->terms[i].var = retain_unmanaged(expr->terms[i].var);
|
||||||
ne->terms[i].coefficient = expr->terms[i].coefficient;
|
ne->terms[i].coefficient = expr->terms[i].coefficient;
|
||||||
}
|
}
|
||||||
ne->constant = constant;
|
ne->constant = constant;
|
||||||
@@ -717,7 +733,7 @@ int push_mul_expr_coeff(lua_State* L, const KiwiExpression* expr, double coeff)
|
|||||||
ne->constant = expr->constant * coeff;
|
ne->constant = expr->constant * coeff;
|
||||||
ne->term_count = expr->term_count;
|
ne->term_count = expr->term_count;
|
||||||
for (int i = 0; i < expr->term_count; i++) {
|
for (int i = 0; i < expr->term_count; i++) {
|
||||||
ne->terms[i].var = kiwi_var_retain(expr->terms[i].var);
|
ne->terms[i].var = retain_unmanaged(expr->terms[i].var);
|
||||||
ne->terms[i].coefficient = expr->terms[i].coefficient * coeff;
|
ne->terms[i].coefficient = expr->terms[i].coefficient * coeff;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@@ -731,16 +747,57 @@ int push_add_expr_expr(lua_State* L, const KiwiExpression* a, const KiwiExpressi
|
|||||||
ne->term_count = na + nb;
|
ne->term_count = na + nb;
|
||||||
|
|
||||||
for (int i = 0; i < na; i++) {
|
for (int i = 0; i < na; i++) {
|
||||||
ne->terms[i].var = kiwi_var_retain(a->terms[i].var);
|
ne->terms[i].var = retain_unmanaged(a->terms[i].var);
|
||||||
ne->terms[i].coefficient = a->terms[i].coefficient;
|
ne->terms[i].coefficient = a->terms[i].coefficient;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < nb; i++) {
|
for (int i = 0; i < nb; i++) {
|
||||||
ne->terms[i + na].var = kiwi_var_retain(b->terms[i].var);
|
ne->terms[i + na].var = retain_unmanaged(b->terms[i].var);
|
||||||
ne->terms[i + na].coefficient = b->terms[i].coefficient;
|
ne->terms[i + na].coefficient = b->terms[i].coefficient;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lkiwi_expr_m_add(lua_State* L) {
|
||||||
|
TypeId type_id_b;
|
||||||
|
double num = 0.0;
|
||||||
|
void* arg_b = try_arg(L, 2, &type_id_b, &num);
|
||||||
|
|
||||||
|
if (type_id_b == EXPR) {
|
||||||
|
int isnum_a;
|
||||||
|
num = lua_tonumberx(L, 1, &isnum_a);
|
||||||
|
if (isnum_a) {
|
||||||
|
auto* expr_b = static_cast<const KiwiExpression*>(arg_b);
|
||||||
|
return push_expr_constant(L, expr_b, num + expr_b->constant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* expr_a = try_expr(L, 1);
|
||||||
|
if (expr_a) {
|
||||||
|
switch (type_id_b) {
|
||||||
|
case EXPR:
|
||||||
|
return push_add_expr_expr(L, expr_a, static_cast<KiwiExpression*>(arg_b));
|
||||||
|
case TERM:
|
||||||
|
return push_add_expr_term(L, expr_a, static_cast<KiwiTerm*>(arg_b));
|
||||||
|
case VAR: {
|
||||||
|
const KiwiTerm term_b {*static_cast<VariableData**>(arg_b), 1.0};
|
||||||
|
return push_add_expr_term(L, expr_a, &term_b);
|
||||||
|
}
|
||||||
|
case NUMBER:
|
||||||
|
return push_expr_constant(L, expr_a, num + expr_a->constant);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return op_error(L, "+", 1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lkiwi_expr_m_sub(lua_State* L) {
|
||||||
|
lua_settop(L, 2);
|
||||||
|
compat_arith_unm(L);
|
||||||
|
lkiwi_expr_m_add(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int lkiwi_expr_m_mul(lua_State* L) {
|
int lkiwi_expr_m_mul(lua_State* L) {
|
||||||
int isnum, expridx = 2;
|
int isnum, expridx = 2;
|
||||||
double num = lua_tonumberx(L, 1, &isnum);
|
double num = lua_tonumberx(L, 1, &isnum);
|
||||||
@@ -773,47 +830,6 @@ int lkiwi_expr_m_unm(lua_State* L) {
|
|||||||
return push_mul_expr_coeff(L, expr, -1.0);
|
return push_mul_expr_coeff(L, expr, -1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_expr_m_add(lua_State* L) {
|
|
||||||
TypeId type_id_b;
|
|
||||||
double num;
|
|
||||||
void* arg_b = try_arg(L, 2, &type_id_b, &num);
|
|
||||||
|
|
||||||
if (type_id_b == EXPR) {
|
|
||||||
int isnum_a;
|
|
||||||
num = lua_tonumberx(L, 1, &isnum_a);
|
|
||||||
if (isnum_a) {
|
|
||||||
auto* expr_b = static_cast<const KiwiExpression*>(arg_b);
|
|
||||||
return push_expr_constant(L, expr_b, num + expr_b->constant);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto* expr_a = try_expr(L, 1);
|
|
||||||
if (expr_a) {
|
|
||||||
switch (type_id_b) {
|
|
||||||
case EXPR:
|
|
||||||
return push_add_expr_expr(L, expr_a, static_cast<KiwiExpression*>(arg_b));
|
|
||||||
case TERM:
|
|
||||||
return push_add_expr_term(L, expr_a, static_cast<KiwiTerm*>(arg_b));
|
|
||||||
case VAR: {
|
|
||||||
const KiwiTerm term_b {static_cast<Variable*>(arg_b), 1.0};
|
|
||||||
return push_add_expr_term(L, expr_a, &term_b);
|
|
||||||
}
|
|
||||||
case NUMBER:
|
|
||||||
return push_expr_constant(L, expr_a, num + expr_a->constant);
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return op_error(L, "+", 1, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int lkiwi_expr_m_sub(lua_State* L) {
|
|
||||||
lua_settop(L, 2);
|
|
||||||
compat_arith_unm(L);
|
|
||||||
lkiwi_expr_m_add(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lkiwi_expr_value(lua_State* L) {
|
int lkiwi_expr_value(lua_State* L) {
|
||||||
const auto* expr = get_expr(L, 1);
|
const auto* expr = get_expr(L, 1);
|
||||||
double sum = expr->constant;
|
double sum = expr->constant;
|
||||||
@@ -831,7 +847,7 @@ int lkiwi_expr_terms(lua_State* L) {
|
|||||||
for (int i = 0; i < expr->term_count; i++) {
|
for (int i = 0; i < expr->term_count; i++) {
|
||||||
const auto* t = &expr->terms[i];
|
const auto* t = &expr->terms[i];
|
||||||
auto* new_term = term_new(L);
|
auto* new_term = term_new(L);
|
||||||
new_term->var = kiwi_var_retain(t->var);
|
new_term->var = retain_unmanaged(t->var);
|
||||||
new_term->coefficient = t->coefficient;
|
new_term->coefficient = t->coefficient;
|
||||||
lua_rawseti(L, -2, i + 1);
|
lua_rawseti(L, -2, i + 1);
|
||||||
}
|
}
|
||||||
@@ -865,10 +881,10 @@ int lkiwi_expr_m_tostring(lua_State* L) {
|
|||||||
int lkiwi_expr_m_gc(lua_State* L) {
|
int lkiwi_expr_m_gc(lua_State* L) {
|
||||||
const auto* expr = get_expr(L, 1);
|
const auto* expr = get_expr(L, 1);
|
||||||
if (expr->owner) {
|
if (expr->owner) {
|
||||||
expr->owner->~Constraint();
|
release_unmanaged(expr->owner);
|
||||||
} else {
|
} else {
|
||||||
for (auto* t = expr->terms; t != expr->terms + expr->term_count; ++t) {
|
for (auto* t = expr->terms; t != expr->terms + expr->term_count; ++t) {
|
||||||
t->var->~Variable();
|
release_unmanaged(t->var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -906,7 +922,8 @@ constexpr const struct luaL_Reg kiwi_expr_m[] = {
|
|||||||
{"eq", lkiwi_eq},
|
{"eq", lkiwi_eq},
|
||||||
{"le", lkiwi_le},
|
{"le", lkiwi_le},
|
||||||
{"ge", lkiwi_ge},
|
{"ge", lkiwi_ge},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int lkiwi_expr_new(lua_State* L) {
|
int lkiwi_expr_new(lua_State* L) {
|
||||||
int nterms = lua_gettop(L) - 1;
|
int nterms = lua_gettop(L) - 1;
|
||||||
@@ -914,11 +931,10 @@ int lkiwi_expr_new(lua_State* L) {
|
|||||||
|
|
||||||
auto* expr = expr_new(L, nterms);
|
auto* expr = expr_new(L, nterms);
|
||||||
expr->constant = constant;
|
expr->constant = constant;
|
||||||
expr->term_count = nterms;
|
|
||||||
|
|
||||||
for (int i = 0; i < nterms; i++) {
|
for (int i = 0; i < nterms; ++i, ++expr->term_count) {
|
||||||
const auto* term = get_term(L, i + 2);
|
const auto* term = get_term(L, i + 2);
|
||||||
expr->terms[i].var = kiwi_var_retain(term->var);
|
expr->terms[i].var = retain_unmanaged(term->var);
|
||||||
expr->terms[i].coefficient = term->coefficient;
|
expr->terms[i].coefficient = term->coefficient;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@@ -956,16 +972,17 @@ int lkiwi_constraint_expression(lua_State* L) {
|
|||||||
auto* c = get_constraint(L, 1);
|
auto* c = get_constraint(L, 1);
|
||||||
const auto& expr = c->expression();
|
const auto& expr = c->expression();
|
||||||
const auto& terms = expr.terms();
|
const auto& terms = expr.terms();
|
||||||
const auto term_count = static_cast<int>(terms.size());
|
const auto term_count = static_cast<int>(terms.size() > INT_MAX ? INT_MAX : terms.size());
|
||||||
|
|
||||||
auto* ne = expr_new(L, term_count);
|
auto* ne = expr_new(L, term_count);
|
||||||
ne->owner = kiwi_constraint_retain(c);
|
ne->owner = retain_unmanaged(c);
|
||||||
ne->constant = expr.constant();
|
ne->constant = expr.constant();
|
||||||
ne->term_count = term_count;
|
ne->term_count = term_count;
|
||||||
|
|
||||||
for (int i = 0; i < term_count; ++i) {
|
for (int i = 0; i < term_count; ++i) {
|
||||||
ne->terms[i].var = const_cast<Variable*>(&terms[i].variable());
|
const auto& t = terms[static_cast<std::size_t>(i)];
|
||||||
ne->terms[i].coefficient = terms[i].coefficient();
|
ne->terms[i].var = const_cast<Variable&>(t.variable()).ptr();
|
||||||
|
ne->terms[i].coefficient = t.coefficient();
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -1025,7 +1042,7 @@ int lkiwi_constraint_m_tostring(lua_State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_constraint_m_gc(lua_State* L) {
|
int lkiwi_constraint_m_gc(lua_State* L) {
|
||||||
get_constraint(L, 1)->~Constraint();
|
release_unmanaged(get_constraint(L, 1));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1057,7 +1074,8 @@ constexpr const struct luaL_Reg kiwi_constraint_m[] = {
|
|||||||
{"expression", lkiwi_constraint_expression},
|
{"expression", lkiwi_constraint_expression},
|
||||||
{"add_to", lkiwi_constraint_add_to},
|
{"add_to", lkiwi_constraint_add_to},
|
||||||
{"remove_from", lkiwi_constraint_remove_from},
|
{"remove_from", lkiwi_constraint_remove_from},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int lkiwi_constraint_new(lua_State* L) {
|
int lkiwi_constraint_new(lua_State* L) {
|
||||||
const auto* lhs = get_expr_opt(L, 1);
|
const auto* lhs = get_expr_opt(L, 1);
|
||||||
@@ -1071,9 +1089,9 @@ int lkiwi_constraint_new(lua_State* L) {
|
|||||||
|
|
||||||
int push_pair_constraint(
|
int push_pair_constraint(
|
||||||
lua_State* L,
|
lua_State* L,
|
||||||
Variable* left,
|
VariableData* left,
|
||||||
double coeff,
|
double coeff,
|
||||||
Variable* right,
|
VariableData* right,
|
||||||
double constant,
|
double constant,
|
||||||
kiwi::RelationalOperator op,
|
kiwi::RelationalOperator op,
|
||||||
double strength
|
double strength
|
||||||
@@ -1130,7 +1148,8 @@ constexpr const struct luaL_Reg lkiwi_constraints[] = {
|
|||||||
{"pair_ratio", lkiwi_constraints_pair_ratio},
|
{"pair_ratio", lkiwi_constraints_pair_ratio},
|
||||||
{"pair", lkiwi_constraints_pair},
|
{"pair", lkiwi_constraints_pair},
|
||||||
{"single", lkiwi_constraints_single},
|
{"single", lkiwi_constraints_single},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
void lkiwi_mod_constraints_new(lua_State* L, int ctx_i) {
|
void lkiwi_mod_constraints_new(lua_State* L, int ctx_i) {
|
||||||
luaL_newlibtable(L, lkiwi_constraints);
|
luaL_newlibtable(L, lkiwi_constraints);
|
||||||
@@ -1181,10 +1200,7 @@ int lkiwi_error_m_tostring(lua_State* L) {
|
|||||||
luaL_tolstring(L, -1, 0);
|
luaL_tolstring(L, -1, 0);
|
||||||
lua_remove(L, -2); // remove item
|
lua_remove(L, -2); // remove item
|
||||||
luaL_addvalue(&buf);
|
luaL_addvalue(&buf);
|
||||||
luaL_addstring(&buf, ")\n");
|
luaL_addstring(&buf, ")");
|
||||||
|
|
||||||
luaL_traceback(L, L, nullptr, 2);
|
|
||||||
luaL_addvalue(&buf);
|
|
||||||
luaL_pushresult(&buf);
|
luaL_pushresult(&buf);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -1192,7 +1208,8 @@ int lkiwi_error_m_tostring(lua_State* L) {
|
|||||||
|
|
||||||
constexpr const struct luaL_Reg lkiwi_error_m[] = {
|
constexpr const struct luaL_Reg lkiwi_error_m[] = {
|
||||||
{"__tostring", lkiwi_error_m_tostring},
|
{"__tostring", lkiwi_error_m_tostring},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int lkiwi_error_mask(lua_State* L) {
|
int lkiwi_error_mask(lua_State* L) {
|
||||||
int invert = lua_toboolean(L, 2);
|
int invert = lua_toboolean(L, 2);
|
||||||
@@ -1238,21 +1255,21 @@ int lkiwi_solver_handle_err(lua_State* L, const KiwiErr* err, const KiwiSolver*
|
|||||||
|
|
||||||
int lkiwi_solver_add_constraint(lua_State* L) {
|
int lkiwi_solver_add_constraint(lua_State* L) {
|
||||||
auto* self = get_solver(L, 1);
|
auto* self = get_solver(L, 1);
|
||||||
const auto& c = *get_constraint(L, 2);
|
auto* c = get_constraint(L, 2);
|
||||||
auto* err = kiwi_solver_add_constraint(self->solver, c);
|
auto* err = kiwi_solver_add_constraint(self->solver, c);
|
||||||
return lkiwi_solver_handle_err(L, err, self);
|
return lkiwi_solver_handle_err(L, err, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_solver_remove_constraint(lua_State* L) {
|
int lkiwi_solver_remove_constraint(lua_State* L) {
|
||||||
auto* self = get_solver(L, 1);
|
auto* self = get_solver(L, 1);
|
||||||
const auto& c = *get_constraint(L, 2);
|
auto* c = get_constraint(L, 2);
|
||||||
auto* err = kiwi_solver_remove_constraint(self->solver, c);
|
auto* err = kiwi_solver_remove_constraint(self->solver, c);
|
||||||
return lkiwi_solver_handle_err(L, err, self);
|
return lkiwi_solver_handle_err(L, err, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_solver_add_edit_var(lua_State* L) {
|
int lkiwi_solver_add_edit_var(lua_State* L) {
|
||||||
auto* self = get_solver(L, 1);
|
auto* self = get_solver(L, 1);
|
||||||
const auto& var = *get_var(L, 2);
|
auto* var = get_var(L, 2);
|
||||||
double strength = luaL_checknumber(L, 3);
|
double strength = luaL_checknumber(L, 3);
|
||||||
auto* err = kiwi_solver_add_edit_var(self->solver, var, strength);
|
auto* err = kiwi_solver_add_edit_var(self->solver, var, strength);
|
||||||
return lkiwi_solver_handle_err(L, err, self);
|
return lkiwi_solver_handle_err(L, err, self);
|
||||||
@@ -1260,14 +1277,14 @@ int lkiwi_solver_add_edit_var(lua_State* L) {
|
|||||||
|
|
||||||
int lkiwi_solver_remove_edit_var(lua_State* L) {
|
int lkiwi_solver_remove_edit_var(lua_State* L) {
|
||||||
auto* self = get_solver(L, 1);
|
auto* self = get_solver(L, 1);
|
||||||
const auto& var = *get_var(L, 2);
|
auto* var = get_var(L, 2);
|
||||||
auto* err = kiwi_solver_remove_edit_var(self->solver, var);
|
auto* err = kiwi_solver_remove_edit_var(self->solver, var);
|
||||||
return lkiwi_solver_handle_err(L, err, self);
|
return lkiwi_solver_handle_err(L, err, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_solver_suggest_value(lua_State* L) {
|
int lkiwi_solver_suggest_value(lua_State* L) {
|
||||||
auto* self = get_solver(L, 1);
|
auto* self = get_solver(L, 1);
|
||||||
const auto& var = *get_var(L, 2);
|
auto* var = get_var(L, 2);
|
||||||
double value = luaL_checknumber(L, 3);
|
double value = luaL_checknumber(L, 3);
|
||||||
auto* err = kiwi_solver_suggest_value(self->solver, var, value);
|
auto* err = kiwi_solver_suggest_value(self->solver, var, value);
|
||||||
return lkiwi_solver_handle_err(L, err, self);
|
return lkiwi_solver_handle_err(L, err, self);
|
||||||
@@ -1285,15 +1302,15 @@ int lkiwi_solver_reset(lua_State* L) {
|
|||||||
|
|
||||||
int lkiwi_solver_has_constraint(lua_State* L) {
|
int lkiwi_solver_has_constraint(lua_State* L) {
|
||||||
auto* s = get_solver(L, 1);
|
auto* s = get_solver(L, 1);
|
||||||
const auto& c = *get_constraint(L, 2);
|
auto* c = get_constraint(L, 2);
|
||||||
lua_pushboolean(L, s->solver.hasConstraint(c));
|
lua_pushboolean(L, s->solver.hasConstraint(Constraint(c)));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_solver_has_edit_var(lua_State* L) {
|
int lkiwi_solver_has_edit_var(lua_State* L) {
|
||||||
auto* s = get_solver(L, 1);
|
auto* s = get_solver(L, 1);
|
||||||
const auto& var = *get_var(L, 2);
|
auto* var = get_var(L, 2);
|
||||||
lua_pushboolean(L, s->solver.hasEditVariable(var));
|
lua_pushboolean(L, s->solver.hasEditVariable(Variable(var)));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1338,26 +1355,26 @@ int lkiwi_add_remove_tab(lua_State* L, F&& fn) {
|
|||||||
|
|
||||||
int lkiwi_solver_add_constraints(lua_State* L) {
|
int lkiwi_solver_add_constraints(lua_State* L) {
|
||||||
return lkiwi_add_remove_tab(L, [](lua_State* L, KiwiSolver* s) {
|
return lkiwi_add_remove_tab(L, [](lua_State* L, KiwiSolver* s) {
|
||||||
return kiwi_solver_add_constraint(s->solver, *get_constraint(L, -1));
|
return kiwi_solver_add_constraint(s->solver, get_constraint(L, -1));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_solver_remove_constraints(lua_State* L) {
|
int lkiwi_solver_remove_constraints(lua_State* L) {
|
||||||
return lkiwi_add_remove_tab(L, [](lua_State* L, KiwiSolver* s) {
|
return lkiwi_add_remove_tab(L, [](lua_State* L, KiwiSolver* s) {
|
||||||
return kiwi_solver_add_constraint(s->solver, *get_constraint(L, -1));
|
return kiwi_solver_add_constraint(s->solver, get_constraint(L, -1));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_solver_add_edit_vars(lua_State* L) {
|
int lkiwi_solver_add_edit_vars(lua_State* L) {
|
||||||
double strength = luaL_checknumber(L, 3);
|
double strength = luaL_checknumber(L, 3);
|
||||||
return lkiwi_add_remove_tab(L, [strength](lua_State* L, KiwiSolver* s) {
|
return lkiwi_add_remove_tab(L, [strength](lua_State* L, KiwiSolver* s) {
|
||||||
return kiwi_solver_add_edit_var(s->solver, *get_var(L, -1), strength);
|
return kiwi_solver_add_edit_var(s->solver, get_var(L, -1), strength);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int lkiwi_solver_remove_edit_vars(lua_State* L) {
|
int lkiwi_solver_remove_edit_vars(lua_State* L) {
|
||||||
return lkiwi_add_remove_tab(L, [](lua_State* L, KiwiSolver* s) {
|
return lkiwi_add_remove_tab(L, [](lua_State* L, KiwiSolver* s) {
|
||||||
return kiwi_solver_remove_edit_var(s->solver, *get_var(L, -1));
|
return kiwi_solver_remove_edit_var(s->solver, get_var(L, -1));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1365,11 +1382,10 @@ int lkiwi_solver_suggest_values(lua_State* L) {
|
|||||||
auto* self = get_solver(L, 1);
|
auto* self = get_solver(L, 1);
|
||||||
int narg = lua_gettop(L);
|
int narg = lua_gettop(L);
|
||||||
|
|
||||||
// block this particularly obnoxious case which is always a bug
|
// catch this obnoxious case which is always a bug
|
||||||
if (lua_type(L, 2) == LUA_TSTRING) {
|
if (lua_type(L, 2) == LUA_TSTRING) {
|
||||||
luaL_typeerror(L, 2, "indexable");
|
luaL_typeerror(L, 2, "indexable");
|
||||||
}
|
}
|
||||||
// block this particularly obnoxious case which is always a bug
|
|
||||||
if (lua_type(L, 3) == LUA_TSTRING) {
|
if (lua_type(L, 3) == LUA_TSTRING) {
|
||||||
luaL_typeerror(L, 3, "indexable");
|
luaL_typeerror(L, 3, "indexable");
|
||||||
}
|
}
|
||||||
@@ -1380,7 +1396,7 @@ int lkiwi_solver_suggest_values(lua_State* L) {
|
|||||||
lua_geti(L, 3, i);
|
lua_geti(L, 3, i);
|
||||||
double value = luaL_checknumber(L, -1);
|
double value = luaL_checknumber(L, -1);
|
||||||
|
|
||||||
const KiwiErr* err = kiwi_solver_suggest_value(self->solver, *var, value);
|
const KiwiErr* err = kiwi_solver_suggest_value(self->solver, var, value);
|
||||||
if (err) {
|
if (err) {
|
||||||
error_new(L, err, 1, narg + 1 /* item_absi */);
|
error_new(L, err, 1, narg + 1 /* item_absi */);
|
||||||
unsigned error_mask = self->error_mask;
|
unsigned error_mask = self->error_mask;
|
||||||
@@ -1445,7 +1461,8 @@ constexpr const struct luaL_Reg kiwi_solver_m[] = {
|
|||||||
{"set_error_mask", lkiwi_solver_set_error_mask},
|
{"set_error_mask", lkiwi_solver_set_error_mask},
|
||||||
{"__tostring", lkiwi_solver_m_tostring},
|
{"__tostring", lkiwi_solver_m_tostring},
|
||||||
{"__gc", lkiwi_solver_m_gc},
|
{"__gc", lkiwi_solver_m_gc},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int lkiwi_solver_new(lua_State* L) {
|
int lkiwi_solver_new(lua_State* L) {
|
||||||
lua_Integer error_mask;
|
lua_Integer error_mask;
|
||||||
@@ -1542,7 +1559,8 @@ constexpr const struct luaL_Reg lkiwi[] = {
|
|||||||
{"eq", lkiwi_eq},
|
{"eq", lkiwi_eq},
|
||||||
{"le", lkiwi_le},
|
{"le", lkiwi_le},
|
||||||
{"ge", lkiwi_ge},
|
{"ge", lkiwi_ge},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int no_member_mt_index(lua_State* L) {
|
int no_member_mt_index(lua_State* L) {
|
||||||
luaL_error(L, "attempt to access non-existent member '%s'", lua_tostring(L, 2));
|
luaL_error(L, "attempt to access non-existent member '%s'", lua_tostring(L, 2));
|
||||||
@@ -1608,6 +1626,8 @@ void compat_init(lua_State*, int) {}
|
|||||||
|
|
||||||
#if defined __GNUC__ && (!defined _WIN32 || defined __CYGWIN__)
|
#if defined __GNUC__ && (!defined _WIN32 || defined __CYGWIN__)
|
||||||
#define LJKIWI_EXPORT __attribute__((__visibility__("default")))
|
#define LJKIWI_EXPORT __attribute__((__visibility__("default")))
|
||||||
|
#elif defined _WIN32
|
||||||
|
#define LJKIWI_EXPORT __declspec(dllexport)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern "C" LJKIWI_EXPORT int luaopen_ljkiwi(lua_State* L) {
|
extern "C" LJKIWI_EXPORT int luaopen_ljkiwi(lua_State* L) {
|
||||||
@@ -1618,6 +1638,8 @@ extern "C" LJKIWI_EXPORT int luaopen_ljkiwi(lua_State* L) {
|
|||||||
int ctx_i = lua_gettop(L);
|
int ctx_i = lua_gettop(L);
|
||||||
|
|
||||||
compat_init(L, ctx_i);
|
compat_init(L, ctx_i);
|
||||||
|
lua_pushliteral(L, "kiwi library memory allocation error");
|
||||||
|
lua_rawseti(L, ctx_i, MEM_ERR_MSG);
|
||||||
|
|
||||||
no_member_mt_new(L);
|
no_member_mt_new(L);
|
||||||
register_type(L, "kiwi.Var", ctx_i, VAR, kiwi_var_m);
|
register_type(L, "kiwi.Var", ctx_i, VAR, kiwi_var_m);
|
||||||
|
|||||||
40
rockspecs/kiwi-0.1.0-1.rockspec
Normal file
40
rockspecs/kiwi-0.1.0-1.rockspec
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
rockspec_format = "3.0"
|
||||||
|
package = "kiwi"
|
||||||
|
version = "0.1.0-1"
|
||||||
|
source = {
|
||||||
|
url = "git+https://github.com/jkl1337/ljkiwi",
|
||||||
|
tag = "v0.1.0",
|
||||||
|
}
|
||||||
|
description = {
|
||||||
|
summary = "LuaJIT FFI and Lua binding for the Kiwi constraint solver.",
|
||||||
|
detailed = [[
|
||||||
|
kiwi is a LuaJIT FFI and Lua binding for the Kiwi constraint solver. Kiwi is a fast
|
||||||
|
implementation of the Cassowary constraint solving algorithm. kiwi provides
|
||||||
|
reasonably efficient bindings using the LuaJIT FFI and convential Lua C bindings.]],
|
||||||
|
license = "MIT",
|
||||||
|
issues_url = "https://github.com/jkl1337/ljkiwi/issues",
|
||||||
|
maintainer = "John Luebs",
|
||||||
|
}
|
||||||
|
dependencies = {
|
||||||
|
"lua >= 5.1",
|
||||||
|
}
|
||||||
|
|
||||||
|
build = {
|
||||||
|
type = "make",
|
||||||
|
build_variables = {
|
||||||
|
LUAROCKS = "1",
|
||||||
|
LUA = "$(LUA)",
|
||||||
|
CFLAGS = "$(CFLAGS)",
|
||||||
|
LUA_INCDIR = "$(LUA_INCDIR)",
|
||||||
|
LUA_LIBDIR = "$(LUA_LIBDIR)",
|
||||||
|
LUALIB = "$(LUALIB)",
|
||||||
|
LIBFLAG = "$(LIBFLAG)",
|
||||||
|
LIB_EXT = "$(LIB_EXTENSION)",
|
||||||
|
OBJ_EXT = "$(OBJ_EXTENSION)",
|
||||||
|
},
|
||||||
|
install_variables = {
|
||||||
|
INST_LIBDIR = "$(LIBDIR)",
|
||||||
|
INST_LUADIR = "$(LUADIR)",
|
||||||
|
LIB_EXT = "$(LIB_EXTENSION)",
|
||||||
|
},
|
||||||
|
}
|
||||||
40
rockspecs/kiwi-0.1.1-1.rockspec
Normal file
40
rockspecs/kiwi-0.1.1-1.rockspec
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
rockspec_format = "3.0"
|
||||||
|
package = "kiwi"
|
||||||
|
version = "0.1.1-1"
|
||||||
|
source = {
|
||||||
|
url = "git+https://github.com/jkl1337/ljkiwi",
|
||||||
|
tag = "v0.1.1",
|
||||||
|
}
|
||||||
|
description = {
|
||||||
|
summary = "LuaJIT FFI and Lua binding for the Kiwi constraint solver.",
|
||||||
|
detailed = [[
|
||||||
|
kiwi is a LuaJIT FFI and Lua binding for the Kiwi constraint solver. Kiwi is a fast
|
||||||
|
implementation of the Cassowary constraint solving algorithm. kiwi provides
|
||||||
|
reasonably efficient bindings using the LuaJIT FFI and convential Lua C bindings.]],
|
||||||
|
license = "MIT",
|
||||||
|
issues_url = "https://github.com/jkl1337/ljkiwi/issues",
|
||||||
|
maintainer = "John Luebs",
|
||||||
|
}
|
||||||
|
dependencies = {
|
||||||
|
"lua >= 5.1",
|
||||||
|
}
|
||||||
|
|
||||||
|
build = {
|
||||||
|
type = "make",
|
||||||
|
build_variables = {
|
||||||
|
LUAROCKS = "1",
|
||||||
|
LUA = "$(LUA)",
|
||||||
|
CFLAGS = "$(CFLAGS)",
|
||||||
|
LUA_INCDIR = "$(LUA_INCDIR)",
|
||||||
|
LUA_LIBDIR = "$(LUA_LIBDIR)",
|
||||||
|
LUALIB = "$(LUALIB)",
|
||||||
|
LIBFLAG = "$(LIBFLAG)",
|
||||||
|
LIB_EXT = "$(LIB_EXTENSION)",
|
||||||
|
OBJ_EXT = "$(OBJ_EXTENSION)",
|
||||||
|
},
|
||||||
|
install_variables = {
|
||||||
|
INST_LIBDIR = "$(LIBDIR)",
|
||||||
|
INST_LUADIR = "$(LUADIR)",
|
||||||
|
LIB_EXT = "$(LIB_EXTENSION)",
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -21,9 +21,12 @@ dependencies = {
|
|||||||
build = {
|
build = {
|
||||||
type = "make",
|
type = "make",
|
||||||
build_variables = {
|
build_variables = {
|
||||||
|
LUAROCKS = "1",
|
||||||
LUA = "$(LUA)",
|
LUA = "$(LUA)",
|
||||||
CFLAGS = "$(CFLAGS)",
|
CFLAGS = "$(CFLAGS)",
|
||||||
LUA_INCDIR = "$(LUA_INCDIR)",
|
LUA_INCDIR = "$(LUA_INCDIR)",
|
||||||
|
LUA_LIBDIR = "$(LUA_LIBDIR)",
|
||||||
|
LUALIB = "$(LUALIB)",
|
||||||
LIBFLAG = "$(LIBFLAG)",
|
LIBFLAG = "$(LIBFLAG)",
|
||||||
LIB_EXT = "$(LIB_EXTENSION)",
|
LIB_EXT = "$(LIB_EXTENSION)",
|
||||||
OBJ_EXT = "$(OBJ_EXTENSION)",
|
OBJ_EXT = "$(OBJ_EXTENSION)",
|
||||||
240
spec/expression_spec.lua
Normal file
240
spec/expression_spec.lua
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
expose("module", function()
|
||||||
|
require("kiwi")
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Expression", function()
|
||||||
|
local kiwi = require("kiwi")
|
||||||
|
local LUA_VERSION = tonumber(_VERSION:match("%d+%.%d+"))
|
||||||
|
|
||||||
|
it("construction", function()
|
||||||
|
local v = kiwi.Var("foo")
|
||||||
|
local v2 = kiwi.Var("bar")
|
||||||
|
local v3 = kiwi.Var("aux")
|
||||||
|
local e1 = kiwi.Expression(0, v * 1, v2 * 2, v3 * 3)
|
||||||
|
local e2 = kiwi.Expression(10, v * 1, v2 * 2, v3 * 3)
|
||||||
|
|
||||||
|
local constants = { 0, 10 }
|
||||||
|
for i, e in ipairs({ e1, e2 }) do
|
||||||
|
assert.equal(constants[i], e.constant)
|
||||||
|
local terms = e:terms()
|
||||||
|
assert.equal(3, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(1.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(2.0, terms[2].coefficient)
|
||||||
|
assert.equal(v3, terms[3].var)
|
||||||
|
assert.equal(3.0, terms[3].coefficient)
|
||||||
|
end
|
||||||
|
|
||||||
|
if LUA_VERSION <= 5.2 then
|
||||||
|
assert.equal("1 foo + 2 bar + 3 aux + 10", tostring(e2))
|
||||||
|
else
|
||||||
|
assert.equal("1.0 foo + 2.0 bar + 3.0 aux + 10.0", tostring(e2))
|
||||||
|
end
|
||||||
|
|
||||||
|
assert.error(function()
|
||||||
|
kiwi.Expression(0, 0, v2 * 2, v3 * 3)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("method", function()
|
||||||
|
local v, t, e
|
||||||
|
before_each(function()
|
||||||
|
v = kiwi.Var("foo")
|
||||||
|
v:set(42)
|
||||||
|
t = kiwi.Term(v, 10)
|
||||||
|
e = t + 5
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("has value", function()
|
||||||
|
v:set(42)
|
||||||
|
assert.equal(425, e:value())
|
||||||
|
v:set(87)
|
||||||
|
assert.equal(875, e:value())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("can be copied", function()
|
||||||
|
local e2 = e:copy()
|
||||||
|
assert.equal(e.constant, e2.constant)
|
||||||
|
local t1, t2 = e:terms(), e2:terms()
|
||||||
|
assert.equal(#t1, #t2)
|
||||||
|
for i = 1, #t1 do
|
||||||
|
assert.equal(t1[i].var, t2[i].var)
|
||||||
|
assert.equal(t1[i].coefficient, t2[i].coefficient)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("neg", function()
|
||||||
|
local neg = -e --[[@as kiwi.Expression]]
|
||||||
|
assert.True(kiwi.is_expression(neg))
|
||||||
|
local terms = neg:terms()
|
||||||
|
assert.equal(1, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(-10.0, terms[1].coefficient)
|
||||||
|
assert.equal(-5, neg.constant)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("bin op", function()
|
||||||
|
local v2, t2, e2
|
||||||
|
before_each(function()
|
||||||
|
v2 = kiwi.Var("bar")
|
||||||
|
t2 = kiwi.Term(v2)
|
||||||
|
e2 = v2 - 10
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("mul", function()
|
||||||
|
for _, prod in ipairs({ e * 2.0, 2 * e }) do
|
||||||
|
assert.True(kiwi.is_expression(prod))
|
||||||
|
local terms = prod:terms()
|
||||||
|
assert.equal(1, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(20.0, terms[1].coefficient)
|
||||||
|
assert.equal(10, prod.constant)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert.error(function()
|
||||||
|
local _ = e * v
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("div", function()
|
||||||
|
local quot = e / 2.0
|
||||||
|
assert.True(kiwi.is_expression(quot))
|
||||||
|
local terms = quot:terms()
|
||||||
|
assert.equal(1, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(5.0, terms[1].coefficient)
|
||||||
|
assert.equal(2.5, quot.constant)
|
||||||
|
|
||||||
|
assert.error(function()
|
||||||
|
local _ = e / v2
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("add", function()
|
||||||
|
for _, sum in ipairs({ e + 2.0, 2 + e }) do
|
||||||
|
assert.True(kiwi.is_expression(sum))
|
||||||
|
assert.equal(7.0, sum.constant)
|
||||||
|
|
||||||
|
local terms = sum:terms()
|
||||||
|
assert.equal(1, #terms)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
end
|
||||||
|
|
||||||
|
local sum = e + v2
|
||||||
|
assert.True(kiwi.is_expression(sum))
|
||||||
|
assert.equal(5, sum.constant)
|
||||||
|
local terms = sum:terms()
|
||||||
|
assert.equal(2, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
sum = e + t2
|
||||||
|
assert.True(kiwi.is_expression(sum))
|
||||||
|
assert.equal(5, sum.constant)
|
||||||
|
terms = sum:terms()
|
||||||
|
assert.equal(2, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
sum = e + e2
|
||||||
|
assert.True(kiwi.is_expression(sum))
|
||||||
|
assert.equal(-5, sum.constant)
|
||||||
|
terms = sum:terms()
|
||||||
|
assert.equal(2, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
assert.error(function()
|
||||||
|
local _ = t + "foo"
|
||||||
|
end)
|
||||||
|
assert.error(function()
|
||||||
|
local _ = t + {}
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("sub", function()
|
||||||
|
local constants = { 3, -3 }
|
||||||
|
for i, diff in ipairs({ e - 2.0, 2 - e }) do
|
||||||
|
local constant = constants[i]
|
||||||
|
assert.True(kiwi.is_expression(diff))
|
||||||
|
assert.equal(constant, diff.constant)
|
||||||
|
|
||||||
|
local terms = diff:terms()
|
||||||
|
assert.equal(1, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(constant < 0 and -10.0 or 10.0, terms[1].coefficient)
|
||||||
|
end
|
||||||
|
|
||||||
|
local diff = e - v2
|
||||||
|
assert.True(kiwi.is_expression(diff))
|
||||||
|
assert.equal(5, diff.constant)
|
||||||
|
local terms = diff:terms()
|
||||||
|
assert.equal(2, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(-1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
diff = e - t2
|
||||||
|
assert.True(kiwi.is_expression(diff))
|
||||||
|
assert.equal(5, diff.constant)
|
||||||
|
terms = diff:terms()
|
||||||
|
assert.equal(2, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(-1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
diff = e - e2
|
||||||
|
assert.True(kiwi.is_expression(diff))
|
||||||
|
assert.equal(15, diff.constant)
|
||||||
|
terms = diff:terms()
|
||||||
|
assert.equal(2, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(-1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
assert.error(function()
|
||||||
|
local _ = e - "foo"
|
||||||
|
end)
|
||||||
|
assert.error(function()
|
||||||
|
local _ = e - {}
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("constraint expr op expr", function()
|
||||||
|
local ops = { "LE", "EQ", "GE" }
|
||||||
|
for i, meth in ipairs({ "le", "eq", "ge" }) do
|
||||||
|
local c = e[meth](e, e2)
|
||||||
|
assert.True(kiwi.is_constraint(c))
|
||||||
|
|
||||||
|
local expr = c:expression()
|
||||||
|
local terms = expr:terms()
|
||||||
|
assert.equal(2, #terms)
|
||||||
|
|
||||||
|
-- order can be randomized due to use of map
|
||||||
|
if terms[1].var ~= v then
|
||||||
|
terms[1], terms[2] = terms[2], terms[1]
|
||||||
|
end
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(-1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
assert.equal(15, expr.constant)
|
||||||
|
assert.equal(ops[i], c:op())
|
||||||
|
assert.equal(kiwi.strength.REQUIRED, c:strength())
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
245
spec/term_spec.lua
Normal file
245
spec/term_spec.lua
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
expose("module", function()
|
||||||
|
require("kiwi")
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Term", function()
|
||||||
|
local kiwi = require("kiwi")
|
||||||
|
local LUA_VERSION = tonumber(_VERSION:match("%d+%.%d+"))
|
||||||
|
|
||||||
|
it("construction", function()
|
||||||
|
local v = kiwi.Var("foo")
|
||||||
|
local t = kiwi.Term(v)
|
||||||
|
assert.equal(v, t.var)
|
||||||
|
assert.equal(1.0, t.coefficient)
|
||||||
|
|
||||||
|
t = kiwi.Term(v, 100)
|
||||||
|
assert.equal(v, t.var)
|
||||||
|
assert.equal(100, t.coefficient)
|
||||||
|
|
||||||
|
if LUA_VERSION <= 5.2 then
|
||||||
|
assert.equal("100 foo", tostring(t))
|
||||||
|
else
|
||||||
|
assert.equal("100.0 foo", tostring(t))
|
||||||
|
end
|
||||||
|
|
||||||
|
assert.error(function()
|
||||||
|
kiwi.Term("")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("method", function()
|
||||||
|
local v, v2, t, t2
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
v = kiwi.Var("foo")
|
||||||
|
t = kiwi.Term(v, 10)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("has value", function()
|
||||||
|
v:set(42)
|
||||||
|
assert.equal(420, t:value())
|
||||||
|
v:set(87)
|
||||||
|
assert.equal(870, t:value())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("has toexpr", function()
|
||||||
|
local e = t:toexpr()
|
||||||
|
assert.True(kiwi.is_expression(e))
|
||||||
|
assert.equal(0, e.constant)
|
||||||
|
local terms = e:terms()
|
||||||
|
assert.equal(1, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("neg", function()
|
||||||
|
local neg = -t --[[@as kiwi.Term]]
|
||||||
|
assert.True(kiwi.is_term(neg))
|
||||||
|
assert.equal(v, neg.var)
|
||||||
|
assert.equal(-10, neg.coefficient)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("bin op", function()
|
||||||
|
before_each(function()
|
||||||
|
v2 = kiwi.Var("bar")
|
||||||
|
t2 = kiwi.Term(v2)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("mul", function()
|
||||||
|
for _, prod in ipairs({ t * 2.0, 2 * t }) do
|
||||||
|
assert.True(kiwi.is_term(prod))
|
||||||
|
assert.equal(v, prod.var)
|
||||||
|
assert.equal(20, prod.coefficient)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert.error(function()
|
||||||
|
local _ = t * v
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("div", function()
|
||||||
|
local quot = t / 2.0
|
||||||
|
assert.True(kiwi.is_term(quot))
|
||||||
|
assert.equal(v, quot.var)
|
||||||
|
assert.equal(5.0, quot.coefficient)
|
||||||
|
|
||||||
|
assert.error(function()
|
||||||
|
local _ = v / v2
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("add", function()
|
||||||
|
for _, sum in ipairs({ t + 2.0, 2 + t }) do
|
||||||
|
assert.True(kiwi.is_expression(sum))
|
||||||
|
assert.equal(2.0, sum.constant)
|
||||||
|
|
||||||
|
local terms = sum:terms()
|
||||||
|
assert.equal(1, #terms)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
end
|
||||||
|
|
||||||
|
local sum = t + v2
|
||||||
|
assert.True(kiwi.is_expression(sum))
|
||||||
|
assert.equal(0, sum.constant)
|
||||||
|
local terms = sum:terms()
|
||||||
|
assert.equal(2, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
sum = t + t2
|
||||||
|
assert.True(kiwi.is_expression(sum))
|
||||||
|
assert.equal(0, sum.constant)
|
||||||
|
terms = sum:terms()
|
||||||
|
assert.equal(2, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
local t3 = kiwi.Term(v2, 20)
|
||||||
|
sum = t3 + sum
|
||||||
|
assert.True(kiwi.is_expression(sum))
|
||||||
|
assert.equal(0, sum.constant)
|
||||||
|
terms = sum:terms()
|
||||||
|
assert.equal(3, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(1.0, terms[2].coefficient)
|
||||||
|
assert.equal(v2, terms[3].var)
|
||||||
|
assert.equal(20.0, terms[3].coefficient)
|
||||||
|
|
||||||
|
assert.error(function()
|
||||||
|
local _ = t + "foo"
|
||||||
|
end)
|
||||||
|
assert.error(function()
|
||||||
|
local _ = t + {}
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("sub", function()
|
||||||
|
local constants = { -2, 2 }
|
||||||
|
for i, diff in ipairs({ t - 2.0, 2 - t }) do
|
||||||
|
local constant = constants[i]
|
||||||
|
assert.True(kiwi.is_expression(diff))
|
||||||
|
assert.equal(constant, diff.constant)
|
||||||
|
|
||||||
|
local terms = diff:terms()
|
||||||
|
assert.equal(1, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(constant < 0 and 10.0 or -10.0, terms[1].coefficient)
|
||||||
|
end
|
||||||
|
|
||||||
|
local diff = t - v2
|
||||||
|
assert.True(kiwi.is_expression(diff))
|
||||||
|
assert.equal(0, diff.constant)
|
||||||
|
local terms = diff:terms()
|
||||||
|
assert.equal(2, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(-1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
diff = t - t2
|
||||||
|
assert.True(kiwi.is_expression(diff))
|
||||||
|
assert.equal(0, diff.constant)
|
||||||
|
terms = diff:terms()
|
||||||
|
assert.equal(2, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(-1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
local t3 = kiwi.Term(v2, 20)
|
||||||
|
diff = t3 - diff
|
||||||
|
assert.True(kiwi.is_expression(diff))
|
||||||
|
assert.equal(0, diff.constant)
|
||||||
|
terms = diff:terms()
|
||||||
|
assert.equal(3, #terms)
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(-10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(1.0, terms[2].coefficient)
|
||||||
|
assert.equal(v2, terms[3].var)
|
||||||
|
assert.equal(20.0, terms[3].coefficient)
|
||||||
|
|
||||||
|
assert.error(function()
|
||||||
|
local _ = t - "foo"
|
||||||
|
end)
|
||||||
|
assert.error(function()
|
||||||
|
local _ = t - {}
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("constraint term op expr", function()
|
||||||
|
local ops = { "LE", "EQ", "GE" }
|
||||||
|
for i, meth in ipairs({ "le", "eq", "ge" }) do
|
||||||
|
local c = t[meth](t, v2 + 1)
|
||||||
|
assert.True(kiwi.is_constraint(c))
|
||||||
|
|
||||||
|
local e = c:expression()
|
||||||
|
local terms = e:terms()
|
||||||
|
assert.equal(2, #terms)
|
||||||
|
|
||||||
|
-- order can be randomized due to use of map
|
||||||
|
if terms[1].var ~= v then
|
||||||
|
terms[1], terms[2] = terms[2], terms[1]
|
||||||
|
end
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(-1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
assert.equal(-1, e.constant)
|
||||||
|
assert.equal(ops[i], c:op())
|
||||||
|
assert.equal(kiwi.strength.REQUIRED, c:strength())
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("constraint term op term", function()
|
||||||
|
for i, meth in ipairs({ "le", "eq", "ge" }) do
|
||||||
|
local c = t[meth](t, t2)
|
||||||
|
assert.True(kiwi.is_constraint(c))
|
||||||
|
|
||||||
|
local e = c:expression()
|
||||||
|
local terms = e:terms()
|
||||||
|
assert.equal(2, #terms)
|
||||||
|
|
||||||
|
-- order can be randomized due to use of map
|
||||||
|
if terms[1].var ~= v then
|
||||||
|
terms[1], terms[2] = terms[2], terms[1]
|
||||||
|
end
|
||||||
|
assert.equal(v, terms[1].var)
|
||||||
|
assert.equal(10.0, terms[1].coefficient)
|
||||||
|
assert.equal(v2, terms[2].var)
|
||||||
|
assert.equal(-1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
assert.equal(0, e.constant)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
@@ -127,6 +127,8 @@ describe("Var", function()
|
|||||||
assert.equal(v2, terms[2].var)
|
assert.equal(v2, terms[2].var)
|
||||||
assert.equal(-1.0, terms[2].coefficient)
|
assert.equal(-1.0, terms[2].coefficient)
|
||||||
|
|
||||||
|
-- TODO: terms and expressions
|
||||||
|
|
||||||
assert.error(function()
|
assert.error(function()
|
||||||
local _ = v - "foo"
|
local _ = v - "foo"
|
||||||
end)
|
end)
|
||||||
|
|||||||
32
t.lua
32
t.lua
@@ -1,32 +0,0 @@
|
|||||||
local kiwi = require("kiwi")
|
|
||||||
|
|
||||||
local c
|
|
||||||
--debug.getupvalue
|
|
||||||
do
|
|
||||||
local v1 = kiwi.Var("v1")
|
|
||||||
local v2 = kiwi.Var("v2")
|
|
||||||
local v3 = kiwi.Var("v3")
|
|
||||||
local v4 = kiwi.Var("v4")
|
|
||||||
local v5 = kiwi.Var("v5")
|
|
||||||
local c1 = (3 * v1 + 4 * v2 + 6 * v3):eq(0)
|
|
||||||
local c2 = (6 * v4 + 4 * v5 + 2 * v1 + 1.4 * v1 / 0.3):le(1000)
|
|
||||||
|
|
||||||
local e = c1:expression()
|
|
||||||
local s = kiwi.Solver()
|
|
||||||
|
|
||||||
s:add_constraints({ c1, c2, c1 })
|
|
||||||
print(s:dumps())
|
|
||||||
end
|
|
||||||
|
|
||||||
-- c = (3 * v1 + 4 * v2 + 6 * v3):eq(0)
|
|
||||||
|
|
||||||
-- local t = c:expression():terms()
|
|
||||||
-- print(t[2].var)
|
|
||||||
|
|
||||||
-- for k, v in ipairs(t) do
|
|
||||||
-- print(k, v.var, v.coefficient)
|
|
||||||
-- end
|
|
||||||
|
|
||||||
-- for k, v in pairs(kiwi.ErrKind) do
|
|
||||||
-- print(k, v)
|
|
||||||
-- end
|
|
||||||
204
tmp/enaml.lua
204
tmp/enaml.lua
@@ -1,204 +0,0 @@
|
|||||||
local kiwi = require("kiwi")
|
|
||||||
|
|
||||||
local Var = kiwi.Var
|
|
||||||
|
|
||||||
---@param solver kiwi.Solver
|
|
||||||
local function build_solver(solver)
|
|
||||||
-- create custom strength
|
|
||||||
--
|
|
||||||
local width = Var("width")
|
|
||||||
local height = Var("height")
|
|
||||||
|
|
||||||
local mmedium = kiwi.strength.create(0.0, 1.0, 0.0, 1.25)
|
|
||||||
local smedium = kiwi.strength.create(0.0, 100, 0.0)
|
|
||||||
local left = Var("left")
|
|
||||||
local top = Var("top")
|
|
||||||
local contents_top = Var("contents_top")
|
|
||||||
local contents_bottom = Var("contents_bottom")
|
|
||||||
local contents_left = Var("contents_left")
|
|
||||||
local contents_right = Var("contents_right")
|
|
||||||
local midline = Var("midline")
|
|
||||||
local ctleft = Var("ctleft")
|
|
||||||
local ctheight = Var("ctheight")
|
|
||||||
local cttop = Var("cttop")
|
|
||||||
local ctwidth = Var("ctwidth")
|
|
||||||
local lb1left = Var("lb1left")
|
|
||||||
local lb1height = Var("lb1height")
|
|
||||||
local lb1top = Var("lb1top")
|
|
||||||
local lb1width = Var("lb1width")
|
|
||||||
local lb2left = Var("lb2left")
|
|
||||||
local lb2height = Var("lb2height")
|
|
||||||
local lb2top = Var("lb2top")
|
|
||||||
local lb2width = Var("lb2width")
|
|
||||||
local lb3left = Var("lb3left")
|
|
||||||
local lb3height = Var("lb3height")
|
|
||||||
local lb3top = Var("lb3top")
|
|
||||||
local lb3width = Var("lb3width")
|
|
||||||
local fl1left = Var("fl1left")
|
|
||||||
local fl1height = Var("fl1height")
|
|
||||||
local fl1top = Var("fl1top")
|
|
||||||
local fl1width = Var("fl1width")
|
|
||||||
local fl2left = Var("fl2left")
|
|
||||||
local fl2height = Var("fl2height")
|
|
||||||
local fl2top = Var("fl2top")
|
|
||||||
local fl2width = Var("fl2width")
|
|
||||||
local fl3left = Var("fl3left")
|
|
||||||
local fl3height = Var("fl3height")
|
|
||||||
local fl3top = Var("fl3top")
|
|
||||||
local fl3width = Var("fl3width")
|
|
||||||
|
|
||||||
solver:add_edit_var(width, kiwi.strength.STRONG)
|
|
||||||
solver:add_edit_var(height, kiwi.strength.STRONG)
|
|
||||||
|
|
||||||
local strong, medium, weak = kiwi.strength.STRONG, kiwi.strength.MEDIUM, kiwi.strength.WEAK
|
|
||||||
local constraints = {
|
|
||||||
(left + -0):ge(0),
|
|
||||||
(height + 0):eq(0, medium),
|
|
||||||
(top + -0):ge(0),
|
|
||||||
(width + -0):ge(0),
|
|
||||||
(height + -0):ge(0),
|
|
||||||
(-top + contents_top + -10):eq(0),
|
|
||||||
(lb3height + -16):eq(0, strong),
|
|
||||||
(lb3height + -16):ge(0, strong),
|
|
||||||
(ctleft + -0):ge(0),
|
|
||||||
(cttop + -0):ge(0),
|
|
||||||
(ctwidth + -0):ge(0),
|
|
||||||
(ctheight + -0):ge(0),
|
|
||||||
(fl3left + -0):ge(0),
|
|
||||||
(ctheight + -24):ge(0, smedium),
|
|
||||||
(ctwidth + -1.67772e+07):le(0, smedium),
|
|
||||||
(ctheight + -24):le(0, smedium),
|
|
||||||
(fl3top + -0):ge(0),
|
|
||||||
(fl3width + -0):ge(0),
|
|
||||||
(fl3height + -0):ge(0),
|
|
||||||
(lb1width + -67):eq(0, weak),
|
|
||||||
(lb2width + -0):ge(0),
|
|
||||||
(lb2height + -0):ge(0),
|
|
||||||
(fl2height + -0):ge(0),
|
|
||||||
(lb3left + -0):ge(0),
|
|
||||||
(fl2width + -125):ge(0, strong),
|
|
||||||
(fl2height + -21):eq(0, strong),
|
|
||||||
(fl2height + -21):ge(0, strong),
|
|
||||||
(lb3top + -0):ge(0),
|
|
||||||
(lb3width + -0):ge(0),
|
|
||||||
(fl1left + -0):ge(0),
|
|
||||||
(fl1width + -0):ge(0),
|
|
||||||
(lb1width + -67):ge(0, strong),
|
|
||||||
(fl2left + -0):ge(0),
|
|
||||||
(lb2width + -66):eq(0, weak),
|
|
||||||
(lb2width + -66):ge(0, strong),
|
|
||||||
(lb2height + -16):eq(0, strong),
|
|
||||||
(fl1height + -0):ge(0),
|
|
||||||
(fl1top + -0):ge(0),
|
|
||||||
(lb2top + -0):ge(0),
|
|
||||||
(-lb2top + lb3top + -lb2height + -10):eq(0, mmedium),
|
|
||||||
(-lb3top + -lb3height + fl3top + -10):ge(0),
|
|
||||||
(-lb3top + -lb3height + fl3top + -10):eq(0, mmedium),
|
|
||||||
(contents_bottom + -fl3height + -fl3top + -0):eq(0, mmedium),
|
|
||||||
(fl1top + -contents_top + 0):ge(0),
|
|
||||||
(fl1top + -contents_top + 0):eq(0, mmedium),
|
|
||||||
(contents_bottom + -fl3height + -fl3top + -0):ge(0),
|
|
||||||
(-left + -width + contents_right + 10):eq(0),
|
|
||||||
(-top + -height + contents_bottom + 10):eq(0),
|
|
||||||
(-left + contents_left + -10):eq(0),
|
|
||||||
(lb3left + -contents_left + 0):eq(0, mmedium),
|
|
||||||
(fl1left + -midline + 0):eq(0, strong),
|
|
||||||
(fl2left + -midline + 0):eq(0, strong),
|
|
||||||
(ctleft + -midline + 0):eq(0, strong),
|
|
||||||
(fl1top + 0.5 * fl1height + -lb1top + -0.5 * lb1height + 0):eq(0, strong),
|
|
||||||
(lb1left + -contents_left + 0):ge(0),
|
|
||||||
(lb1left + -contents_left + 0):eq(0, mmedium),
|
|
||||||
(-lb1left + fl1left + -lb1width + -10):ge(0),
|
|
||||||
(-lb1left + fl1left + -lb1width + -10):eq(0, mmedium),
|
|
||||||
(-fl1left + contents_right + -fl1width + -0):ge(0),
|
|
||||||
(width + 0):eq(0, medium),
|
|
||||||
(-fl1top + fl2top + -fl1height + -10):ge(0),
|
|
||||||
(-fl1top + fl2top + -fl1height + -10):eq(0, mmedium),
|
|
||||||
(cttop + -fl2top + -fl2height + -10):ge(0),
|
|
||||||
(-ctheight + -cttop + fl3top + -10):ge(0),
|
|
||||||
(contents_bottom + -fl3height + -fl3top + -0):ge(0),
|
|
||||||
(cttop + -fl2top + -fl2height + -10):eq(0, mmedium),
|
|
||||||
(-fl1left + contents_right + -fl1width + -0):eq(0, mmedium),
|
|
||||||
(-lb2top + -0.5 * lb2height + fl2top + 0.5 * fl2height + 0):eq(0, strong),
|
|
||||||
(-contents_left + lb2left + 0):ge(0),
|
|
||||||
(-contents_left + lb2left + 0):eq(0, mmedium),
|
|
||||||
(fl2left + -lb2width + -lb2left + -10):ge(0),
|
|
||||||
(-ctheight + -cttop + fl3top + -10):eq(0, mmedium),
|
|
||||||
(contents_bottom + -fl3height + -fl3top + -0):eq(0, mmedium),
|
|
||||||
(lb1top + -0):ge(0),
|
|
||||||
(lb1width + -0):ge(0),
|
|
||||||
(lb1height + -0):ge(0),
|
|
||||||
(fl2left + -lb2width + -lb2left + -10):eq(0, mmedium),
|
|
||||||
(-fl2left + -fl2width + contents_right + -0):eq(0, mmedium),
|
|
||||||
(-fl2left + -fl2width + contents_right + -0):ge(0),
|
|
||||||
(lb3left + -contents_left + 0):ge(0),
|
|
||||||
(lb1left + -0):ge(0),
|
|
||||||
(0.5 * ctheight + cttop + -lb3top + -0.5 * lb3height + 0):eq(0, strong),
|
|
||||||
(ctleft + -lb3left + -lb3width + -10):ge(0),
|
|
||||||
(-ctwidth + -ctleft + contents_right + -0):ge(0),
|
|
||||||
(ctleft + -lb3left + -lb3width + -10):eq(0, mmedium),
|
|
||||||
(fl3left + -contents_left + 0):ge(0),
|
|
||||||
(fl3left + -contents_left + 0):eq(0, mmedium),
|
|
||||||
(-ctwidth + -ctleft + contents_right + -0):eq(0, mmedium),
|
|
||||||
(-fl3left + contents_right + -fl3width + -0):eq(0, mmedium),
|
|
||||||
(-contents_top + lb1top + 0):ge(0),
|
|
||||||
(-contents_top + lb1top + 0):eq(0, mmedium),
|
|
||||||
(-fl3left + contents_right + -fl3width + -0):ge(0),
|
|
||||||
(lb2top + -lb1top + -lb1height + -10):ge(0),
|
|
||||||
(-lb2top + lb3top + -lb2height + -10):ge(0),
|
|
||||||
(lb2top + -lb1top + -lb1height + -10):eq(0, mmedium),
|
|
||||||
(fl1height + -21):eq(0, strong),
|
|
||||||
(fl1height + -21):ge(0, strong),
|
|
||||||
(lb2left + -0):ge(0),
|
|
||||||
(lb2height + -16):ge(0, strong),
|
|
||||||
(fl2top + -0):ge(0),
|
|
||||||
(fl2width + -0):ge(0),
|
|
||||||
(lb1height + -16):ge(0, strong),
|
|
||||||
(lb1height + -16):eq(0, strong),
|
|
||||||
(fl3width + -125):ge(0, strong),
|
|
||||||
(fl3height + -21):eq(0, strong),
|
|
||||||
(fl3height + -21):ge(0, strong),
|
|
||||||
(lb3height + -0):ge(0),
|
|
||||||
(ctwidth + -119):ge(0, smedium),
|
|
||||||
(lb3width + -24):eq(0, weak),
|
|
||||||
(lb3width + -24):ge(0, strong),
|
|
||||||
(fl1width + -125):ge(0, strong),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c in ipairs(constraints) do
|
|
||||||
print(c)
|
|
||||||
solver:add_constraint(c)
|
|
||||||
end
|
|
||||||
|
|
||||||
return width, height
|
|
||||||
end
|
|
||||||
|
|
||||||
local function main()
|
|
||||||
local sizes = {
|
|
||||||
{ w = 400, h = 600 },
|
|
||||||
{ w = 600, h = 400 },
|
|
||||||
{ w = 800, h = 1200 },
|
|
||||||
{ w = 1200, h = 800 },
|
|
||||||
{ w = 400, h = 800 },
|
|
||||||
{ w = 800, h = 400 },
|
|
||||||
}
|
|
||||||
|
|
||||||
for i = 1, 1 do
|
|
||||||
local solver = kiwi.Solver()
|
|
||||||
|
|
||||||
local width, height = build_solver(solver)
|
|
||||||
|
|
||||||
for _, size in ipairs(sizes) do
|
|
||||||
solver:suggest_value(width, size.w)
|
|
||||||
solver:suggest_value(height, size.h)
|
|
||||||
solver:update_vars()
|
|
||||||
print(width:value(), height:value())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
main()
|
|
||||||
collectgarbage("collect")
|
|
||||||
collectgarbage("collect")
|
|
||||||
collectgarbage("collect")
|
|
||||||
collectgarbage("collect")
|
|
||||||
90
tmp/ex.lua
90
tmp/ex.lua
@@ -1,90 +0,0 @@
|
|||||||
local kiwi = require("kiwi")
|
|
||||||
local Var = kiwi.Var
|
|
||||||
|
|
||||||
local Button = setmetatable({}, {
|
|
||||||
__call = function(_, identifier)
|
|
||||||
return setmetatable({
|
|
||||||
left = Var(identifier .. " left"),
|
|
||||||
width = Var(identifier .. " width"),
|
|
||||||
}, {
|
|
||||||
__tostring = function(self)
|
|
||||||
return "Button(" .. self.left:value() .. ", " .. self.width:value() .. ")"
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
local b1 = Button("b1")
|
|
||||||
local b2 = Button("b2")
|
|
||||||
|
|
||||||
local left_edge = Var("left")
|
|
||||||
local right_edge = Var("width")
|
|
||||||
|
|
||||||
local STRONG = kiwi.Strength.STRONG
|
|
||||||
|
|
||||||
local constraints
|
|
||||||
|
|
||||||
for i = 1, 200 do
|
|
||||||
q = kiwi.Term(left_edge, 2.0)
|
|
||||||
end
|
|
||||||
|
|
||||||
for i = 1, 50, 1 do
|
|
||||||
-- stylua: ignore start
|
|
||||||
constraints = {
|
|
||||||
left_edge :eq(0.0),
|
|
||||||
-- two buttons are the same width
|
|
||||||
b1.width :eq(b2.width),
|
|
||||||
-- button1 starts 50 for the left margin
|
|
||||||
(1.00 * b1.left) :eq(left_edge + 50),
|
|
||||||
-- button2 ends 50 from the right margin
|
|
||||||
right_edge :eq(b2.left + b2.width + 50),
|
|
||||||
-- button2 starts at least 100 from the end of button1. This is the "elastic" constraint
|
|
||||||
b2.left :ge(b1.left + b1.width + 100),
|
|
||||||
-- button1 has a minimum width of 87
|
|
||||||
b1.width :ge(87),
|
|
||||||
-- button1 has a preferred width of 87
|
|
||||||
b1.width :eq(87, STRONG),
|
|
||||||
-- button2 has minimum width of 113
|
|
||||||
b2.width :ge(113),
|
|
||||||
-- button2 has a preferred width of 113
|
|
||||||
b2.width :eq(113, STRONG),
|
|
||||||
}
|
|
||||||
-- stylua: ignore end
|
|
||||||
--
|
|
||||||
k = kiwi.Strength.create(0.0, 1.0, 0.0, 1.25)
|
|
||||||
end
|
|
||||||
local solver = kiwi.Solver()
|
|
||||||
|
|
||||||
for _, c in ipairs(constraints) do
|
|
||||||
print(c)
|
|
||||||
solver:add_constraint(c)
|
|
||||||
end
|
|
||||||
|
|
||||||
solver:update_vars()
|
|
||||||
|
|
||||||
print(b1) -- Button(50, 113)
|
|
||||||
print(b2) -- Button(263, 113)
|
|
||||||
print(left_edge:value()) -- 0
|
|
||||||
print(right_edge:value()) -- 426
|
|
||||||
|
|
||||||
solver:add_edit_var(right_edge, STRONG)
|
|
||||||
solver:suggest_value(right_edge, 500)
|
|
||||||
solver:update_vars()
|
|
||||||
-- solver:dump()
|
|
||||||
-- print(b1) -- Button(50, 113)
|
|
||||||
-- print(b2) -- Button(337, 113)
|
|
||||||
-- print(right_edge:value()) -- 500
|
|
||||||
|
|
||||||
-- print(solver:dumps())
|
|
||||||
|
|
||||||
-- solver = kiwi.Solver()
|
|
||||||
|
|
||||||
-- local trailing = Var("trailing")
|
|
||||||
-- local leading = Var("leading")
|
|
||||||
|
|
||||||
-- solver:add_constraint(kiwi.new_single_constraint(trailing, 86))
|
|
||||||
-- solver:add_constraint(kiwi.new_pair_constraint(trailing, leading, 8.0))
|
|
||||||
-- print(solver:dumps())
|
|
||||||
-- solver:update_vars()
|
|
||||||
-- print(trailing:value()) -- 86
|
|
||||||
-- print(leading:value()) -- 76
|
|
||||||
28
tmp/ex2.lua
28
tmp/ex2.lua
@@ -1,28 +0,0 @@
|
|||||||
local kiwi = require("kiwi")
|
|
||||||
|
|
||||||
local Var = kiwi.Var
|
|
||||||
|
|
||||||
local function doit(v, i)
|
|
||||||
v:set(i)
|
|
||||||
end
|
|
||||||
|
|
||||||
local v = Var("fuckit")
|
|
||||||
|
|
||||||
local s = kiwi.Solver()
|
|
||||||
|
|
||||||
local e
|
|
||||||
for i = 1, 1000 do
|
|
||||||
e = kiwi.Expression(5.0, kiwi.Term(v, 3.0), kiwi.Term(v, 2.0))
|
|
||||||
end
|
|
||||||
--local e = (v + 1)
|
|
||||||
|
|
||||||
s:add_constraint(e:eq(100))
|
|
||||||
|
|
||||||
print(v:value())
|
|
||||||
|
|
||||||
s:update_vars()
|
|
||||||
|
|
||||||
v:set_name("fuck")
|
|
||||||
print(v:value())
|
|
||||||
|
|
||||||
s:dump()
|
|
||||||
49
tmp/ex3.lua
49
tmp/ex3.lua
@@ -1,49 +0,0 @@
|
|||||||
local kiwi = require("kiwi")
|
|
||||||
local ffi = require("ffi")
|
|
||||||
|
|
||||||
local t
|
|
||||||
local e
|
|
||||||
local strformat = string.format
|
|
||||||
local Strength = kiwi.Strength
|
|
||||||
|
|
||||||
--local bad_str = "-1 v8 + -1 v4 + -1 v2 + -4 v9 + -2 v3 + -1 v6 + -1 v5 + -1 v1 + -2 v7 + -5"
|
|
||||||
-- local b = ""
|
|
||||||
|
|
||||||
function s(self)
|
|
||||||
local ops = {
|
|
||||||
[0] = "<=",
|
|
||||||
">=",
|
|
||||||
"==",
|
|
||||||
}
|
|
||||||
local strengths = {
|
|
||||||
[Strength.REQUIRED] = "required",
|
|
||||||
[Strength.STRONG] = "strong",
|
|
||||||
[Strength.MEDIUM] = "medium",
|
|
||||||
[Strength.WEAK] = "weak",
|
|
||||||
}
|
|
||||||
local strength = self:strength()
|
|
||||||
--local e = self:expression()
|
|
||||||
local s = strformat("%s %s %s", "aaa", tostring(self:expression()), tonumber(self:op()))
|
|
||||||
return s
|
|
||||||
end
|
|
||||||
|
|
||||||
for i = 1, 2000 do
|
|
||||||
local v1 = kiwi.Var("v1")
|
|
||||||
local v2 = kiwi.Var("v2")
|
|
||||||
local v3 = kiwi.Var("v3")
|
|
||||||
local v4 = kiwi.Var("v4")
|
|
||||||
|
|
||||||
local v5 = kiwi.Var("v5")
|
|
||||||
local v6 = kiwi.Var("v6")
|
|
||||||
local v7 = kiwi.Var("v7")
|
|
||||||
local v8 = kiwi.Var("v8")
|
|
||||||
local v9 = kiwi.Var("v9")
|
|
||||||
local v10 = kiwi.Var("v9")
|
|
||||||
|
|
||||||
do
|
|
||||||
--
|
|
||||||
local c = ((-(v1 + v2 + 2 * v3 + v4 + 3)):eq(v5 + v6 + 2 * v7 + v8 + 4 * v9 + 3))
|
|
||||||
s(c)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print(t)
|
|
||||||
37
tmp/ex4.lua
37
tmp/ex4.lua
@@ -1,37 +0,0 @@
|
|||||||
local kiwi = require("kiwi")
|
|
||||||
local ffi = require("ffi")
|
|
||||||
|
|
||||||
local Var = kiwi.Var
|
|
||||||
|
|
||||||
local v1 = Var("v1")
|
|
||||||
local v2 = Var("v2")
|
|
||||||
local v3 = Var("v3")
|
|
||||||
local v4 = Var("v4")
|
|
||||||
local v5 = Var("v5")
|
|
||||||
local v6 = Var("v6")
|
|
||||||
|
|
||||||
local f1 = kiwi.f1
|
|
||||||
local f2 = kiwi.f2
|
|
||||||
|
|
||||||
local single = kiwi.constraints.single
|
|
||||||
|
|
||||||
local function execute_times(f, times)
|
|
||||||
local begin = os.clock()
|
|
||||||
for _ = 1, times do
|
|
||||||
f()
|
|
||||||
end
|
|
||||||
local finish = os.clock()
|
|
||||||
return finish - begin
|
|
||||||
end
|
|
||||||
|
|
||||||
local t = execute_times(function()
|
|
||||||
return kiwi.constraints.pair_ratio(v1, 2.0, v2, 3.0)
|
|
||||||
end, 6000000)
|
|
||||||
|
|
||||||
print(t)
|
|
||||||
|
|
||||||
-- local t = execute_times(function()
|
|
||||||
-- return pair_ratio2(v1, 2.0, v2, 3.0)
|
|
||||||
-- end, 2000000)
|
|
||||||
|
|
||||||
-- print(t)
|
|
||||||
41
tmp/ex5.lua
41
tmp/ex5.lua
@@ -1,41 +0,0 @@
|
|||||||
local kiwi = require("kiwi")
|
|
||||||
|
|
||||||
local Var = kiwi.Var
|
|
||||||
|
|
||||||
x1 = Var("x1")
|
|
||||||
x2 = Var("x2")
|
|
||||||
xm = Var("xm")
|
|
||||||
|
|
||||||
local constraints = { x1:ge(0), x2:le(100), x2:ge(x1 + 10), xm:eq((x1 + x2) / 2) }
|
|
||||||
|
|
||||||
local solver = kiwi.Solver()
|
|
||||||
|
|
||||||
for _, c in ipairs(constraints) do
|
|
||||||
solver:add_constraint(c)
|
|
||||||
end
|
|
||||||
|
|
||||||
local c = kiwi.constraints.single(x1, 40, "EQ", kiwi.Strength.WEAK)
|
|
||||||
solver:add_constraint(c)
|
|
||||||
|
|
||||||
constraints[#constraints + 1] = c
|
|
||||||
|
|
||||||
solver:add_edit_var(xm, kiwi.Strength.STRONG)
|
|
||||||
|
|
||||||
solver:suggest_value(xm, 60)
|
|
||||||
|
|
||||||
solver:update_vars()
|
|
||||||
|
|
||||||
print(xm:value(), x1:value(), x2:value())
|
|
||||||
|
|
||||||
for _, c in ipairs(constraints) do
|
|
||||||
print(c, c:violated())
|
|
||||||
end
|
|
||||||
|
|
||||||
solver:suggest_value(xm, 90)
|
|
||||||
solver:update_vars()
|
|
||||||
|
|
||||||
print(xm:value(), x1:value(), x2:value())
|
|
||||||
|
|
||||||
for _, c in ipairs(constraints) do
|
|
||||||
print(c, c:violated())
|
|
||||||
end
|
|
||||||
26
tmp/t.lua
26
tmp/t.lua
@@ -1,26 +0,0 @@
|
|||||||
local k = require("kiwi")
|
|
||||||
|
|
||||||
s = k.Solver()
|
|
||||||
|
|
||||||
--s:set_error_mask({ "KiwiErrUnknownEditVariable" }, false)
|
|
||||||
|
|
||||||
local v1 = k.Var("v1")
|
|
||||||
local v2 = k.Var("v2")
|
|
||||||
local v3 = k.Var("v3")
|
|
||||||
local v4 = k.Var("v4")
|
|
||||||
|
|
||||||
print((2 * v1 - 5 * v2))
|
|
||||||
|
|
||||||
os.exit()
|
|
||||||
local c1 = k.constraints.pair_ratio(v1, -4, v2, 22, "GE", 1000)
|
|
||||||
local c2 = k.constraints.pair_ratio(v3, -6, v4, 29, "LE", 1000)
|
|
||||||
|
|
||||||
local vo = c1:expression():terms()[1].var
|
|
||||||
print(v1 == vo)
|
|
||||||
-- print(v1:name())
|
|
||||||
-- print(v1 * 5)
|
|
||||||
|
|
||||||
local e = k.Expression(6, k.Term(v1, 2), k.Term(v2, 3))
|
|
||||||
--print(e)
|
|
||||||
|
|
||||||
--print(k.strength.MEDIUM)
|
|
||||||
66
tmp/test.lua
66
tmp/test.lua
@@ -1,66 +0,0 @@
|
|||||||
local kiwi = require("kiwi")
|
|
||||||
|
|
||||||
local Var = kiwi.Var
|
|
||||||
local Strength = kiwi.Strength
|
|
||||||
|
|
||||||
local Button_mt = {}
|
|
||||||
|
|
||||||
---@class Button
|
|
||||||
---@field left kiwi.Var
|
|
||||||
---@field width kiwi.Var
|
|
||||||
---@overload fun(identifier: string): Button
|
|
||||||
local Button = setmetatable({}, Button_mt)
|
|
||||||
|
|
||||||
function Button_mt.__call(_, identifier)
|
|
||||||
return setmetatable({
|
|
||||||
left = Var("left" .. identifier),
|
|
||||||
width = Var("width" .. identifier),
|
|
||||||
}, {
|
|
||||||
__index = Button,
|
|
||||||
__tostring = function(self)
|
|
||||||
return "Button(" .. self.left:value() .. ", " .. self.width:value() .. ")"
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local b1 = Button("b1")
|
|
||||||
local b2 = Button("b2")
|
|
||||||
|
|
||||||
local left_limit = kiwi.Var("left")
|
|
||||||
local right_limit = kiwi.Var("width")
|
|
||||||
|
|
||||||
|
|
||||||
-- stylua: ignore start
|
|
||||||
local constraints = {
|
|
||||||
left_limit :eq( 0 ),
|
|
||||||
b1.width :eq( b2.width ),
|
|
||||||
b1.left :eq( left_limit + 50),
|
|
||||||
right_limit:eq( b2.left + b2.width + 50),
|
|
||||||
b2.left :ge( b1.left + b1.width + 100),
|
|
||||||
|
|
||||||
b1.width :ge( 87 ),
|
|
||||||
b1.width :eq( 87, Strength.STRONG),
|
|
||||||
|
|
||||||
b2.width :ge( 113 ),
|
|
||||||
b2.width :eq( 113, Strength.STRONG),
|
|
||||||
}
|
|
||||||
-- stylua: ignore end
|
|
||||||
|
|
||||||
local s = kiwi.Solver()
|
|
||||||
|
|
||||||
for _, c in ipairs(constraints) do
|
|
||||||
s:add_constraint(c)
|
|
||||||
end
|
|
||||||
|
|
||||||
print(constraints[4]:expression())
|
|
||||||
print(constraints[5]:expression())
|
|
||||||
|
|
||||||
kiwi.Constraint(b1.left + 1, "EQ")
|
|
||||||
|
|
||||||
s:update_vars()
|
|
||||||
|
|
||||||
print(b1)
|
|
||||||
print(b2)
|
|
||||||
print(left_limit:value())
|
|
||||||
print(right_limit:value())
|
|
||||||
print(s:dumps())
|
|
||||||
Reference in New Issue
Block a user