From 858a9264fd8b9a9313c2f9424c23fb3f560803dc Mon Sep 17 00:00:00 2001 From: Alex Mikhalev Date: Fri, 7 Feb 2025 10:28:34 -0800 Subject: [PATCH] [WIP] optimization support --- Cargo.lock | 752 +--------------------------------- Cargo.toml | 13 +- src/lib.rs | 5 + src/main.rs | 50 ++- src/optimization.rs | 316 ++++++++++++-- src/optimization/params.rs | 173 ++++++++ src/optimization/residuals.rs | 64 +++ src/{tracing.rs => trace.rs} | 0 src/ui.rs | 237 ++++++++--- 9 files changed, 761 insertions(+), 849 deletions(-) create mode 100644 src/lib.rs create mode 100644 src/optimization/params.rs create mode 100644 src/optimization/residuals.rs rename src/{tracing.rs => trace.rs} (100%) diff --git a/Cargo.lock b/Cargo.lock index 3de00cc..6fa451e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,80 +18,6 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" -[[package]] -name = "accesskit" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4803cf8c252f374ae6bfbb341e49e5a37f7601f2ce74a105927a663eba952c67" - -[[package]] -name = "accesskit_consumer" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee8cf1202a4f94d31837f1902ab0a75c77b65bf59719e093703abe83efd74ec" -dependencies = [ - "accesskit", - "parking_lot", -] - -[[package]] -name = "accesskit_macos" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10be25f2b27bc33aa1647072e86b948b41596f1af1ae43a2b4b9be5d2011cbda" -dependencies = [ - "accesskit", - "accesskit_consumer", - "objc2", - "once_cell", - "parking_lot", -] - -[[package]] -name = "accesskit_unix" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630e7ee8f93c6246478bf0df6760db899b28d9ad54353a5f2d3157138ba817fc" -dependencies = [ - "accesskit", - "accesskit_consumer", - "async-channel", - "atspi", - "futures-lite", - "parking_lot", - "serde", - "zbus", -] - -[[package]] -name = "accesskit_windows" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13c462fabdd950ef14308a9390b07fa2e2e3aabccba1f3ea36ea2231bb942ab" -dependencies = [ - "accesskit", - "accesskit_consumer", - "arrayvec", - "once_cell", - "parking_lot", - "paste", - "windows", -] - -[[package]] -name = "accesskit_winit" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17727888757ec027ec221db33070e226ee07df44425b583bc67684204d35eff9" -dependencies = [ - "accesskit", - "accesskit_macos", - "accesskit_unix", - "accesskit_windows", - "parking_lot", - "winit", -] - [[package]] name = "adler" version = "1.0.2" @@ -120,15 +46,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "aho-corasick" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" -dependencies = [ - "memchr", -] - [[package]] name = "android-activity" version = "0.4.1" @@ -243,16 +160,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" -[[package]] -name = "async-broadcast" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" -dependencies = [ - "event-listener", - "futures-core", -] - [[package]] name = "async-channel" version = "1.8.0" @@ -278,38 +185,6 @@ dependencies = [ "slab", ] -[[package]] -name = "async-fs" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" -dependencies = [ - "async-lock", - "autocfg", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite", - "log", - "parking", - "polling", - "rustix", - "slab", - "socket2", - "waker-fn", -] - [[package]] name = "async-lock" version = "2.7.0" @@ -319,78 +194,18 @@ dependencies = [ "event-listener", ] -[[package]] -name = "async-recursion" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - [[package]] name = "async-task" version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" -[[package]] -name = "async-trait" -version = "0.1.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "atomic-waker" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" - [[package]] name = "atomic_refcell" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79d6dc922a2792b006573f60b2648076355daeae5ce9cb59507e5908c9625d31" -[[package]] -name = "atspi" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab84c09a770065868da0d713f1f4b35af85d96530a868f1c1a6c249178379187" -dependencies = [ - "async-recursion", - "async-trait", - "atspi-macros", - "enumflags2", - "futures-lite", - "serde", - "tracing", - "zbus", - "zbus_names", -] - -[[package]] -name = "atspi-macros" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ebc5a6f61f6996eca56a4cece7b3fe7da3b86f0473c7b71ab44e229f3acce4" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn 1.0.109", - "zbus", - "zbus_names", - "zvariant", -] - [[package]] name = "atty" version = "0.2.14" @@ -567,15 +382,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "block-sys" version = "0.1.0-beta.1" @@ -595,21 +401,6 @@ dependencies = [ "objc2-encode", ] -[[package]] -name = "blocking" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" -dependencies = [ - "async-channel", - "async-lock", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "log", -] - [[package]] name = "bumpalo" version = "3.12.1" @@ -636,12 +427,6 @@ dependencies = [ "syn 2.0.15", ] -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - [[package]] name = "bytes" version = "1.4.0" @@ -822,15 +607,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cpufeatures" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.3.2" @@ -919,37 +695,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer", - "crypto-common", -] - [[package]] name = "dirs" version = "4.0.0" @@ -1051,7 +796,6 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6412a21e0bde7c0918f7fb44bbbb86b5e1f88e63c026a4e747cc7af02f76dfbe" dependencies = [ - "accesskit", "ahash 0.8.3", "epaint", "nohash-hasher", @@ -1064,7 +808,6 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab43597ba41f0ce39a364ad83185594578bfd8b3409b99dbcbb01df23afc3dbb" dependencies = [ - "accesskit_winit", "android-activity", "arboard", "egui", @@ -1105,27 +848,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "enumflags2" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c041f5090df68b32bcd905365fd51769c8b9d553fe87fde0b683534f10c01bd2" -dependencies = [ - "enumflags2_derive", - "serde", -] - -[[package]] -name = "enumflags2_derive" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - [[package]] name = "epaint" version = "0.21.0" @@ -1151,27 +873,6 @@ dependencies = [ "serde", ] -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "error-code" version = "2.3.1" @@ -1273,44 +974,6 @@ dependencies = [ "waker-fn", ] -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "gethostname" version = "0.2.3" @@ -1359,9 +1022,9 @@ dependencies = [ [[package]] name = "glutin" -version = "0.30.7" +version = "0.30.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89bab9ec7715de13d5d5402238e66f48e3a5ae636ebb45aba4013c962e2ff15" +checksum = "62f9b771a65f0a1e3ddb6aa16f867d87dc73c922411c255e6c4ab7f6d45c7327" dependencies = [ "bitflags", "cfg_aliases", @@ -1376,7 +1039,7 @@ dependencies = [ "once_cell", "raw-window-handle", "wayland-sys 0.30.1", - "windows-sys 0.45.0", + "windows-sys", "x11-dl", ] @@ -1394,12 +1057,12 @@ dependencies = [ [[package]] name = "glutin_egl_sys" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5aaf0abb5c4148685b33101ae326a207946b4d3764d6cdc79f8316cdaa8367d" +checksum = "1b3bcbddc51573b977fc6dca5d93867e4f29682cdbaf5d13e48f4fa4346d4d87" dependencies = [ "gl_generator", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -1455,18 +1118,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "idna" version = "0.3.0" @@ -1499,17 +1150,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "itertools" version = "0.10.5" @@ -1538,7 +1178,7 @@ dependencies = [ "log", "thiserror", "walkdir", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -1611,12 +1251,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" -[[package]] -name = "linux-raw-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" - [[package]] name = "lock_api" version = "0.4.9" @@ -1688,15 +1322,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.8.0" @@ -1731,7 +1356,7 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -1827,19 +1452,6 @@ dependencies = [ "memoffset 0.6.5", ] -[[package]] -name = "nix" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" -dependencies = [ - "bitflags", - "cfg-if", - "libc", - "memoffset 0.7.1", - "static_assertions", -] - [[package]] name = "nohash-hasher" version = "0.2.0" @@ -2022,16 +1634,6 @@ dependencies = [ "redox_syscall 0.3.5", ] -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - [[package]] name = "os_str_bytes" version = "6.5.0" @@ -2079,7 +1681,7 @@ dependencies = [ "libc", "redox_syscall 0.2.16", "smallvec", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -2110,12 +1712,6 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "pkg-config" version = "0.3.26" @@ -2163,22 +1759,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2334,8 +1914,6 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ - "aho-corasick", - "memchr", "regex-syntax 0.7.1", ] @@ -2375,20 +1953,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.37.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] - [[package]] name = "rustversion" version = "1.0.12" @@ -2459,18 +2023,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-xml-rs" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0bf1ba0696ccf0872866277143ff1fd14d22eec235d2b23702f95e6660f7dfa" -dependencies = [ - "log", - "serde", - "thiserror", - "xml-rs", -] - [[package]] name = "serde_derive" version = "1.0.160" @@ -2493,28 +2045,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_repr" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "sharded-slab" version = "0.1.4" @@ -2559,6 +2089,7 @@ dependencies = [ "nohash-hasher", "rand", "rand_distr", + "slotmap", "tracing", "tracing-chrome", "tracing-error", @@ -2661,22 +2192,6 @@ dependencies = [ "wayland-client", ] -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "str-buf" version = "1.0.6" @@ -2717,19 +2232,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" -[[package]] -name = "tempfile" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.45.0", -] - [[package]] name = "term" version = "0.7.0" @@ -2968,16 +2470,6 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" -[[package]] -name = "uds_windows" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" -dependencies = [ - "tempfile", - "winapi", -] - [[package]] name = "unicode-bidi" version = "0.3.13" @@ -3288,49 +2780,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0286ba339aa753e70765d521bb0242cc48e1194562bfa2a2ad7ac8a6de28f5d5" -dependencies = [ - "windows-implement", - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-implement" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9539b6bd3eadbd9de66c9666b22d802b833da7e996bc06896142e09854a61767" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] @@ -3339,28 +2795,13 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -3369,84 +2810,42 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - [[package]] name = "winit" version = "0.28.3" @@ -3478,7 +2877,7 @@ dependencies = [ "wayland-protocols", "wayland-scanner", "web-sys", - "windows-sys 0.45.0", + "windows-sys", "x11-dl", ] @@ -3533,121 +2932,8 @@ dependencies = [ "nom", ] -[[package]] -name = "xdg-home" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" -dependencies = [ - "nix 0.26.2", - "winapi", -] - [[package]] name = "xml-rs" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" - -[[package]] -name = "zbus" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29242fa5ec5693629ae74d6eb1f69622a9511f600986d6d9779bccf36ac316e3" -dependencies = [ - "async-broadcast", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-recursion", - "async-task", - "async-trait", - "byteorder", - "derivative", - "enumflags2", - "event-listener", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix 0.26.2", - "once_cell", - "ordered-stream", - "rand", - "serde", - "serde-xml-rs", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "winapi", - "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537793e26e9af85f774801dc52c6f6292352b2b517c5cf0449ffd3735732a53a" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "regex", - "syn 1.0.109", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34f314916bd89bdb9934154627fab152f4f28acdda03e7c4c68181b214fe7e3" -dependencies = [ - "serde", - "static_assertions", - "zvariant", -] - -[[package]] -name = "zvariant" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe4914a985446d6fd287019b5fceccce38303d71407d9e6e711d44954a05d8" -dependencies = [ - "byteorder", - "enumflags2", - "libc", - "serde", - "static_assertions", - "zvariant_derive", -] - -[[package]] -name = "zvariant_derive" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34c20260af4b28b3275d6676c7e2a6be0d4332e8e0aba4616d34007fd84e462a" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b22993dbc4d128a17a3b6c92f1c63872dd67198537ee728d8b5d7c40640a8b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] diff --git a/Cargo.toml b/Cargo.toml index 6f0e440..8e094db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" argmin = { version = "0.8.1", features = [] } argmin-math = { version = "0.3.0", features = ["nalgebra_v0_32"] } bevy_ecs = "0.10.1" -eframe = { version = "0.21.3" } +eframe = { version = "0.21.3", default-features = false, features = ["default_fonts", "glow"] } indexmap = "1.8.1" levenberg-marquardt = "0.13.0" nalgebra = { version = "0.32.0", features = ["rand"] } @@ -17,13 +17,18 @@ nalgebra-sparse = "0.9.0" nohash-hasher = "0.2.0" rand = "0.8.5" rand_distr = "0.4.3" -tracing = { version = "0.1.37", optional = true } +slotmap = "1.0.6" +tracing = "0.1.37" tracing-chrome = { version = "0.7.1", optional = true } tracing-error = { version = "0.2.0", optional = true } -tracing-subscriber = { version = "0.3.17", features = ["env-filter"], optional = true } +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } [dev-dependencies] criterion = "0.4.0" [features] -trace = ["tracing", "tracing-subscriber", "tracing-chrome", "tracing-error", "bevy_ecs/trace"] +trace = ["tracing-chrome", "tracing-error", "bevy_ecs/trace"] + +[[bench]] +name = "my_benchmark" +harness = false diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..cc864b2 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,5 @@ +#![feature(generic_const_exprs)] + +pub mod geometry; +pub mod optimization; +// mod relations; diff --git a/src/main.rs b/src/main.rs index dad1147..5f2e247 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,40 @@ +#![feature(generic_const_exprs)] + use bevy_ecs::prelude::*; use eframe::egui; +use rand::seq::SliceRandom; +use rand_distr::Distribution; + +use sketchrs::geometry; -mod geometry; -pub mod optimization; -mod relations; #[cfg(feature = "trace")] -mod tracing; +mod trace; mod ui; fn init(mut commands: Commands) { - let p1 = geometry::insert_point_at(&mut commands, (10., 30.)); - let p2 = geometry::insert_point_at(&mut commands, (-20., 15.)); - geometry::insert_point_at(&mut commands, (0., -10.)); - commands.spawn(geometry::LineBundle::new(p1, p2)); + // let p1 = geometry::insert_point_at(&mut commands, (10., 30.)); + // let p2 = geometry::insert_point_at(&mut commands, (-20., 15.)); + // geometry::insert_point_at(&mut commands, (0., -10.)); + // commands.spawn(geometry::LineBundle::new(p1, p2)); + + // Insert 1000 random points + let dist = rand_distr::Uniform::new(-100., 100.); + let mut rng = rand::thread_rng(); + let mut points = Vec::new(); + for _ in 0..1000 { + let p = geometry::insert_point_at( + &mut commands, + (dist.sample(&mut rng), dist.sample(&mut rng)), + ); + points.push(p); + } + + // Insert 1000 random lines + for _ in 0..1000 { + let p1 = *points.choose(&mut rng).unwrap(); + let p2 = *points.choose(&mut rng).unwrap(); + commands.spawn(geometry::LineBundle::new(p1, p2)); + } } struct MyApp { @@ -31,9 +53,9 @@ impl MyApp { let mut world = World::default(); #[cfg(feature = "trace")] - tracing::setup(&mut world); + trace::setup(&mut world); - world.init_resource::(); + ui::init(&mut world); let mut init_sched = Schedule::new(); init_sched.add_system(init); @@ -51,6 +73,9 @@ impl MyApp { impl eframe::App for MyApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + let span = tracing::info_span!("update"); + let _enter = span.enter(); + self.world.insert_resource(ui::ContextRes(ctx.clone())); self.update_schedule.run(&mut self.world); @@ -60,6 +85,11 @@ impl eframe::App for MyApp { } fn main() { + tracing_subscriber::fmt() + .with_max_level(tracing::Level::INFO) + .with_target(false) + .init(); + let options = eframe::NativeOptions::default(); eframe::run_native("sketchrs", options, Box::new(|_cc| Box::::default())).unwrap(); } diff --git a/src/optimization.rs b/src/optimization.rs index 55bef5a..969a12e 100644 --- a/src/optimization.rs +++ b/src/optimization.rs @@ -1,4 +1,9 @@ -use std::{borrow::Borrow, fmt::Debug}; +use std::{borrow::Borrow, fmt::Debug, ops::Index}; + +mod params; +mod residuals; + +use params::ReadParams; use argmin_math; use levenberg_marquardt::{self, TerminationReason}; @@ -7,11 +12,154 @@ use nalgebra_sparse::CooMatrix; use crate::geometry::Scalar; +pub type Idx = u32; + +use self::{params::{Parameters, Param}, residuals::WriteResiduals}; + type SVector = nalgebra::SVector; type SMatrix = nalgebra::SMatrix; type DVector = nalgebra::DVector; type DMatrix = nalgebra::DMatrix; + +trait CostFunction { + type Params; + // const NUM_RESIDUALS: usize; + type Residuals; + type Jacobian; + + fn evaluate( + &self, + params: &Self::Params, + residual: Option<&mut Self::Residuals>, + jacobian: Option<&mut Self::Jacobian>, + ); + + fn residual(&self, x: &Self::Params) -> Self::Residuals + where + Self::Residuals: Default, + { + let mut residual = Self::Residuals::default(); + self.evaluate(x, Some(&mut residual), None); + residual + } + + fn jacobian(&self, x: &Self::Params) -> Self::Jacobian + where + Self::Jacobian: Default, + { + let mut jacobian = Self::Jacobian::default(); + self.evaluate(x, None, Some(&mut jacobian)); + jacobian + } + + fn with_indices( + self, + param_indices: PI, + residual_indices: RI, + jacobian_indices: JI, + ) -> IndexedCostFunctionImpl + where + Self: Sized, + { + IndexedCostFunctionImpl { + residual: self, + param_indices, + residual_indices, + jacobian_indices, + } + } +} + +trait IndexedCostFunction { + fn evaluate( + &self, + params: &Parameters, + residuals: Option<&mut [Scalar]>, + jacobian: Option<&mut [Scalar]>, + ); +} + +struct IndexedCostFunctionImpl { + residual: R, + param_indices: PI, + residual_indices: RI, + jacobian_indices: JI, +} + +impl IndexedCostFunction for IndexedCostFunctionImpl +where + R: CostFunction, + R::Params: ReadParams, + R::Residuals: WriteResiduals + Default, + R::Jacobian: WriteResiduals + Default, +{ + fn evaluate( + &self, + params: &Parameters, + residuals: Option<&mut [Scalar]>, + jacobian: Option<&mut [Scalar]>, + ) { + // let p = R::Params::read(params, self.param_indices); + + // let mut res = residuals.and_then(|_| Some(R::Residuals::default())); + // let mut jac = jacobian.and_then(|_| Some(R::Jacobian::default())); + // self.residual.evaluate(&p, res.as_mut(), jac.as_mut()); + + // if let (Some(res), Some(residuals)) = (res, residuals) { + // res.write(self.residual_indices, residuals); + // } + // if let (Some(jac), Some(jacobian)) = (jac, jacobian) { + // jac.write(self.jacobian_indices, jacobian); + // } + } +} + +slotmap::new_key_type!{ struct ParamKey; } + +struct ParamIndex(Idx); + +#[derive(Default)] +struct CostFunctions { + functions: Vec>, + param_indices: slotmap::SlotMap, + num_residuals: Idx, +} + +impl CostFunctions { + fn new() -> Self { + Self::default() + } + + // fn add_param(&mut self) -> ParamIndex { + // // let index = self.params_size; + // // self.params_size += P::SIZE; + // ParamIndex(index) + // } + + // fn add(&mut self, residual: R, param_indices: PI, residual_indices: RI, jacobian_indices: JI) + // where + // R: CostFunction, + // R::Params: ReadParams, + // R::Residuals: WriteResiduals + Default, + // R::Jacobian: WriteResiduals + Default, + // { + // self.functions.push(Box::new( + // residual + // .with_indices(param_indices, residual_indices, jacobian_indices), + // )); + // self.param_indices.push(param_indices.into()); + // self.residual_indices.push(residual_indices.into()); + // self.jacobian_indices.push(jacobian_indices.into()); + // } + + fn evaluate(&self, params: &Parameters, residuals: Option<&mut [Scalar]>, jacobian: Option<&mut [Scalar]>) { + // for (i, f) in self.functions.iter().enumerate() { + // f.evaluate(params, residuals, jacobian); + // } + } +} + trait SResBlock: Clone + Debug { fn evaluate( &self, @@ -39,20 +187,20 @@ trait SResBlock: Clone + Debug { } // Probably should move to own trait - fn into_indexed( - self, - in_indices: [Index; NIN], - out_indices: [Index; NOUT], - ) -> Box - where - Self: Sized + 'static, - { - Box::new(IndexedResBlockImpl { - residual: self, - in_indices, - out_indices, - }) - } + // fn into_indexed( + // self, + // in_indices: [Index; NIN], + // out_indices: [Index; NOUT], + // ) -> Box + // where + // Self: Sized + 'static, + // { + // Box::new(IndexedResBlockImpl { + // residual: self, + // in_indices, + // out_indices, + // }) + // } } fn square(x: Scalar) -> Scalar { @@ -84,6 +232,36 @@ impl SResBlock<2> for Equal1D { #[derive(Clone, Debug)] pub struct AxisDistance(Scalar); +/* +impl ResBlock for AxisDistance { + fn parameter_sizes(&self) -> &[usize] { + &[2] + } + + fn n_residuals(&self) -> usize { + 1 + } + + fn evaluate( + &self, + x: &[&[Scalar]], + residual: Option<&mut [Scalar]>, + jacobian: Option<&mut [&mut [Scalar]]>, + ) { + let [x1, x2] = x[0] else { panic!() }; + let dx = x2 - x1; + let r = self.0; + if let Some(residual) = residual { + residual[0] = square(dx) - square(r); + } + if let Some(jacobian) = jacobian { + jacobian[0][0] = -2. * dx; + jacobian[0][1] = 2. * dx; + } + } +} +*/ + impl SResBlock<2> for AxisDistance { fn evaluate( &self, @@ -107,6 +285,34 @@ impl SResBlock<2> for AxisDistance { #[derive(Clone, Debug)] pub struct PointPointDistance(Scalar); +impl CostFunction for PointPointDistance { + type Params = (SVector<2>, SVector<2>); + type Residuals = Scalar; + type Jacobian = (SMatrix<2, 1>, SMatrix<2, 1>); + + fn evaluate( + &self, + (p1, p2): &Self::Params, + residual: Option<&mut Self::Residuals>, + jacobian: Option<&mut Self::Jacobian>, + ) { + let d = p2 - p1; + if let Some(residual) = residual { + *residual = square(d.x) + square(d.y) - square(self.0); + } + if let Some(jacobian) = jacobian { + jacobian.0.copy_from(&(2. * d)); + jacobian.1.copy_from(&(-2. * d)); + // jacobian.row_mut(0).copy_from(&(2. * d).transpose()); + // jacobian.row_mut(1).copy_from(&(-2. * d).transpose()); + // jacobian[0] = -2. * d.x; + // jacobian[1] = -2. * d.y; + // jacobian[2] = 2. * d.x; + // jacobian[3] = 2. * d.y; + } + } +} + impl SResBlock<4> for PointPointDistance { fn evaluate( &self, @@ -116,9 +322,8 @@ impl SResBlock<4> for PointPointDistance { ) { let [x1, y1, x2, y2] = *x.as_ref(); let (dx, dy) = (x2 - x1, y2 - y1); - let r = self.0; if let Some(residual) = residual { - residual[0] = square(dx) + square(dy) - square(r); + residual[0] = square(dx) + square(dy) - square(self.0); } if let Some(jacobian) = jacobian { jacobian[0] = -2. * dx; @@ -153,6 +358,33 @@ impl SResBlock<2> for PointDistance { #[derive(Clone, Debug)] pub struct Collinear; +impl CostFunction for Collinear { + type Params = (SVector<2>, SVector<2>, SVector<2>); + type Residuals = Scalar; + type Jacobian = (SVector<2>, SVector<2>, SVector<2>); + + fn evaluate( + &self, + (p1, p2, p3): &Self::Params, + residual: Option<&mut Self::Residuals>, + jacobian: Option<&mut Self::Jacobian>, + ) { + let d21 = p2 - p1; + let d32 = p3 - p2; + if let Some(residual) = residual { + *residual = d21.y * d32.x - d32.y * d21.x; + } + if let Some(jacobian) = jacobian { + jacobian.0.x = d32.y; + jacobian.0.y = -d32.x; + jacobian.1.x = p1.y - p3.y; + jacobian.1.y = p3.x - p1.x; + jacobian.2.x = d21.y; + jacobian.2.y = -d21.x; + } + } +} + impl SResBlock<6> for Collinear { fn evaluate( &self, @@ -171,15 +403,14 @@ impl SResBlock<6> for Collinear { if let Some(jacobian) = jacobian { jacobian[0] = dy32; jacobian[1] = -dx32; - jacobian[2] = -dy32; - jacobian[3] = -dx21; + jacobian[2] = y1 - y3; + jacobian[3] = x3 - x1; jacobian[4] = dy21; jacobian[5] = -dx21; } } } -type Index = u32; trait ScatterResBlock: Debug { fn shape(&self) -> (usize, usize); @@ -194,9 +425,9 @@ trait ScatterResBlock: Debug { #[derive(Clone, Debug)] struct ScatterResBlockImpl { residual: R, - in_indices: [Index; NIN], - residual_indices: [Index; NOUT], - jacobian_indices: [[Index; NIN]; NOUT], + in_indices: [Idx; NIN], + residual_indices: [Idx; NOUT], + jacobian_indices: [[Idx; NIN]; NOUT], } impl ScatterResBlockImpl { @@ -278,6 +509,7 @@ where } } +/* trait IndexedResBlock { fn shape(&self) -> (usize, usize); fn in_indices(&self) -> &[Index]; @@ -323,20 +555,22 @@ where }) } } +*/ #[derive(Debug)] -struct Residuals { +struct ResidualBlocks { res_blocks: Vec>, shape: (usize, usize), } -impl Residuals { - fn new(res_blocks: Vec>, shape: (usize, usize)) -> Self { - let res_blocks = res_blocks - .into_iter() - .map(|r| r.into_scatter(shape)) - .collect(); - Self { res_blocks, shape } +impl ResidualBlocks { + fn new(res_blocks: Vec>, shape: (usize, usize)) -> Self { + // let res_blocks = res_blocks + // .into_iter() + // .map(|r| r.into_scatter(shape)) + // .collect(); + // Self { res_blocks, shape } + todo!() } fn residuals_into>(&self, x: X, residuals: &mut [Scalar]) { @@ -379,7 +613,7 @@ impl Residuals { #[derive(Debug)] struct Problem { - residuals: Residuals, + residuals: ResidualBlocks, x: DVector, } @@ -412,18 +646,18 @@ pub fn test() { let nvar = 4; let nres = 3; - let residuals = Residuals::new( - vec![ - PointDistance(SVector::<2>::new(1., 0.), 0.75).into_indexed([0, 1], [0]), - PointPointDistance(1.0).into_indexed([0, 1, 2, 3], [2]), - PointDistance(SVector::<2>::new(0., 0.), 1.25).into_indexed([2, 3], [1]), - ], - (nres, nvar), - ); + // let residuals = CostFunction::new( + // vec![ + // PointDistance(SVector::<2>::new(1., 0.), 0.75).into_indexed([0, 1], [0]), + // PointPointDistance(1.0).into_indexed([0, 1, 2, 3], [2]), + // PointDistance(SVector::<2>::new(0., 0.), 1.25).into_indexed([2, 3], [1]), + // ], + // (nres, nvar), + // ); let dist = rand_distr::Normal::new(0., 1.0).unwrap(); let prob = Problem { - residuals, + residuals: todo!(), x: DVector::from_distribution_generic( Dyn(nvar), Const::<1>, diff --git a/src/optimization/params.rs b/src/optimization/params.rs new file mode 100644 index 0000000..c43b12a --- /dev/null +++ b/src/optimization/params.rs @@ -0,0 +1,173 @@ +use nalgebra::{SMatrix, SVector}; + +use crate::geometry::Scalar; + +use super::Idx; + +pub struct Parameters { + values: Vec, +} + +pub trait Param { + const SIZE: usize; + type Jacobian; + + fn from_array(array: [Scalar; Self::SIZE]) -> Self; + + fn from_slice(slice: &[Scalar]) -> Self + where + [Scalar; Self::SIZE]: Sized, + Self: Sized, + { + let array = + TryInto::<[Scalar; Self::SIZE]>::try_into(slice).expect("Slice has wrong length"); + Self::from_array(array) + } + + fn copy_to_array(&self, array: &mut [Scalar; Self::SIZE]); + + fn copy_to_slice(&self, slice: &mut [Scalar]) + where + [Scalar; Self::SIZE]: Sized, + { + let array = + TryInto::<&mut [Scalar; Self::SIZE]>::try_into(slice).expect("Slice has wrong length"); + self.copy_to_array(array) + } +} + +trait WithJacobian { + type Jacobian; +} + +trait Jacobian {} + +impl Param for Scalar { + const SIZE: usize = 1; + + type Jacobian = SVector; + + fn from_slice(slice: &[Scalar]) -> Self { + let [x] = slice else { + panic!("Slice has wrong length"); + }; + *x + } + + fn copy_to_slice(&self, slice: &mut [Scalar]) { + let [x] = slice else { + panic!("Slice has wrong length"); + }; + *x = *self; + } + + fn copy_to_array(&self, array: &mut [Scalar; Self::SIZE]) { + array[0] = *self; + } + + fn from_array(array: [Scalar; Self::SIZE]) -> Self { + array[0] + } +} + +pub trait ReadParams { + fn read(params: &Parameters, index: I) -> Self; +} + +// impl ReadParams for Scalar { +// fn read(params: &Parameters, index: Idx) -> Self { +// params.values[index as usize] +// } +// } + +impl Param for [Scalar; M] { + const SIZE: usize = M; + type Jacobian = SMatrix; + + fn from_array(array: [Scalar; Self::SIZE]) -> Self { + unsafe { std::mem::transmute_copy(&array) } + } + + fn copy_to_array(&self, slice: &mut [Scalar; Self::SIZE]) { + slice.copy_from_slice(self) + } +} + +impl Param for SMatrix { + const SIZE: usize = M; + type Jacobian = SMatrix; + + fn from_slice(slice: &[Scalar]) -> Self { + Self::from_column_slice(slice) + } + + fn from_array(array: [Scalar; Self::SIZE]) -> Self { + Self::from_slice(&array) + } + + fn copy_to_slice(&self, slice: &mut [Scalar]) { + slice.copy_from_slice(self.as_slice()) + } + + fn copy_to_array(&self, array: &mut [Scalar; Self::SIZE]) { + array.copy_from_slice(self.as_slice()) + } +} + +impl ReadParams for P +where + [Scalar; P::SIZE]: Sized, +{ + fn read(params: &Parameters, index: Idx) -> Self { + Self::from_slice(¶ms.values[index as usize..][..Self::SIZE]) + } +} + +// impl ReadParams for SMatrix { +// fn read(params: &Parameters, index: Idx) -> Self { +// SMatrix::from_column_slice(¶ms.values[index as usize..][..R * C]) +// } +// } + +// TODO: Implement for tuples of arbitrary length using a macro +impl ReadParams<(I1, I2)> for (T1, T2) +where + T1: ReadParams, + T2: ReadParams, +{ + fn read(params: &Parameters, index: (I1, I2)) -> Self { + (T1::read(params, index.0), T2::read(params, index.1)) + } +} + +impl ReadParams<(I1, I2, I3)> for (T1, T2, T3) +where + T1: ReadParams, + T2: ReadParams, + T3: ReadParams, +{ + fn read(params: &Parameters, index: (I1, I2, I3)) -> Self { + ( + T1::read(params, index.0), + T2::read(params, index.1), + T3::read(params, index.2), + ) + } +} + +impl ReadParams<(I1, I2, I3, I4)> for (T1, T2, T3, T4) +where + T1: ReadParams, + T2: ReadParams, + T3: ReadParams, + T4: ReadParams, +{ + fn read(params: &Parameters, index: (I1, I2, I3, I4)) -> Self { + ( + T1::read(params, index.0), + T2::read(params, index.1), + T3::read(params, index.2), + T4::read(params, index.3), + ) + } +} diff --git a/src/optimization/residuals.rs b/src/optimization/residuals.rs new file mode 100644 index 0000000..4cb5459 --- /dev/null +++ b/src/optimization/residuals.rs @@ -0,0 +1,64 @@ +use nalgebra::SMatrix; + +use crate::geometry::Scalar; + +pub trait WriteResiduals { + fn write(&self, index: Index, residuals: &mut [Scalar]); +} + +impl WriteResiduals for Scalar +where + usize: From, +{ + fn write(&self, index: I, residuals: &mut [Scalar]) { + residuals[usize::from(index)] = *self; + } +} + +impl WriteResiduals for SMatrix +where + usize: From, +{ + fn write(&self, index: I, residuals: &mut [Scalar]) { + residuals[usize::from(index)..][..R * C].copy_from_slice(self.as_slice()); + } +} + +impl WriteResiduals<(I1, I2)> for (T1, T2) +where + T1: WriteResiduals, + T2: WriteResiduals, +{ + fn write(&self, index: (I1, I2), residuals: &mut [Scalar]) { + self.0.write(index.0, residuals); + self.1.write(index.1, residuals); + } +} + +impl WriteResiduals<(I1, I2, I3)> for (T1, T2, T3) +where + T1: WriteResiduals, + T2: WriteResiduals, + T3: WriteResiduals, +{ + fn write(&self, index: (I1, I2, I3), residuals: &mut [Scalar]) { + self.0.write(index.0, residuals); + self.1.write(index.1, residuals); + self.2.write(index.2, residuals); + } +} + +impl WriteResiduals<(I1, I2, I3, I4)> for (T1, T2, T3, T4) +where + T1: WriteResiduals, + T2: WriteResiduals, + T3: WriteResiduals, + T4: WriteResiduals, +{ + fn write(&self, index: (I1, I2, I3, I4), residuals: &mut [Scalar]) { + self.0.write(index.0, residuals); + self.1.write(index.1, residuals); + self.2.write(index.2, residuals); + self.3.write(index.3, residuals); + } +} diff --git a/src/tracing.rs b/src/trace.rs similarity index 100% rename from src/tracing.rs rename to src/trace.rs diff --git a/src/ui.rs b/src/ui.rs index c9940b6..f124971 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,11 +1,12 @@ -use std::ops::Deref; +use std::{cmp::Ordering, ops::Deref}; use bevy_ecs::{prelude::*, query::WorldQuery, system::ReadOnlySystem}; use eframe::{ - egui::{self, CursorIcon, Painter, Response, Sense}, + egui::{self, plot::Plot, CursorIcon, Painter, Response, ScrollArea, Sense}, emath::RectTransform, - epaint::{Color32, Hsva, Pos2, Rect, Stroke, Vec2}, + epaint::{Color32, Hsva, Pos2, Rect, Rounding, Stroke, Vec2}, }; +use tracing::info; use crate::geometry::{self}; @@ -25,12 +26,46 @@ impl Default for Tool { } } +#[derive(Resource, Clone)] +struct Viewport { + scale: f32, + center: Pos2, +} + +impl Default for Viewport { + fn default() -> Self { + Self { + scale: 1.0, + center: Pos2::ZERO, + } + } +} + +fn update_viewport(mut viewport: ResMut, ui: Res) { + // if ui.response.hovered() && ui.response.ctx.input(|i| i.modifiers.command_only()) { + if ui.response.hovered() { + let delta = ui.response.ctx.input(|i| i.scroll_delta); + if delta != Vec2::ZERO { + // viewport.scale *= 1.0 + delta.y * 0.05; + viewport.scale *= (1.0f32 - 0.005).powf(-delta.y); + } + } +} + #[derive(Resource)] struct UiRes { response: Response, painter: Painter, to_screen: ToScreen, } + +impl UiRes { + fn to_screen(&self) -> ToScreen { + self.to_screen + } +} + +#[derive(Clone, Copy)] struct ToScreen(RectTransform); impl ToScreen { @@ -64,15 +99,36 @@ fn color_for_var_status(status: geometry::VarStatus) -> Hsva { } } +#[derive(Default, Resource)] +struct SelectionState { + start_point: Option, + end_point: Option, +} + +impl SelectionState { + fn selection_rect(&self) -> Option { + if let (Some(start), Some(end)) = (self.start_point, self.end_point) { + Some(Rect::from_two_pos(start, end)) + } else { + None + } + } +} + const POINT_RADIUS: f32 = 3.0; fn update_hover_point( ui: Res, points: Query<(geometry::PointId, &geometry::ComputedPointPos)>, + sel: Res, mut commands: Commands, ) { points.for_each(|(id, pos)| { - let hovered = if let Some(hover_pos) = ui.response.hover_pos() { + let hovered = if let Some(sel_rect) = sel.selection_rect() { + let center = ui.to_screen.transform_pos(pos); + + sel_rect.contains(center) + } else if let Some(hover_pos) = ui.response.hover_pos() { let center = ui.to_screen.transform_pos(pos); (hover_pos - center).length() < (POINT_RADIUS * 3.) @@ -91,15 +147,18 @@ fn update_hover_point( fn update_hover_line( ui: Res, lines: Query<(Entity, &geometry::ComputedLinePos)>, + sel: Res, mut commands: Commands, ) { lines.for_each(|(id, pos)| { - let hovered = if let Some(hover_pos) = ui.response.hover_pos() { - let points = [ - ui.to_screen.transform_pos(&pos.start), - ui.to_screen.transform_pos(&pos.end), - ]; - + let points = [ + ui.to_screen.transform_pos(&pos.start), + ui.to_screen.transform_pos(&pos.end), + ]; + let hovered = if let Some(sel_rect) = sel.selection_rect() { + // TODO: check if line intersects rect + sel_rect.contains(points[0]) || sel_rect.contains(points[1]) + } else if let Some(hover_pos) = ui.response.hover_pos() { let b = points[1] - points[0]; let a = hover_pos - points[0]; let p = a.dot(b) / b.dot(b); @@ -121,27 +180,44 @@ fn select_tool( ui: Res, hovered: Query>, selected: Query>, + mut sel: ResMut, mut commands: Commands, ) { ui.response.ctx.output_mut(|output| { - output.cursor_icon = if !hovered.is_empty() { + output.cursor_icon = if sel.selection_rect().is_some() { + CursorIcon::Grabbing + } else if !hovered.is_empty() { CursorIcon::PointingHand } else { CursorIcon::Default } }); - if ui.response.clicked() { + if ui.response.drag_released() { if !ui.response.ctx.input(|input| input.modifiers.shift) { selected.for_each(|selected| { commands.entity(selected).remove::(); }); } - // TODO: choose which to select - if let Some(hovered) = hovered.iter().next() { - commands.entity(hovered).insert(Selected); + for entity in hovered.iter() { + commands.entity(entity).insert(Selected); + // Only select one if not dragging + // if sel.selection_rect().is_none() { + // break; + // } } } + if ui.response.dragged() { + // TODO: choose which to select + if sel.start_point.is_none() { + sel.start_point = ui.response.hover_pos(); + } else if ui.response.hover_pos() != sel.start_point { + sel.end_point = ui.response.hover_pos(); + } + } else { + sel.start_point = None; + sel.end_point = None; + } if ui .response @@ -162,6 +238,17 @@ fn select_tool( } } +fn paint_selection(ui: Res, sel: Res) { + if let Some(rect) = sel.selection_rect() { + ui.painter.rect( + rect, + Rounding::none(), + Hsva::new(0.0, 0.0, 0.5, 0.5), + Stroke::new(1.0, Color32::WHITE), + ) + } +} + #[derive(Default)] struct DragDelta(Vec2); @@ -199,7 +286,7 @@ fn move_tool( selected.for_each(|selected| { commands.entity(selected.0).remove::(); }); - drag_delta.0 = Vec2::ZERO; + (*drag_delta).0 = Vec2::ZERO; None } else { selected.iter().next() @@ -208,7 +295,7 @@ fn move_tool( if let Some((_, point)) = selected { if response.drag_started() { let drag_point_pos = point_pos.get(point).unwrap(); - drag_delta.0 = hover_pos - to_screen.transform_pos(&drag_point_pos); + (*drag_delta).0 = hover_pos - to_screen.transform_pos(&drag_point_pos); } let move_to = to_screen.inverse_transform_to_point(hover_pos - drag_delta.0); point_pos.set(point, move_to).unwrap(); @@ -330,8 +417,11 @@ fn paint_lines( let mut color = color_for_var_status(pos.status); color.v -= 0.6; - if hovered.contains(id) || selected.contains(id) { + if selected.contains(id) { color.s -= 0.8; + color.v += 0.2; + } else if hovered.contains(id) { + color.s -= 0.4; } let stroke = Stroke::new(2.0, color); @@ -350,9 +440,11 @@ fn paint_points( let center = ui.to_screen.transform_pos(pos); let color = color_for_var_status(pos.status); - let stroke = if selected.contains(id) || hovered.contains(id) { - // color.s -= 0.8; + let stroke = if selected.contains(id) { Stroke::new(1.0, Color32::WHITE) + } else if hovered.contains(id) { + // color.s -= 0.8; + Stroke::new(1.0, Color32::WHITE.linear_multiply(0.1)) } else { Stroke::default() }; @@ -414,6 +506,7 @@ pub enum ShowEntitiesStage { PostTools, ToolsFlush, Paint, + PostPaint, } impl Default for ShowEntitiesSchedule { @@ -430,11 +523,14 @@ impl Default for ShowEntitiesSchedule { ShowEntitiesStage::ToolsFlush, ShowEntitiesStage::PostTools, ShowEntitiesStage::Paint, + ShowEntitiesStage::PostPaint, ) .chain(), ) + .add_system(update_viewport.in_base_set(ShowEntitiesStage::Update)) .add_systems( (geometry::update_point_pos, geometry::update_line_pos) + .chain() .in_base_set(ShowEntitiesStage::Update), ) .add_system(apply_system_buffers.in_base_set(ShowEntitiesStage::UpdateFlush)) @@ -456,6 +552,7 @@ impl Default for ShowEntitiesSchedule { .add_system(geometry::remove_dangling_lines.in_base_set(ShowEntitiesStage::PostTools)) .add_systems( (geometry::update_point_pos, geometry::update_line_pos) + .chain() .in_base_set(ShowEntitiesStage::PostTools), ) .add_system(apply_system_buffers.in_base_set(ShowEntitiesStage::ToolsFlush)) @@ -463,7 +560,8 @@ impl Default for ShowEntitiesSchedule { (paint_lines, paint_points) .chain() .in_base_set(ShowEntitiesStage::Paint), - ); + ) + .add_system((paint_selection).in_base_set(ShowEntitiesStage::PostPaint)); Self(schedule) } } @@ -474,13 +572,18 @@ fn central_panel(world: &mut World, mut schedule: Local) { .frame(egui::Frame::none()) .show(&ctx, |ui| { let sense = match *world.resource::() { - Tool::Move => Sense::drag(), - Tool::Select | Tool::AddPoint | Tool::AddLine | Tool::AddRelation => Sense::click(), + Tool::Select | Tool::Move => Sense::drag(), + Tool::AddPoint | Tool::AddLine | Tool::AddRelation => Sense::click(), }; + info!("render"); let (response, painter) = ui.allocate_painter(ui.available_size(), sense); + let viewport = world.get_resource::().unwrap().clone(); let to_screen = ToScreen(RectTransform::from_to( - Rect::from_center_size(Pos2::ZERO, response.rect.size() / 2.0), + Rect::from_center_size( + viewport.center, + response.rect.size() * (viewport.scale / 2.0), + ), response.rect, )); world.insert_resource(UiRes { @@ -496,10 +599,10 @@ fn central_panel(world: &mut World, mut schedule: Local) { } #[derive(WorldQuery)] -struct SelectableEntity<'a> { +struct SelectableEntity { id: Entity, - point: Option<&'a geometry::Point>, - line: Option<&'a geometry::Line>, + point: Option<&'static geometry::Point>, + line: Option<&'static geometry::Line>, } fn side_panel_ui( @@ -508,45 +611,51 @@ fn side_panel_ui( tool: Res, ) { let tool = *tool; - ui.vertical(|ui| match tool { - Tool::Select => { - let mut count = 0; - selected.for_each(|sel| { - count += 1; - if sel.point.is_some() { - ui.label(format!("Selected point {}", sel.id.index())); - } else if sel.line.is_some() { - ui.label(format!("Selected line {}", sel.id.index())); + ui.heading("Selection:"); + ScrollArea::vertical().show(ui, |ui| { + match tool { + Tool::Select => { + let mut count = 0; + let mut selection: Vec<_> = selected.iter().collect(); + selection.sort_by(|a, b| a.id.index().cmp(&b.id.index())); + selection.iter().for_each(|sel| { + count += 1; + if sel.point.is_some() { + ui.label(format!("Selected point {}", sel.id.index())); + } else if sel.line.is_some() { + ui.label(format!("Selected line {}", sel.id.index())); + } + }); + if count == 0 { + ui.label("Nothing selected"); } - }); - if count == 0 { - ui.label("Nothing selected"); } - } - Tool::Move => { - let mut count = 0; - selected.for_each(|sel| { - count += 1; - if sel.point.is_some() { - ui.label(format!("Selected point {}", sel.id.index())); - } else if sel.line.is_some() { - ui.label(format!("Selected line {}", sel.id.index())); + Tool::Move => { + let mut count = 0; + selected.for_each(|sel| { + count += 1; + if sel.point.is_some() { + ui.label(format!("Selected point {}", sel.id.index())); + } else if sel.line.is_some() { + ui.label(format!("Selected line {}", sel.id.index())); + } + }); + if count == 0 { + ui.label("Nothing selected"); } - }); - if count == 0 { - ui.label("Nothing selected"); } - } - Tool::AddPoint => { - ui.label("Click to add a point"); - } - Tool::AddLine => { - ui.label("Click to add a line"); - } - Tool::AddRelation => { - ui.label("Click to add a relation"); - } + Tool::AddPoint => { + ui.label("Click to add a point"); + } + Tool::AddLine => { + ui.label("Click to add a line"); + } + Tool::AddRelation => { + ui.label("Click to add a relation"); + } + }; }); + ui.separator(); } fn side_panel( @@ -569,6 +678,12 @@ fn bottom_panel(ctx: Res) { }); } +pub fn init(world: &mut World) { + world.init_resource::(); + world.init_resource::(); + world.init_resource::(); +} + pub fn add_to_schedule(schedule: &mut Schedule) { schedule.add_systems((prepare, toolbar, side_panel, bottom_panel, central_panel).chain()); }