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]]
|
||||
name = "aho-corasick"
|
||||
version = "0.6.9"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
@ -11,18 +13,18 @@ name = "alga"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libm 0.1.2 (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.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-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]]
|
||||
name = "approx"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
@ -30,11 +32,16 @@ name = "atty"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (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.2 (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]]
|
||||
name = "bitflags"
|
||||
version = "1.0.4"
|
||||
@ -44,8 +51,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
name = "cad_rs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.6.0 (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.1 (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)",
|
||||
"nalgebra 0.16.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -53,7 +60,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.6"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -66,24 +73,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.5.0"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -107,22 +114,22 @@ name = "itertools"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "lazy_static"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.48"
|
||||
version = "0.2.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -130,7 +137,7 @@ name = "log"
|
||||
version = "0.4.6"
|
||||
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)",
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -143,12 +150,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.1.3"
|
||||
version = "2.2.0"
|
||||
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]]
|
||||
name = "nalgebra"
|
||||
@ -156,11 +159,11 @@ version = "0.16.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"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-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)",
|
||||
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -170,12 +173,20 @@ name = "num-complex"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
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"
|
||||
|
||||
[[package]]
|
||||
@ -189,10 +200,10 @@ version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"libc 0.2.48 (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.55 (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]]
|
||||
@ -215,7 +226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.51"
|
||||
version = "0.1.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -223,24 +234,24 @@ name = "redox_termios"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "regex"
|
||||
version = "1.1.0"
|
||||
version = "1.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.5 (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.2.0 (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)",
|
||||
"utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.5"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -256,11 +267,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.1"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.55 (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)",
|
||||
]
|
||||
|
||||
@ -269,7 +281,7 @@ name = "thread_local"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
@ -289,7 +301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
@ -319,49 +331,51 @@ name = "wincolor"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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)",
|
||||
]
|
||||
|
||||
[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 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 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 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 either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
|
||||
"checksum env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afb070faf94c85d17d50ca44f6ad076bce18ae92f0037d350947240a36e9d42e"
|
||||
"checksum fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81f7f8eb465745ea9b02e2704612a9946a59fa40572086c6fd49d6ddcf30bf31"
|
||||
"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b"
|
||||
"checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a"
|
||||
"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 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 lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
|
||||
"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047"
|
||||
"checksum libm 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "03c0bb6d5ce1b5cc6fd0578ec1cbc18c9d88b5b591a5c7c1d6c6175e266a0819"
|
||||
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
||||
"checksum libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "42914d39aad277d9e176efbdad68acb1d5443ab65afe0e0e4f0d49352a950880"
|
||||
"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 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 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 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.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 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 regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
|
||||
"checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861"
|
||||
"checksum regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f0a0bcab2fd7d1d7c54fa9eae6f43eddeb9ce2e7352f8518a814a4f65d60c58"
|
||||
"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 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 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 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-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"
|
||||
|
@ -1,17 +1,34 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::math::eqn::Eqns;
|
||||
use crate::math::{Point2, Region, Region1, Region2, Scalar};
|
||||
use std::fmt;
|
||||
|
||||
type EntityId = i64;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Var<T: Clone, TRegion: Region<T>> {
|
||||
pub struct Constrainable<T: Clone, TRegion: Region<T>> {
|
||||
id: EntityId,
|
||||
value: T,
|
||||
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 {
|
||||
Self { value, constraints }
|
||||
Self {
|
||||
id: 0,
|
||||
value,
|
||||
constraints,
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
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 {
|
||||
&self.constraints
|
||||
}
|
||||
@ -37,28 +78,22 @@ impl<T: Clone, TRegion: Region<T>> Var<T, TRegion> {
|
||||
}
|
||||
}
|
||||
|
||||
type ScalarVar = Var<Scalar, Region1>;
|
||||
type PointVar = Var<Point2, Region2>;
|
||||
pub type CScalar = Constrainable<Scalar, Region1>;
|
||||
pub type CPoint = Constrainable<Point2<Scalar>, Region2>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Point {
|
||||
// pub id: i64,
|
||||
pub pos: PointVar,
|
||||
}
|
||||
pub type PointRef = Rc<RefCell<CPoint>>;
|
||||
|
||||
pub type PointRef = Rc<RefCell<Point>>;
|
||||
|
||||
impl Point {
|
||||
pub fn new_ref(pos: PointVar) -> PointRef {
|
||||
Rc::new(RefCell::new(Point { pos }))
|
||||
impl CPoint {
|
||||
pub fn into_ref(self) -> PointRef {
|
||||
Rc::new(RefCell::new(self))
|
||||
}
|
||||
}
|
||||
|
||||
struct Line {
|
||||
p1: PointRef,
|
||||
p2: PointRef,
|
||||
len: ScalarVar,
|
||||
dir: ScalarVar,
|
||||
len: CScalar,
|
||||
dir: CScalar,
|
||||
}
|
||||
|
||||
// struct System {
|
||||
|
65
src/main.rs
65
src/main.rs
@ -17,34 +17,51 @@ mod math;
|
||||
mod relation;
|
||||
|
||||
fn main() {
|
||||
use entity::{Point, PointRef, Var};
|
||||
use math::Point2;
|
||||
use entity::{CPoint, PointRef};
|
||||
use math::{Eqn, Eqns, Expr, Point2, Region2, Rot2};
|
||||
use relation::{Relation, ResolveResult};
|
||||
|
||||
env_logger::init();
|
||||
|
||||
println!("Hello, world!");
|
||||
let origin = Point::new_ref(Var::new_single(Point2::new(0., 0.)));
|
||||
let p1 = Point::new_ref(Var::new_full(Point2::new(1., 1.)));
|
||||
let p2 = Point::new_ref(Var::new_full(Point2::new(4., 4.)));
|
||||
let p3 = Point::new_ref(Var::new_full(Point2::new(2., 2.)));
|
||||
let u1 = math::Unknown(1);
|
||||
let u2 = math::Unknown(2);
|
||||
// let u1 = eqn::Expr::from(1.);
|
||||
// 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 print_points = |points: &Vec<PointRef>| {
|
||||
println!(
|
||||
"origin, p1, p2, p3:\n {:?}\n {:?}\n {:?}\n {:?}",
|
||||
points[0], points[1], points[2], points[3],
|
||||
"origin: {}\np1: {}\np2: {}\np3: {}",
|
||||
points[0].borrow(),
|
||||
points[1].borrow(),
|
||||
points[2].borrow(),
|
||||
points[3].borrow(),
|
||||
);
|
||||
};
|
||||
print_points(&points);
|
||||
// let c1 = relation::Coincident {
|
||||
// p1: origin.clone(),
|
||||
// p2: p1.clone(),
|
||||
// };
|
||||
let c2 = relation::PointAngle::new_vertical(p1.clone(), p2.clone());
|
||||
let c1 = relation::Coincident {
|
||||
p1: origin.clone(),
|
||||
p2: p1.clone(),
|
||||
};
|
||||
let c4 = relation::PointAngle::new_vertical(p1.clone(), p2.clone());
|
||||
let c3 = relation::PointAngle::new_horizontal(p2.clone(), p3.clone());
|
||||
let c4 = relation::AlignedDistance::new_vertical(p1.clone(), p2.clone(), 12.);
|
||||
let mut relations: Vec<Box<dyn Relation>> =
|
||||
vec![/*Box::new(c1),*/ Box::new(c2), Box::new(c3), Box::new(c4)];
|
||||
let c2 = relation::AlignedDistance::new_vertical(p1.clone(), p2.clone(), 12.);
|
||||
let c5 = relation::PointAngle::new(p1.clone(), p3.clone(), Rot2::from_angle_deg(0.572938698));
|
||||
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 any_underconstrained = true;
|
||||
let mut any_constrained = true;
|
||||
@ -55,10 +72,11 @@ fn main() {
|
||||
let newly_constrained = relations.drain_filter(|r| {
|
||||
let rr = r.resolve();
|
||||
println!("resolve result: {:?}", rr);
|
||||
println!(
|
||||
"origin, p1, p2, p3:\n {:?}\n {:?}\n {:?}\n {:?}",
|
||||
origin, p1, p2, p3
|
||||
);
|
||||
print_points(&points);
|
||||
let mut pos = p2.borrow().val().clone();
|
||||
println!("p2 pos: {}", pos);
|
||||
let mut pos = p3.borrow().val().clone();
|
||||
println!("p3 pos: {}", pos);
|
||||
match rr {
|
||||
ResolveResult::Underconstrained => {
|
||||
any_underconstrained = true;
|
||||
@ -83,4 +101,11 @@ fn main() {
|
||||
} else {
|
||||
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::iter::FromIterator;
|
||||
|
||||
use crate::math::Scalar;
|
||||
|
||||
// an unknown variable with an id
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
use super::expr::Expr;
|
||||
use super::unknown::*;
|
||||
use crate::math::expr::Expr::*;
|
||||
|
||||
#[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: &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);
|
||||
pub struct Eqn(pub Expr, pub Expr);
|
||||
|
||||
impl Unknowns for Eqn {
|
||||
fn unknowns(&self) -> UnknownSet {
|
||||
@ -481,61 +45,71 @@ impl Eqn {
|
||||
Eqn(self.0.simplify(), self.1.simplify())
|
||||
}
|
||||
|
||||
pub fn solve(&self, for_u: Unknown) -> Option<Expr> {
|
||||
use Expr::*;
|
||||
if !self.has_unknown(for_u) {
|
||||
return None;
|
||||
}
|
||||
let (l, r) = (
|
||||
self.0
|
||||
.clone() /*.distribute()*/
|
||||
.simplify(),
|
||||
self.1
|
||||
.clone() /*.distribute()*/
|
||||
.simplify(),
|
||||
);
|
||||
let (mut l, mut r) = ord_by_unkn(l, r, for_u)?;
|
||||
loop {
|
||||
trace!("solve: {} == {}", l, r);
|
||||
let (new_l, new_r): (Expr, Expr) = match l {
|
||||
Unkn(u) => return if u == for_u { Some(r.simplify()) } else { None },
|
||||
Sum(es) => {
|
||||
let (us, not_us): (Vec<_>, Vec<_>) =
|
||||
es.into_iter().partition(|e| e.has_unknown(for_u));
|
||||
if us.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
(
|
||||
Sum(us).simplify(),
|
||||
Expr::new_minus(r, Sum(not_us)).simplify(),
|
||||
)
|
||||
pub fn substitute(self, eqns: &Eqns) -> Eqn {
|
||||
Self(self.0.substitute(eqns), self.1.substitute(eqns))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn solve_eqn(eqn: &Eqn, for_u: Unknown) -> Option<Expr> {
|
||||
use Expr::*;
|
||||
if !eqn.has_unknown(for_u) {
|
||||
return None;
|
||||
}
|
||||
trace!("solve: {}", eqn);
|
||||
let (mut l, mut r) = (eqn.0.clone().distribute().simplify(), eqn.1.clone().distribute().simplify());
|
||||
if l == r {
|
||||
return None
|
||||
};
|
||||
l = (l - r).distribute().simplify();
|
||||
r = Expr::Const(0.);
|
||||
loop {
|
||||
trace!("solve iter: {} == {}", l, r);
|
||||
let (new_l, new_r): (Expr, Expr) = match l {
|
||||
Unkn(u) => return if u == for_u { Some(r.simplify()) } else { None },
|
||||
Sum(es) => {
|
||||
let (us, not_us): (Vec<_>, Vec<_>) =
|
||||
es.into_iter().partition(|e| e.has_unknown(for_u));
|
||||
if us.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
Product(es) => {
|
||||
let (us, not_us): (Vec<_>, Vec<_>) =
|
||||
es.into_iter().partition(|e| e.has_unknown(for_u));
|
||||
if us.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
(
|
||||
Product(us).simplify(),
|
||||
Expr::new_div(r, Product(not_us)).simplify(),
|
||||
)
|
||||
(
|
||||
us.into_iter().next().unwrap(),
|
||||
if not_us.len() == 0 {
|
||||
r
|
||||
} else {
|
||||
Expr::new_minus(r, Sum(not_us))
|
||||
},
|
||||
)
|
||||
}
|
||||
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) => {
|
||||
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,
|
||||
}
|
||||
(
|
||||
us.into_iter().next().unwrap(),
|
||||
if not_us.len() == 0 {
|
||||
r
|
||||
} else {
|
||||
Expr::new_div(r, Product(not_us))
|
||||
},
|
||||
)
|
||||
}
|
||||
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,
|
||||
};
|
||||
l = new_l;
|
||||
r = new_r;
|
||||
}
|
||||
}
|
||||
Const(_) => return None,
|
||||
};
|
||||
l = new_l.distribute().simplify();
|
||||
r = new_r.distribute().simplify();
|
||||
}
|
||||
}
|
||||
|
||||
@ -546,9 +120,9 @@ impl fmt::Display for Eqn {
|
||||
}
|
||||
|
||||
#[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 {
|
||||
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)]
|
||||
mod tests {
|
||||
use super::super::Scalar;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
@ -592,7 +265,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_solve() {
|
||||
fn test_solve_eqn() {
|
||||
use Expr::*;
|
||||
let _ = env_logger::try_init();
|
||||
let u1 = Unknown(1);
|
||||
@ -600,27 +273,27 @@ mod tests {
|
||||
let e2 = Const(1.);
|
||||
|
||||
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());
|
||||
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());
|
||||
assert_eq!(eqn.solve(u1), Some(Const(4.)));
|
||||
let e3 = Expr::new_minus(Const(1.), Const(1.));
|
||||
assert_eq!(solve_eqn(&eqn, u1), Some(Const(4.)));
|
||||
let e3 = Expr::from(1.) - 1.;
|
||||
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 eqn = Eqn(e1, e2);
|
||||
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!(relative_eq!(const_expr(e.clone()).unwrap(), 5. / 3.));
|
||||
|
||||
let e1 = Expr::new_product(Const(2.), Expr::new_minus(Const(1.), Const(4.)));
|
||||
let e2 = Expr::new_minus(Expr::new_product(Unkn(u1), Const(2.)), Unkn(u1));
|
||||
let e1 = Expr::from(2.) * (Expr::from(1.) - 4.);
|
||||
let e2 = (u1 * 2.) - u1;
|
||||
info!(
|
||||
"e1==e2: {}=={} => {}=={}",
|
||||
e1,
|
||||
@ -629,18 +302,12 @@ mod tests {
|
||||
e2.clone().simplify()
|
||||
);
|
||||
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!(relative_eq!(const_expr(e.clone()).unwrap(), -6.));
|
||||
|
||||
let e1 = Expr::new_product(Const(2.), Expr::new_minus(Const(1.), Const(4.)));
|
||||
let e2 = Expr::new_div(
|
||||
Expr::new_sum(
|
||||
Expr::new_product(Unkn(u1), Const(2.)),
|
||||
Expr::new_product(Unkn(u1), Unkn(u1)),
|
||||
),
|
||||
Unkn(u1),
|
||||
);
|
||||
let e1 = Expr::from(2.) * (Expr::from(1.) - 4.);
|
||||
let e2 = ((u1 * 2.) + (u1 * u1)) / u1;
|
||||
info!(
|
||||
"{}=={} distrib=> {}=={}",
|
||||
e1,
|
||||
@ -656,32 +323,93 @@ mod tests {
|
||||
e2.clone().distribute().simplify()
|
||||
);
|
||||
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!(relative_eq!(const_expr(e.clone()).unwrap(), -8.));
|
||||
|
||||
let e1 = Expr::new_product(Const(2.), Expr::new_minus(Const(1.), Const(4.)));
|
||||
let e2 = Expr::new_div(
|
||||
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),
|
||||
);
|
||||
let e1 = Expr::from(2.) * (Expr::from(1.) - (4.));
|
||||
let e2 = ((u1 * 2.) + (((u1 * u1) + u1) + (Expr::from(2.) - (1. + 1.)))) / u1;
|
||||
info!(
|
||||
"e1==e2: {}=={} => {}=={}",
|
||||
e1,
|
||||
e2,
|
||||
e1.clone().distribute().simplify(),
|
||||
e2.clone().distribute().simplify().simplify()
|
||||
e2.clone().distribute().simplify()
|
||||
);
|
||||
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!(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 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 Vec2 = nalgebra::Vector2<Scalar>;
|
||||
pub type Point2 = nalgebra::Point2<Scalar>;
|
||||
// #[derive(Clone, Copy, PartialEq, Debug)]
|
||||
// pub enum Value {
|
||||
// Known(Scalar),
|
||||
// Unkn(Unknown),
|
||||
// }
|
||||
|
||||
pub type Rot2 = nalgebra::UnitComplex<Scalar>;
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type Value = Expr;
|
||||
|
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::math::{Line2, Vec2, Point2, Region1, Region2, Rot2, Scalar};
|
||||
use crate::entity::{CPoint as PointEntity, PointRef};
|
||||
use crate::math::{GenericRegion, Line2, Point2, Region1, Region2, Rot2, Scalar, Value, Vec2};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum ResolveResult {
|
||||
@ -31,9 +31,14 @@ pub struct Coincident {
|
||||
impl Relation for Coincident {
|
||||
fn resolve(&self) -> ResolveResult {
|
||||
let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut());
|
||||
let r = { p1.pos.constraints().intersect(p2.pos.constraints()) };
|
||||
p1.pos.reconstrain(r.clone());
|
||||
p2.pos.reconstrain(r.clone());
|
||||
let r = {
|
||||
p1.constraints()
|
||||
.clone()
|
||||
.intersect(p2.constraints().clone())
|
||||
.simplify()
|
||||
};
|
||||
p1.reconstrain(r.clone());
|
||||
p2.reconstrain(r.clone());
|
||||
ResolveResult::from_r2(&r)
|
||||
}
|
||||
}
|
||||
@ -50,11 +55,19 @@ impl PointAngle {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
use Region2::*;
|
||||
let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut());
|
||||
let constrain_line = |p1: &Point2, p2: &mut PointEntity| {
|
||||
let line = Region2::Line(Line2::new(*p1, self.angle, Region1::Full));
|
||||
let new_constraint = p2.pos.constraints().intersect(&line);
|
||||
p2.pos.reconstrain(new_constraint);
|
||||
ResolveResult::from_r2(p2.pos.constraints())
|
||||
let constrain_line = |p1: &Point2<Value>, p2: &mut PointEntity| {
|
||||
let line = Region2::Line(Line2::new(p1.clone(), self.angle, Region1::Full));
|
||||
trace!(
|
||||
"PointAngle line: {}, p2 constraint: {}",
|
||||
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,
|
||||
(Singleton(p1), Singleton(p2)) => {
|
||||
// if the angle p1 and p2 form is parallel to self.angle, the result
|
||||
// will have a y component of 0
|
||||
let r = self.angle.to_rotation_matrix().inverse() * (p2 - p1);
|
||||
if relative_eq!(r.y, 0.) {
|
||||
ResolveResult::Constrained
|
||||
} else {
|
||||
ResolveResult::Overconstrained
|
||||
}
|
||||
let r = self.angle.conj() * (p2.clone() - p1.clone());
|
||||
trace!("angle.cos: {}", r.x);
|
||||
// if relative_eq!(r.y, 0.) {
|
||||
ResolveResult::Constrained
|
||||
// } else {
|
||||
// ResolveResult::Overconstrained
|
||||
// }
|
||||
}
|
||||
(Singleton(p), _) => constrain_line(p, &mut *p2),
|
||||
(_, Singleton(p)) => constrain_line(p, &mut *p1),
|
||||
@ -87,34 +107,29 @@ impl Relation for PointAngle {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Axis {
|
||||
Vertical,
|
||||
Horizontal,
|
||||
}
|
||||
|
||||
pub struct AlignedDistance {
|
||||
pub p1: PointRef,
|
||||
pub p2: PointRef,
|
||||
pub axis: Axis,
|
||||
pub angle: Rot2,
|
||||
pub distance: Scalar,
|
||||
}
|
||||
|
||||
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 {
|
||||
p1,
|
||||
p2,
|
||||
axis,
|
||||
angle,
|
||||
distance,
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
use Region2::*;
|
||||
let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut());
|
||||
let constrain_line = |p1: Point2, p2: &mut PointEntity| {
|
||||
let angle = match self.axis {
|
||||
Axis::Horizontal => Rot2::from_cos_sin_unchecked(0., 1.),
|
||||
Axis::Vertical => Rot2::from_cos_sin_unchecked(1., 0.),
|
||||
};
|
||||
let line = Region2::Line(Line2::new(p1, angle, Region1::Full));
|
||||
let new_constraint = p2.pos.constraints().intersect(&line);
|
||||
p2.pos.reconstrain(new_constraint);
|
||||
ResolveResult::from_r2(p2.pos.constraints())
|
||||
let constrain_line = |p1: Point2<Value>, p2: &mut PointEntity| {
|
||||
let angle = self.angle + Rot2::up();
|
||||
let line = Region2::Line(Line2::new(p1.clone(), angle, Region1::Full)).simplify();
|
||||
trace!(
|
||||
"AlignedDistance line: {}, p2 constraint: {}",
|
||||
line,
|
||||
p2.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 {
|
||||
Axis::Horizontal => Vec2::new(self.distance, 0.),
|
||||
Axis::Vertical => Vec2::new(0., self.distance),
|
||||
};
|
||||
match (&mut p1.pos.constraints(), &mut p2.pos.constraints()) {
|
||||
let offset: Vec2<Scalar> = self.angle * self.distance;
|
||||
match (&mut p1.constraints(), &mut p2.constraints()) {
|
||||
(Empty, _) | (_, Empty) => ResolveResult::Overconstrained,
|
||||
(Singleton(p1), Singleton(p2)) => {
|
||||
let r = p2 - p1;
|
||||
let d = match self.axis {
|
||||
Axis::Horizontal => r.x,
|
||||
Axis::Vertical => r.y,
|
||||
};
|
||||
if relative_eq!(d, self.distance) {
|
||||
ResolveResult::Constrained
|
||||
} else {
|
||||
ResolveResult::Overconstrained
|
||||
}
|
||||
let r = p2.clone() - p1.clone();
|
||||
let d = self.angle.dot(r);
|
||||
// if relative_eq!(d, self.distance) {
|
||||
ResolveResult::Constrained
|
||||
// } else {
|
||||
// ResolveResult::Overconstrained
|
||||
// }
|
||||
}
|
||||
(Singleton(pos), _) => constrain_line(pos + offset, &mut *p2),
|
||||
(_, Singleton(pos)) => constrain_line(pos - offset, &mut *p1),
|
||||
(Singleton(pos), _) => constrain_line(pos.clone() + offset, &mut *p2),
|
||||
(_, Singleton(pos)) => constrain_line(pos.clone() - offset, &mut *p1),
|
||||
_ => ResolveResult::Underconstrained,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Midpoint {
|
||||
pub p1: PointRef,
|
||||
pub p2: PointRef,
|
||||
pub mid: PointRef,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user