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
|
||||
ReflowComments: false
|
||||
SeparateDefinitionBlocks: Always # New in v14.
|
||||
SortIncludes: false
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
|
||||
98
.github/workflows/busted.yml
vendored
98
.github/workflows/busted.yml
vendored
@@ -1,3 +1,4 @@
|
||||
---
|
||||
name: Busted
|
||||
|
||||
on: [push, pull_request]
|
||||
@@ -7,13 +8,25 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
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:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
if: ${{ !startsWith(matrix.lua_version, 'luajit-') }}
|
||||
|
||||
- name: Setup ‘lua’
|
||||
uses: jkl1337/gh-actions-lua@master
|
||||
with:
|
||||
@@ -23,14 +36,75 @@ jobs:
|
||||
- name: Setup dependencies
|
||||
run: |
|
||||
luarocks install busted
|
||||
luarocks install luacov-coveralls
|
||||
- name: Build C library
|
||||
run: make
|
||||
- name: Run busted tests
|
||||
run: busted -c -v
|
||||
- name: Report test coverage
|
||||
if: success()
|
||||
continue-on-error: true
|
||||
run: luacov-coveralls -e .luarocks -e spec
|
||||
luarocks install luacov-reporter-lcov
|
||||
- name: Build C++ library
|
||||
run: |
|
||||
luarocks make --no-install
|
||||
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_modules
|
||||
/.luarocks
|
||||
/config.mk
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.gcov
|
||||
*.lcov
|
||||
*.out
|
||||
*.pch
|
||||
*.gch
|
||||
*.lib
|
||||
*.so
|
||||
*.o
|
||||
*.obj
|
||||
*.exp
|
||||
*.dll
|
||||
*.rock
|
||||
.cache/
|
||||
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
|
||||
RM := rm
|
||||
LIBFLAG := -shared
|
||||
LIB_EXT := so
|
||||
LIB_EXT := $(if $(filter Windows_NT,$(OS)),dll,so)
|
||||
LUA_INCDIR := /usr/include
|
||||
|
||||
SRCDIR := .
|
||||
|
||||
OPTFLAG := -O2
|
||||
CCFLAGS += $(OPTFLAG) -fPIC -Wall -fstrict-flex-arrays -fvisibility=hidden -Wformat=2 -Wconversion -Wimplicit-fallthrough
|
||||
SANITIZE_FLAGS := -fsanitize=undefined -fsanitize=address -fsanitize=alignment \
|
||||
-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
|
||||
|
||||
ifdef SANITIZE
|
||||
CCFLAGS += $(SANITIZE_FLAGS)
|
||||
endif
|
||||
ifdef LTO
|
||||
CCFLAGS += $(LTO_FLAGS)
|
||||
endif
|
||||
ifeq ($(OS),Windows_NT)
|
||||
is_clang = $(filter %clang++,$(CXX))
|
||||
is_gcc = $(filter %g++,$(CXX))
|
||||
|
||||
override CPPFLAGS += -I$(SRCDIR) -I$(SRCDIR)/kiwi -I$(LUA_INCDIR)
|
||||
override CXXFLAGS += -std=c++14 -fno-rtti $(CCFLAGS)
|
||||
override CFLAGS += -std=c99 $(CCFLAGS)
|
||||
|
||||
ifneq ($(filter %gcc,$(CC)),)
|
||||
CXX := $(patsubst %gcc,%g++,$(CC))
|
||||
PCH := ljkiwi.hpp.gch
|
||||
ifdef FSANITIZE
|
||||
$(error "FSANITIZE is not supported on Windows")
|
||||
endif
|
||||
else
|
||||
ifneq ($(filter %clang,$(CC)),)
|
||||
CXX := $(patsubst %clang,%clang++,$(CC))
|
||||
override CXXFLAGS += -pedantic -Wno-c99-extensions
|
||||
PCH := ljkiwi.hpp.pch
|
||||
endif
|
||||
endif
|
||||
uname_s := $(shell uname -s)
|
||||
ifeq ($(uname_s),Darwin)
|
||||
is_clang = 1
|
||||
is_gcc =
|
||||
|
||||
ifdef LUA
|
||||
LUA_VERSION ?= $(lastword $(shell $(LUA) -e "print(_VERSION)"))
|
||||
endif
|
||||
CC := env MACOSX_DEPLOYMENT_TARGET=11.0 gcc
|
||||
CXX := env MACOSX_DEPLOYMENT_TARGET=11.0 g++
|
||||
LIBFLAG := -bundle -undefined dynamic_lookup
|
||||
|
||||
ifndef LUA_VERSION
|
||||
LJKIWI_CKIWI := 1
|
||||
else
|
||||
ifneq ($(LUA_VERSION),5.1)
|
||||
LJKIWI_CKIWI :=
|
||||
else
|
||||
is_clang = $(filter %clang++,$(CXX))
|
||||
is_gcc = $(filter %g++,$(CXX))
|
||||
|
||||
SANITIZE_FLAGS += -fsanitize=bounds-strict
|
||||
endif
|
||||
endif
|
||||
|
||||
OBJS := luakiwi.o
|
||||
ifdef LJKIWI_CKIWI
|
||||
OBJS += ckiwi.o
|
||||
-include config.mk
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
@@ -61,25 +130,26 @@ install:
|
||||
$(CP) -f ljkiwi.$(LIB_EXT) $(INST_LIBDIR)/ljkiwi.$(LIB_EXT)
|
||||
$(CP) -f kiwi.lua $(INST_LUADIR)/kiwi.lua
|
||||
|
||||
clean:
|
||||
$(RM) -f ljkiwi.$(LIB_EXT) $(OBJS) $(PCH)
|
||||
mostlyclean:
|
||||
$(RM) -f ljkiwi.$(LIB_EXT) $(objs) $(objs:.o=.gcda) $(objs:.o=.gcno)
|
||||
|
||||
ckiwi.o: $(PCH) ckiwi.cpp ckiwi.h
|
||||
luakiwi.o: $(PCH) luakiwi-int.h luacompat.h
|
||||
clean: mostlyclean
|
||||
$(RM) -f $(PCH)
|
||||
|
||||
ljkiwi.$(LIB_EXT): $(OBJS)
|
||||
$(CXX) $(CCFLAGS) $(LIBFLAG) -o $@ $(OBJS)
|
||||
ckiwi.o: $(PCH) ckiwi.cpp ckiwi.h $(kiwi_lib_srcs)
|
||||
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
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -x c++-header -o $@ $<
|
||||
|
||||
%.hpp.pch: %.hpp
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -x c++-header -o $@ $<
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -x c++-header $<
|
||||
|
||||
%.o: %.cpp
|
||||
$(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://coveralls.io/github/jkl1337/ljkiwi?branch=master)
|
||||
[](https://luarocks.org/modules/jkl/ljkiwi)
|
||||
[](https://luarocks.org/modules/jkl/kiwi)
|
||||
|
||||
# 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.
|
||||
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.
|
||||
Since the C++ Kiwi library is well tested and widely used it was simpler to provide a LuaJIT FFI wrapper and use that.
|
||||
This package has no dependencies other than a C++14 toolchain to compile the included Kiwi library and a small C wrapper.
|
||||
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, 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
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
|
||||
## 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 <kiwi/kiwi.h>
|
||||
|
||||
#include <climits>
|
||||
#include <cstdarg>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#if defined(__GNUC__) && !defined(LJKIWI_NO_BUILTIN)
|
||||
#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 {
|
||||
KiwiErrUnknown,
|
||||
"An unhandled C++ exception occurred."};
|
||||
"An unhandled C++ exception occurred."
|
||||
};
|
||||
|
||||
static const constexpr KiwiErr kKiwiErrNullObjectArg0 {
|
||||
KiwiErrNullObject,
|
||||
"null object passed as argument #0 (self)"};
|
||||
"null object passed as argument #0 (self)"
|
||||
};
|
||||
|
||||
static const constexpr KiwiErr kKiwiErrNullObjectArg1 {
|
||||
KiwiErrNullObject,
|
||||
"null object passed as argument #1"};
|
||||
"null object passed as argument #1"
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
inline const KiwiErr* wrap_err(F&& f) {
|
||||
const KiwiErr* wrap_err(F&& f) {
|
||||
try {
|
||||
f();
|
||||
} catch (const UnsatisfiableConstraint& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrUnsatisfiableConstraint,
|
||||
"The constraint cannot be satisfied."};
|
||||
"The constraint cannot be satisfied."
|
||||
};
|
||||
return &err;
|
||||
} catch (const UnknownConstraint& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrUnknownConstraint,
|
||||
"The constraint has not been added to the solver."};
|
||||
"The constraint has not been added to the solver."
|
||||
};
|
||||
return &err;
|
||||
|
||||
} catch (const DuplicateConstraint& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrDuplicateConstraint,
|
||||
"The constraint has already been added to the solver."};
|
||||
"The constraint has already been added to the solver."
|
||||
};
|
||||
return &err;
|
||||
|
||||
} catch (const UnknownEditVariable& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrUnknownEditVariable,
|
||||
"The edit variable has not been added to the solver."};
|
||||
"The edit variable has not been added to the solver."
|
||||
};
|
||||
return &err;
|
||||
|
||||
} catch (const DuplicateEditVariable& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrDuplicateEditVariable,
|
||||
"The edit variable has already been added to the solver."};
|
||||
"The edit variable has already been added to the solver."
|
||||
};
|
||||
return &err;
|
||||
|
||||
} catch (const BadRequiredStrength& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrBadRequiredStrength,
|
||||
"A required strength cannot be used in this context."};
|
||||
"A required strength cannot be used in this context."
|
||||
};
|
||||
return &err;
|
||||
|
||||
} catch (const InternalSolverError& ex) {
|
||||
static const constexpr KiwiErr base {
|
||||
KiwiErrInternalSolverError,
|
||||
"An internal solver error occurred."};
|
||||
"An internal solver error occurred."
|
||||
};
|
||||
return new_error(&base, ex);
|
||||
} catch (std::bad_alloc&) {
|
||||
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>
|
||||
inline const KiwiErr* wrap_err(P self, F&& f) {
|
||||
const KiwiErr* wrap_err(P self, F&& f) {
|
||||
if (lk_unlikely(!self)) {
|
||||
return &kKiwiErrNullObjectArg0;
|
||||
}
|
||||
@@ -110,36 +122,51 @@ inline const KiwiErr* wrap_err(P self, F&& 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)) {
|
||||
return &kKiwiErrNullObjectArg0;
|
||||
} else if (lk_unlikely(!item)) {
|
||||
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
|
||||
|
||||
extern "C" {
|
||||
|
||||
KiwiTypeInfo kiwi_ti_KiwiVar = {sizeof(KiwiVar), alignof(KiwiVar)};
|
||||
KiwiTypeInfo kiwi_ti_KiwiConstraint = {sizeof(KiwiConstraint), alignof(KiwiConstraint)};
|
||||
|
||||
void kiwi_var_construct(const char* name, void* mem) {
|
||||
new (mem) KiwiVar {lk_likely(name) ? name : ""};
|
||||
KiwiVar* kiwi_var_construct(const char* name) {
|
||||
return make_unmanaged<VariableData>(lk_likely(name) ? name : "");
|
||||
}
|
||||
|
||||
void kiwi_var_release(KiwiVar* var) {
|
||||
if (lk_likely(var))
|
||||
var->~KiwiVar();
|
||||
release_unmanaged(var);
|
||||
}
|
||||
|
||||
void kiwi_var_retain(KiwiVar* var) {
|
||||
if (lk_likely(var)) {
|
||||
alignas(KiwiVar) unsigned char buf[sizeof(KiwiVar)];
|
||||
new (buf) KiwiVar(*var);
|
||||
}
|
||||
retain_unmanaged(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) {
|
||||
if (lk_likely(var))
|
||||
if (lk_likely(var && name))
|
||||
var->setName(name);
|
||||
}
|
||||
|
||||
@@ -160,60 +187,60 @@ void kiwi_var_set_value(KiwiVar* var, double 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) {
|
||||
if (lk_unlikely(!expr))
|
||||
return;
|
||||
alignas(KiwiVar) unsigned char buf[sizeof(KiwiVar)];
|
||||
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) {
|
||||
if (lk_unlikely(!expr))
|
||||
if (lk_unlikely(!expr || !expr->owner))
|
||||
return;
|
||||
|
||||
if (expr->owner) {
|
||||
expr->owner->~KiwiConstraint();
|
||||
} else {
|
||||
if (expr->owner == expr) {
|
||||
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* rhs,
|
||||
enum KiwiRelOp op,
|
||||
double strength,
|
||||
void* mem
|
||||
double strength
|
||||
) {
|
||||
if (strength < 0.0) {
|
||||
strength = kiwi::strength::required;
|
||||
}
|
||||
|
||||
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) {
|
||||
for (auto* t = lhs->terms_; t != lhs->terms_ + lhs->term_count; ++t) {
|
||||
if (t->var)
|
||||
terms.emplace_back(*t->var, t->coefficient);
|
||||
for (int i = 0; i < lhs->term_count; ++i) {
|
||||
const auto& t = lhs->terms_[i];
|
||||
if (t.var)
|
||||
terms.emplace_back(Variable(t.var), t.coefficient);
|
||||
}
|
||||
}
|
||||
if (rhs) {
|
||||
for (auto* t = rhs->terms_; t != rhs->terms_ + rhs->term_count; ++t) {
|
||||
if (t->var)
|
||||
terms.emplace_back(*t->var, -t->coefficient);
|
||||
for (int i = 0; i < rhs->term_count; ++i) {
|
||||
const auto& t = rhs->terms_[i];
|
||||
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)),
|
||||
static_cast<RelationalOperator>(op),
|
||||
strength
|
||||
@@ -221,15 +248,11 @@ void kiwi_constraint_construct(
|
||||
}
|
||||
|
||||
void kiwi_constraint_release(KiwiConstraint* c) {
|
||||
if (lk_likely(c))
|
||||
c->~KiwiConstraint();
|
||||
release_unmanaged(c);
|
||||
}
|
||||
|
||||
void kiwi_constraint_retain(KiwiConstraint* c) {
|
||||
if (lk_likely(c)) {
|
||||
alignas(KiwiConstraint) unsigned char buf[sizeof(KiwiConstraint)];
|
||||
new (buf) KiwiConstraint(*c);
|
||||
}
|
||||
retain_unmanaged(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& terms = expr.terms();
|
||||
const auto term_count = static_cast<int>(terms.size());
|
||||
if (!out || out_size < term_count)
|
||||
return term_count;
|
||||
int n = terms.size() < INT_MAX ? static_cast<int>(terms.size()) : INT_MAX;
|
||||
if (!out || out_size < n)
|
||||
return n;
|
||||
|
||||
for (int i = 0; i < term_count; ++i) {
|
||||
out->terms_[i].var = const_cast<KiwiVar*>(&terms[i].variable());
|
||||
out->terms_[i].coefficient = terms[i].coefficient();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
const auto& t = terms[static_cast<std::size_t>(i)];
|
||||
out->terms_[i].var = const_cast<Variable&>(t.variable()).ptr();
|
||||
out->terms_[i].coefficient = t.coefficient();
|
||||
}
|
||||
out->constant = expr.constant();
|
||||
out->term_count = term_count;
|
||||
out->owner = c;
|
||||
kiwi_constraint_retain(c);
|
||||
out->term_count = n;
|
||||
out->owner = retain_unmanaged(c);
|
||||
|
||||
return term_count;
|
||||
return n;
|
||||
}
|
||||
|
||||
struct KiwiSolver {
|
||||
@@ -271,15 +294,13 @@ struct KiwiSolver {
|
||||
Solver solver;
|
||||
};
|
||||
|
||||
KiwiTypeInfo kiwi_ti_KiwiSolver = {sizeof(KiwiSolver), alignof(KiwiSolver)};
|
||||
|
||||
void kiwi_solver_construct(unsigned error_mask, void* mem) {
|
||||
new (mem) KiwiSolver {error_mask};
|
||||
KiwiSolver* kiwi_solver_construct(unsigned error_mask) {
|
||||
return new KiwiSolver {error_mask};
|
||||
}
|
||||
|
||||
void kiwi_solver_destroy(KiwiSolver* s) {
|
||||
if (lk_likely(s))
|
||||
s->~KiwiSolver();
|
||||
delete 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;
|
||||
}
|
||||
|
||||
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, const KiwiConstraint* constraint) {
|
||||
return wrap_err(s, constraint, [](auto& s, const auto& c) { s.addConstraint(c); });
|
||||
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, KiwiConstraint* constraint) {
|
||||
return wrap_err(s, constraint, [](auto&& s, auto&& c) { s.addConstraint(Constraint(c)); });
|
||||
}
|
||||
|
||||
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, const KiwiConstraint* constraint) {
|
||||
return wrap_err(s, constraint, [](auto& s, const auto& c) { s.removeConstraint(c); });
|
||||
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, KiwiConstraint* constraint) {
|
||||
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))
|
||||
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) {
|
||||
return wrap_err(s, var, [strength](auto& s, const auto& v) {
|
||||
s.addEditVariable(v, strength);
|
||||
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, KiwiVar* var, double strength) {
|
||||
return wrap_err(s, var, [strength](auto&& s, auto&& v) {
|
||||
s.addEditVariable(Variable(v), strength);
|
||||
});
|
||||
}
|
||||
|
||||
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, const KiwiVar* var) {
|
||||
return wrap_err(s, var, [](auto& s, const auto& v) { s.removeEditVariable(v); });
|
||||
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, KiwiVar* var) {
|
||||
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))
|
||||
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) {
|
||||
return wrap_err(s, var, [value](auto& s, const auto& v) { s.suggestValue(v, value); });
|
||||
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, KiwiVar* var, double value) {
|
||||
return wrap_err(s, var, [value](auto&& s, auto&& v) { s.suggestValue(Variable(v), value); });
|
||||
}
|
||||
|
||||
void kiwi_solver_update_vars(KiwiSolver* s) {
|
||||
@@ -353,4 +374,31 @@ char* kiwi_solver_dumps(const KiwiSolver* s) {
|
||||
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"
|
||||
|
||||
112
ckiwi/ckiwi.h
112
ckiwi/ckiwi.h
@@ -10,30 +10,27 @@
|
||||
#ifdef __cplusplus
|
||||
|
||||
namespace kiwi {
|
||||
class Variable;
|
||||
class Constraint;
|
||||
class VariableData;
|
||||
class ConstraintData;
|
||||
} // namespace kiwi
|
||||
|
||||
typedef kiwi::VariableData KiwiVar;
|
||||
typedef kiwi::ConstraintData KiwiConstraint;
|
||||
|
||||
extern "C" {
|
||||
|
||||
typedef kiwi::Variable KiwiVar;
|
||||
typedef kiwi::Constraint KiwiConstraint;
|
||||
#else
|
||||
typedef struct KiwiVar KiwiVar;
|
||||
typedef struct KiwiConstraint KiwiConstraint;
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct KiwiTypeInfo {
|
||||
unsigned size;
|
||||
unsigned align;
|
||||
} KiwiTypeInfo;
|
||||
|
||||
#if __GNUC__
|
||||
#pragma GCC visibility push(default)
|
||||
#if defined __GNUC__ && (!defined _WIN32 || defined __CYGWIN__)
|
||||
#define LJKIWI_EXP __attribute__((visibility("default")))
|
||||
#elif defined _WIN32
|
||||
#define LJKIWI_EXP __declspec(dllexport)
|
||||
#endif
|
||||
|
||||
extern KiwiTypeInfo kiwi_ti_KiwiVar, kiwi_ti_KiwiConstraint, kiwi_ti_KiwiSolver;
|
||||
|
||||
// LuaJIT start
|
||||
enum KiwiErrKind {
|
||||
KiwiErrNone,
|
||||
@@ -59,12 +56,12 @@ typedef struct KiwiTerm {
|
||||
typedef struct KiwiExpression {
|
||||
double constant;
|
||||
int term_count;
|
||||
KiwiConstraint* owner;
|
||||
void* owner;
|
||||
|
||||
#if defined(LJKIWI_LUAJIT_DEF)
|
||||
KiwiTerm terms_[?];
|
||||
#elif defined(LJKIWI_USE_FAM_1)
|
||||
KiwiTerm terms_[1]; // LuaJIT: struct KiwiTerm terms_[?];
|
||||
KiwiTerm terms_[1];
|
||||
#else
|
||||
KiwiTerm terms_[];
|
||||
#endif
|
||||
@@ -77,58 +74,67 @@ typedef struct KiwiErr {
|
||||
bool must_free;
|
||||
} 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;
|
||||
|
||||
void kiwi_var_construct(const char* name, void* mem);
|
||||
void kiwi_var_release(KiwiVar* var);
|
||||
void kiwi_var_retain(KiwiVar* var);
|
||||
LJKIWI_EXP void kiwi_tuple_init(KiwiTuple* tuple, int count, ...);
|
||||
LJKIWI_EXP void kiwi_tuple_destroy(KiwiTuple* tuple);
|
||||
|
||||
const char* kiwi_var_name(const KiwiVar* var);
|
||||
void kiwi_var_set_name(KiwiVar* var, const char* name);
|
||||
double kiwi_var_value(const KiwiVar* var);
|
||||
void kiwi_var_set_value(KiwiVar* var, double value);
|
||||
bool kiwi_var_eq(const KiwiVar* var, const KiwiVar* other);
|
||||
LJKIWI_EXP KiwiVar* kiwi_var_construct(const char* name);
|
||||
LJKIWI_EXP void kiwi_var_release(KiwiVar* var);
|
||||
LJKIWI_EXP void kiwi_var_retain(KiwiVar* var);
|
||||
|
||||
void kiwi_expression_retain(KiwiExpression* expr);
|
||||
void kiwi_expression_destroy(KiwiExpression* expr);
|
||||
LJKIWI_EXP const char* kiwi_var_name(const KiwiVar* var);
|
||||
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* rhs,
|
||||
enum KiwiRelOp op,
|
||||
double strength,
|
||||
void* mem
|
||||
double strength
|
||||
);
|
||||
void kiwi_constraint_release(KiwiConstraint* c);
|
||||
void kiwi_constraint_retain(KiwiConstraint* c);
|
||||
LJKIWI_EXP void kiwi_constraint_release(KiwiConstraint* c);
|
||||
LJKIWI_EXP void kiwi_constraint_retain(KiwiConstraint* c);
|
||||
|
||||
double kiwi_constraint_strength(const KiwiConstraint* c);
|
||||
enum KiwiRelOp kiwi_constraint_op(const KiwiConstraint* c);
|
||||
bool kiwi_constraint_violated(const KiwiConstraint* c);
|
||||
int kiwi_constraint_expression(KiwiConstraint* c, KiwiExpression* out, int out_size);
|
||||
LJKIWI_EXP double kiwi_constraint_strength(const KiwiConstraint* c);
|
||||
LJKIWI_EXP enum KiwiRelOp kiwi_constraint_op(const KiwiConstraint* c);
|
||||
LJKIWI_EXP bool kiwi_constraint_violated(const KiwiConstraint* c);
|
||||
LJKIWI_EXP int kiwi_constraint_expression(KiwiConstraint* c, KiwiExpression* out, int out_size);
|
||||
|
||||
void kiwi_solver_construct(unsigned error_mask, void* mem);
|
||||
void kiwi_solver_destroy(KiwiSolver* s);
|
||||
unsigned kiwi_solver_get_error_mask(const KiwiSolver* s);
|
||||
void kiwi_solver_set_error_mask(KiwiSolver* s, unsigned mask);
|
||||
LJKIWI_EXP KiwiSolver* kiwi_solver_construct(unsigned error_mask);
|
||||
LJKIWI_EXP void kiwi_solver_destroy(KiwiSolver* s);
|
||||
LJKIWI_EXP unsigned kiwi_solver_get_error_mask(const KiwiSolver* s);
|
||||
LJKIWI_EXP 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_remove_constraint(KiwiSolver* s, const KiwiConstraint* constraint);
|
||||
bool kiwi_solver_has_constraint(const KiwiSolver* s, const KiwiConstraint* constraint);
|
||||
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, const KiwiVar* var, double strength);
|
||||
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, const KiwiVar* var);
|
||||
bool kiwi_solver_has_edit_var(const KiwiSolver* s, const KiwiVar* var);
|
||||
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, const KiwiVar* var, double value);
|
||||
void kiwi_solver_update_vars(KiwiSolver* sp);
|
||||
void kiwi_solver_reset(KiwiSolver* sp);
|
||||
void kiwi_solver_dump(const KiwiSolver* sp);
|
||||
char* kiwi_solver_dumps(const KiwiSolver* sp);
|
||||
LJKIWI_EXP const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, KiwiConstraint* constraint);
|
||||
LJKIWI_EXP const KiwiErr*
|
||||
kiwi_solver_remove_constraint(KiwiSolver* s, KiwiConstraint* constraint);
|
||||
LJKIWI_EXP bool kiwi_solver_has_constraint(const KiwiSolver* s, KiwiConstraint* constraint);
|
||||
LJKIWI_EXP const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, KiwiVar* var, double strength);
|
||||
LJKIWI_EXP const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, KiwiVar* var);
|
||||
LJKIWI_EXP bool kiwi_solver_has_edit_var(const KiwiSolver* s, KiwiVar* var);
|
||||
LJKIWI_EXP const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, KiwiVar* var, double value);
|
||||
LJKIWI_EXP void kiwi_solver_update_vars(KiwiSolver* sp);
|
||||
LJKIWI_EXP void kiwi_solver_reset(KiwiSolver* sp);
|
||||
LJKIWI_EXP void kiwi_solver_dump(const KiwiSolver* sp);
|
||||
LJKIWI_EXP char* kiwi_solver_dumps(const KiwiSolver* sp);
|
||||
// LuaJIT end
|
||||
|
||||
#if __GNUC__
|
||||
#pragma GCC visibility pop
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
150
kiwi.lua
150
kiwi.lua
@@ -24,29 +24,9 @@ kiwi.ljkiwi = ljkiwi
|
||||
ffi.cdef([[
|
||||
void free(void *);
|
||||
|
||||
typedef struct KiwiTypeInfo {
|
||||
unsigned size;
|
||||
unsigned align;
|
||||
} 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
|
||||
typedef struct KiwiVar KiwiVar;
|
||||
typedef struct KiwiConstraint KiwiConstraint;
|
||||
typedef struct KiwiSolver KiwiSolver;]])
|
||||
|
||||
ffi.cdef([[
|
||||
enum KiwiErrKind {
|
||||
@@ -73,7 +53,8 @@ typedef struct KiwiTerm {
|
||||
typedef struct KiwiExpression {
|
||||
double constant;
|
||||
int term_count;
|
||||
KiwiConstraint* owner;
|
||||
void* owner;
|
||||
|
||||
KiwiTerm terms_[?];
|
||||
} KiwiExpression;
|
||||
|
||||
@@ -83,9 +64,17 @@ typedef struct KiwiErr {
|
||||
bool must_free;
|
||||
} KiwiErr;
|
||||
|
||||
typedef struct KiwiTuple {
|
||||
int count;
|
||||
const double* values[?];
|
||||
} KiwiTuple;
|
||||
|
||||
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_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);
|
||||
double kiwi_var_value(const 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);
|
||||
void kiwi_expression_destroy(KiwiExpression* expr);
|
||||
|
||||
void kiwi_constraint_construct(
|
||||
KiwiConstraint* kiwi_constraint_construct(
|
||||
const KiwiExpression* lhs,
|
||||
const KiwiExpression* rhs,
|
||||
enum KiwiRelOp op,
|
||||
double strength,
|
||||
void* mem
|
||||
double strength
|
||||
);
|
||||
void kiwi_constraint_release(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);
|
||||
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);
|
||||
unsigned kiwi_solver_get_error_mask(const KiwiSolver* s);
|
||||
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_remove_constraint(KiwiSolver* s, const KiwiConstraint* constraint);
|
||||
bool kiwi_solver_has_constraint(const KiwiSolver* s, const KiwiConstraint* constraint);
|
||||
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, const KiwiVar* var, double strength);
|
||||
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, const KiwiVar* var);
|
||||
bool kiwi_solver_has_edit_var(const KiwiSolver* s, const KiwiVar* var);
|
||||
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, const KiwiVar* var, double value);
|
||||
const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, KiwiConstraint* constraint);
|
||||
const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, KiwiConstraint* constraint);
|
||||
bool kiwi_solver_has_constraint(const KiwiSolver* s, KiwiConstraint* constraint);
|
||||
const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, KiwiVar* var, double strength);
|
||||
const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, KiwiVar* var);
|
||||
bool kiwi_solver_has_edit_var(const KiwiSolver* s, KiwiVar* var);
|
||||
const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, KiwiVar* var, double value);
|
||||
void kiwi_solver_update_vars(KiwiSolver* sp);
|
||||
void kiwi_solver_reset(KiwiSolver* sp);
|
||||
void kiwi_solver_dump(const KiwiSolver* sp);
|
||||
@@ -196,38 +183,49 @@ function kiwi.is_var(o)
|
||||
end
|
||||
|
||||
local Term = ffi.typeof("struct KiwiTerm") --[[@as kiwi.Term]]
|
||||
local SIZEOF_TERM = assert(ffi.sizeof(Term))
|
||||
kiwi.Term = Term
|
||||
|
||||
function kiwi.is_term(o)
|
||||
return ffi_istype(Term, o)
|
||||
end
|
||||
|
||||
local Expression = ffi.typeof("KiwiExpression") --[[@as kiwi.Expression]]
|
||||
local Expression = ffi.typeof("struct KiwiExpression") --[[@as kiwi.Expression]]
|
||||
kiwi.Expression = Expression
|
||||
|
||||
function kiwi.is_expression(o)
|
||||
return ffi_istype(Expression, o)
|
||||
end
|
||||
|
||||
local Constraint = ffi.typeof("KiwiConstraint") --[[@as kiwi.Constraint]]
|
||||
local Constraint = ffi.typeof("struct KiwiConstraint") --[[@as kiwi.Constraint]]
|
||||
kiwi.Constraint = Constraint
|
||||
|
||||
function kiwi.is_constraint(o)
|
||||
return ffi_istype(Constraint, o)
|
||||
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 var kiwi.Var
|
||||
---@param coeff number?
|
||||
---@nodiscard
|
||||
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]]
|
||||
for i = 0, expr.term_count - 1 do
|
||||
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
|
||||
ffi_copy(ret.terms_, expr.terms_, SIZEOF_TERM * expr.term_count)
|
||||
local dt = ret.terms_[expr.term_count]
|
||||
dt.var = var
|
||||
dt.coefficient = coeff or 1.0
|
||||
@@ -248,7 +246,7 @@ local function new_expr_one(constant, var, coeff)
|
||||
dt.coefficient = coeff or 1.0
|
||||
ret.constant = constant
|
||||
ret.term_count = 1
|
||||
ljkiwi.kiwi_var_retain(var)
|
||||
ljkiwi.kiwi_expression_retain(ret)
|
||||
return ret
|
||||
end
|
||||
|
||||
@@ -303,8 +301,6 @@ local OP_NAMES = {
|
||||
EQ = "==",
|
||||
}
|
||||
|
||||
local SIZEOF_TERM = ffi.sizeof(Term) --[[@as integer]]
|
||||
|
||||
local tmpexpr = ffi_new(Expression, 2) --[[@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])
|
||||
end
|
||||
|
||||
local c = ffi_new(Constraint)
|
||||
ljkiwi.kiwi_constraint_construct(el, er, op, strength or REQUIRED, c)
|
||||
return ffi_gc(c, ljkiwi.kiwi_constraint_release) --[[@as kiwi.Constraint]]
|
||||
return ffi_gc(
|
||||
ljkiwi.kiwi_constraint_construct(el, er, op, strength or REQUIRED),
|
||||
ljkiwi.kiwi_constraint_release
|
||||
) --[[@as kiwi.Constraint]]
|
||||
end
|
||||
|
||||
--- Define a constraint with expressions as `a <= b`.
|
||||
@@ -431,9 +428,7 @@ do
|
||||
}
|
||||
|
||||
function Var_mt:__new(name)
|
||||
local v = ffi_new(self)
|
||||
ljkiwi.kiwi_var_construct(name, v)
|
||||
return ffi_gc(v, ljkiwi.kiwi_var_release)
|
||||
return ffi_gc(ljkiwi.kiwi_var_construct(name), ljkiwi.kiwi_var_release)
|
||||
end
|
||||
|
||||
function Var_mt.__mul(a, b)
|
||||
@@ -477,10 +472,6 @@ do
|
||||
return a + -b
|
||||
end
|
||||
|
||||
function Var_mt:__eq(other)
|
||||
return ljkiwi.kiwi_var_eq(self, other)
|
||||
end
|
||||
|
||||
function Var_mt:__tostring()
|
||||
return self:name() .. "(" .. self:value() .. ")"
|
||||
end
|
||||
@@ -526,8 +517,10 @@ do
|
||||
end
|
||||
|
||||
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)
|
||||
t.var = var
|
||||
t.coefficient = coefficient or 1.0
|
||||
return t
|
||||
end
|
||||
|
||||
@@ -646,13 +639,7 @@ do
|
||||
---@nodiscard
|
||||
local function new_expr_constant(expr, constant)
|
||||
local ret = ffi_gc(ffi_new(Expression, expr.term_count), ljkiwi.kiwi_expression_destroy) --[[@as kiwi.Expression]]
|
||||
|
||||
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
|
||||
ffi_copy(ret.terms_, expr.terms_, SIZEOF_TERM * expr.term_count)
|
||||
ret.constant = constant
|
||||
ret.term_count = expr.term_count
|
||||
ljkiwi.kiwi_expression_retain(ret)
|
||||
@@ -820,9 +807,10 @@ do
|
||||
}
|
||||
|
||||
function Constraint_mt:__new(lhs, rhs, op, strength)
|
||||
local c = ffi_new(self)
|
||||
ljkiwi.kiwi_constraint_construct(lhs, rhs, op or "EQ", strength or REQUIRED, c)
|
||||
return ffi_gc(c, ljkiwi.kiwi_constraint_release)
|
||||
return ffi_gc(
|
||||
ljkiwi.kiwi_constraint_construct(lhs, rhs, op or "EQ", strength or REQUIRED),
|
||||
ljkiwi.kiwi_constraint_release
|
||||
)
|
||||
end
|
||||
|
||||
local OPS = { [0] = "<=", ">=", "==" }
|
||||
@@ -868,9 +856,10 @@ do
|
||||
tmpexpr.constant = constant ~= nil and constant or 0
|
||||
tmpexpr.term_count = 2
|
||||
|
||||
local c = ffi_new(Constraint)
|
||||
ljkiwi.kiwi_constraint_construct(tmpexpr, nil, op or "EQ", strength or REQUIRED, c)
|
||||
return ffi_gc(c, ljkiwi.kiwi_constraint_release) --[[@as kiwi.Constraint]]
|
||||
return ffi_gc(
|
||||
ljkiwi.kiwi_constraint_construct(tmpexpr, nil, op or "EQ", strength or REQUIRED),
|
||||
ljkiwi.kiwi_constraint_release
|
||||
) --[[@as kiwi.Constraint]]
|
||||
end
|
||||
|
||||
local pair_ratio = constraints.pair_ratio
|
||||
@@ -904,9 +893,10 @@ do
|
||||
t.var = var
|
||||
t.coefficient = 1.0
|
||||
|
||||
local c = ffi_new(Constraint)
|
||||
ljkiwi.kiwi_constraint_construct(tmpexpr, nil, op or "EQ", strength or REQUIRED, c)
|
||||
return ffi_gc(c, ljkiwi.kiwi_constraint_release) --[[@as kiwi.Constraint]]
|
||||
return ffi_gc(
|
||||
ljkiwi.kiwi_constraint_construct(tmpexpr, nil, op or "EQ", strength or REQUIRED),
|
||||
ljkiwi.kiwi_constraint_release
|
||||
) --[[@as kiwi.Constraint]]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1182,20 +1172,16 @@ do
|
||||
__index = Solver_cls,
|
||||
}
|
||||
|
||||
local Solver = ffi.typeof("struct KiwiSolver") --[[@as kiwi.Solver]]
|
||||
kiwi.Solver = Solver
|
||||
|
||||
function Solver_mt:__new(error_mask)
|
||||
if type(error_mask) == "table" then
|
||||
error_mask = kiwi.error_mask(error_mask)
|
||||
end
|
||||
|
||||
local s = ffi_new(Solver)
|
||||
ljkiwi.kiwi_solver_construct(error_mask or 0, s)
|
||||
return ffi_gc(s, ljkiwi.kiwi_solver_destroy) --[[@as kiwi.Constraint]]
|
||||
return ffi_gc(ljkiwi.kiwi_solver_construct(error_mask or 0), ljkiwi.kiwi_solver_destroy) --[[@as kiwi.Constraint]]
|
||||
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)
|
||||
return ffi_istype(Solver, s)
|
||||
|
||||
@@ -26,17 +26,70 @@ enum RelationalOperator
|
||||
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:
|
||||
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(const Expression &expr,
|
||||
RelationalOperator op,
|
||||
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;
|
||||
|
||||
@@ -44,32 +97,10 @@ public:
|
||||
|
||||
~Constraint() = default;
|
||||
|
||||
const Expression &expression() const
|
||||
{
|
||||
return m_data->m_expression;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
const Expression &expression() const { return m_data->expression(); }
|
||||
RelationalOperator op() const { return m_data->op(); }
|
||||
double strength() const { return m_data->strength(); }
|
||||
bool violated() const { return m_data->violated(); }
|
||||
|
||||
bool operator!() const
|
||||
{
|
||||
@@ -81,46 +112,10 @@ public:
|
||||
Constraint& operator=(Constraint &&) noexcept = default;
|
||||
|
||||
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;
|
||||
|
||||
public:
|
||||
|
||||
friend bool operator<(const Constraint &lhs, const Constraint &rhs)
|
||||
{
|
||||
return lhs.m_data < rhs.m_data;
|
||||
|
||||
@@ -13,63 +13,54 @@
|
||||
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
|
||||
{
|
||||
|
||||
public:
|
||||
class Context
|
||||
{
|
||||
public:
|
||||
Context() = default;
|
||||
virtual ~Context() {} // LCOV_EXCL_LINE
|
||||
};
|
||||
explicit Variable(VariableData *p) : m_data(p) {}
|
||||
VariableData *ptr() { return m_data; }
|
||||
|
||||
Variable(Context *context = 0) : m_data(new VariableData("", context)) {}
|
||||
|
||||
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() : m_data(new VariableData("")) {}
|
||||
|
||||
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(Variable&&) noexcept = default;
|
||||
|
||||
~Variable() = default;
|
||||
|
||||
const std::string &name() const
|
||||
{
|
||||
return m_data->m_name;
|
||||
}
|
||||
const std::string &name() const { return m_data->name(); }
|
||||
void setName(const char *name) { m_data->setName(name); }
|
||||
void setName(const std::string &name) { m_data->setName(name); }
|
||||
|
||||
void setName(const char *name)
|
||||
{
|
||||
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;
|
||||
}
|
||||
double value() const { return m_data->value(); }
|
||||
void setValue(double value) { m_data->setValue(value); }
|
||||
|
||||
// operator== is used for symbolics
|
||||
bool equals(const Variable &other) const
|
||||
@@ -82,32 +73,6 @@ public:
|
||||
Variable& operator=(Variable&&) noexcept = default;
|
||||
|
||||
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;
|
||||
|
||||
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_
|
||||
#define LKIWI_LUACOMPAT_H_
|
||||
#ifndef LJKIWI_LUACOMPAT_H_
|
||||
#define LJKIWI_LUACOMPAT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
#include <lauxlib.h>
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 && defined(__GNUC__)
|
||||
#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
|
||||
|
||||
#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) {
|
||||
if (i < 0 && i > LUA_REGISTRYINDEX)
|
||||
i += lua_gettop(L) + 1;
|
||||
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);
|
||||
if (isnum != NULL) {
|
||||
*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;
|
||||
}
|
||||
|
||||
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;
|
||||
lua_Number n = lua_tonumberx(L, i, &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)
|
||||
#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.
|
||||
inline void compat_arith_unm(lua_State* L) {
|
||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
|
||||
lua_Number n = lua_tonumber(L, -1);
|
||||
if (n != 0 || lua_isnumber(L, -1)) {
|
||||
int isnum;
|
||||
lua_Number n = lua_tonumberx(L, -1, &isnum);
|
||||
if (isnum) {
|
||||
lua_pop(L, 1);
|
||||
lua_pushnumber(L, -n);
|
||||
} else {
|
||||
@@ -78,26 +79,28 @@ enum KiwiErrKind {
|
||||
};
|
||||
|
||||
struct KiwiTerm {
|
||||
Variable* var;
|
||||
VariableData* var;
|
||||
double coefficient;
|
||||
};
|
||||
|
||||
struct KiwiExpression {
|
||||
double constant;
|
||||
int term_count;
|
||||
Constraint* owner;
|
||||
ConstraintData* owner;
|
||||
|
||||
#if !defined(_MSC_VER) || _MSC_VER >= 1900
|
||||
KiwiTerm terms[];
|
||||
|
||||
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
|
||||
KiwiTerm terms[1];
|
||||
|
||||
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
|
||||
|
||||
@@ -138,49 +141,57 @@ template<typename F>
|
||||
inline const KiwiErr* wrap_err(F&& f) {
|
||||
static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
|
||||
KiwiErrUnknown,
|
||||
"An unhandled C++ exception occurred."};
|
||||
"An unhandled C++ exception occurred."
|
||||
};
|
||||
|
||||
try {
|
||||
f();
|
||||
} catch (const UnsatisfiableConstraint&) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrUnsatisfiableConstraint,
|
||||
"The constraint cannot be satisfied."};
|
||||
"The constraint cannot be satisfied."
|
||||
};
|
||||
return &err;
|
||||
} catch (const UnknownConstraint&) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrUnknownConstraint,
|
||||
"The constraint has not been added to the solver."};
|
||||
"The constraint has not been added to the solver."
|
||||
};
|
||||
return &err;
|
||||
|
||||
} catch (const DuplicateConstraint&) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrDuplicateConstraint,
|
||||
"The constraint has already been added to the solver."};
|
||||
"The constraint has already been added to the solver."
|
||||
};
|
||||
return &err;
|
||||
|
||||
} catch (const UnknownEditVariable&) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrUnknownEditVariable,
|
||||
"The edit variable has not been added to the solver."};
|
||||
"The edit variable has not been added to the solver."
|
||||
};
|
||||
return &err;
|
||||
|
||||
} catch (const DuplicateEditVariable&) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrDuplicateEditVariable,
|
||||
"The edit variable has already been added to the solver."};
|
||||
"The edit variable has already been added to the solver."
|
||||
};
|
||||
return &err;
|
||||
|
||||
} catch (const BadRequiredStrength&) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrBadRequiredStrength,
|
||||
"A required strength cannot be used in this context."};
|
||||
"A required strength cannot be used in this context."
|
||||
};
|
||||
return &err;
|
||||
|
||||
} catch (const InternalSolverError& ex) {
|
||||
static const constexpr KiwiErr base {
|
||||
KiwiErrInternalSolverError,
|
||||
"An internal solver error occurred."};
|
||||
"An internal solver error occurred."
|
||||
};
|
||||
return new_error(&base, ex);
|
||||
} catch (std::bad_alloc&) {
|
||||
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>
|
||||
inline const KiwiErr* wrap_err(P& s, F&& f) {
|
||||
inline const KiwiErr* wrap_err(P&& s, F&& f) {
|
||||
return wrap_err([&]() { f(s); });
|
||||
}
|
||||
|
||||
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); });
|
||||
}
|
||||
|
||||
inline Variable* kiwi_var_retain(Variable* var) {
|
||||
alignas(Variable) unsigned char buf[sizeof(Variable)];
|
||||
new (buf) Variable(*var);
|
||||
return var;
|
||||
template<typename T, typename... Args>
|
||||
inline T* make_unmanaged(Args... args) {
|
||||
auto* p = new (std::nothrow) T(std::forward<Args>(args)...);
|
||||
if (lk_unlikely(!p))
|
||||
return nullptr;
|
||||
|
||||
p->m_refcount = 1;
|
||||
return p;
|
||||
}
|
||||
|
||||
inline Constraint* kiwi_constraint_retain(Constraint* c) {
|
||||
alignas(Constraint) unsigned char buf[sizeof(Constraint)];
|
||||
new (buf) Constraint(*c);
|
||||
return c;
|
||||
template<typename T>
|
||||
inline void release_unmanaged(T* p) {
|
||||
if (lk_likely(p)) {
|
||||
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* rhs,
|
||||
RelationalOperator op,
|
||||
double strength,
|
||||
void* mem
|
||||
double strength
|
||||
) {
|
||||
if (strength < 0.0) {
|
||||
strength = kiwi::strength::required;
|
||||
}
|
||||
|
||||
try {
|
||||
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) {
|
||||
for (auto* t = lhs->terms; t != lhs->terms + lhs->term_count; ++t) {
|
||||
terms.emplace_back(*t->var, t->coefficient);
|
||||
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) {
|
||||
for (auto* t = rhs->terms; t != rhs->terms + rhs->term_count; ++t) {
|
||||
terms.emplace_back(*t->var, -t->coefficient);
|
||||
for (int i = 0; i < rhs->term_count; ++i) {
|
||||
const auto& t = rhs->terms[i];
|
||||
terms.emplace_back(Variable(t.var), -t.coefficient);
|
||||
}
|
||||
}
|
||||
return new (mem) Constraint(
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
inline const KiwiErr* kiwi_solver_add_constraint(Solver& s, const Constraint& constraint) {
|
||||
return wrap_err(s, constraint, [](auto& solver, const auto& c) { solver.addConstraint(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_constraint(Solver& s, ConstraintData* constraint) {
|
||||
return wrap_err(s, constraint, [](auto&& solver, auto&& c) {
|
||||
solver.addConstraint(Constraint(c));
|
||||
});
|
||||
}
|
||||
|
||||
inline const KiwiErr* kiwi_solver_add_edit_var(Solver& s, const Variable& var, double strength) {
|
||||
return wrap_err(s, var, [strength](auto& solver, const auto& v) {
|
||||
solver.addEditVariable(v, strength);
|
||||
inline const KiwiErr* kiwi_solver_remove_constraint(Solver& s, ConstraintData* constraint) {
|
||||
return wrap_err(s, constraint, [](auto&& solver, auto&& c) {
|
||||
solver.removeConstraint(Constraint(c));
|
||||
});
|
||||
}
|
||||
|
||||
inline const KiwiErr* kiwi_solver_remove_edit_var(Solver& s, const Variable& var) {
|
||||
return wrap_err(s, var, [](auto& solver, const auto& v) { solver.removeEditVariable(v); });
|
||||
inline const KiwiErr* kiwi_solver_add_edit_var(Solver& s, VariableData* var, double strength) {
|
||||
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) {
|
||||
return wrap_err(s, var, [value](auto& solver, const auto& v) {
|
||||
solver.suggestValue(v, value);
|
||||
inline const KiwiErr* kiwi_solver_remove_edit_var(Solver& s, VariableData* var) {
|
||||
return wrap_err(s, var, [](auto&& solver, auto&& v) {
|
||||
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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "luacompat.h"
|
||||
#include "luakiwi-int.h"
|
||||
|
||||
namespace {
|
||||
@@ -13,9 +11,7 @@ namespace {
|
||||
|
||||
enum TypeId { NOTYPE, VAR = 1, TERM, EXPR, CONSTRAINT, SOLVER, ERROR, NUMBER };
|
||||
|
||||
const int ERR_KIND_TAB = NUMBER + 1;
|
||||
const int VAR_SUB_FN = ERR_KIND_TAB + 1;
|
||||
const int CONTEXT_TAB_MAX = VAR_SUB_FN + 1;
|
||||
enum { ERR_KIND_TAB = NUMBER + 1, VAR_SUB_FN, MEM_ERR_MSG, CONTEXT_TAB_MAX };
|
||||
|
||||
constexpr const char* const lkiwi_error_kinds[] = {
|
||||
"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;
|
||||
}
|
||||
|
||||
inline Variable* try_var(lua_State* L, int idx) {
|
||||
return static_cast<Variable*>(try_type(L, idx, VAR));
|
||||
inline VariableData* try_var(lua_State* L, int idx) {
|
||||
return *static_cast<VariableData**>(try_type(L, idx, VAR));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
inline Variable* get_var(lua_State* L, int idx) {
|
||||
return static_cast<Variable*>(check_arg(L, idx, VAR));
|
||||
inline VariableData* get_var(lua_State* L, int idx) {
|
||||
return *static_cast<VariableData**>(check_arg(L, idx, VAR));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
inline Constraint* get_constraint(lua_State* L, int idx) {
|
||||
return static_cast<Constraint*>(check_arg(L, idx, CONSTRAINT));
|
||||
inline ConstraintData* get_constraint(lua_State* L, int idx) {
|
||||
return *static_cast<ConstraintData**>(check_arg(L, idx, CONSTRAINT));
|
||||
}
|
||||
|
||||
inline KiwiSolver* get_solver(lua_State* L, int idx) {
|
||||
return static_cast<KiwiSolver*>(check_arg(L, idx, SOLVER));
|
||||
}
|
||||
|
||||
// note this expects the 2nd upvalue to have the variable weak table
|
||||
template<typename... Args>
|
||||
inline Variable* var_new(lua_State* L, Args&&... args) {
|
||||
auto* var = new (lua_newuserdata(L, sizeof(Variable))) Variable(std::forward<Args>(args)...);
|
||||
VariableData** var_new(lua_State* L) {
|
||||
auto** varp = static_cast<VariableData**>(lua_newuserdata(L, sizeof(VariableData*)));
|
||||
push_type(L, VAR);
|
||||
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
|
||||
// a true compatibility shim has performance implications here
|
||||
lua_pushlightuserdata(L, p);
|
||||
lua_pushlightuserdata(L, var);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawset(L, lua_upvalueindex(2));
|
||||
#else
|
||||
@@ -237,7 +239,7 @@ inline Variable* var_new(lua_State* L, Args&&... args) {
|
||||
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)));
|
||||
push_type(L, TERM);
|
||||
lua_setmetatable(L, -2);
|
||||
@@ -246,24 +248,30 @@ inline KiwiTerm* term_new(lua_State* L) {
|
||||
|
||||
inline KiwiExpression* expr_new(lua_State* L, int nterms) {
|
||||
auto* expr = static_cast<KiwiExpression*>(lua_newuserdata(L, KiwiExpression::sz(nterms)));
|
||||
expr->term_count = 0;
|
||||
expr->owner = nullptr;
|
||||
push_type(L, EXPR);
|
||||
lua_setmetatable(L, -2);
|
||||
return expr;
|
||||
}
|
||||
|
||||
inline Constraint* constraint_new(
|
||||
inline ConstraintData* constraint_new(
|
||||
lua_State* L,
|
||||
const KiwiExpression* lhs,
|
||||
const KiwiExpression* rhs,
|
||||
kiwi::RelationalOperator op,
|
||||
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);
|
||||
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
|
||||
@@ -288,7 +296,7 @@ KiwiExpression* toexpr(lua_State* L, int idx, KiwiExpression* temp) {
|
||||
temp->term_count = 1;
|
||||
push_type(L, VAR);
|
||||
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;
|
||||
return temp;
|
||||
}
|
||||
@@ -332,7 +340,7 @@ inline int push_expr_one(lua_State* L, double constant, const KiwiTerm* term) {
|
||||
expr->constant = constant;
|
||||
expr->term_count = 1;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -341,20 +349,21 @@ inline int push_expr_pair(lua_State* L, double constant, const KiwiTerm* ta, con
|
||||
e->constant = constant;
|
||||
e->term_count = 2;
|
||||
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].var = kiwi_var_retain(tb->var);
|
||||
e->terms[1].var = retain_unmanaged(tb->var);
|
||||
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);
|
||||
e->constant = constant;
|
||||
e->term_count = 2;
|
||||
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].var = kiwi_var_retain(t->var);
|
||||
e->terms[1].var = retain_unmanaged(t->var);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -365,23 +374,23 @@ int push_add_expr_term(lua_State* L, const KiwiExpression* expr, const KiwiTerm*
|
||||
int i = 0;
|
||||
for (; i < expr->term_count; ++i) {
|
||||
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].var = kiwi_var_retain(t->var);
|
||||
e->terms[i].var = retain_unmanaged(t->var);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lkiwi_var_m_add(lua_State* L) {
|
||||
TypeId type_id_b;
|
||||
double num;
|
||||
double num = 0.0;
|
||||
void* arg_b = try_arg(L, 2, &type_id_b, &num);
|
||||
|
||||
if (type_id_b == VAR) {
|
||||
int isnum_a;
|
||||
num = lua_tonumberx(L, 1, &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);
|
||||
}
|
||||
}
|
||||
@@ -390,7 +399,7 @@ int lkiwi_var_m_add(lua_State* L) {
|
||||
if (var_a) {
|
||||
switch (type_id_b) {
|
||||
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);
|
||||
}
|
||||
case TERM:
|
||||
@@ -436,7 +445,7 @@ int lkiwi_var_m_mul(lua_State* L) {
|
||||
auto* var = try_var(L, varidx);
|
||||
if (var) {
|
||||
auto* term = term_new(L);
|
||||
term->var = kiwi_var_retain(var);
|
||||
term->var = retain_unmanaged(var);
|
||||
term->coefficient = num;
|
||||
return 1;
|
||||
}
|
||||
@@ -452,20 +461,20 @@ int lkiwi_var_m_div(lua_State* L) {
|
||||
return op_error(L, "/", 1, 2);
|
||||
}
|
||||
auto* term = term_new(L);
|
||||
term->var = kiwi_var_retain(var);
|
||||
term->var = retain_unmanaged(var);
|
||||
term->coefficient = 1.0 / num;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lkiwi_var_m_unm(lua_State* 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;
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -476,7 +485,7 @@ int lkiwi_var_m_tostring(lua_State* L) {
|
||||
}
|
||||
|
||||
int lkiwi_var_m_gc(lua_State* L) {
|
||||
get_var(L, 1)->~Variable();
|
||||
release_unmanaged(get_var(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -509,7 +518,7 @@ int lkiwi_var_toterm(lua_State* L) {
|
||||
double coefficient = luaL_optnumber(L, 2, 1.0);
|
||||
auto* term = term_new(L);
|
||||
|
||||
term->var = kiwi_var_retain(var);
|
||||
term->var = retain_unmanaged(var);
|
||||
term->coefficient = coefficient;
|
||||
|
||||
return 1;
|
||||
@@ -538,59 +547,21 @@ constexpr const struct luaL_Reg kiwi_var_m[] = {
|
||||
{"eq", lkiwi_eq},
|
||||
{"le", lkiwi_le},
|
||||
{"ge", lkiwi_ge},
|
||||
{0, 0}};
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
int lkiwi_var_new(lua_State* L) {
|
||||
const char* name = luaL_optstring(L, 1, "");
|
||||
var_new(L, name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lkiwi_term_m_mul(lua_State* L) {
|
||||
int isnum, termidx = 2;
|
||||
double num = lua_tonumberx(L, 1, &isnum);
|
||||
auto* varp = var_new(L);
|
||||
var_register(L, *varp = make_unmanaged<VariableData>(name));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int lkiwi_term_m_add(lua_State* L) {
|
||||
TypeId type_id_b;
|
||||
double num;
|
||||
double num = 0.0;
|
||||
void* arg_b = try_arg(L, 2, &type_id_b, &num);
|
||||
|
||||
if (type_id_b == TERM) {
|
||||
@@ -607,7 +578,7 @@ int lkiwi_term_m_add(lua_State* L) {
|
||||
case TERM:
|
||||
return push_expr_pair(L, 0.0, term_a, static_cast<KiwiTerm*>(arg_b));
|
||||
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);
|
||||
}
|
||||
case EXPR:
|
||||
@@ -628,6 +599,48 @@ int lkiwi_term_m_sub(lua_State* L) {
|
||||
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) {
|
||||
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) {
|
||||
get_term(L, 1)->var->~Variable();
|
||||
release_unmanaged(get_term(L, 1)->var);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -660,8 +673,10 @@ int lkiwi_term_m_index(lua_State* L) {
|
||||
#else
|
||||
lua_rawgetp(L, lua_upvalueindex(2), term->var);
|
||||
#endif
|
||||
if (lua_isnil(L, -1))
|
||||
var_new(L, *term->var);
|
||||
if (lua_isnil(L, -1)) {
|
||||
auto* varp = var_new(L);
|
||||
var_register(L, *varp = retain_unmanaged(term->var));
|
||||
}
|
||||
return 1;
|
||||
} else if (len == 11 && memcmp("coefficient", k, len) == 0) {
|
||||
lua_pushnumber(L, term->coefficient);
|
||||
@@ -690,13 +705,14 @@ constexpr const struct luaL_Reg kiwi_term_m[] = {
|
||||
{"eq", lkiwi_eq},
|
||||
{"le", lkiwi_le},
|
||||
{"ge", lkiwi_ge},
|
||||
{0, 0}};
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
int lkiwi_term_new(lua_State* L) {
|
||||
auto* var = get_var(L, 1);
|
||||
double coefficient = luaL_optnumber(L, 2, 1.0);
|
||||
auto* term = term_new(L);
|
||||
term->var = kiwi_var_retain(var);
|
||||
term->var = retain_unmanaged(var);
|
||||
term->coefficient = coefficient;
|
||||
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) {
|
||||
auto* ne = expr_new(L, expr->term_count);
|
||||
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->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->term_count = expr->term_count;
|
||||
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;
|
||||
}
|
||||
return 1;
|
||||
@@ -731,16 +747,57 @@ int push_add_expr_expr(lua_State* L, const KiwiExpression* a, const KiwiExpressi
|
||||
ne->term_count = na + nb;
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 isnum, expridx = 2;
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
const auto* expr = get_expr(L, 1);
|
||||
double sum = expr->constant;
|
||||
@@ -831,7 +847,7 @@ int lkiwi_expr_terms(lua_State* L) {
|
||||
for (int i = 0; i < expr->term_count; i++) {
|
||||
const auto* t = &expr->terms[i];
|
||||
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;
|
||||
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) {
|
||||
const auto* expr = get_expr(L, 1);
|
||||
if (expr->owner) {
|
||||
expr->owner->~Constraint();
|
||||
release_unmanaged(expr->owner);
|
||||
} else {
|
||||
for (auto* t = expr->terms; t != expr->terms + expr->term_count; ++t) {
|
||||
t->var->~Variable();
|
||||
release_unmanaged(t->var);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -906,7 +922,8 @@ constexpr const struct luaL_Reg kiwi_expr_m[] = {
|
||||
{"eq", lkiwi_eq},
|
||||
{"le", lkiwi_le},
|
||||
{"ge", lkiwi_ge},
|
||||
{0, 0}};
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
int lkiwi_expr_new(lua_State* L) {
|
||||
int nterms = lua_gettop(L) - 1;
|
||||
@@ -914,11 +931,10 @@ int lkiwi_expr_new(lua_State* L) {
|
||||
|
||||
auto* expr = expr_new(L, nterms);
|
||||
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);
|
||||
expr->terms[i].var = kiwi_var_retain(term->var);
|
||||
expr->terms[i].var = retain_unmanaged(term->var);
|
||||
expr->terms[i].coefficient = term->coefficient;
|
||||
}
|
||||
return 1;
|
||||
@@ -956,16 +972,17 @@ int lkiwi_constraint_expression(lua_State* L) {
|
||||
auto* c = get_constraint(L, 1);
|
||||
const auto& expr = c->expression();
|
||||
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);
|
||||
ne->owner = kiwi_constraint_retain(c);
|
||||
ne->owner = retain_unmanaged(c);
|
||||
ne->constant = expr.constant();
|
||||
ne->term_count = term_count;
|
||||
|
||||
for (int i = 0; i < term_count; ++i) {
|
||||
ne->terms[i].var = const_cast<Variable*>(&terms[i].variable());
|
||||
ne->terms[i].coefficient = terms[i].coefficient();
|
||||
const auto& t = terms[static_cast<std::size_t>(i)];
|
||||
ne->terms[i].var = const_cast<Variable&>(t.variable()).ptr();
|
||||
ne->terms[i].coefficient = t.coefficient();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -1025,7 +1042,7 @@ int lkiwi_constraint_m_tostring(lua_State* L) {
|
||||
}
|
||||
|
||||
int lkiwi_constraint_m_gc(lua_State* L) {
|
||||
get_constraint(L, 1)->~Constraint();
|
||||
release_unmanaged(get_constraint(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1057,7 +1074,8 @@ constexpr const struct luaL_Reg kiwi_constraint_m[] = {
|
||||
{"expression", lkiwi_constraint_expression},
|
||||
{"add_to", lkiwi_constraint_add_to},
|
||||
{"remove_from", lkiwi_constraint_remove_from},
|
||||
{0, 0}};
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
int lkiwi_constraint_new(lua_State* L) {
|
||||
const auto* lhs = get_expr_opt(L, 1);
|
||||
@@ -1071,9 +1089,9 @@ int lkiwi_constraint_new(lua_State* L) {
|
||||
|
||||
int push_pair_constraint(
|
||||
lua_State* L,
|
||||
Variable* left,
|
||||
VariableData* left,
|
||||
double coeff,
|
||||
Variable* right,
|
||||
VariableData* right,
|
||||
double constant,
|
||||
kiwi::RelationalOperator op,
|
||||
double strength
|
||||
@@ -1130,7 +1148,8 @@ constexpr const struct luaL_Reg lkiwi_constraints[] = {
|
||||
{"pair_ratio", lkiwi_constraints_pair_ratio},
|
||||
{"pair", lkiwi_constraints_pair},
|
||||
{"single", lkiwi_constraints_single},
|
||||
{0, 0}};
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
void lkiwi_mod_constraints_new(lua_State* L, int ctx_i) {
|
||||
luaL_newlibtable(L, lkiwi_constraints);
|
||||
@@ -1181,10 +1200,7 @@ int lkiwi_error_m_tostring(lua_State* L) {
|
||||
luaL_tolstring(L, -1, 0);
|
||||
lua_remove(L, -2); // remove item
|
||||
luaL_addvalue(&buf);
|
||||
luaL_addstring(&buf, ")\n");
|
||||
|
||||
luaL_traceback(L, L, nullptr, 2);
|
||||
luaL_addvalue(&buf);
|
||||
luaL_addstring(&buf, ")");
|
||||
luaL_pushresult(&buf);
|
||||
|
||||
return 1;
|
||||
@@ -1192,7 +1208,8 @@ int lkiwi_error_m_tostring(lua_State* L) {
|
||||
|
||||
constexpr const struct luaL_Reg lkiwi_error_m[] = {
|
||||
{"__tostring", lkiwi_error_m_tostring},
|
||||
{0, 0}};
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
int lkiwi_error_mask(lua_State* L) {
|
||||
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) {
|
||||
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);
|
||||
return lkiwi_solver_handle_err(L, err, self);
|
||||
}
|
||||
|
||||
int lkiwi_solver_remove_constraint(lua_State* L) {
|
||||
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);
|
||||
return lkiwi_solver_handle_err(L, err, self);
|
||||
}
|
||||
|
||||
int lkiwi_solver_add_edit_var(lua_State* L) {
|
||||
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);
|
||||
auto* err = kiwi_solver_add_edit_var(self->solver, var, strength);
|
||||
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) {
|
||||
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);
|
||||
return lkiwi_solver_handle_err(L, err, self);
|
||||
}
|
||||
|
||||
int lkiwi_solver_suggest_value(lua_State* L) {
|
||||
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);
|
||||
auto* err = kiwi_solver_suggest_value(self->solver, var, value);
|
||||
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) {
|
||||
auto* s = get_solver(L, 1);
|
||||
const auto& c = *get_constraint(L, 2);
|
||||
lua_pushboolean(L, s->solver.hasConstraint(c));
|
||||
auto* c = get_constraint(L, 2);
|
||||
lua_pushboolean(L, s->solver.hasConstraint(Constraint(c)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lkiwi_solver_has_edit_var(lua_State* L) {
|
||||
auto* s = get_solver(L, 1);
|
||||
const auto& var = *get_var(L, 2);
|
||||
lua_pushboolean(L, s->solver.hasEditVariable(var));
|
||||
auto* var = get_var(L, 2);
|
||||
lua_pushboolean(L, s->solver.hasEditVariable(Variable(var)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1338,26 +1355,26 @@ int lkiwi_add_remove_tab(lua_State* L, F&& fn) {
|
||||
|
||||
int lkiwi_solver_add_constraints(lua_State* L) {
|
||||
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) {
|
||||
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) {
|
||||
double strength = luaL_checknumber(L, 3);
|
||||
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) {
|
||||
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);
|
||||
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) {
|
||||
luaL_typeerror(L, 2, "indexable");
|
||||
}
|
||||
// block this particularly obnoxious case which is always a bug
|
||||
if (lua_type(L, 3) == LUA_TSTRING) {
|
||||
luaL_typeerror(L, 3, "indexable");
|
||||
}
|
||||
@@ -1380,7 +1396,7 @@ int lkiwi_solver_suggest_values(lua_State* L) {
|
||||
lua_geti(L, 3, i);
|
||||
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) {
|
||||
error_new(L, err, 1, narg + 1 /* item_absi */);
|
||||
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},
|
||||
{"__tostring", lkiwi_solver_m_tostring},
|
||||
{"__gc", lkiwi_solver_m_gc},
|
||||
{0, 0}};
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
int lkiwi_solver_new(lua_State* L) {
|
||||
lua_Integer error_mask;
|
||||
@@ -1542,7 +1559,8 @@ constexpr const struct luaL_Reg lkiwi[] = {
|
||||
{"eq", lkiwi_eq},
|
||||
{"le", lkiwi_le},
|
||||
{"ge", lkiwi_ge},
|
||||
{0, 0}};
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
int no_member_mt_index(lua_State* L) {
|
||||
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__)
|
||||
#define LJKIWI_EXPORT __attribute__((__visibility__("default")))
|
||||
#elif defined _WIN32
|
||||
#define LJKIWI_EXPORT __declspec(dllexport)
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
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 = {
|
||||
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)",
|
||||
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(-1.0, terms[2].coefficient)
|
||||
|
||||
-- TODO: terms and expressions
|
||||
|
||||
assert.error(function()
|
||||
local _ = v - "foo"
|
||||
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