Compare commits
14 Commits
master
...
solve-eqns
Author | SHA1 | Date | |
---|---|---|---|
ce4a662847 | |||
067f868222 | |||
8a33818790 | |||
3abe5c6573 | |||
a6c4906773 | |||
233c8eaad5 | |||
461088da3a | |||
dc674dbce0 | |||
f9b1f8924c | |||
157535b5ff | |||
8da067fec2 | |||
2d5d2fda4b | |||
556719f52d | |||
6cede1f2dd |
142
Cargo.lock
generated
142
Cargo.lock
generated
@ -1,9 +1,11 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.6.9"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -11,18 +13,18 @@ name = "alga"
|
|||||||
version = "0.7.2"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libm 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libm 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "approx"
|
name = "approx"
|
||||||
version = "0.3.1"
|
version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -30,11 +32,16 @@ name = "atty"
|
|||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@ -44,8 +51,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
name = "cad_rs"
|
name = "cad_rs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nalgebra 0.16.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nalgebra 0.16.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -53,7 +60,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "0.1.6"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -66,24 +73,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.5.0"
|
version = "1.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_logger"
|
name = "env_logger"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fuchsia-cprng"
|
name = "fuchsia-cprng"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -107,22 +114,22 @@ name = "itertools"
|
|||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.2.0"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.48"
|
version = "0.2.55"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -130,7 +137,7 @@ name = "log"
|
|||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -143,12 +150,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.1.3"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
|
||||||
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nalgebra"
|
name = "nalgebra"
|
||||||
@ -156,11 +159,11 @@ version = "0.16.13"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alga 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"alga 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"generic-array 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"generic-array 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"matrixmultiply 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"matrixmultiply 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -170,12 +173,20 @@ name = "num-complex"
|
|||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.6"
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "numtoa"
|
||||||
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -189,10 +200,10 @@ version = "0.5.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -215,7 +226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.1.51"
|
version = "0.1.54"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -223,24 +234,24 @@ name = "redox_termios"
|
|||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.1.0"
|
version = "1.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.5"
|
version = "0.6.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -256,11 +267,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termion"
|
name = "termion"
|
||||||
version = "1.5.1"
|
version = "1.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
"numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -269,7 +281,7 @@ name = "thread_local"
|
|||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -289,7 +301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.6"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -306,7 +318,7 @@ name = "winapi-util"
|
|||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -319,49 +331,51 @@ name = "wincolor"
|
|||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
|
"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c"
|
||||||
"checksum alga 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24bb00eeca59f2986c747b8c2f271d52310ce446be27428fc34705138b155778"
|
"checksum alga 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24bb00eeca59f2986c747b8c2f271d52310ce446be27428fc34705138b155778"
|
||||||
"checksum approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c57ff1a5b00753647aebbbcf4ea67fa1e711a65ea7a30eb90dbf07de2485aee"
|
"checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3"
|
||||||
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||||
|
"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
|
||||||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||||
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
|
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||||
"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
|
"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b"
|
||||||
"checksum env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afb070faf94c85d17d50ca44f6ad076bce18ae92f0037d350947240a36e9d42e"
|
"checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a"
|
||||||
"checksum fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81f7f8eb465745ea9b02e2704612a9946a59fa40572086c6fd49d6ddcf30bf31"
|
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||||
"checksum generic-array 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8107dafa78c80c848b71b60133954b4a58609a3a1a5f9af037ecc7f67280f369"
|
"checksum generic-array 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8107dafa78c80c848b71b60133954b4a58609a3a1a5f9af037ecc7f67280f369"
|
||||||
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
|
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
|
||||||
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
|
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
|
||||||
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
|
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
||||||
"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047"
|
"checksum libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "42914d39aad277d9e176efbdad68acb1d5443ab65afe0e0e4f0d49352a950880"
|
||||||
"checksum libm 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "03c0bb6d5ce1b5cc6fd0578ec1cbc18c9d88b5b591a5c7c1d6c6175e266a0819"
|
"checksum libm 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfcefbd3a8aad4c0552fa150c28291becc386b1b056988493517875e987e99e3"
|
||||||
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
|
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
|
||||||
"checksum matrixmultiply 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "dcad67dcec2d58ff56f6292582377e6921afdf3bfbd533e26fb8900ae575e002"
|
"checksum matrixmultiply 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "dcad67dcec2d58ff56f6292582377e6921afdf3bfbd533e26fb8900ae575e002"
|
||||||
"checksum memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e1dd4eaac298c32ce07eb6ed9242eda7d82955b9170b7d6db59b2e02cc63fcb8"
|
"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
|
||||||
"checksum nalgebra 0.16.13 (registry+https://github.com/rust-lang/crates.io-index)" = "8e0799b53947b9c9048a1537f024f22f54701bbb75274f65955d081a87c0b739"
|
"checksum nalgebra 0.16.13 (registry+https://github.com/rust-lang/crates.io-index)" = "8e0799b53947b9c9048a1537f024f22f54701bbb75274f65955d081a87c0b739"
|
||||||
"checksum num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "107b9be86cd2481930688277b675b0114578227f034674726605b8a482d8baf8"
|
"checksum num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "107b9be86cd2481930688277b675b0114578227f034674726605b8a482d8baf8"
|
||||||
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
|
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
||||||
|
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||||
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
|
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
|
||||||
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||||
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
|
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
|
||||||
"checksum rawpointer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebac11a9d2e11f2af219b8b8d833b76b1ea0e054aa0e8d8e9e4cbde353bdf019"
|
"checksum rawpointer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebac11a9d2e11f2af219b8b8d833b76b1ea0e054aa0e8d8e9e4cbde353bdf019"
|
||||||
"checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85"
|
"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252"
|
||||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||||
"checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
|
"checksum regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f0a0bcab2fd7d1d7c54fa9eae6f43eddeb9ce2e7352f8518a814a4f65d60c58"
|
||||||
"checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861"
|
"checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96"
|
||||||
"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
|
"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
|
||||||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
"checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea"
|
||||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||||
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
|
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
|
||||||
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
|
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
|
||||||
"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
|
"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
|
||||||
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
|
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
|
||||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
|
"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
|
||||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
@ -1,17 +1,34 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::math::eqn::Eqns;
|
||||||
use crate::math::{Point2, Region, Region1, Region2, Scalar};
|
use crate::math::{Point2, Region, Region1, Region2, Scalar};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
type EntityId = i64;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Var<T: Clone, TRegion: Region<T>> {
|
pub struct Constrainable<T: Clone, TRegion: Region<T>> {
|
||||||
|
id: EntityId,
|
||||||
value: T,
|
value: T,
|
||||||
constraints: TRegion,
|
constraints: TRegion,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone, TRegion: Region<T>> Var<T, TRegion> {
|
impl<T: Clone + fmt::Display, TRegion: Region<T> + fmt::Display> fmt::Display
|
||||||
|
for Constrainable<T, TRegion>
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{{ {} ∈ {} }}", self.value, self.constraints)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone, TRegion: Region<T> + Clone> Constrainable<T, TRegion> {
|
||||||
pub fn new(value: T, constraints: TRegion) -> Self {
|
pub fn new(value: T, constraints: TRegion) -> Self {
|
||||||
Self { value, constraints }
|
Self {
|
||||||
|
id: 0,
|
||||||
|
value,
|
||||||
|
constraints,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_full(value: T) -> Self {
|
pub fn new_full(value: T) -> Self {
|
||||||
@ -22,6 +39,30 @@ impl<T: Clone, TRegion: Region<T>> Var<T, TRegion> {
|
|||||||
Self::new(value.clone(), TRegion::singleton(value))
|
Self::new(value.clone(), TRegion::singleton(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn val(&self) -> &T {
|
||||||
|
&self.value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_with(&mut self, eqns: &Eqns) -> bool {
|
||||||
|
let resolved_constraints = self.constraints.clone().substitute(eqns).simplify();
|
||||||
|
if let Some(n) = resolved_constraints.nearest(&self.value) {
|
||||||
|
self.value = n;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate_with(&mut self, eqns: &Eqns) -> bool {
|
||||||
|
self.constraints = self.constraints.clone().substitute(eqns).simplify();
|
||||||
|
if let Some(n) = self.constraints.nearest(&self.value) {
|
||||||
|
self.value = n;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn constraints(&self) -> &TRegion {
|
pub fn constraints(&self) -> &TRegion {
|
||||||
&self.constraints
|
&self.constraints
|
||||||
}
|
}
|
||||||
@ -37,28 +78,22 @@ impl<T: Clone, TRegion: Region<T>> Var<T, TRegion> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ScalarVar = Var<Scalar, Region1>;
|
pub type CScalar = Constrainable<Scalar, Region1>;
|
||||||
type PointVar = Var<Point2, Region2>;
|
pub type CPoint = Constrainable<Point2<Scalar>, Region2>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub type PointRef = Rc<RefCell<CPoint>>;
|
||||||
pub struct Point {
|
|
||||||
// pub id: i64,
|
|
||||||
pub pos: PointVar,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type PointRef = Rc<RefCell<Point>>;
|
impl CPoint {
|
||||||
|
pub fn into_ref(self) -> PointRef {
|
||||||
impl Point {
|
Rc::new(RefCell::new(self))
|
||||||
pub fn new_ref(pos: PointVar) -> PointRef {
|
|
||||||
Rc::new(RefCell::new(Point { pos }))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Line {
|
struct Line {
|
||||||
p1: PointRef,
|
p1: PointRef,
|
||||||
p2: PointRef,
|
p2: PointRef,
|
||||||
len: ScalarVar,
|
len: CScalar,
|
||||||
dir: ScalarVar,
|
dir: CScalar,
|
||||||
}
|
}
|
||||||
|
|
||||||
// struct System {
|
// struct System {
|
||||||
|
65
src/main.rs
65
src/main.rs
@ -17,34 +17,51 @@ mod math;
|
|||||||
mod relation;
|
mod relation;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
use entity::{Point, PointRef, Var};
|
use entity::{CPoint, PointRef};
|
||||||
use math::Point2;
|
use math::{Eqn, Eqns, Expr, Point2, Region2, Rot2};
|
||||||
use relation::{Relation, ResolveResult};
|
use relation::{Relation, ResolveResult};
|
||||||
|
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
let origin = Point::new_ref(Var::new_single(Point2::new(0., 0.)));
|
let u1 = math::Unknown(1);
|
||||||
let p1 = Point::new_ref(Var::new_full(Point2::new(1., 1.)));
|
let u2 = math::Unknown(2);
|
||||||
let p2 = Point::new_ref(Var::new_full(Point2::new(4., 4.)));
|
// let u1 = eqn::Expr::from(1.);
|
||||||
let p3 = Point::new_ref(Var::new_full(Point2::new(2., 2.)));
|
// let u2 = eqn::Expr::from(1.);
|
||||||
|
let origin = CPoint::new_single(Point2::new(0., 0.)).into_ref();
|
||||||
|
// let p1 = Point::new_ref(Var::new_full(Point2::new((1.).into(), (1.).into())));
|
||||||
|
let p1 = CPoint::new(
|
||||||
|
Point2::new(0., 0.),
|
||||||
|
Region2::Singleton(Point2::new((u1).into(), (u2).into())),
|
||||||
|
)
|
||||||
|
.into_ref();
|
||||||
|
let p2 = CPoint::new_full(Point2::new(4., 4.)).into_ref();
|
||||||
|
let p3 = CPoint::new_full(Point2::new(2., 2.)).into_ref();
|
||||||
let mut points: Vec<PointRef> = vec![origin.clone(), p1.clone(), p2.clone(), p3.clone()];
|
let mut points: Vec<PointRef> = vec![origin.clone(), p1.clone(), p2.clone(), p3.clone()];
|
||||||
let print_points = |points: &Vec<PointRef>| {
|
let print_points = |points: &Vec<PointRef>| {
|
||||||
println!(
|
println!(
|
||||||
"origin, p1, p2, p3:\n {:?}\n {:?}\n {:?}\n {:?}",
|
"origin: {}\np1: {}\np2: {}\np3: {}",
|
||||||
points[0], points[1], points[2], points[3],
|
points[0].borrow(),
|
||||||
|
points[1].borrow(),
|
||||||
|
points[2].borrow(),
|
||||||
|
points[3].borrow(),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
print_points(&points);
|
print_points(&points);
|
||||||
// let c1 = relation::Coincident {
|
let c1 = relation::Coincident {
|
||||||
// p1: origin.clone(),
|
p1: origin.clone(),
|
||||||
// p2: p1.clone(),
|
p2: p1.clone(),
|
||||||
// };
|
};
|
||||||
let c2 = relation::PointAngle::new_vertical(p1.clone(), p2.clone());
|
let c4 = relation::PointAngle::new_vertical(p1.clone(), p2.clone());
|
||||||
let c3 = relation::PointAngle::new_horizontal(p2.clone(), p3.clone());
|
let c3 = relation::PointAngle::new_horizontal(p2.clone(), p3.clone());
|
||||||
let c4 = relation::AlignedDistance::new_vertical(p1.clone(), p2.clone(), 12.);
|
let c2 = relation::AlignedDistance::new_vertical(p1.clone(), p2.clone(), 12.);
|
||||||
let mut relations: Vec<Box<dyn Relation>> =
|
let c5 = relation::PointAngle::new(p1.clone(), p3.clone(), Rot2::from_angle_deg(0.572938698));
|
||||||
vec![/*Box::new(c1),*/ Box::new(c2), Box::new(c3), Box::new(c4)];
|
let mut relations: Vec<Box<dyn Relation>> = vec![
|
||||||
|
/*Box::new(c1),*/ Box::new(c2),
|
||||||
|
Box::new(c3),
|
||||||
|
Box::new(c4),
|
||||||
|
Box::new(c5),
|
||||||
|
];
|
||||||
let mut constrained: Vec<Box<dyn Relation>> = Vec::new();
|
let mut constrained: Vec<Box<dyn Relation>> = Vec::new();
|
||||||
let mut any_underconstrained = true;
|
let mut any_underconstrained = true;
|
||||||
let mut any_constrained = true;
|
let mut any_constrained = true;
|
||||||
@ -55,10 +72,11 @@ fn main() {
|
|||||||
let newly_constrained = relations.drain_filter(|r| {
|
let newly_constrained = relations.drain_filter(|r| {
|
||||||
let rr = r.resolve();
|
let rr = r.resolve();
|
||||||
println!("resolve result: {:?}", rr);
|
println!("resolve result: {:?}", rr);
|
||||||
println!(
|
print_points(&points);
|
||||||
"origin, p1, p2, p3:\n {:?}\n {:?}\n {:?}\n {:?}",
|
let mut pos = p2.borrow().val().clone();
|
||||||
origin, p1, p2, p3
|
println!("p2 pos: {}", pos);
|
||||||
);
|
let mut pos = p3.borrow().val().clone();
|
||||||
|
println!("p3 pos: {}", pos);
|
||||||
match rr {
|
match rr {
|
||||||
ResolveResult::Underconstrained => {
|
ResolveResult::Underconstrained => {
|
||||||
any_underconstrained = true;
|
any_underconstrained = true;
|
||||||
@ -83,4 +101,11 @@ fn main() {
|
|||||||
} else {
|
} else {
|
||||||
println!("All constraints have been solved")
|
println!("All constraints have been solved")
|
||||||
}
|
}
|
||||||
|
let e1 = Eqn::new(Expr::Unkn(u1), Expr::Const(1.));
|
||||||
|
let e2 = Eqn::new(Expr::Unkn(u2), Expr::Const(1.));
|
||||||
|
let eqns = Eqns(vec![e1, e2].into());
|
||||||
|
for p in &mut points {
|
||||||
|
p.borrow_mut().resolve_with(&eqns);
|
||||||
|
}
|
||||||
|
print_points(&points);
|
||||||
}
|
}
|
||||||
|
790
src/math/eqn.rs
790
src/math/eqn.rs
@ -1,450 +1,14 @@
|
|||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::borrow::{Borrow, Cow};
|
||||||
|
use std::collections::btree_map::Entry;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::iter::FromIterator;
|
|
||||||
|
|
||||||
use crate::math::Scalar;
|
use super::expr::Expr;
|
||||||
|
use super::unknown::*;
|
||||||
// an unknown variable with an id
|
use crate::math::expr::Expr::*;
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct Unknown(i64);
|
|
||||||
|
|
||||||
pub type UnknownSet = BTreeSet<Unknown>;
|
|
||||||
|
|
||||||
pub trait Unknowns {
|
|
||||||
fn unknowns(&self) -> UnknownSet;
|
|
||||||
fn has_unknowns(&self) -> bool;
|
|
||||||
fn has_unknown(&self, u: Unknown) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Unknowns for Scalar {
|
|
||||||
fn unknowns(&self) -> UnknownSet {
|
|
||||||
UnknownSet::new()
|
|
||||||
}
|
|
||||||
fn has_unknowns(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
fn has_unknown(&self, _: Unknown) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Unknowns for Unknown {
|
|
||||||
fn unknowns(&self) -> UnknownSet {
|
|
||||||
FromIterator::from_iter(Some(*self))
|
|
||||||
}
|
|
||||||
fn has_unknowns(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
fn has_unknown(&self, u: Unknown) -> bool {
|
|
||||||
*self == u
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Unknown {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "u{}", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Expr {
|
pub struct Eqn(pub Expr, pub Expr);
|
||||||
Unkn(Unknown),
|
|
||||||
Const(Scalar),
|
|
||||||
Sum(Exprs),
|
|
||||||
Neg(Box<Expr>),
|
|
||||||
Product(Exprs),
|
|
||||||
Div(Box<Expr>, Box<Expr>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Exprs = Vec<Expr>;
|
|
||||||
|
|
||||||
impl Unknowns for Exprs {
|
|
||||||
fn unknowns(&self) -> UnknownSet {
|
|
||||||
self.iter().flat_map(|e: &Expr| e.unknowns()).collect()
|
|
||||||
}
|
|
||||||
fn has_unknowns(&self) -> bool {
|
|
||||||
self.iter().any(|e: &Expr| e.has_unknowns())
|
|
||||||
}
|
|
||||||
fn has_unknown(&self, u: Unknown) -> bool {
|
|
||||||
self.iter().any(|e: &Expr| e.has_unknown(u))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_separated_exprs(es: &Exprs, f: &mut fmt::Formatter, sep: &str) -> fmt::Result {
|
|
||||||
let mut is_first = true;
|
|
||||||
for e in es {
|
|
||||||
if is_first {
|
|
||||||
is_first = false;
|
|
||||||
} else {
|
|
||||||
write!(f, "{}", sep)?
|
|
||||||
}
|
|
||||||
write!(f, "({})", e)?
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_common_terms(l: &mut Vec<Expr>, r: &mut Vec<Expr>) -> Vec<Expr> {
|
|
||||||
let common: Vec<_> = l.drain_filter(|e| r.contains(e)).collect();
|
|
||||||
common.iter().for_each(|e| {
|
|
||||||
r.remove_item(e);
|
|
||||||
});
|
|
||||||
common
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_term(terms: &mut Vec<Expr>, term: &Expr) -> Option<Expr> {
|
|
||||||
terms.remove_item(term)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sum_fold(l: Expr, r: Expr) -> Expr {
|
|
||||||
use itertools::Itertools;
|
|
||||||
use Expr::*;
|
|
||||||
match (l, r) {
|
|
||||||
(Const(lc), Const(rc)) => Const(lc + rc),
|
|
||||||
(Const(c), o) | (o, Const(c)) if relative_eq!(c, 0.) => o,
|
|
||||||
(Product(mut l), Product(mut r)) => {
|
|
||||||
let comm = remove_common_terms(&mut l, &mut r);
|
|
||||||
Expr::new_product(Sum(comm), Expr::new_sum(Product(l), Product(r))).simplify()
|
|
||||||
}
|
|
||||||
(Product(mut l), r) | (r, Product(mut l)) => {
|
|
||||||
let comm = remove_term(&mut l, &r);
|
|
||||||
match comm {
|
|
||||||
Some(_) => {
|
|
||||||
Expr::new_product(r, Expr::new_sum(Product(l), Const(1.))).simplify()
|
|
||||||
}
|
|
||||||
None => Expr::new_sum(Product(l), r),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(l, r) => Expr::new_sum(l, r),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn group_sum(es: Exprs) -> Exprs {
|
|
||||||
use Expr::*;
|
|
||||||
let mut common: BTreeMap<UnknownSet, Expr> = BTreeMap::new();
|
|
||||||
for e in es {
|
|
||||||
let unkns = e.unknowns();
|
|
||||||
match common.get_mut(&unkns) {
|
|
||||||
None => {
|
|
||||||
match e {
|
|
||||||
Const(c) if relative_eq!(c, 0.) => (),
|
|
||||||
e => {
|
|
||||||
common.insert(unkns, e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Some(existing) => {
|
|
||||||
match existing {
|
|
||||||
Sum(ref mut es) => {
|
|
||||||
// already failed at merging, so just add it to the list
|
|
||||||
es.push(e);
|
|
||||||
}
|
|
||||||
other => {
|
|
||||||
*other = sum_fold(other.clone(), e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
common.into_iter().map(|(_, v)| v).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn product_fold(l: Expr, r: Expr) -> Expr {
|
|
||||||
use itertools::Itertools;
|
|
||||||
use Expr::*;
|
|
||||||
match (l, r) {
|
|
||||||
(Const(lc), Const(rc)) => Const(lc * rc),
|
|
||||||
(Const(c), o) | (o, Const(c)) if relative_eq!(c, 1.) => o,
|
|
||||||
(Div(num, den), mul) | (mul, Div(num, den)) => {
|
|
||||||
if mul == *den {
|
|
||||||
*num
|
|
||||||
} else {
|
|
||||||
Expr::Div(Box::new(Expr::Product(vec![*num, mul])), den).simplify()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(l, r) => Expr::new_product(l, r),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn group_product(es: Exprs) -> Exprs {
|
|
||||||
use Expr::*;
|
|
||||||
let mut common: BTreeMap<UnknownSet, Expr> = BTreeMap::new();
|
|
||||||
for e in es {
|
|
||||||
let unkns = e.unknowns();
|
|
||||||
match common.get_mut(&unkns) {
|
|
||||||
None => {
|
|
||||||
match e {
|
|
||||||
Const(c) if relative_eq!(c, 1.) => (),
|
|
||||||
e => {
|
|
||||||
common.insert(unkns, e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Some(existing) => {
|
|
||||||
match existing {
|
|
||||||
Sum(ref mut es) => {
|
|
||||||
// already failed at merging, so just add it to the list
|
|
||||||
es.push(e);
|
|
||||||
}
|
|
||||||
other => *other = product_fold(other.clone(), e),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
common.into_iter().map(|(_, v)| v).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn distribute_product_sums(mut es: Exprs) -> Expr {
|
|
||||||
trace!("distribute_product_sums: {}", Product(es.clone()));
|
|
||||||
use itertools::Itertools;
|
|
||||||
use Expr::*;
|
|
||||||
let sums = es
|
|
||||||
.drain_filter(|e| match e {
|
|
||||||
Sum(_) => true,
|
|
||||||
_ => false,
|
|
||||||
})
|
|
||||||
.map(|e| {
|
|
||||||
trace!("sum in product: {}", e);
|
|
||||||
match e {
|
|
||||||
Sum(es) => es,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let products: Vec<_> = sums.multi_cartesian_product().collect();
|
|
||||||
if products.is_empty() {
|
|
||||||
trace!("no sums to distribute");
|
|
||||||
return Product(es);
|
|
||||||
}
|
|
||||||
let sums = products
|
|
||||||
.into_iter()
|
|
||||||
.map(|mut prod| {
|
|
||||||
prod.extend(es.clone());
|
|
||||||
trace!("prod: {}", Product(prod.clone()));
|
|
||||||
Product(prod)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Sum(sums)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Unknowns for Expr {
|
|
||||||
fn unknowns(&self) -> UnknownSet {
|
|
||||||
use Expr::*;
|
|
||||||
match self {
|
|
||||||
Unkn(u) => u.unknowns(),
|
|
||||||
Const(_) => UnknownSet::default(),
|
|
||||||
Sum(es) | Product(es) => es.unknowns(),
|
|
||||||
Div(l, r) => l.unknowns().union(&r.unknowns()).cloned().collect(),
|
|
||||||
Neg(e) => e.unknowns(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn has_unknowns(&self) -> bool {
|
|
||||||
use Expr::*;
|
|
||||||
match self {
|
|
||||||
Unkn(u) => u.has_unknowns(),
|
|
||||||
Const(_) => false,
|
|
||||||
Sum(es) | Product(es) => es.has_unknowns(),
|
|
||||||
Div(l, r) => l.has_unknowns() || r.has_unknowns(),
|
|
||||||
Neg(e) => e.has_unknowns(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn has_unknown(&self, u: Unknown) -> bool {
|
|
||||||
use Expr::*;
|
|
||||||
match self {
|
|
||||||
Unkn(u1) => u1.has_unknown(u),
|
|
||||||
Const(_) => false,
|
|
||||||
Sum(es) | Product(es) => es.has_unknown(u),
|
|
||||||
Div(l, r) => l.has_unknown(u) || r.has_unknown(u),
|
|
||||||
Neg(e) => e.has_unknown(u),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Expr {
|
|
||||||
pub fn new_sum(e1: Expr, e2: Expr) -> Expr {
|
|
||||||
Expr::Sum(vec![e1, e2])
|
|
||||||
}
|
|
||||||
pub fn new_product(e1: Expr, e2: Expr) -> Expr {
|
|
||||||
Expr::Product(vec![e1, e2])
|
|
||||||
}
|
|
||||||
pub fn new_neg(e1: Expr) -> Expr {
|
|
||||||
Expr::Neg(Box::new(e1))
|
|
||||||
}
|
|
||||||
pub fn new_div(num: Expr, den: Expr) -> Expr {
|
|
||||||
Expr::Div(Box::new(num), Box::new(den))
|
|
||||||
}
|
|
||||||
pub fn new_minus(e1: Expr, e2: Expr) -> Expr {
|
|
||||||
Expr::Sum(vec![e1, Expr::new_neg(e2)])
|
|
||||||
}
|
|
||||||
pub fn new_inv(den: Expr) -> Expr {
|
|
||||||
Expr::new_div(Expr::Const(1.), den)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_zero(&self) -> bool {
|
|
||||||
use Expr::*;
|
|
||||||
match self {
|
|
||||||
Const(c) => relative_eq!(*c, 0.),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_one(&self) -> bool {
|
|
||||||
use Expr::*;
|
|
||||||
match self {
|
|
||||||
Const(c) => relative_eq!(*c, 1.),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn simplify(self) -> Expr {
|
|
||||||
use Expr::*;
|
|
||||||
match self {
|
|
||||||
Sum(es) => {
|
|
||||||
let mut new_es: Vec<_> = es
|
|
||||||
.into_iter()
|
|
||||||
.map(|e| e.simplify())
|
|
||||||
.flat_map(|e| match e {
|
|
||||||
Sum(more_es) => more_es,
|
|
||||||
other => vec![other],
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let pre_new_es = new_es.clone();
|
|
||||||
new_es = group_sum(new_es);
|
|
||||||
trace!(
|
|
||||||
"simplify sum {} => {}",
|
|
||||||
Sum(pre_new_es),
|
|
||||||
Sum(new_es.clone())
|
|
||||||
);
|
|
||||||
|
|
||||||
match new_es.len() {
|
|
||||||
0 => Const(0.), // none
|
|
||||||
1 => new_es.into_iter().next().unwrap(), // one
|
|
||||||
_ => Sum(new_es), // many
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Product(es) => {
|
|
||||||
let new_es: Vec<_> = es
|
|
||||||
.into_iter()
|
|
||||||
.map(|e| e.simplify())
|
|
||||||
.flat_map(|e| match e {
|
|
||||||
Product(more_es) => more_es,
|
|
||||||
other => vec![other],
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let pre_new_es = new_es.clone();
|
|
||||||
let new_es = group_product(new_es);
|
|
||||||
trace!(
|
|
||||||
"simplify product {} => {}",
|
|
||||||
Product(pre_new_es),
|
|
||||||
Product(new_es.clone())
|
|
||||||
);
|
|
||||||
match new_es.len() {
|
|
||||||
0 => Const(1.), // none
|
|
||||||
1 => new_es.into_iter().next().unwrap(), // one
|
|
||||||
_ => Product(new_es), // many
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Neg(mut v) => {
|
|
||||||
*v = v.simplify();
|
|
||||||
trace!("simplify neg {}", Neg(v.clone()));
|
|
||||||
match v {
|
|
||||||
box Const(c) => Const(-c),
|
|
||||||
box Neg(v) => *v,
|
|
||||||
e => Product(vec![Const(-1.), *e]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Div(mut num, mut den) => {
|
|
||||||
*num = num.simplify();
|
|
||||||
*den = den.simplify();
|
|
||||||
trace!("simplify div {}", Div(num.clone(), den.clone()));
|
|
||||||
match (num, den) {
|
|
||||||
(box Const(num), box Const(den)) => Const(num / den),
|
|
||||||
(num, box Const(den)) => {
|
|
||||||
if relative_eq!(den, 1.) {
|
|
||||||
*num
|
|
||||||
} else {
|
|
||||||
Expr::new_product(*num, Const(1. / den))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(num, box Div(dennum, denden)) => {
|
|
||||||
Div(Box::new(Product(vec![*num, *denden])), dennum).simplify()
|
|
||||||
}
|
|
||||||
(box Product(mut es), box den) => match es.remove_item(&den) {
|
|
||||||
Some(_) => Product(es),
|
|
||||||
None => Expr::new_div(Product(es), den),
|
|
||||||
},
|
|
||||||
(num, den) => {
|
|
||||||
if num == den {
|
|
||||||
Expr::Const(1.)
|
|
||||||
} else {
|
|
||||||
Div(num, den)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e => e,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn distribute(self) -> Expr {
|
|
||||||
use Expr::*;
|
|
||||||
trace!("distribute {}", self);
|
|
||||||
match self {
|
|
||||||
Sum(mut es) => {
|
|
||||||
for e in &mut es {
|
|
||||||
*e = e.clone().distribute();
|
|
||||||
}
|
|
||||||
Sum(es)
|
|
||||||
}
|
|
||||||
Product(es) => distribute_product_sums(es),
|
|
||||||
Div(mut num, mut den) => {
|
|
||||||
*num = num.distribute();
|
|
||||||
*den = den.distribute();
|
|
||||||
match (num, den) {
|
|
||||||
(box Sum(es), box den) => Sum(es
|
|
||||||
.into_iter()
|
|
||||||
.map(|e| Expr::new_div(e, den.clone()))
|
|
||||||
.collect()),
|
|
||||||
(mut num, mut den) => Div(num, den),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Neg(v) => match v {
|
|
||||||
// box Sum(mut l, mut r) => {
|
|
||||||
// *l = Neg(l.clone()).distribute();
|
|
||||||
// *r = Neg(r.clone()).distribute();
|
|
||||||
// Sum(l, r)
|
|
||||||
// }
|
|
||||||
// box Product(mut l, r) => {
|
|
||||||
// *l = Neg(l.clone()).distribute();
|
|
||||||
// Product(l, r)
|
|
||||||
// }
|
|
||||||
box Neg(v) => v.distribute(),
|
|
||||||
box Div(mut num, mut den) => {
|
|
||||||
*num = Neg(num.clone()).distribute();
|
|
||||||
*den = Neg(den.clone()).distribute();
|
|
||||||
Div(num, den)
|
|
||||||
}
|
|
||||||
e => Neg(e),
|
|
||||||
},
|
|
||||||
e => e,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Expr {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
use Expr::*;
|
|
||||||
match self {
|
|
||||||
Unkn(u) => write!(f, "{}", u),
|
|
||||||
Const(c) => write!(f, "{}", c),
|
|
||||||
Sum(es) => write_separated_exprs(es, f, " + "),
|
|
||||||
Product(es) => write_separated_exprs(es, f, " * "),
|
|
||||||
Div(num, den) => write!(f, "({}) / ({})", num, den),
|
|
||||||
Neg(e) => write!(f, "-({})", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct Eqn(Expr, Expr);
|
|
||||||
|
|
||||||
impl Unknowns for Eqn {
|
impl Unknowns for Eqn {
|
||||||
fn unknowns(&self) -> UnknownSet {
|
fn unknowns(&self) -> UnknownSet {
|
||||||
@ -481,61 +45,71 @@ impl Eqn {
|
|||||||
Eqn(self.0.simplify(), self.1.simplify())
|
Eqn(self.0.simplify(), self.1.simplify())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn solve(&self, for_u: Unknown) -> Option<Expr> {
|
pub fn substitute(self, eqns: &Eqns) -> Eqn {
|
||||||
use Expr::*;
|
Self(self.0.substitute(eqns), self.1.substitute(eqns))
|
||||||
if !self.has_unknown(for_u) {
|
}
|
||||||
return None;
|
}
|
||||||
}
|
|
||||||
let (l, r) = (
|
pub fn solve_eqn(eqn: &Eqn, for_u: Unknown) -> Option<Expr> {
|
||||||
self.0
|
use Expr::*;
|
||||||
.clone() /*.distribute()*/
|
if !eqn.has_unknown(for_u) {
|
||||||
.simplify(),
|
return None;
|
||||||
self.1
|
}
|
||||||
.clone() /*.distribute()*/
|
trace!("solve: {}", eqn);
|
||||||
.simplify(),
|
let (mut l, mut r) = (eqn.0.clone().distribute().simplify(), eqn.1.clone().distribute().simplify());
|
||||||
);
|
if l == r {
|
||||||
let (mut l, mut r) = ord_by_unkn(l, r, for_u)?;
|
return None
|
||||||
loop {
|
};
|
||||||
trace!("solve: {} == {}", l, r);
|
l = (l - r).distribute().simplify();
|
||||||
let (new_l, new_r): (Expr, Expr) = match l {
|
r = Expr::Const(0.);
|
||||||
Unkn(u) => return if u == for_u { Some(r.simplify()) } else { None },
|
loop {
|
||||||
Sum(es) => {
|
trace!("solve iter: {} == {}", l, r);
|
||||||
let (us, not_us): (Vec<_>, Vec<_>) =
|
let (new_l, new_r): (Expr, Expr) = match l {
|
||||||
es.into_iter().partition(|e| e.has_unknown(for_u));
|
Unkn(u) => return if u == for_u { Some(r.simplify()) } else { None },
|
||||||
if us.len() != 1 {
|
Sum(es) => {
|
||||||
return None;
|
let (us, not_us): (Vec<_>, Vec<_>) =
|
||||||
}
|
es.into_iter().partition(|e| e.has_unknown(for_u));
|
||||||
(
|
if us.len() != 1 {
|
||||||
Sum(us).simplify(),
|
return None;
|
||||||
Expr::new_minus(r, Sum(not_us)).simplify(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Product(es) => {
|
(
|
||||||
let (us, not_us): (Vec<_>, Vec<_>) =
|
us.into_iter().next().unwrap(),
|
||||||
es.into_iter().partition(|e| e.has_unknown(for_u));
|
if not_us.len() == 0 {
|
||||||
if us.len() != 1 {
|
r
|
||||||
return None;
|
} else {
|
||||||
}
|
Expr::new_minus(r, Sum(not_us))
|
||||||
(
|
},
|
||||||
Product(us).simplify(),
|
)
|
||||||
Expr::new_div(r, Product(not_us)).simplify(),
|
}
|
||||||
)
|
Product(es) => {
|
||||||
|
let (us, not_us): (Vec<_>, Vec<_>) =
|
||||||
|
es.into_iter().partition(|e| e.has_unknown(for_u));
|
||||||
|
if us.len() != 1 {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
Neg(v) => (*v, Expr::new_neg(r)),
|
(
|
||||||
Div(num, den) => {
|
us.into_iter().next().unwrap(),
|
||||||
let (nu, du) = (num.has_unknown(for_u), den.has_unknown(for_u));
|
if not_us.len() == 0 {
|
||||||
match (nu, du) {
|
r
|
||||||
(true, false) => (*num, Expr::new_product(r, *den)),
|
} else {
|
||||||
(false, true) => (Expr::new_product(r, *den), *num),
|
Expr::new_div(r, Product(not_us))
|
||||||
(true, true) => return None, // TODO: simplify
|
},
|
||||||
(false, false) => return None,
|
)
|
||||||
}
|
}
|
||||||
|
Neg(v) => (*v, Expr::new_neg(r)),
|
||||||
|
Div(num, den) => {
|
||||||
|
let (nu, du) = (num.has_unknown(for_u), den.has_unknown(for_u));
|
||||||
|
match (nu, du) {
|
||||||
|
(true, false) => (*num, Expr::new_product(r, *den)),
|
||||||
|
(false, true) => (Expr::new_product(r, *den), *num),
|
||||||
|
(true, true) => return None, // TODO: simplify
|
||||||
|
(false, false) => return None,
|
||||||
}
|
}
|
||||||
Const(_) => return None,
|
}
|
||||||
};
|
Const(_) => return None,
|
||||||
l = new_l;
|
};
|
||||||
r = new_r;
|
l = new_l.distribute().simplify();
|
||||||
}
|
r = new_r.distribute().simplify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,9 +120,9 @@ impl fmt::Display for Eqn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Eqns(Vec<Eqn>);
|
pub struct Eqns<'a>(pub Cow<'a, [Eqn]>);
|
||||||
|
|
||||||
impl Unknowns for Eqns {
|
impl<'a> Unknowns for Eqns<'a> {
|
||||||
fn unknowns(&self) -> UnknownSet {
|
fn unknowns(&self) -> UnknownSet {
|
||||||
self.0.iter().flat_map(|eqn: &Eqn| eqn.unknowns()).collect()
|
self.0.iter().flat_map(|eqn: &Eqn| eqn.unknowns()).collect()
|
||||||
}
|
}
|
||||||
@ -560,8 +134,107 @@ impl Unknowns for Eqns {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> fmt::Display for Eqns<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{{ ")?;
|
||||||
|
let mut first = true;
|
||||||
|
for eq in (&self.0 as &Borrow<[Eqn]>).borrow() {
|
||||||
|
if first {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
write!(f, " && ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{}", eq)?
|
||||||
|
}
|
||||||
|
write!(f, " }}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn solve_eqns<'a>(eqns: &Eqns<'a>, for_vars: &[Unknown]) -> Option<Eqns<'static>> {
|
||||||
|
let eqn_unknowns = eqns.unknowns();
|
||||||
|
let has_all_vars = for_vars.iter().all(|u| eqn_unknowns.contains(u));
|
||||||
|
if !has_all_vars {
|
||||||
|
// return None;
|
||||||
|
}
|
||||||
|
let mut var_sols = BTreeMap::<Unknown, Vec<Expr>>::new();
|
||||||
|
for u in &eqn_unknowns {
|
||||||
|
for eq in (&eqns.0 as &Borrow<[Eqn]>).borrow() {
|
||||||
|
let sol = solve_eqn(eq, *u);
|
||||||
|
if let Some(sol) = sol {
|
||||||
|
//println!("sol {0} -> {2} from {1}", u, eq, sol);
|
||||||
|
match var_sols.entry(*u) {
|
||||||
|
Entry::Vacant(vac) => {
|
||||||
|
vac.insert(vec![sol]);
|
||||||
|
}
|
||||||
|
Entry::Occupied(mut occ) => {
|
||||||
|
if !occ.get().contains(&sol) {
|
||||||
|
occ.get_mut().push(sol);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
//println!("no sol for {0} in {1}", u, eq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut new_eqns: Vec<Eqn> = Vec::new();
|
||||||
|
let mut all_have_sols = true;
|
||||||
|
for e in &var_sols {
|
||||||
|
let mut has_sol = false;
|
||||||
|
let mut last_expr = Unkn(*e.0);
|
||||||
|
println!("{}", e.0);
|
||||||
|
for s in e.1 {
|
||||||
|
println!(" == {}", s);
|
||||||
|
new_eqns.push(Eqn(last_expr, s.clone()));
|
||||||
|
last_expr = s.clone();
|
||||||
|
match s {
|
||||||
|
Const(_) => { has_sol = true; }
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if !has_sol {
|
||||||
|
all_have_sols = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut new_eqns = Eqns(new_eqns.into());
|
||||||
|
println!("new eqns: {}", new_eqns);
|
||||||
|
if all_have_sols {
|
||||||
|
return Some(new_eqns);
|
||||||
|
}
|
||||||
|
return solve_eqns(&new_eqns, for_vars);
|
||||||
|
/*
|
||||||
|
let mut more_eqns: Vec<Eqn> = Vec::new();
|
||||||
|
let mut new_orig_eqns = eqns.clone();
|
||||||
|
for eq in new_orig_eqns.0.to_mut() {
|
||||||
|
let u = match eq.0 {
|
||||||
|
Unkn(u) => u,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
let my_new_eqns = Eqns(
|
||||||
|
new_eqns
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.filter(|e| !e.1.has_unknown(u))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
let r =
|
||||||
|
eq.1.clone()
|
||||||
|
.substitute(&my_new_eqns)
|
||||||
|
.distribute()
|
||||||
|
.simplify();
|
||||||
|
more_eqns.push(Eqn(Unkn(u), r));
|
||||||
|
}
|
||||||
|
let mut more_eqns = Eqns(more_eqns.into());
|
||||||
|
println!("more eqns: {}", more_eqns);
|
||||||
|
*/
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::super::Scalar;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -592,7 +265,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_solve() {
|
fn test_solve_eqn() {
|
||||||
use Expr::*;
|
use Expr::*;
|
||||||
let _ = env_logger::try_init();
|
let _ = env_logger::try_init();
|
||||||
let u1 = Unknown(1);
|
let u1 = Unknown(1);
|
||||||
@ -600,27 +273,27 @@ mod tests {
|
|||||||
let e2 = Const(1.);
|
let e2 = Const(1.);
|
||||||
|
|
||||||
let eqn = Eqn(e1.clone(), e2.clone());
|
let eqn = Eqn(e1.clone(), e2.clone());
|
||||||
assert_eq!(eqn.solve(u1), Some(Const(1.)));
|
assert_eq!(solve_eqn(&eqn, u1), Some(Const(1.)));
|
||||||
let eqn = Eqn(e2.clone(), e1.clone());
|
let eqn = Eqn(e2.clone(), e1.clone());
|
||||||
assert_eq!(eqn.solve(u1), Some(Const(1.)));
|
assert_eq!(solve_eqn(&eqn, u1), Some(Const(1.)));
|
||||||
|
|
||||||
let e3 = Expr::new_sum(Const(1.), Expr::new_sum(Const(1.), Const(2.)));
|
let e3: Expr = Expr::from(1.) + 1. + 2.;
|
||||||
let eqn = Eqn(e1.clone(), e3.clone());
|
let eqn = Eqn(e1.clone(), e3.clone());
|
||||||
assert_eq!(eqn.solve(u1), Some(Const(4.)));
|
assert_eq!(solve_eqn(&eqn, u1), Some(Const(4.)));
|
||||||
let e3 = Expr::new_minus(Const(1.), Const(1.));
|
let e3 = Expr::from(1.) - 1.;
|
||||||
let eqn = Eqn(e1.clone(), e3.clone());
|
let eqn = Eqn(e1.clone(), e3.clone());
|
||||||
assert_eq!(eqn.solve(u1), Some(Const(0.)));
|
assert_eq!(solve_eqn(&eqn, u1), Some(Const(0.)));
|
||||||
|
|
||||||
let e1 = Expr::new_div(Const(2.), Expr::new_minus(Const(1.), Const(4.)));
|
let e1 = Expr::from(2.) / (Expr::from(1.) - 4.);
|
||||||
let e2 = Expr::new_minus(Const(1.), Unkn(u1));
|
let e2 = Expr::new_minus(Const(1.), Unkn(u1));
|
||||||
let eqn = Eqn(e1, e2);
|
let eqn = Eqn(e1, e2);
|
||||||
info!("eqn: {} => {}", eqn, eqn.clone().simplify());
|
info!("eqn: {} => {}", eqn, eqn.clone().simplify());
|
||||||
let e = eqn.solve(u1).unwrap();
|
let e = solve_eqn(&eqn, u1).unwrap();
|
||||||
assert!(const_expr(e.clone()).is_some());
|
assert!(const_expr(e.clone()).is_some());
|
||||||
assert!(relative_eq!(const_expr(e.clone()).unwrap(), 5. / 3.));
|
assert!(relative_eq!(const_expr(e.clone()).unwrap(), 5. / 3.));
|
||||||
|
|
||||||
let e1 = Expr::new_product(Const(2.), Expr::new_minus(Const(1.), Const(4.)));
|
let e1 = Expr::from(2.) * (Expr::from(1.) - 4.);
|
||||||
let e2 = Expr::new_minus(Expr::new_product(Unkn(u1), Const(2.)), Unkn(u1));
|
let e2 = (u1 * 2.) - u1;
|
||||||
info!(
|
info!(
|
||||||
"e1==e2: {}=={} => {}=={}",
|
"e1==e2: {}=={} => {}=={}",
|
||||||
e1,
|
e1,
|
||||||
@ -629,18 +302,12 @@ mod tests {
|
|||||||
e2.clone().simplify()
|
e2.clone().simplify()
|
||||||
);
|
);
|
||||||
let eqn = Eqn(e1, e2);
|
let eqn = Eqn(e1, e2);
|
||||||
let e = eqn.solve(u1).unwrap();
|
let e = solve_eqn(&eqn, u1).unwrap();
|
||||||
assert!(const_expr(e.clone()).is_some());
|
assert!(const_expr(e.clone()).is_some());
|
||||||
assert!(relative_eq!(const_expr(e.clone()).unwrap(), -6.));
|
assert!(relative_eq!(const_expr(e.clone()).unwrap(), -6.));
|
||||||
|
|
||||||
let e1 = Expr::new_product(Const(2.), Expr::new_minus(Const(1.), Const(4.)));
|
let e1 = Expr::from(2.) * (Expr::from(1.) - 4.);
|
||||||
let e2 = Expr::new_div(
|
let e2 = ((u1 * 2.) + (u1 * u1)) / u1;
|
||||||
Expr::new_sum(
|
|
||||||
Expr::new_product(Unkn(u1), Const(2.)),
|
|
||||||
Expr::new_product(Unkn(u1), Unkn(u1)),
|
|
||||||
),
|
|
||||||
Unkn(u1),
|
|
||||||
);
|
|
||||||
info!(
|
info!(
|
||||||
"{}=={} distrib=> {}=={}",
|
"{}=={} distrib=> {}=={}",
|
||||||
e1,
|
e1,
|
||||||
@ -656,32 +323,93 @@ mod tests {
|
|||||||
e2.clone().distribute().simplify()
|
e2.clone().distribute().simplify()
|
||||||
);
|
);
|
||||||
let eqn = Eqn(e1, e2);
|
let eqn = Eqn(e1, e2);
|
||||||
let e = eqn.solve(u1).unwrap();
|
let e = solve_eqn(&eqn, u1).unwrap();
|
||||||
assert!(const_expr(e.clone()).is_some());
|
assert!(const_expr(e.clone()).is_some());
|
||||||
assert!(relative_eq!(const_expr(e.clone()).unwrap(), -8.));
|
assert!(relative_eq!(const_expr(e.clone()).unwrap(), -8.));
|
||||||
|
|
||||||
let e1 = Expr::new_product(Const(2.), Expr::new_minus(Const(1.), Const(4.)));
|
let e1 = Expr::from(2.) * (Expr::from(1.) - (4.));
|
||||||
let e2 = Expr::new_div(
|
let e2 = ((u1 * 2.) + (((u1 * u1) + u1) + (Expr::from(2.) - (1. + 1.)))) / u1;
|
||||||
Expr::new_sum(
|
|
||||||
Expr::new_product(Unkn(u1), Const(2.)),
|
|
||||||
Expr::new_sum(
|
|
||||||
Expr::new_sum(Expr::new_product(Unkn(u1), Unkn(u1)), Unkn(u1)),
|
|
||||||
Expr::new_minus(Const(2.), Const(1. + 1.)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Unkn(u1),
|
|
||||||
);
|
|
||||||
info!(
|
info!(
|
||||||
"e1==e2: {}=={} => {}=={}",
|
"e1==e2: {}=={} => {}=={}",
|
||||||
e1,
|
e1,
|
||||||
e2,
|
e2,
|
||||||
e1.clone().distribute().simplify(),
|
e1.clone().distribute().simplify(),
|
||||||
e2.clone().distribute().simplify().simplify()
|
e2.clone().distribute().simplify()
|
||||||
);
|
);
|
||||||
let eqn = Eqn(e1, e2);
|
let eqn = Eqn(e1, e2);
|
||||||
let e = eqn.solve(u1).unwrap();
|
let e = solve_eqn(&eqn, u1).unwrap();
|
||||||
assert!(const_expr(e.clone()).is_some());
|
assert!(const_expr(e.clone()).is_some());
|
||||||
assert!(relative_eq!(const_expr(e.clone()).unwrap(), -9.));
|
assert!(relative_eq!(const_expr(e.clone()).unwrap(), -9.));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_solve_eqn2() {
|
||||||
|
let _ = env_logger::try_init();
|
||||||
|
|
||||||
|
let u1 = Unknown(1);
|
||||||
|
let e1 = Expr::Unkn(u1);
|
||||||
|
let e2 = (u1 * 2.) - 2.;
|
||||||
|
info!(
|
||||||
|
"e1==e2: {}=={} => {}=={}",
|
||||||
|
e1,
|
||||||
|
e2,
|
||||||
|
e1.clone().distribute().simplify(),
|
||||||
|
e2.clone().distribute().simplify(),
|
||||||
|
);
|
||||||
|
let eqn = Eqn(e1, e2);
|
||||||
|
let e = solve_eqn(&eqn, u1).unwrap();
|
||||||
|
assert!(const_expr(e.clone()).is_some());
|
||||||
|
assert!(relative_eq!(const_expr(e.clone()).unwrap(), 2.));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_solve_equations() {
|
||||||
|
let _ = env_logger::try_init();
|
||||||
|
|
||||||
|
use Expr::*;
|
||||||
|
|
||||||
|
let x = Unknown(1);
|
||||||
|
let y = Unknown(2);
|
||||||
|
let t1 = Unknown(3);
|
||||||
|
let t2 = Unknown(4);
|
||||||
|
|
||||||
|
let eqns = Eqns(
|
||||||
|
vec![
|
||||||
|
Eqn::new(x.into(), t1 * 1.),
|
||||||
|
Eqn::new(y.into(), t1 / 2.),
|
||||||
|
Eqn::new(x.into(), Const(0.0) + t2 / 2.),
|
||||||
|
Eqn::new(y.into(), t2 / 2.),
|
||||||
|
]
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
println!("eqns: {}", eqns);
|
||||||
|
let sol = solve_eqns(&eqns, &[t1, t2]).unwrap();
|
||||||
|
println!("sols: {}", sol);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_solve_equations2() {
|
||||||
|
let _ = env_logger::try_init();
|
||||||
|
|
||||||
|
use Expr::*;
|
||||||
|
|
||||||
|
let x = Unknown(1);
|
||||||
|
let y = Unknown(2);
|
||||||
|
let t1 = Unknown(3);
|
||||||
|
let t2 = Unknown(4);
|
||||||
|
|
||||||
|
let eqns = Eqns(
|
||||||
|
vec![
|
||||||
|
Eqn::new(x.into(), t1 * 1.),
|
||||||
|
Eqn::new(y.into(), t1 / 2.),
|
||||||
|
Eqn::new(x.into(), Const(1.0) + t2 / 1.),
|
||||||
|
Eqn::new(y.into(), t2 / 2.),
|
||||||
|
]
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
println!("eqns: {}", eqns);
|
||||||
|
let sol = solve_eqns(&eqns, &[t1, t2]).unwrap();
|
||||||
|
println!("sols: {}", sol);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
582
src/math/expr.rs
Normal file
582
src/math/expr.rs
Normal file
@ -0,0 +1,582 @@
|
|||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::eqn::{Eqn, Eqns};
|
||||||
|
use super::unknown::{Unknown, UnknownSet, Unknowns};
|
||||||
|
use super::Scalar;
|
||||||
|
use std::f64::NAN;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum Expr {
|
||||||
|
Unkn(Unknown),
|
||||||
|
Const(Scalar),
|
||||||
|
Sum(Exprs),
|
||||||
|
Neg(Box<Expr>),
|
||||||
|
Product(Exprs),
|
||||||
|
Div(Box<Expr>, Box<Expr>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Exprs = Vec<Expr>;
|
||||||
|
|
||||||
|
impl Unknowns for Exprs {
|
||||||
|
fn unknowns(&self) -> UnknownSet {
|
||||||
|
self.iter().flat_map(|e: &Expr| e.unknowns()).collect()
|
||||||
|
}
|
||||||
|
fn has_unknowns(&self) -> bool {
|
||||||
|
self.iter().any(|e: &Expr| e.has_unknowns())
|
||||||
|
}
|
||||||
|
fn has_unknown(&self, u: Unknown) -> bool {
|
||||||
|
self.iter().any(|e: &Expr| e.has_unknown(u))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_separated_exprs(es: &[Expr], f: &mut fmt::Formatter, sep: &str) -> fmt::Result {
|
||||||
|
write!(f, "(")?;
|
||||||
|
let mut is_first = es.len() > 1;
|
||||||
|
for e in es {
|
||||||
|
if is_first {
|
||||||
|
is_first = false;
|
||||||
|
} else {
|
||||||
|
write!(f, "{}", sep)?
|
||||||
|
}
|
||||||
|
write!(f, "{}", e)?
|
||||||
|
}
|
||||||
|
write!(f, ")")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_common_terms(l: &mut Vec<Expr>, r: &mut Vec<Expr>) -> Vec<Expr> {
|
||||||
|
let common: Vec<_> = l.drain_filter(|e| r.contains(e)).collect();
|
||||||
|
common.iter().for_each(|e| {
|
||||||
|
r.remove_item(e);
|
||||||
|
});
|
||||||
|
common
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_term(terms: &mut Vec<Expr>, term: &Expr) -> Option<Expr> {
|
||||||
|
terms.remove_item(term)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sum_fold(l: Expr, r: Expr) -> Expr {
|
||||||
|
use Expr::*;
|
||||||
|
match (l, r) {
|
||||||
|
(Const(lc), Const(rc)) => Const(lc + rc),
|
||||||
|
(Const(c), o) | (o, Const(c)) if relative_eq!(c, 0.) => o,
|
||||||
|
(Product(mut l), Product(mut r)) => {
|
||||||
|
let comm = remove_common_terms(&mut l, &mut r);
|
||||||
|
if comm.is_empty() {
|
||||||
|
Expr::new_sum(Product(l), Product(r))
|
||||||
|
} else {
|
||||||
|
Expr::new_product(Product(comm), Expr::new_sum(Product(l), Product(r)).collapse())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Product(mut l), r) | (r, Product(mut l)) => {
|
||||||
|
let comm = remove_term(&mut l, &r);
|
||||||
|
match comm {
|
||||||
|
Some(_) => Expr::new_product(r, Expr::new_sum(Product(l), Const(1.))).collapse(),
|
||||||
|
None => Expr::new_sum(Product(l), r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Div(box ln, box ld), Div(box rn, box rd)) => {
|
||||||
|
if ld == rd {
|
||||||
|
Expr::new_div(Expr::new_sum(ln, rn), ld).collapse()
|
||||||
|
} else {
|
||||||
|
Expr::new_div(Expr::new_sum(Expr::new_product(ln, rd.clone()), Expr::new_product(rn, ld.clone())),
|
||||||
|
Expr::new_product(ld, rd)).collapse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(l, r) => Expr::new_sum(l, r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn group_sum(es: Exprs) -> Exprs {
|
||||||
|
use Expr::*;
|
||||||
|
let mut common: BTreeMap<UnknownSet, Expr> = BTreeMap::new();
|
||||||
|
for e in es {
|
||||||
|
match e {
|
||||||
|
Const(c) if relative_eq!(c, 0.) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
let unkns = e.unknowns();
|
||||||
|
match common.get_mut(&unkns) {
|
||||||
|
None => {
|
||||||
|
common.insert(unkns, e);
|
||||||
|
}
|
||||||
|
Some(existing) => {
|
||||||
|
match existing {
|
||||||
|
Sum(ref mut es) => {
|
||||||
|
// already failed at merging, so just add it to the list
|
||||||
|
es.push(e);
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
*other = sum_fold(other.clone(), e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for c in common.values() {
|
||||||
|
trace!("group sum value: {}", c);
|
||||||
|
}
|
||||||
|
common
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_, v)| v)
|
||||||
|
.filter(|e| match e {
|
||||||
|
Const(c) if relative_eq!(*c, 0.) => false,
|
||||||
|
_ => true,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn product_fold(l: Expr, r: Expr) -> Expr {
|
||||||
|
use Expr::*;
|
||||||
|
match (l, r) {
|
||||||
|
(Const(lc), Const(rc)) => Const(lc * rc),
|
||||||
|
(Const(c), o) | (o, Const(c)) if relative_eq!(c, 1.) => o,
|
||||||
|
(Const(c), _) | (_, Const(c)) if relative_eq!(c, 0.) => Const(0.),
|
||||||
|
(Div(num, den), mul) | (mul, Div(num, den)) => {
|
||||||
|
if mul == *den {
|
||||||
|
*num
|
||||||
|
} else {
|
||||||
|
Expr::Div(Box::new(Expr::Product(vec![*num, mul])), den).simplify()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Product(mut ls), Product(mut rs)) => {
|
||||||
|
ls.append(&mut rs);
|
||||||
|
Product(ls)
|
||||||
|
}
|
||||||
|
(Product(mut ps), o) | (o, Product(mut ps)) => {
|
||||||
|
ps.push(o);
|
||||||
|
Product(ps)
|
||||||
|
}
|
||||||
|
(l, r) => Expr::new_product(l, r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn group_product(es: Exprs) -> Exprs {
|
||||||
|
use Expr::*;
|
||||||
|
let es2 = es.clone();
|
||||||
|
let mut consts: Option<Scalar> = None;
|
||||||
|
let mut other = Exprs::new();
|
||||||
|
for e in es {
|
||||||
|
let unkns = e.unknowns();
|
||||||
|
match e {
|
||||||
|
Const(c) => match consts {
|
||||||
|
None => consts = Some(c),
|
||||||
|
Some(cs) => consts = Some(c * cs),
|
||||||
|
},
|
||||||
|
e => other.push(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(cs) = consts {
|
||||||
|
if relative_eq!(cs, 0.0) {
|
||||||
|
other.clear();
|
||||||
|
other.push(Const(0.0))
|
||||||
|
} else if relative_ne!(cs, 1.0) {
|
||||||
|
other.push(Const(cs))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
trace!("group product: {:?} => {:?}", es2, other);
|
||||||
|
other
|
||||||
|
}
|
||||||
|
|
||||||
|
fn distribute_product_sums(mut es: Exprs) -> Expr {
|
||||||
|
let es_pre = es.clone();
|
||||||
|
use itertools::Itertools;
|
||||||
|
use Expr::*;
|
||||||
|
for e in &mut es {
|
||||||
|
*e = e.clone().distribute();
|
||||||
|
}
|
||||||
|
let sums = es
|
||||||
|
.drain_filter(|e| match e {
|
||||||
|
Sum(_) => true,
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.map(|e| {
|
||||||
|
trace!("sum in product: {}", e);
|
||||||
|
match e.simplify() {
|
||||||
|
Sum(es) => es,
|
||||||
|
o => vec![o],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let products: Vec<_> = sums.multi_cartesian_product().collect();
|
||||||
|
if products.is_empty() {
|
||||||
|
trace!("distribute_product_sums: no sums to distribute");
|
||||||
|
return Product(es);
|
||||||
|
}
|
||||||
|
let sums = products
|
||||||
|
.into_iter()
|
||||||
|
.map(|mut prod| {
|
||||||
|
prod.extend(es.clone());
|
||||||
|
trace!("prod: {}", Product(prod.clone()));
|
||||||
|
Product(prod)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let res = Sum(sums);
|
||||||
|
trace!("distribute_product_sums: {} => {}", Product(es_pre), res);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Unknowns for Expr {
|
||||||
|
fn unknowns(&self) -> UnknownSet {
|
||||||
|
use Expr::*;
|
||||||
|
match self {
|
||||||
|
Unkn(u) => u.unknowns(),
|
||||||
|
Const(_) => UnknownSet::default(),
|
||||||
|
Sum(es) | Product(es) => es.unknowns(),
|
||||||
|
Div(l, r) => l.unknowns().union(&r.unknowns()).cloned().collect(),
|
||||||
|
Neg(e) => e.unknowns(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn has_unknowns(&self) -> bool {
|
||||||
|
use Expr::*;
|
||||||
|
match self {
|
||||||
|
Unkn(u) => u.has_unknowns(),
|
||||||
|
Const(_) => false,
|
||||||
|
Sum(es) | Product(es) => es.has_unknowns(),
|
||||||
|
Div(l, r) => l.has_unknowns() || r.has_unknowns(),
|
||||||
|
Neg(e) => e.has_unknowns(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn has_unknown(&self, u: Unknown) -> bool {
|
||||||
|
use Expr::*;
|
||||||
|
match self {
|
||||||
|
Unkn(u1) => u1.has_unknown(u),
|
||||||
|
Const(_) => false,
|
||||||
|
Sum(es) | Product(es) => es.has_unknown(u),
|
||||||
|
Div(l, r) => l.has_unknown(u) || r.has_unknown(u),
|
||||||
|
Neg(e) => e.has_unknown(u),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Expr {
|
||||||
|
pub fn new_sum(e1: Expr, e2: Expr) -> Expr {
|
||||||
|
Expr::Sum(vec![e1, e2])
|
||||||
|
}
|
||||||
|
pub fn new_product(e1: Expr, e2: Expr) -> Expr {
|
||||||
|
Expr::Product(vec![e1, e2])
|
||||||
|
}
|
||||||
|
pub fn new_neg(e1: Expr) -> Expr {
|
||||||
|
Expr::Neg(Box::new(e1))
|
||||||
|
}
|
||||||
|
pub fn new_div(num: Expr, den: Expr) -> Expr {
|
||||||
|
Expr::Div(Box::new(num), Box::new(den))
|
||||||
|
}
|
||||||
|
pub fn new_minus(e1: Expr, e2: Expr) -> Expr {
|
||||||
|
Expr::Sum(vec![e1, Expr::new_neg(e2)])
|
||||||
|
}
|
||||||
|
pub fn new_inv(den: Expr) -> Expr {
|
||||||
|
Expr::new_div(Expr::Const(1.), den)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_zero(&self) -> bool {
|
||||||
|
use Expr::*;
|
||||||
|
match self.clone().simplify() {
|
||||||
|
Const(c) => relative_eq!(c, 0.),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_one(&self) -> bool {
|
||||||
|
use Expr::*;
|
||||||
|
match self.clone().simplify() {
|
||||||
|
Const(c) => relative_eq!(c, 1.),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn substitute<'a>(self, eqns: &Eqns<'a>) -> Expr {
|
||||||
|
use Expr::*;
|
||||||
|
for eqn in (&eqns.0 as &Borrow<[Eqn]>).borrow() {
|
||||||
|
if self == eqn.0 {
|
||||||
|
return eqn.1.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Sum(mut es) => {
|
||||||
|
for e in &mut es {
|
||||||
|
*e = e.clone().substitute(eqns);
|
||||||
|
}
|
||||||
|
Sum(es)
|
||||||
|
}
|
||||||
|
Product(mut es) => {
|
||||||
|
for e in &mut es {
|
||||||
|
*e = e.clone().substitute(eqns);
|
||||||
|
}
|
||||||
|
Product(es)
|
||||||
|
}
|
||||||
|
Neg(mut e) => {
|
||||||
|
*e = e.substitute(eqns);
|
||||||
|
Neg(e)
|
||||||
|
}
|
||||||
|
Div(mut num, mut den) => {
|
||||||
|
*num = num.substitute(eqns);
|
||||||
|
*den = den.substitute(eqns);
|
||||||
|
Div(num, den)
|
||||||
|
}
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collapse(self) -> Expr {
|
||||||
|
use Expr::*;
|
||||||
|
match self {
|
||||||
|
Sum(es) => {
|
||||||
|
let mut new_es: Vec<_> = es
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| e.collapse())
|
||||||
|
.flat_map(|e| match e {
|
||||||
|
Sum(more_es) => more_es,
|
||||||
|
other => vec![other],
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let consts = new_es.drain_filter(|e| match e {
|
||||||
|
Const(_) => true,
|
||||||
|
_ => false
|
||||||
|
}).fold(Const(0.), |l, r| match (l, r) {
|
||||||
|
(Const(lc), Const(rc)) => Const(lc + rc),
|
||||||
|
_ => unreachable!(),
|
||||||
|
});
|
||||||
|
new_es.push(consts);
|
||||||
|
|
||||||
|
match new_es.len() {
|
||||||
|
0 => Const(0.), // none
|
||||||
|
1 => new_es.into_iter().next().unwrap(), // one
|
||||||
|
_ => Sum(new_es), // many
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Product(es) => {
|
||||||
|
let new_es: Vec<_> = es
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| e.collapse())
|
||||||
|
.flat_map(|e| match e {
|
||||||
|
Product(more_es) => more_es,
|
||||||
|
other => vec![other],
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
match new_es.len() {
|
||||||
|
0 => Const(1.), // none
|
||||||
|
1 => new_es.into_iter().next().unwrap(), // one
|
||||||
|
_ => {
|
||||||
|
if new_es.iter().any(|e| e.is_zero()) {
|
||||||
|
Const(0.)
|
||||||
|
} else {
|
||||||
|
Product(new_es)
|
||||||
|
}
|
||||||
|
}, // many
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Neg(mut v) => {
|
||||||
|
*v = v.collapse();
|
||||||
|
match v {
|
||||||
|
box Const(c) => Const(-c),
|
||||||
|
box Neg(v) => *v,
|
||||||
|
box Product(mut es) => {
|
||||||
|
es.push(Const(-1.));
|
||||||
|
Product(es).collapse()
|
||||||
|
}
|
||||||
|
e => Product(vec![Const(-1.), *e]).collapse(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Div(mut num, mut den) => {
|
||||||
|
*num = num.collapse();
|
||||||
|
*den = den.collapse();
|
||||||
|
match (num, den) {
|
||||||
|
(box Const(num), box Const(den)) => Const(num / den),
|
||||||
|
(box Const(num), den) => {
|
||||||
|
if relative_eq!(num, 0.) {
|
||||||
|
Const(0.)
|
||||||
|
} else {
|
||||||
|
Div(Box::new(Const(num)), den)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(num, box Const(den)) => {
|
||||||
|
if relative_eq!(den, 1.) {
|
||||||
|
*num
|
||||||
|
} else {
|
||||||
|
Expr::new_product(*num, Const(1. / den)).collapse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(num, box Div(dennum, denden)) => {
|
||||||
|
Div(Box::new(Product(vec![*num, *denden])), dennum).collapse()
|
||||||
|
}
|
||||||
|
(box Product(mut es), box den) => match es.remove_item(&den) {
|
||||||
|
Some(_) => Product(es),
|
||||||
|
None => Expr::new_div(Product(es), den),
|
||||||
|
},
|
||||||
|
(num, den) => {
|
||||||
|
if num == den {
|
||||||
|
Expr::Const(1.)
|
||||||
|
} else {
|
||||||
|
Div(num, den)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn simplify(self) -> Expr {
|
||||||
|
use Expr::*;
|
||||||
|
match self {
|
||||||
|
Sum(es) => {
|
||||||
|
let pre_new_es = es.clone();
|
||||||
|
let mut new_es: Vec<_> = es
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| e.simplify())
|
||||||
|
.flat_map(|e| match e {
|
||||||
|
Sum(more_es) => more_es,
|
||||||
|
other => vec![other],
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
new_es = group_sum(new_es);
|
||||||
|
trace!(
|
||||||
|
"simplify sum {} => {}",
|
||||||
|
Sum(pre_new_es),
|
||||||
|
Sum(new_es.clone())
|
||||||
|
);
|
||||||
|
|
||||||
|
match new_es.len() {
|
||||||
|
0 => Const(0.), // none
|
||||||
|
1 => new_es.into_iter().next().unwrap(), // one
|
||||||
|
_ => Sum(new_es), // many
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Product(es) => {
|
||||||
|
let pre_new_es = es.clone();
|
||||||
|
let new_es: Vec<_> = es
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| e.simplify())
|
||||||
|
.flat_map(|e| match e {
|
||||||
|
Product(more_es) => more_es,
|
||||||
|
other => vec![other],
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let new_es = group_product(new_es);
|
||||||
|
trace!(
|
||||||
|
"simplify product {} => {}",
|
||||||
|
Product(pre_new_es),
|
||||||
|
Product(new_es.clone())
|
||||||
|
);
|
||||||
|
match new_es.len() {
|
||||||
|
0 => Const(1.), // none
|
||||||
|
1 => new_es.into_iter().next().unwrap(), // one
|
||||||
|
_ => Product(new_es), // many
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Neg(mut v) => {
|
||||||
|
*v = v.simplify();
|
||||||
|
trace!("simplify neg {}", Neg(v.clone()));
|
||||||
|
match v {
|
||||||
|
box Const(c) => Const(-c),
|
||||||
|
box Neg(v) => *v,
|
||||||
|
box Product(mut es) => {
|
||||||
|
es.push(Const(-1.));
|
||||||
|
Product(es).simplify()
|
||||||
|
}
|
||||||
|
e => Product(vec![Const(-1.), *e]).simplify(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Div(mut num, mut den) => {
|
||||||
|
*num = num.simplify();
|
||||||
|
*den = den.simplify();
|
||||||
|
trace!("simplify div {}", Div(num.clone(), den.clone()));
|
||||||
|
match (num, den) {
|
||||||
|
(box Const(num), box Const(den)) => Const(num / den),
|
||||||
|
(num, box Const(den)) => {
|
||||||
|
if relative_eq!(den, 1.) {
|
||||||
|
*num
|
||||||
|
} else {
|
||||||
|
Expr::new_product(*num, Const(1. / den)).simplify()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(num, box Div(dennum, denden)) => {
|
||||||
|
Div(Box::new(Product(vec![*num, *denden])), dennum).simplify()
|
||||||
|
}
|
||||||
|
(box Product(mut es), box den) => match es.remove_item(&den) {
|
||||||
|
Some(_) => Product(es).simplify(),
|
||||||
|
None => Expr::new_div(Product(es), den),
|
||||||
|
},
|
||||||
|
(num, den) => {
|
||||||
|
if num == den {
|
||||||
|
Expr::Const(1.)
|
||||||
|
} else {
|
||||||
|
Div(num, den)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Const(c) => if c.is_nan() {
|
||||||
|
println!("NAN!");
|
||||||
|
Const(c)
|
||||||
|
} else { Const(c) },
|
||||||
|
e => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn distribute(self) -> Expr {
|
||||||
|
use Expr::*;
|
||||||
|
match self {
|
||||||
|
Sum(mut es) => {
|
||||||
|
let es_pre = es.clone();
|
||||||
|
for e in &mut es {
|
||||||
|
*e = e.clone().distribute();
|
||||||
|
}
|
||||||
|
let res = Sum(es);
|
||||||
|
trace!("distribute sum {} => {}", Sum(es_pre), res);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
Product(es) => distribute_product_sums(es),
|
||||||
|
Div(num, den) => match (num, den) {
|
||||||
|
(box Sum(es), box den) => Sum(es
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| Expr::new_div(e, den.clone()).distribute())
|
||||||
|
.collect()),
|
||||||
|
(num, den) => Div(num, den),
|
||||||
|
},
|
||||||
|
Neg(v) => match v {
|
||||||
|
box Const(c) => Const(-c),
|
||||||
|
box Sum(mut es) => {
|
||||||
|
for e in &mut es {
|
||||||
|
*e = Expr::new_neg(e.clone()).distribute();
|
||||||
|
}
|
||||||
|
Sum(es)
|
||||||
|
}
|
||||||
|
box Product(mut es) => {
|
||||||
|
for e in &mut es {
|
||||||
|
*e = e.clone().distribute();
|
||||||
|
}
|
||||||
|
es.push(Const(-1.));
|
||||||
|
Product(es)
|
||||||
|
}
|
||||||
|
box Neg(v) => v.distribute(),
|
||||||
|
box Div(mut num, mut den) => {
|
||||||
|
*num = Neg(num.clone());
|
||||||
|
Div(num, den).distribute()
|
||||||
|
}
|
||||||
|
e => Neg(Box::new(e.distribute())),
|
||||||
|
},
|
||||||
|
e => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Expr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use Expr::*;
|
||||||
|
match self {
|
||||||
|
Unkn(u) => write!(f, "{}", u),
|
||||||
|
Const(c) => write!(f, "{}", c),
|
||||||
|
Sum(es) => write_separated_exprs(es, f, " + "),
|
||||||
|
Product(es) => write_separated_exprs(es, f, " * "),
|
||||||
|
Div(num, den) => write!(f, "({} / {})", num, den),
|
||||||
|
Neg(e) => write!(f, "-({})", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
230
src/math/mod.rs
230
src/math/mod.rs
@ -1,217 +1,23 @@
|
|||||||
pub mod eqn;
|
pub mod eqn;
|
||||||
|
pub mod expr;
|
||||||
|
pub mod ops;
|
||||||
|
pub mod region;
|
||||||
|
pub mod unknown;
|
||||||
|
pub mod vec;
|
||||||
|
|
||||||
|
pub use eqn::{Eqn, Eqns};
|
||||||
|
pub use expr::{Expr, Exprs};
|
||||||
|
pub use ops::*;
|
||||||
|
pub use region::{GenericRegion, Line2, Region, Region1, Region2};
|
||||||
|
pub use unknown::{Unknown, UnknownSet, Unknowns};
|
||||||
|
pub use vec::*;
|
||||||
|
|
||||||
pub type Scalar = f64;
|
pub type Scalar = f64;
|
||||||
|
|
||||||
pub type Vec2 = nalgebra::Vector2<Scalar>;
|
// #[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
pub type Point2 = nalgebra::Point2<Scalar>;
|
// pub enum Value {
|
||||||
|
// Known(Scalar),
|
||||||
|
// Unkn(Unknown),
|
||||||
|
// }
|
||||||
|
|
||||||
pub type Rot2 = nalgebra::UnitComplex<Scalar>;
|
pub type Value = Expr;
|
||||||
|
|
||||||
pub trait Region<T> {
|
|
||||||
fn full() -> Self;
|
|
||||||
fn singleton(value: T) -> Self;
|
|
||||||
|
|
||||||
fn nearest(&self, value: &T) -> Option<T>;
|
|
||||||
fn contains(&self, value: &T) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Region1 {
|
|
||||||
Empty,
|
|
||||||
Singleton(Scalar),
|
|
||||||
Range(Scalar, Scalar),
|
|
||||||
Union(Box<Region1>, Box<Region1>),
|
|
||||||
Full,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Region<Scalar> for Region1 {
|
|
||||||
fn full() -> Self {
|
|
||||||
Region1::Full
|
|
||||||
}
|
|
||||||
|
|
||||||
fn singleton(value: Scalar) -> Self {
|
|
||||||
Region1::Singleton(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contains(&self, n: &Scalar) -> bool {
|
|
||||||
use Region1::*;
|
|
||||||
match self {
|
|
||||||
Empty => false,
|
|
||||||
Singleton(n1) => relative_eq!(n1, n),
|
|
||||||
Range(l, u) => *l <= *n && *n <= *u,
|
|
||||||
Union(r1, r2) => r1.contains(n) || r2.contains(n),
|
|
||||||
Full => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nearest(&self, s: &Scalar) -> Option<Scalar> {
|
|
||||||
use Region1::*;
|
|
||||||
match self {
|
|
||||||
Empty => None,
|
|
||||||
Full => Some(*s),
|
|
||||||
Singleton(n) => Some(*n),
|
|
||||||
Range(l, u) => match (l < s, s < u) {
|
|
||||||
(true, true) => Some(*s),
|
|
||||||
(true, false) => Some(*u),
|
|
||||||
(false, true) => Some(*l),
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
Union(r1, r2) => {
|
|
||||||
let distance = |a: Scalar, b: Scalar| (a - b).abs();
|
|
||||||
match (r1.nearest(s), r2.nearest(s)) {
|
|
||||||
(None, None) => None,
|
|
||||||
(Some(n), None) | (None, Some(n)) => Some(n),
|
|
||||||
(Some(n1), Some(n2)) => Some({
|
|
||||||
if distance(*s, n1) <= distance(*s, n2) {
|
|
||||||
n1
|
|
||||||
} else {
|
|
||||||
n2
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// line starting at start, point at angle dir, with range extent
|
|
||||||
// ie. start + (cos dir, sin dir) * t for t in extent
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Line2 {
|
|
||||||
start: Point2,
|
|
||||||
dir: Rot2,
|
|
||||||
extent: Region1,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Line2 {
|
|
||||||
pub fn new(start: Point2, dir: Rot2, extent: Region1) -> Self {
|
|
||||||
Self { start, dir, extent }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn evaluate(&self, t: Scalar) -> Point2 {
|
|
||||||
self.start + self.dir * Vec2::new(t, 0.)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn nearest(&self, p: &Point2) -> Point2 {
|
|
||||||
// rotate angle 90 degrees
|
|
||||||
let perp_dir = self.dir * Rot2::from_cos_sin_unchecked(0., 1.);
|
|
||||||
let perp = Line2::new(*p, perp_dir, Region1::Full);
|
|
||||||
if let Region2::Singleton(np) = self.intersect(&perp) {
|
|
||||||
np
|
|
||||||
} else {
|
|
||||||
panic!("Line2::nearest not found!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn intersect(&self, other: &Line2) -> Region2 {
|
|
||||||
// if the two lines are parallel...
|
|
||||||
let dirs = self.dir / other.dir;
|
|
||||||
if relative_eq!(dirs.sin_angle(), 0.) {
|
|
||||||
let starts = self.dir.to_rotation_matrix().inverse() * (other.start - self.start);
|
|
||||||
return if relative_eq!(starts.y, 0.) {
|
|
||||||
// and they are colinear
|
|
||||||
Region2::Line(self.clone())
|
|
||||||
} else {
|
|
||||||
// they are parallel and never intersect
|
|
||||||
Region2::Empty
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// TODO: respect extent
|
|
||||||
let (a, b) = (self, other);
|
|
||||||
let (a_0, a_v, b_0, b_v) = (a.start, a.dir, b.start, b.dir);
|
|
||||||
let (a_c, a_s, b_c, b_s) = (
|
|
||||||
a_v.cos_angle(),
|
|
||||||
a_v.sin_angle(),
|
|
||||||
b_v.cos_angle(),
|
|
||||||
b_v.sin_angle(),
|
|
||||||
);
|
|
||||||
let t_b = (a_0.x * a_s - a_0.y * a_c + a_0.x * a_s + b_0.y * a_c) / (a_s * b_c - a_c * b_s);
|
|
||||||
Region2::Singleton(b.evaluate(t_b))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Region2 {
|
|
||||||
Empty,
|
|
||||||
// single point at 0
|
|
||||||
Singleton(Point2),
|
|
||||||
Line(Line2),
|
|
||||||
#[allow(dead_code)]
|
|
||||||
Union(Box<Region2>, Box<Region2>),
|
|
||||||
Full,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Region<Point2> for Region2 {
|
|
||||||
fn full() -> Self {
|
|
||||||
Region2::Full
|
|
||||||
}
|
|
||||||
|
|
||||||
fn singleton(value: Point2) -> Self {
|
|
||||||
Region2::Singleton(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contains(&self, p: &Point2) -> bool {
|
|
||||||
self.nearest(p).map_or(false, |n| relative_eq!(n, p))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nearest(&self, p: &Point2) -> Option<Point2> {
|
|
||||||
use Region2::*;
|
|
||||||
match self {
|
|
||||||
Empty => None,
|
|
||||||
Full => Some(*p),
|
|
||||||
Singleton(n) => Some(*n),
|
|
||||||
Line(line) => Some(line.nearest(p)),
|
|
||||||
Union(r1, r2) => {
|
|
||||||
use nalgebra::distance;
|
|
||||||
match (r1.nearest(p), r2.nearest(p)) {
|
|
||||||
(None, None) => None,
|
|
||||||
(Some(n), None) | (None, Some(n)) => Some(n),
|
|
||||||
(Some(n1), Some(n2)) => Some({
|
|
||||||
if distance(p, &n1) <= distance(p, &n2) {
|
|
||||||
n1
|
|
||||||
} else {
|
|
||||||
n2
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Region2 {
|
|
||||||
pub fn union(r1: Region2, r2: Region2) -> Region2 {
|
|
||||||
use Region2::*;
|
|
||||||
match (r1, r2) {
|
|
||||||
(Empty, r) | (r, Empty) => r,
|
|
||||||
(Full, _) | (_, Full) => Full,
|
|
||||||
(r1, r2) => Union(Box::new(r1), Box::new(r2)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn intersect(&self, other: &Region2) -> Region2 {
|
|
||||||
use Region2::*;
|
|
||||||
match (self, other) {
|
|
||||||
(Empty, _) | (_, Empty) => Empty,
|
|
||||||
(Full, r) | (r, Full) => r.clone(),
|
|
||||||
(Singleton(n1), Singleton(n2)) => {
|
|
||||||
if n1 == n2 {
|
|
||||||
Singleton(*n1)
|
|
||||||
} else {
|
|
||||||
Empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Singleton(n), o) | (o, Singleton(n)) => {
|
|
||||||
if o.contains(n) {
|
|
||||||
Singleton(*n)
|
|
||||||
} else {
|
|
||||||
Empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Line(l1), Line(l2)) => l1.intersect(l2),
|
|
||||||
(Union(un1, un2), o) | (o, Union(un1, un2)) => {
|
|
||||||
Self::union(un1.intersect(o), un2.intersect(o))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
197
src/math/ops.rs
Normal file
197
src/math/ops.rs
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
use std::ops;
|
||||||
|
|
||||||
|
use super::{Expr, Scalar, Unknown};
|
||||||
|
|
||||||
|
impl From<Scalar> for Expr {
|
||||||
|
fn from(c: Scalar) -> Expr {
|
||||||
|
Expr::Const(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Unknown> for Expr {
|
||||||
|
fn from(u: Unknown) -> Expr {
|
||||||
|
Expr::Unkn(u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Add<Expr> for Expr {
|
||||||
|
type Output = Expr;
|
||||||
|
fn add(self, rhs: Expr) -> Expr {
|
||||||
|
Expr::new_sum(self, rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Add<Scalar> for Expr {
|
||||||
|
type Output = Expr;
|
||||||
|
fn add(self, rhs: Scalar) -> Expr {
|
||||||
|
Expr::new_sum(self, rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Add<Unknown> for Expr {
|
||||||
|
type Output = Expr;
|
||||||
|
fn add(self, rhs: Unknown) -> Expr {
|
||||||
|
Expr::new_sum(self, rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Sub<Expr> for Expr {
|
||||||
|
type Output = Expr;
|
||||||
|
fn sub(self, rhs: Expr) -> Expr {
|
||||||
|
Expr::new_minus(self, rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Sub<Scalar> for Expr {
|
||||||
|
type Output = Expr;
|
||||||
|
fn sub(self, rhs: Scalar) -> Expr {
|
||||||
|
Expr::new_minus(self, rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Sub<Unknown> for Expr {
|
||||||
|
type Output = Expr;
|
||||||
|
fn sub(self, rhs: Unknown) -> Expr {
|
||||||
|
Expr::new_minus(self, rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Mul<Expr> for Expr {
|
||||||
|
type Output = Expr;
|
||||||
|
fn mul(self, rhs: Expr) -> Expr {
|
||||||
|
Expr::new_product(self, rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Mul<Scalar> for Expr {
|
||||||
|
type Output = Expr;
|
||||||
|
fn mul(self, rhs: Scalar) -> Expr {
|
||||||
|
Expr::new_product(self, rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Mul<Unknown> for Expr {
|
||||||
|
type Output = Expr;
|
||||||
|
fn mul(self, rhs: Unknown) -> Expr {
|
||||||
|
Expr::new_product(self, rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Div<Expr> for Expr {
|
||||||
|
type Output = Expr;
|
||||||
|
fn div(self, rhs: Expr) -> Expr {
|
||||||
|
Expr::new_div(self, rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Div<Scalar> for Expr {
|
||||||
|
type Output = Expr;
|
||||||
|
fn div(self, rhs: Scalar) -> Expr {
|
||||||
|
Expr::new_div(self, rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Div<Unknown> for Expr {
|
||||||
|
type Output = Expr;
|
||||||
|
fn div(self, rhs: Unknown) -> Expr {
|
||||||
|
Expr::new_div(self, rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Neg for Expr {
|
||||||
|
type Output = Expr;
|
||||||
|
fn neg(self) -> Expr {
|
||||||
|
Expr::new_neg(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Add<Expr> for Unknown {
|
||||||
|
type Output = Expr;
|
||||||
|
fn add(self, rhs: Expr) -> Expr {
|
||||||
|
Expr::new_sum(self.into(), rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Add<Scalar> for Unknown {
|
||||||
|
type Output = Expr;
|
||||||
|
fn add(self, rhs: Scalar) -> Expr {
|
||||||
|
Expr::new_sum(self.into(), rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Add<Unknown> for Unknown {
|
||||||
|
type Output = Expr;
|
||||||
|
fn add(self, rhs: Unknown) -> Expr {
|
||||||
|
Expr::new_sum(self.into(), rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Sub<Expr> for Unknown {
|
||||||
|
type Output = Expr;
|
||||||
|
fn sub(self, rhs: Expr) -> Expr {
|
||||||
|
Expr::new_minus(self.into(), rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Sub<Scalar> for Unknown {
|
||||||
|
type Output = Expr;
|
||||||
|
fn sub(self, rhs: Scalar) -> Expr {
|
||||||
|
Expr::new_minus(self.into(), rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Sub<Unknown> for Unknown {
|
||||||
|
type Output = Expr;
|
||||||
|
fn sub(self, rhs: Unknown) -> Expr {
|
||||||
|
Expr::new_minus(self.into(), rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Mul<Expr> for Unknown {
|
||||||
|
type Output = Expr;
|
||||||
|
fn mul(self, rhs: Expr) -> Expr {
|
||||||
|
Expr::new_product(self.into(), rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Mul<Scalar> for Unknown {
|
||||||
|
type Output = Expr;
|
||||||
|
fn mul(self, rhs: Scalar) -> Expr {
|
||||||
|
Expr::new_product(self.into(), rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Mul<Unknown> for Unknown {
|
||||||
|
type Output = Expr;
|
||||||
|
fn mul(self, rhs: Unknown) -> Expr {
|
||||||
|
Expr::new_product(self.into(), rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Div<Expr> for Unknown {
|
||||||
|
type Output = Expr;
|
||||||
|
fn div(self, rhs: Expr) -> Expr {
|
||||||
|
Expr::new_div(self.into(), rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Div<Scalar> for Unknown {
|
||||||
|
type Output = Expr;
|
||||||
|
fn div(self, rhs: Scalar) -> Expr {
|
||||||
|
Expr::new_div(self.into(), rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Div<Unknown> for Unknown {
|
||||||
|
type Output = Expr;
|
||||||
|
fn div(self, rhs: Unknown) -> Expr {
|
||||||
|
Expr::new_div(self.into(), rhs.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Neg for Unknown {
|
||||||
|
type Output = Expr;
|
||||||
|
fn neg(self) -> Expr {
|
||||||
|
Expr::new_neg(self.into())
|
||||||
|
}
|
||||||
|
}
|
437
src/math/region.rs
Normal file
437
src/math/region.rs
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::{eqn, Expr, Point2, Rot2, Scalar, Value};
|
||||||
|
|
||||||
|
// pub type Vec2 = nalgebra::Vector2<Value>;
|
||||||
|
// pub type Point2 = nalgebra::Point2<Value>;
|
||||||
|
|
||||||
|
// pub type Rot2 = nalgebra::UnitComplex<Value>;
|
||||||
|
|
||||||
|
pub trait GenericRegion {
|
||||||
|
fn full() -> Self;
|
||||||
|
fn intersection(self, other: Self) -> Self;
|
||||||
|
fn simplify(self) -> Self;
|
||||||
|
fn substitute(self, eqns: &eqn::Eqns) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Region<T>: GenericRegion {
|
||||||
|
fn singleton(value: T) -> Self;
|
||||||
|
|
||||||
|
fn nearest(&self, value: &T) -> Option<T>;
|
||||||
|
fn contains(&self, value: &T) -> Option<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Region1 {
|
||||||
|
Empty,
|
||||||
|
Singleton(Value),
|
||||||
|
Range(Value, Value),
|
||||||
|
Intersection(Box<Region1>, Box<Region1>),
|
||||||
|
// Union(Box<Region1>, Box<Region1>),
|
||||||
|
Full,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Region1 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use Region1::*;
|
||||||
|
match self {
|
||||||
|
Empty => write!(f, "Ø"),
|
||||||
|
Singleton(v) => write!(f, "{{ {} }}", v),
|
||||||
|
Range(l, u) => write!(f, "[ {}, {} ]", l, u),
|
||||||
|
Intersection(r1, r2) => write!(f, "{} ∩ {}", r1, r2),
|
||||||
|
Full => write!(f, "ℝ"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericRegion for Region1 {
|
||||||
|
fn intersection(self, other: Region1) -> Self {
|
||||||
|
Region1::Intersection(Box::new(self), Box::new(other))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn full() -> Self {
|
||||||
|
Region1::Full
|
||||||
|
}
|
||||||
|
|
||||||
|
fn simplify(self) -> Self {
|
||||||
|
use Region1::*;
|
||||||
|
match self {
|
||||||
|
Singleton(n) => Singleton(n.simplify()),
|
||||||
|
Range(l, u) => Range(l.simplify(), u.simplify()),
|
||||||
|
Intersection(r1, r2) => r1.simplify().intersection(r2.simplify()),
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn substitute(self, eqns: &eqn::Eqns) -> Self {
|
||||||
|
use Region1::*;
|
||||||
|
match self {
|
||||||
|
Singleton(n) => Singleton(n.substitute(eqns)),
|
||||||
|
Range(l, u) => Range(l.substitute(eqns), u.substitute(eqns)),
|
||||||
|
Intersection(r1, r2) => r1.substitute(eqns).intersection(r2.substitute(eqns)),
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Region<Scalar> for Region1 {
|
||||||
|
fn singleton(value: Scalar) -> Self {
|
||||||
|
Region1::Singleton(value.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(&self, n: &Scalar) -> Option<bool> {
|
||||||
|
use Expr::Const;
|
||||||
|
use Region1::*;
|
||||||
|
match self {
|
||||||
|
Empty => Some(false),
|
||||||
|
Singleton(n1) => match n1 {
|
||||||
|
Const(c) => Some(relative_eq!(c, n)),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Range(l, u) => match (l, u) {
|
||||||
|
(Const(cl), Const(cu)) => Some(*cl <= *n && *n <= *cu),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Intersection(r1, r2) => r1
|
||||||
|
.contains(n)
|
||||||
|
.and_then(|c1| r2.contains(n).map(|c2| c1 && c2)),
|
||||||
|
// Union(r1, r2) => r1.contains(n) || r2.contains(n),
|
||||||
|
Full => Some(true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nearest(&self, s: &Scalar) -> Option<Scalar> {
|
||||||
|
use Expr::Const;
|
||||||
|
use Region1::*;
|
||||||
|
match self {
|
||||||
|
Empty => None,
|
||||||
|
Full => Some(*s),
|
||||||
|
Singleton(n) => match n {
|
||||||
|
Const(c) => Some(*c),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Range(l, u) => match (l, u) {
|
||||||
|
(Const(cl), Const(cu)) => match (cl < s, s < cu) {
|
||||||
|
(true, true) => Some(*s),
|
||||||
|
(true, false) => Some(*cu),
|
||||||
|
(false, true) => Some(*cl),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Intersection(r1, r2) => unimplemented!(), /*Union(r1, r2) => {
|
||||||
|
let distance = |a: Scalar, b: Scalar| (a - b).abs();
|
||||||
|
match (r1.nearest(s), r2.nearest(s)) {
|
||||||
|
(None, None) => None,
|
||||||
|
(Some(n), None) | (None, Some(n)) => Some(n),
|
||||||
|
(Some(n1), Some(n2)) => Some({
|
||||||
|
if distance(*s, n1) <= distance(*s, n2) {
|
||||||
|
n1
|
||||||
|
} else {
|
||||||
|
n2
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// line starting at start, point at angle dir, with range extent
|
||||||
|
// ie. start + (cos dir, sin dir) * t for t in extent
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Line2 {
|
||||||
|
start: Point2<Value>,
|
||||||
|
dir: Rot2,
|
||||||
|
extent: Region1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Line2 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{{ (x, y, t): ⟨x, y⟩ = {} + t{} & t ∈ {} }}",
|
||||||
|
self.start, self.dir, self.extent
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Line2 {
|
||||||
|
pub fn new(start: Point2<Value>, dir: Rot2, extent: Region1) -> Self {
|
||||||
|
Self { start, dir, extent }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(&self, t: Value) -> Point2<Value> {
|
||||||
|
self.start.clone() + self.dir * t
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate_extent(&self) -> Option<Point2<Value>> {
|
||||||
|
match &self.extent {
|
||||||
|
Region1::Singleton(t) => Some(self.evaluate(t.clone())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_extent(self, new_extent: Region1) -> Line2 {
|
||||||
|
Line2 {
|
||||||
|
start: self.start,
|
||||||
|
dir: self.dir,
|
||||||
|
extent: new_extent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nearest(&self, p: &Point2<Value>) -> Point2<Value> {
|
||||||
|
// rotate angle 90 degrees
|
||||||
|
let perp_dir = self.dir + Rot2::cardinal(1);
|
||||||
|
let perp = Line2::new(p.clone(), perp_dir, Region1::Full);
|
||||||
|
match self.intersect(&perp) {
|
||||||
|
Region2::Singleton(np) => np,
|
||||||
|
Region2::Line(l) => l.evaluate_extent().expect("Line2::nearest not found"),
|
||||||
|
_ => panic!("Line2::nearest not found!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect(&self, other: &Line2) -> Region2 {
|
||||||
|
// if the two lines are parallel...
|
||||||
|
let dirs = self.dir - other.dir;
|
||||||
|
if relative_eq!(dirs.sin(), 0.) {
|
||||||
|
let starts = self.dir.conj() * (other.start.clone() - self.start.clone());
|
||||||
|
return if starts.y.simplify().is_zero() {
|
||||||
|
// and they are colinear
|
||||||
|
Region2::Line(self.clone())
|
||||||
|
} else {
|
||||||
|
// they are parallel and never intersect
|
||||||
|
Region2::Empty
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// TODO: respect extent
|
||||||
|
let (a, b) = (self, other);
|
||||||
|
let (a_0, a_v, b_0, b_v) = (
|
||||||
|
a.start.clone(),
|
||||||
|
a.dir,
|
||||||
|
b.start.clone(),
|
||||||
|
b.dir,
|
||||||
|
);
|
||||||
|
let (a_c, a_s, b_c, b_s) = (a_v.cos(), a_v.sin(), b_v.cos(), b_v.sin());
|
||||||
|
let t_b = (a_0.x.clone() * a_s
|
||||||
|
- a_0.y.clone() * a_c
|
||||||
|
- b_0.x.clone() * a_s
|
||||||
|
+ b_0.y.clone() * a_c)
|
||||||
|
/ (a_s * b_c - a_c * b_s);
|
||||||
|
// Region2::Singleton(b.evaluate(t_b))
|
||||||
|
trace!("intersect a: {}, b: {}, t_b = {}", a, b, t_b);
|
||||||
|
let res = Region2::Line(b.clone().with_extent(Region1::Singleton(t_b.simplify())));
|
||||||
|
trace!("intersect a: {}, b: {} = {}", a, b, res);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn simplify(self) -> Region2 {
|
||||||
|
let new_l = Line2 {
|
||||||
|
start: self.start.simplify(),
|
||||||
|
dir: self.dir,
|
||||||
|
extent: self.extent.simplify(),
|
||||||
|
};
|
||||||
|
trace!(
|
||||||
|
"line {}: simplify evaluate extent: {:?}",
|
||||||
|
new_l,
|
||||||
|
new_l.evaluate_extent()
|
||||||
|
);
|
||||||
|
if let Some(p) = new_l.evaluate_extent() {
|
||||||
|
return Region2::Singleton(p.simplify());
|
||||||
|
}
|
||||||
|
Region2::Line(new_l)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn substitute(self, eqns: &eqn::Eqns) -> Self {
|
||||||
|
Line2 {
|
||||||
|
start: self.start.substitute(eqns),
|
||||||
|
dir: self.dir,
|
||||||
|
extent: self.extent.substitute(eqns),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Region2 {
|
||||||
|
Empty,
|
||||||
|
// single point at 0
|
||||||
|
Singleton(Point2<Value>),
|
||||||
|
Line(Line2),
|
||||||
|
// #[allow(dead_code)]
|
||||||
|
// Union(Box<Region2>, Box<Region2>),
|
||||||
|
Intersection(Box<Region2>, Box<Region2>),
|
||||||
|
Full,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Region2 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use Region2::*;
|
||||||
|
match self {
|
||||||
|
Empty => write!(f, "ز"),
|
||||||
|
Singleton(v) => write!(f, "{{ {} }}", v),
|
||||||
|
Line(l) => l.fmt(f),
|
||||||
|
Intersection(r1, r2) => write!(f, "{} ∩ {}", r1, r2),
|
||||||
|
Full => write!(f, "ℝ²"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericRegion for Region2 {
|
||||||
|
fn full() -> Self {
|
||||||
|
Region2::Full
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intersection(self, other: Self) -> Self {
|
||||||
|
use Region2::*;
|
||||||
|
match (self, other) {
|
||||||
|
(Empty, _) | (_, Empty) => Empty,
|
||||||
|
(Full, r) | (r, Full) => r,
|
||||||
|
(r1, r2) => Intersection(Box::new(r1), Box::new(r2)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn simplify(self) -> Region2 {
|
||||||
|
use Region2::*;
|
||||||
|
match self {
|
||||||
|
Singleton(n) => Singleton(n.simplify()),
|
||||||
|
Line(l) => l.simplify(),
|
||||||
|
Intersection(r1, r2) => r1.simplify().intersect(r2.simplify()),
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn substitute(self, eqns: &eqn::Eqns) -> Self {
|
||||||
|
use Region2::*;
|
||||||
|
match self {
|
||||||
|
Singleton(n) => Singleton(n.substitute(eqns)),
|
||||||
|
Line(l) => Line(l.substitute(eqns)),
|
||||||
|
Intersection(r1, r2) => r1.substitute(eqns).intersection(r2.substitute(eqns)),
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Region<Point2<Scalar>> for Region2 {
|
||||||
|
fn singleton(value: Point2<Scalar>) -> Self {
|
||||||
|
Region2::Singleton(value.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(&self, p: &Point2<Scalar>) -> Option<bool> {
|
||||||
|
self.nearest(p).map(|n| n == *p)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nearest(&self, p: &Point2<Scalar>) -> Option<Point2<Scalar>> {
|
||||||
|
use Expr::Const;
|
||||||
|
use Region2::*;
|
||||||
|
match self {
|
||||||
|
Empty => None,
|
||||||
|
Full => Some(p.clone()),
|
||||||
|
Singleton(n) => match (&n.x, &n.y) {
|
||||||
|
(Const(cx), Const(cy)) => Some(Point2::new(*cx, *cy)),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Line(line) => {
|
||||||
|
let pv: Point2<Value> = p.clone().into();
|
||||||
|
let n = line.nearest(&pv).simplify();
|
||||||
|
trace!("line {} nearest to {}: {}", line, pv, n);
|
||||||
|
match (n.x, n.y) {
|
||||||
|
(Const(cx), Const(cy)) => Some(Point2::new(cx, cy)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Intersection(r1, r2) => {
|
||||||
|
None
|
||||||
|
// r1.clone().intersect((**r2).clone()).nearest(p)
|
||||||
|
} /*Union(r1, r2) => {
|
||||||
|
use nalgebra::distance;
|
||||||
|
match (r1.nearest(p), r2.nearest(p)) {
|
||||||
|
(None, None) => None,
|
||||||
|
(Some(n), None) | (None, Some(n)) => Some(n),
|
||||||
|
(Some(n1), Some(n2)) => Some({
|
||||||
|
if distance(p, &n1) <= distance(p, &n2) {
|
||||||
|
n1
|
||||||
|
} else {
|
||||||
|
n2
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Region<Point2<Value>> for Region2 {
|
||||||
|
fn singleton(value: Point2<Value>) -> Self {
|
||||||
|
Region2::Singleton(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(&self, p: &Point2<Value>) -> Option<bool> {
|
||||||
|
self.nearest(p)
|
||||||
|
.map(|n| n.simplify() == p.clone().simplify())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nearest(&self, p: &Point2<Value>) -> Option<Point2<Value>> {
|
||||||
|
use Region2::*;
|
||||||
|
match self {
|
||||||
|
Empty => None,
|
||||||
|
Full => Some(p.clone()),
|
||||||
|
Singleton(n) => Some(n.clone()),
|
||||||
|
Line(line) => Some(line.nearest(p)),
|
||||||
|
Intersection(r1, r2) => r1.clone().intersect((**r2).clone()).nearest(p), /*Union(r1, r2) => {
|
||||||
|
use nalgebra::distance;
|
||||||
|
match (r1.nearest(p), r2.nearest(p)) {
|
||||||
|
(None, None) => None,
|
||||||
|
(Some(n), None) | (None, Some(n)) => Some(n),
|
||||||
|
(Some(n1), Some(n2)) => Some({
|
||||||
|
if distance(p, &n1) <= distance(p, &n2) {
|
||||||
|
n1
|
||||||
|
} else {
|
||||||
|
n2
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Region2 {
|
||||||
|
/*
|
||||||
|
pub fn union(r1: Region2, r2: Region2) -> Region2 {
|
||||||
|
use Region2::*;
|
||||||
|
match (r1, r2) {
|
||||||
|
(Empty, r) | (r, Empty) => r,
|
||||||
|
(Full, _) | (_, Full) => Full,
|
||||||
|
(r1, r2) => Union(Box::new(r1), Box::new(r2)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub fn intersect(self, other: Region2) -> Region2 {
|
||||||
|
use Region2::*;
|
||||||
|
match (self, other) {
|
||||||
|
(Empty, _) | (_, Empty) => Empty,
|
||||||
|
(Full, r) | (r, Full) => r.clone(),
|
||||||
|
(Singleton(n1), Singleton(n2)) => {
|
||||||
|
if n1 == n2 {
|
||||||
|
Singleton(n1)
|
||||||
|
} else {
|
||||||
|
Region2::intersection(Singleton(n1), Singleton(n2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Singleton(n), o) | (o, Singleton(n)) => {
|
||||||
|
if o.contains(&n).unwrap_or(false) {
|
||||||
|
Singleton(n)
|
||||||
|
} else {
|
||||||
|
Region2::intersection(Singleton(n), o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Intersection(r1, r2), o) | (o, Intersection(r1, r2)) => r1.intersect(*r2).intersect(o),
|
||||||
|
(Line(l1), Line(l2)) => l1.intersect(&l2).simplify(),
|
||||||
|
/*(Union(un1, un2), o) | (o, Union(un1, un2)) => {
|
||||||
|
Self::union(un1.intersect(o), un2.intersect(o))
|
||||||
|
}*/
|
||||||
|
(r1, r2) => Intersection(Box::new(r1), Box::new(r2)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
src/math/unknown.rs
Normal file
47
src/math/unknown.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use std::collections::BTreeSet;
|
||||||
|
use std::fmt;
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
|
use super::Scalar;
|
||||||
|
|
||||||
|
// an unknown variable with an id
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Unknown(pub i64);
|
||||||
|
|
||||||
|
pub type UnknownSet = BTreeSet<Unknown>;
|
||||||
|
|
||||||
|
pub trait Unknowns {
|
||||||
|
fn unknowns(&self) -> UnknownSet;
|
||||||
|
fn has_unknowns(&self) -> bool;
|
||||||
|
fn has_unknown(&self, u: Unknown) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Unknowns for Scalar {
|
||||||
|
fn unknowns(&self) -> UnknownSet {
|
||||||
|
UnknownSet::new()
|
||||||
|
}
|
||||||
|
fn has_unknowns(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn has_unknown(&self, _: Unknown) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Unknowns for Unknown {
|
||||||
|
fn unknowns(&self) -> UnknownSet {
|
||||||
|
FromIterator::from_iter(Some(*self))
|
||||||
|
}
|
||||||
|
fn has_unknowns(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
fn has_unknown(&self, u: Unknown) -> bool {
|
||||||
|
*self == u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Unknown {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "u{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
291
src/math/vec.rs
Normal file
291
src/math/vec.rs
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
use super::eqn::Eqns;
|
||||||
|
use super::{Scalar, Value};
|
||||||
|
|
||||||
|
use std::ops;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
|
pub struct Vec2<T> {
|
||||||
|
pub x: T,
|
||||||
|
pub y: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Vec2<T> {
|
||||||
|
pub fn new(x: T, y: T) -> Self {
|
||||||
|
Self { x, y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vec2<Scalar> {
|
||||||
|
pub fn normal2(self) -> Scalar {
|
||||||
|
self.x * self.x + self.y * self.y
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn normal(self) -> Scalar {
|
||||||
|
self.normal2().sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn normalize(self) -> Vec2<Scalar> {
|
||||||
|
self.clone() / self.normal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vec2<Value> {
|
||||||
|
pub fn simplify(self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x.simplify(),
|
||||||
|
y: self.y.simplify(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ops::Add<U>, U> ops::Add<Vec2<U>> for Vec2<T> {
|
||||||
|
type Output = Vec2<T::Output>;
|
||||||
|
fn add(self, rhs: Vec2<U>) -> Self::Output {
|
||||||
|
Self::Output {
|
||||||
|
x: self.x + rhs.x,
|
||||||
|
y: self.y + rhs.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ops::Sub<U>, U> ops::Sub<Vec2<U>> for Vec2<T> {
|
||||||
|
type Output = Vec2<T::Output>;
|
||||||
|
fn sub(self, rhs: Vec2<U>) -> Self::Output {
|
||||||
|
Self::Output {
|
||||||
|
x: self.x - rhs.x,
|
||||||
|
y: self.y - rhs.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ops::Mul<U>, U: Clone> ops::Mul<U> for Vec2<T> {
|
||||||
|
type Output = Vec2<T::Output>;
|
||||||
|
fn mul(self, rhs: U) -> Self::Output {
|
||||||
|
Self::Output {
|
||||||
|
x: self.x * rhs.clone(),
|
||||||
|
y: self.y * rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ops::Div<U>, U: Clone> ops::Div<U> for Vec2<T> {
|
||||||
|
type Output = Vec2<T::Output>;
|
||||||
|
fn div(self, rhs: U) -> Self::Output {
|
||||||
|
Self::Output {
|
||||||
|
x: self.x / rhs.clone(),
|
||||||
|
y: self.y / rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
|
pub struct Point2<T> {
|
||||||
|
pub x: T,
|
||||||
|
pub y: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Point2<T> {
|
||||||
|
pub fn new(x: T, y: T) -> Point2<T> {
|
||||||
|
Point2 { x, y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Point2<Value> {
|
||||||
|
pub fn simplify(self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x.distribute().simplify(),
|
||||||
|
y: self.y.distribute().simplify(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn substitute(self, eqns: &Eqns) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x.substitute(eqns),
|
||||||
|
y: self.y.substitute(eqns),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Point2<Scalar>> for Point2<Value> {
|
||||||
|
fn from(sp: Point2<Scalar>) -> Self {
|
||||||
|
Self {
|
||||||
|
x: sp.x.into(),
|
||||||
|
y: sp.y.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ops::Add<U>, U> ops::Add<Vec2<U>> for Point2<T> {
|
||||||
|
type Output = Point2<T::Output>;
|
||||||
|
fn add(self, rhs: Vec2<U>) -> Self::Output {
|
||||||
|
Point2 {
|
||||||
|
x: self.x + rhs.x,
|
||||||
|
y: self.y + rhs.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ops::Sub<U>, U> ops::Sub<Vec2<U>> for Point2<T> {
|
||||||
|
type Output = Point2<T::Output>;
|
||||||
|
fn sub(self, rhs: Vec2<U>) -> Self::Output {
|
||||||
|
Point2 {
|
||||||
|
x: self.x - rhs.x,
|
||||||
|
y: self.y - rhs.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ops::Sub<U>, U> ops::Sub<Point2<U>> for Point2<T> {
|
||||||
|
type Output = Vec2<T::Output>;
|
||||||
|
fn sub(self, rhs: Point2<U>) -> Self::Output {
|
||||||
|
Vec2 {
|
||||||
|
x: self.x - rhs.x,
|
||||||
|
y: self.y - rhs.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
impl<T: fmt::Display> fmt::Display for Point2<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "⟨{}, {}⟩", self.x, self.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
pub struct Rot2 {
|
||||||
|
cos: Scalar,
|
||||||
|
sin: Scalar,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Rot2 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "⟨{}, {}⟩", self.cos, self.sin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rot2 {
|
||||||
|
pub fn from_cos_sin_unchecked(cos: Scalar, sin: Scalar) -> Self {
|
||||||
|
Self { cos, sin }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn up() -> Self {
|
||||||
|
Self { cos: 0., sin: 1. }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn right() -> Self {
|
||||||
|
Self { cos: 1., sin: 0. }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_cos_sin(cos: Scalar, sin: Scalar) -> Self {
|
||||||
|
Vec2 { x: cos, y: sin }.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_angle(angle: Scalar) -> Self {
|
||||||
|
Self {
|
||||||
|
cos: angle.cos(),
|
||||||
|
sin: angle.sin(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_angle_deg(angle_deg: Scalar) -> Self {
|
||||||
|
Self::from_angle(angle_deg * std::f64::consts::PI / 180.)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cardinal(index: i64) -> Self {
|
||||||
|
match index % 4 {
|
||||||
|
0 => Rot2 { cos: 1., sin: 0. },
|
||||||
|
1 => Rot2 { cos: 0., sin: 1. },
|
||||||
|
2 => Rot2 { cos: -1., sin: 0. },
|
||||||
|
3 => Rot2 { cos: 0., sin: -1. },
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cos(&self) -> Scalar {
|
||||||
|
self.cos
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sin(&self) -> Scalar {
|
||||||
|
self.sin
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn conj(self) -> Self {
|
||||||
|
Self {
|
||||||
|
cos: self.cos,
|
||||||
|
sin: -self.sin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dot(self, v: Vec2<Value>) -> Value {
|
||||||
|
v.x * self.cos + v.y * self.sin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec2<Scalar>> for Rot2 {
|
||||||
|
fn from(v: Vec2<Scalar>) -> Rot2 {
|
||||||
|
let v = v.normalize();
|
||||||
|
Rot2 { cos: v.x, sin: v.y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Mul<Scalar> for Rot2 {
|
||||||
|
type Output = Vec2<Scalar>;
|
||||||
|
fn mul(self, rhs: Scalar) -> Vec2<Scalar> {
|
||||||
|
Vec2 {
|
||||||
|
x: self.cos * rhs,
|
||||||
|
y: self.sin * rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Mul<Value> for Rot2 {
|
||||||
|
type Output = Vec2<Value>;
|
||||||
|
fn mul(self, rhs: Value) -> Vec2<Value> {
|
||||||
|
Vec2 {
|
||||||
|
x: rhs.clone() * self.cos,
|
||||||
|
y: rhs * self.sin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Add<Rot2> for Rot2 {
|
||||||
|
type Output = Rot2;
|
||||||
|
fn add(self, rhs: Rot2) -> Rot2 {
|
||||||
|
Rot2 {
|
||||||
|
cos: self.cos * rhs.cos - self.sin * rhs.sin,
|
||||||
|
sin: self.cos * rhs.sin + self.sin * rhs.cos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Sub<Rot2> for Rot2 {
|
||||||
|
type Output = Rot2;
|
||||||
|
fn sub(self, rhs: Rot2) -> Rot2 {
|
||||||
|
Rot2 {
|
||||||
|
cos: self.cos * rhs.cos + self.sin * rhs.sin,
|
||||||
|
sin: self.sin * rhs.cos - self.cos * rhs.sin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Mul<Vec2<Scalar>> for Rot2 {
|
||||||
|
type Output = Vec2<Scalar>;
|
||||||
|
fn mul(self, rhs: Vec2<Scalar>) -> Vec2<Scalar> {
|
||||||
|
Vec2 {
|
||||||
|
x: self.cos * rhs.x - self.sin * rhs.y,
|
||||||
|
y: self.cos * rhs.y + self.sin * rhs.x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Mul<Vec2<Value>> for Rot2 {
|
||||||
|
type Output = Vec2<Value>;
|
||||||
|
fn mul(self, rhs: Vec2<Value>) -> Vec2<Value> {
|
||||||
|
Vec2 {
|
||||||
|
x: rhs.x.clone() * self.cos - rhs.y.clone() * self.sin,
|
||||||
|
y: rhs.y * self.cos + rhs.x * self.sin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
128
src/relation.rs
128
src/relation.rs
@ -1,5 +1,5 @@
|
|||||||
use crate::entity::{Point as PointEntity, PointRef};
|
use crate::entity::{CPoint as PointEntity, PointRef};
|
||||||
use crate::math::{Line2, Vec2, Point2, Region1, Region2, Rot2, Scalar};
|
use crate::math::{GenericRegion, Line2, Point2, Region1, Region2, Rot2, Scalar, Value, Vec2};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum ResolveResult {
|
pub enum ResolveResult {
|
||||||
@ -31,9 +31,14 @@ pub struct Coincident {
|
|||||||
impl Relation for Coincident {
|
impl Relation for Coincident {
|
||||||
fn resolve(&self) -> ResolveResult {
|
fn resolve(&self) -> ResolveResult {
|
||||||
let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut());
|
let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut());
|
||||||
let r = { p1.pos.constraints().intersect(p2.pos.constraints()) };
|
let r = {
|
||||||
p1.pos.reconstrain(r.clone());
|
p1.constraints()
|
||||||
p2.pos.reconstrain(r.clone());
|
.clone()
|
||||||
|
.intersect(p2.constraints().clone())
|
||||||
|
.simplify()
|
||||||
|
};
|
||||||
|
p1.reconstrain(r.clone());
|
||||||
|
p2.reconstrain(r.clone());
|
||||||
ResolveResult::from_r2(&r)
|
ResolveResult::from_r2(&r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,11 +55,19 @@ impl PointAngle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_horizontal(p1: PointRef, p2: PointRef) -> Self {
|
pub fn new_horizontal(p1: PointRef, p2: PointRef) -> Self {
|
||||||
Self::new(p1, p2, Rot2::from_cos_sin_unchecked(1., 0.))
|
Self::new(
|
||||||
|
p1,
|
||||||
|
p2,
|
||||||
|
Rot2::from_cos_sin_unchecked(1., 0.),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_vertical(p1: PointRef, p2: PointRef) -> Self {
|
pub fn new_vertical(p1: PointRef, p2: PointRef) -> Self {
|
||||||
Self::new(p1, p2, Rot2::from_cos_sin_unchecked(0., 1.))
|
Self::new(
|
||||||
|
p1,
|
||||||
|
p2,
|
||||||
|
Rot2::from_cos_sin_unchecked(0., 1.),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,23 +75,30 @@ impl Relation for PointAngle {
|
|||||||
fn resolve(&self) -> ResolveResult {
|
fn resolve(&self) -> ResolveResult {
|
||||||
use Region2::*;
|
use Region2::*;
|
||||||
let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut());
|
let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut());
|
||||||
let constrain_line = |p1: &Point2, p2: &mut PointEntity| {
|
let constrain_line = |p1: &Point2<Value>, p2: &mut PointEntity| {
|
||||||
let line = Region2::Line(Line2::new(*p1, self.angle, Region1::Full));
|
let line = Region2::Line(Line2::new(p1.clone(), self.angle, Region1::Full));
|
||||||
let new_constraint = p2.pos.constraints().intersect(&line);
|
trace!(
|
||||||
p2.pos.reconstrain(new_constraint);
|
"PointAngle line: {}, p2 constraint: {}",
|
||||||
ResolveResult::from_r2(p2.pos.constraints())
|
line,
|
||||||
|
p2.constraints()
|
||||||
|
);
|
||||||
|
let new_constraint = p2.constraints().clone().intersection(line).simplify();
|
||||||
|
trace!("PointAngle new_constraint: {}", new_constraint);
|
||||||
|
p2.reconstrain(new_constraint);
|
||||||
|
ResolveResult::from_r2(p2.constraints())
|
||||||
};
|
};
|
||||||
match (&mut p1.pos.constraints(), &mut p2.pos.constraints()) {
|
match (&mut p1.constraints(), &mut p2.constraints()) {
|
||||||
(Empty, _) | (_, Empty) => ResolveResult::Overconstrained,
|
(Empty, _) | (_, Empty) => ResolveResult::Overconstrained,
|
||||||
(Singleton(p1), Singleton(p2)) => {
|
(Singleton(p1), Singleton(p2)) => {
|
||||||
// if the angle p1 and p2 form is parallel to self.angle, the result
|
// if the angle p1 and p2 form is parallel to self.angle, the result
|
||||||
// will have a y component of 0
|
// will have a y component of 0
|
||||||
let r = self.angle.to_rotation_matrix().inverse() * (p2 - p1);
|
let r = self.angle.conj() * (p2.clone() - p1.clone());
|
||||||
if relative_eq!(r.y, 0.) {
|
trace!("angle.cos: {}", r.x);
|
||||||
ResolveResult::Constrained
|
// if relative_eq!(r.y, 0.) {
|
||||||
} else {
|
ResolveResult::Constrained
|
||||||
ResolveResult::Overconstrained
|
// } else {
|
||||||
}
|
// ResolveResult::Overconstrained
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
(Singleton(p), _) => constrain_line(p, &mut *p2),
|
(Singleton(p), _) => constrain_line(p, &mut *p2),
|
||||||
(_, Singleton(p)) => constrain_line(p, &mut *p1),
|
(_, Singleton(p)) => constrain_line(p, &mut *p1),
|
||||||
@ -87,34 +107,29 @@ impl Relation for PointAngle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Axis {
|
|
||||||
Vertical,
|
|
||||||
Horizontal,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AlignedDistance {
|
pub struct AlignedDistance {
|
||||||
pub p1: PointRef,
|
pub p1: PointRef,
|
||||||
pub p2: PointRef,
|
pub p2: PointRef,
|
||||||
pub axis: Axis,
|
pub angle: Rot2,
|
||||||
pub distance: Scalar,
|
pub distance: Scalar,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AlignedDistance {
|
impl AlignedDistance {
|
||||||
pub fn new(p1: PointRef, p2: PointRef, axis: Axis, distance: Scalar) -> Self {
|
pub fn new(p1: PointRef, p2: PointRef, angle: Rot2, distance: Scalar) -> Self {
|
||||||
Self {
|
Self {
|
||||||
p1,
|
p1,
|
||||||
p2,
|
p2,
|
||||||
axis,
|
angle,
|
||||||
distance,
|
distance,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_vertical(p1: PointRef, p2: PointRef, distance: Scalar) -> Self {
|
pub fn new_vertical(p1: PointRef, p2: PointRef, distance: Scalar) -> Self {
|
||||||
Self::new(p1, p2, Axis::Vertical, distance)
|
Self::new(p1, p2, Rot2::up(), distance)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_horizontal(p1: PointRef, p2: PointRef, distance: Scalar) -> Self {
|
pub fn new_horizontal(p1: PointRef, p2: PointRef, distance: Scalar) -> Self {
|
||||||
Self::new(p1, p2, Axis::Horizontal, distance)
|
Self::new(p1, p2, Rot2::right(), distance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,37 +137,40 @@ impl Relation for AlignedDistance {
|
|||||||
fn resolve(&self) -> ResolveResult {
|
fn resolve(&self) -> ResolveResult {
|
||||||
use Region2::*;
|
use Region2::*;
|
||||||
let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut());
|
let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut());
|
||||||
let constrain_line = |p1: Point2, p2: &mut PointEntity| {
|
let constrain_line = |p1: Point2<Value>, p2: &mut PointEntity| {
|
||||||
let angle = match self.axis {
|
let angle = self.angle + Rot2::up();
|
||||||
Axis::Horizontal => Rot2::from_cos_sin_unchecked(0., 1.),
|
let line = Region2::Line(Line2::new(p1.clone(), angle, Region1::Full)).simplify();
|
||||||
Axis::Vertical => Rot2::from_cos_sin_unchecked(1., 0.),
|
trace!(
|
||||||
};
|
"AlignedDistance line: {}, p2 constraint: {}",
|
||||||
let line = Region2::Line(Line2::new(p1, angle, Region1::Full));
|
line,
|
||||||
let new_constraint = p2.pos.constraints().intersect(&line);
|
p2.constraints()
|
||||||
p2.pos.reconstrain(new_constraint);
|
);
|
||||||
ResolveResult::from_r2(p2.pos.constraints())
|
let new_constraint = p2.constraints().clone().intersection(line).simplify();
|
||||||
|
trace!("AlignedDistance new_constraint: {}", new_constraint);
|
||||||
|
p2.reconstrain(new_constraint);
|
||||||
|
ResolveResult::from_r2(p2.constraints())
|
||||||
};
|
};
|
||||||
let offset = match self.axis {
|
let offset: Vec2<Scalar> = self.angle * self.distance;
|
||||||
Axis::Horizontal => Vec2::new(self.distance, 0.),
|
match (&mut p1.constraints(), &mut p2.constraints()) {
|
||||||
Axis::Vertical => Vec2::new(0., self.distance),
|
|
||||||
};
|
|
||||||
match (&mut p1.pos.constraints(), &mut p2.pos.constraints()) {
|
|
||||||
(Empty, _) | (_, Empty) => ResolveResult::Overconstrained,
|
(Empty, _) | (_, Empty) => ResolveResult::Overconstrained,
|
||||||
(Singleton(p1), Singleton(p2)) => {
|
(Singleton(p1), Singleton(p2)) => {
|
||||||
let r = p2 - p1;
|
let r = p2.clone() - p1.clone();
|
||||||
let d = match self.axis {
|
let d = self.angle.dot(r);
|
||||||
Axis::Horizontal => r.x,
|
// if relative_eq!(d, self.distance) {
|
||||||
Axis::Vertical => r.y,
|
ResolveResult::Constrained
|
||||||
};
|
// } else {
|
||||||
if relative_eq!(d, self.distance) {
|
// ResolveResult::Overconstrained
|
||||||
ResolveResult::Constrained
|
// }
|
||||||
} else {
|
|
||||||
ResolveResult::Overconstrained
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(Singleton(pos), _) => constrain_line(pos + offset, &mut *p2),
|
(Singleton(pos), _) => constrain_line(pos.clone() + offset, &mut *p2),
|
||||||
(_, Singleton(pos)) => constrain_line(pos - offset, &mut *p1),
|
(_, Singleton(pos)) => constrain_line(pos.clone() - offset, &mut *p1),
|
||||||
_ => ResolveResult::Underconstrained,
|
_ => ResolveResult::Underconstrained,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Midpoint {
|
||||||
|
pub p1: PointRef,
|
||||||
|
pub p2: PointRef,
|
||||||
|
pub mid: PointRef,
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user