diff --git a/Cargo.lock b/Cargo.lock index 080cd68..7f37377 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,18 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] [[package]] name = "allocator-api2" @@ -8,12 +20,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - [[package]] name = "approx" version = "0.5.1" @@ -30,10 +36,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] -name = "bitflags" -version = "2.13.0" +name = "bytemuck" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" [[package]] name = "byteorder" @@ -47,26 +53,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chacha20" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" -dependencies = [ - "cfg-if", - "cpufeatures", - "rand_core", -] - -[[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -107,7 +93,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79127ed59a85d7687c409e9978547cffb7dc79675355ed22da6b66fd5f6ead01" dependencies = [ - "itertools", + "itertools 0.11.0", "num-traits", ] @@ -117,35 +103,22 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - [[package]] name = "float_next_after" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "geo" -version = "0.30.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4416397671d8997e9a3e7ad99714f4f00a22e9eaa9b966a5985d2194fc9e02e1" +checksum = "f811f663912a69249fa620dcd2a005db7254529da2d8a0b23942e81f47084501" dependencies = [ "earcutr", "float_next_after", "geo-types", "geographiclib-rs", - "i_overlay", "log", "num-traits", "robust 1.1.0", @@ -155,13 +128,12 @@ dependencies = [ [[package]] name = "geo-types" -version = "0.7.19" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94776032c45f950d30a13af6113c2ad5625316c9abfbccee4dd5a6695f8fe0f5" +checksum = "9ff16065e5720f376fbced200a5ae0f47ace85fd70b7e54269790281353b6d61" dependencies = [ "approx", "num-traits", - "rayon", "rstar", "serde", ] @@ -177,16 +149,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", - "r-efi", - "rand_core", - "wasip2", - "wasip3", + "wasi", ] [[package]] @@ -200,21 +169,14 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.5" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ + "ahash", "allocator-api2", - "equivalent", - "foldhash", ] -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - [[package]] name = "heapless" version = "0.8.0" @@ -225,74 +187,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "i_float" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85df3a416829bb955fdc2416c7b73680c8dcea8d731f2c7aa23e1042fe1b8343" -dependencies = [ - "serde", -] - -[[package]] -name = "i_key_sort" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "347c253b4748a1a28baf94c9ce133b6b166f08573157e05afe718812bc599fcd" - -[[package]] -name = "i_overlay" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0542dfef184afdd42174a03dcc0625b6147fb73e1b974b1a08a2a42ac35cee49" -dependencies = [ - "i_float", - "i_key_sort", - "i_shape", - "i_tree", - "rayon", -] - -[[package]] -name = "i_shape" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a38f5a42678726718ff924f6d4a0e79b129776aeed298f71de4ceedbd091bce" -dependencies = [ - "i_float", - "serde", -] - -[[package]] -name = "i_tree" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155181bc97d770181cf9477da51218a19ee92a8e5be642e796661aee2b601139" - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", -] - [[package]] name = "itertools" version = "0.11.0" @@ -303,22 +197,25 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.18" +name = "itertools" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] [[package]] -name = "leb128fmt" -version = "0.1.0" +name = "lazy_static" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libm" @@ -333,10 +230,72 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] -name = "memchr" -version = "2.8.1" +name = "matrixmultiply" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "nalgebra" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d506eb7e08d6329505faa8a3a00a5dcc6de9f76e0c77e4b75763ae3c770831ff" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "rand", + "rand_distr", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-complex" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] [[package]] name = "num-traits" @@ -349,20 +308,28 @@ dependencies = [ ] [[package]] -name = "prettyplease" -version = "0.2.37" +name = "once_cell" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -377,33 +344,56 @@ dependencies = [ ] [[package]] -name = "r-efi" -version = "6.0.0" +name = "rand" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] [[package]] -name = "rand" -version = "0.10.1" +name = "rand_chacha" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ - "chacha20", - "getrandom", + "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" -version = "0.10.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.12.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -411,9 +401,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.13.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -443,52 +433,45 @@ dependencies = [ ] [[package]] -name = "semver" -version = "1.0.28" +name = "safe_arch" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" dependencies = [ - "serde_core", - "serde_derive", + "bytemuck", ] [[package]] -name = "serde_core" -version = "1.0.228" +name = "serde" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.55", ] [[package]] -name = "serde_json" -version = "1.0.150" +name = "simba" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "f0b7840f121a46d63066ee7a99fc81dcabbc6105e437cae43528cea199b5a05f" dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", + "approx", + "num-complex", + "num-traits", + "paste", + "wide", ] [[package]] @@ -499,11 +482,11 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "spade" -version = "2.14.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a14e31a007e9f85c32784b04f89e6e194bb252a4d41b4a8ccd9e77245d901c8c" +checksum = "61addf9117b11d1f5b4bf6fe94242ba25f59d2d4b2080544b771bd647024fd00" dependencies = [ - "hashbrown 0.15.5", + "hashbrown", "num-traits", "robust 1.1.0", "smallvec", @@ -516,16 +499,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] -name = "syn" -version = "2.0.117" +name = "statrs" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "2d08e5e1748192713cc281da8b16924fb46be7b0c2431854eadc785823e5696e" +dependencies = [ + "approx", + "lazy_static", + "nalgebra", + "num-traits", + "rand", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -533,169 +546,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] -name = "unicode-xid" -version = "0.2.6" +name = "version_check" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] -name = "wasip2" -version = "1.0.3+wasi-0.2.9" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wide" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89beec544f246e679fc25490e3f8e08003bc4bf612068f325120dad4cea02c1c" dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", + "bytemuck", + "safe_arch", ] [[package]] name = "xenobalanus" -version = "0.1.4" +version = "0.1.3" dependencies = [ "delaunator", "geo", + "geo-types", + "itertools 0.12.1", "rand", "rayon", + "statrs", ] [[package]] -name = "zmij" -version = "1.0.21" +name = "zerocopy" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.55", +] diff --git a/Cargo.toml b/Cargo.toml index f89cbbb..861b613 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,20 +1,16 @@ [package] name = "xenobalanus" -version = "0.2.0" +version = "0.1.3" edition = "2021" -authors = ["Tobias Raayoni Last "] -repository = "https://github.com/randogoth/xenobalanus" -readme = "readme.md" -license = "MIT" -license-file = "LICENSE" -keywords = ["dtscan", "dbscan", "delfin", "cluster", "void", "delaunay", "astronomy", "attractor"] -categories = ["algorithms", "science", "mathematics"] [lib] name = "xenobalanus" [dependencies] delaunator = "1.0.2" -geo = "0.30" -rand = "0.10" -rayon = "1.12" +geo = "0.28.0" +geo-types = "0.7.13" +itertools = "0.12.1" +rand = "0.8.5" +rayon = "1.9.0" +statrs = "0.16.0" diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 4763a89..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Tobias Raayoni Last - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/devbox.json b/devbox.json deleted file mode 100644 index c8f865f..0000000 --- a/devbox.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.16.0/.schema/devbox.schema.json", - "packages": ["rustup@latest"], - "shell": { - "init_hook": [ - "echo 'Welcome to devbox!' > /dev/null" - ], - "scripts": { - "test": [ - "echo \"Error: no test specified\" && exit 1" - ] - } - } -} diff --git a/devbox.lock b/devbox.lock deleted file mode 100644 index 818bec3..0000000 --- a/devbox.lock +++ /dev/null @@ -1,58 +0,0 @@ -{ - "lockfile_version": "1", - "packages": { - "github:NixOS/nixpkgs/nixpkgs-unstable": { - "last_modified": "2026-05-29T05:01:12Z", - "resolved": "github:NixOS/nixpkgs/e9a7635a57597d9754eccebdfc7045e6c8600e6b?lastModified=1780030872&narHash=sha256-u6WU%2Fyd%2Fo8iYQrHX3RAwO1hYa3LkoSL%2BWNQD0rJfJZQ%3D" - }, - "rustup@latest": { - "last_modified": "2026-05-21T08:15:18Z", - "plugin_version": "0.0.1", - "resolved": "github:NixOS/nixpkgs/4a29d733e8a7d5b824c3d8c958a946a9867b3eb2#rustup", - "source": "devbox-search", - "version": "1.29.0", - "systems": { - "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/nl70ay30nl2szm2g03xa0d7ai69dgbln-rustup-1.29.0", - "default": true - } - ], - "store_path": "/nix/store/nl70ay30nl2szm2g03xa0d7ai69dgbln-rustup-1.29.0" - }, - "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/684175m8xqfisacqw9rdc97fn2vpp0fj-rustup-1.29.0", - "default": true - } - ], - "store_path": "/nix/store/684175m8xqfisacqw9rdc97fn2vpp0fj-rustup-1.29.0" - }, - "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/4hqncs7jnqc64z49r1w09b235074li9q-rustup-1.29.0", - "default": true - } - ], - "store_path": "/nix/store/4hqncs7jnqc64z49r1w09b235074li9q-rustup-1.29.0" - }, - "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/dpqx1vygrisrvnnwgym1226x317vqp4a-rustup-1.29.0", - "default": true - } - ], - "store_path": "/nix/store/dpqx1vygrisrvnnwgym1226x317vqp4a-rustup-1.29.0" - } - } - } - } -} diff --git a/readme.md b/readme.md index 2261de9..dae142b 100644 --- a/readme.md +++ b/readme.md @@ -44,9 +44,7 @@ fn main() { println!("Generated Delaunay triangulation"); // Pre-process triangles - // 1 - attractors, 2 - voids, 0 - both - // true/false - parallel processing - xeno.preprocess(0, false); + xeno.preprocess(0); // Execute delfin function with the generated GeometryData let min_area: f32 = 1000.0; // threshold for voidness diff --git a/src/lib.rs b/src/lib.rs index d9b71a1..fd76c73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,30 +1,6 @@ -/* -MIT License - -Copyright (c) 2024 Tobias Raayoni Last - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - use delaunator::{triangulate, Point as DelaunatorPoint}; use geo::{Point as GeoPoint, Coord}; -use rand::RngExt; +use rand::Rng; use rayon::prelude::*; use std::cmp::{min, max}; use std::collections::{HashMap, HashSet}; @@ -64,12 +40,6 @@ impl Point { bearing } - pub fn bearing_rad(&self, point: Point) -> f32 { - let delta_x = point.x - self.x; - let delta_y = point.y - self.y; - delta_y.atan2(delta_x) - } - } impl From for Coord { @@ -113,6 +83,7 @@ pub struct GeometryData { pub edge_to_triangles: HashMap>, // Maps an edge to triangle indices pub edge_lengths: HashMap, // Edge lengths pub vertex_connections: HashMap>, // Direct connections between vertices, for DTSCAN + pub triangle_blacklist: HashSet, // invalidated triangles } impl GeometryData { @@ -121,7 +92,8 @@ impl GeometryData { triangles: Vec::new(), edge_to_triangles: HashMap::new(), edge_lengths: HashMap::new(), - vertex_connections: HashMap::new(), // Adjusted for DTSCAN + vertex_connections: HashMap::new(), + triangle_blacklist: HashSet::new(), } } fn add_triangle(&mut self, index: usize, points: &[Point], tri_idx: &[usize], types: usize) { @@ -189,6 +161,10 @@ impl GeometryData { }; } } + + pub fn remove_triangle(&mut self, triangle_index: usize) { + self.triangle_blacklist.insert(triangle_index); + } } @@ -227,26 +203,53 @@ impl Xenobalanus { self.points = points } - pub fn triangle(&self, index: usize) -> TriangleData { - self.geometry_data.triangles[index].clone() + pub fn triangle(&self, index: usize) -> Option<&TriangleData> { + if !self.geometry_data.triangle_blacklist.contains(&index) { + Some(&self.geometry_data.triangles[index]) + } else { + None + } } - pub fn triangle_data(&self) -> &Vec { - &self.geometry_data.triangles + pub fn triangle_data(&self) -> Vec { + self.geometry_data.triangles.iter() + .enumerate() + .filter_map(|(index, triangle)| { + if self.geometry_data.triangle_blacklist.contains(&index) { + None // Skip blacklisted triangles + } else { + Some(triangle.clone()) // Include this triangle + } + }) + .collect() + } + + pub fn triangle_area(&self, index: usize) -> f32 { + self.triangle(index).map_or(0.0, |tri| tri.area.unwrap_or_default()) } pub fn triangles_flat(&self) -> Vec { - self.triangulation.clone() + let mut filtered_vertices = Vec::new(); + for (i, chunk) in self.triangulation.chunks(3).enumerate() { + // Skip this triangle if it's blacklisted + if self.geometry_data.triangle_blacklist.contains(&i) { + continue; + } + filtered_vertices.extend_from_slice(chunk); + } + filtered_vertices } pub fn triangle_vertices(&self) -> Vec> { - self.triangulation.chunks(3).map(|chunk| { + let triangulation = self.triangles_flat(); + triangulation.chunks(3).map(|chunk| { chunk.iter().map(|&index| index).collect() }).collect() } pub fn triangle_coordinates(&self) -> Vec> { - self.triangulation.chunks(3).map(|chunk| { + let triangulation = self.triangles_flat(); + triangulation.chunks(3).map(|chunk| { chunk.iter().map(|&index| { let point = &self.points[index]; (point.x, point.y) // Each point is represented by a Vec of its coordinates @@ -258,6 +261,45 @@ impl Xenobalanus { self.triangulation = vertices } + /// Helper function to calculate the sign of an area defined by three points. + fn sign(p1: &Point, p2: &Point, p3: &Point) -> f32 { + (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y) + } + + /// Determines if a point is inside a triangle. + fn point_in_triangle(pt: &Point, v1: &Point, v2: &Point, v3: &Point) -> bool { + let d1 = Self::sign(pt, v1, v2); + let d2 = Self::sign(pt, v2, v3); + let d3 = Self::sign(pt, v3, v1); + + let has_neg = d1 < 0.0 || d2 < 0.0 || d3 < 0.0; + let has_pos = d1 > 0.0 || d2 > 0.0 || d3 > 0.0; + + !(has_neg && has_pos) // True if the point is inside the triangle + } + + /// Finds the triangle that contains the given point. + pub fn find_containing_triangle(&self, new_point: &Point) -> Option { + for (index, triangle) in self.geometry_data.triangles.iter().enumerate() { + // Skip if the triangle is blacklisted + if self.geometry_data.triangle_blacklist.contains(&index) { + continue; + } + + // Retrieve the vertices of the triangle + let v1 = &self.points[triangle.vertices[0]]; + let v2 = &self.points[triangle.vertices[1]]; + let v3 = &self.points[triangle.vertices[2]]; + + // Check if the new_point is inside the current triangle + if Self::point_in_triangle(new_point, v1, v2, v3) { + return Some(index); // Return the index of the containing triangle + } + } + + None // Return None if no containing triangle is found + } + // Additional methods moved into GeometryProcessor, operating on self.geometry_data pub fn random_points(&mut self, center: (f32, f32), side_length: f32, num_points: u32) { // generate random points in a square @@ -265,10 +307,10 @@ impl Xenobalanus { let max_x = center.0 + side_length / 2.0; let min_y = center.1 - side_length / 2.0; let max_y = center.1 + side_length / 2.0; - let mut rng = rand::rng(); + let mut rng: rand::prelude::ThreadRng = rand::thread_rng(); for _ in 0..num_points { - let x = min_x + rng.random_range(0.0..=1.0) as f32 * ( max_x - min_x); - let y: f32 = min_y + rng.random_range(0.0..=1.0) as f32 * ( max_y - min_y); + let x = min_x + rng.gen_range(0.0..=1.0) as f32 * ( max_x - min_x); + let y: f32 = min_y + rng.gen_range(0.0..=1.0) as f32 * ( max_y - min_y); self.points.push(Point {x, y}); } } @@ -277,38 +319,33 @@ impl Xenobalanus { &self.geometry_data.edge_lengths } - pub fn delaunay(&mut self) { - // Convert geo::Point to delaunator::Point for triangulation - let delaunator_points: Vec = self.points.iter() + fn triangulate(&self, points: &Vec) -> Vec { + + let delaunator_points: Vec = points.iter() .map(|point: &Point| DelaunatorPoint { x: point.x as f64, y: point.y as f64 }) .collect(); // Perform Delaunay triangulation let result: delaunator::Triangulation = triangulate(&delaunator_points); - self.triangulation = result.triangles + result.triangles } - pub fn preprocess(&mut self, types: usize, parallel: bool) { - if parallel { + pub fn delaunay(&mut self) { + self.triangulation = self.triangulate(&self.points); + } - let geometry_data = Arc::new(Mutex::new(GeometryData::new())); - - self.triangulation.par_chunks(3).enumerate().for_each(|(index, tri_idx)| { - let gd = geometry_data.clone(); // Clone Arc for use in each thread, not the data itself - - // Perform locked update - let mut gd_lock = gd.lock().unwrap(); - gd_lock.add_triangle(index, &self.points, tri_idx, types); - }); - - self.geometry_data = Arc::try_unwrap(geometry_data).unwrap().into_inner().unwrap(); - } else { - - self.triangulation.chunks(3).enumerate().for_each(|(index, tri_idx)| { - self.geometry_data.add_triangle(index, &self.points, tri_idx, types); - }); - - } + pub fn preprocess(&mut self, types: usize) { + let geometry_data = Arc::new(Mutex::new(GeometryData::new())); + + self.triangulation.par_chunks(3).enumerate().for_each(|(index, tri_idx)| { + let gd = geometry_data.clone(); // Clone Arc for use in each thread, not the data itself + + // Perform locked update + let mut gd_lock = gd.lock().unwrap(); + gd_lock.add_triangle(index, &self.points, tri_idx, types); + }); + + self.geometry_data = Arc::try_unwrap(geometry_data).unwrap().into_inner().unwrap(); } pub fn delfin( @@ -320,7 +357,7 @@ impl Xenobalanus { let mut processed_triangles: HashSet = HashSet::new(); // Create a sorted list of triangles by their terminal edge length that meet the minimum distance criteria. - let mut triangles_sorted: Vec<(usize, f32)> = self.geometry_data.triangles.iter() + let mut triangles_sorted: Vec<(usize, f32)> = self.triangle_data().iter() .filter_map(|t| t.terminal_edge.and_then(|e| self.geometry_data.edge_lengths.get(&e).map(|&l| (t.index, l)))) .filter(|&(_, length)| length >= min_distance) .collect(); @@ -344,7 +381,7 @@ impl Xenobalanus { processed_triangles.insert(triangle_index); // Get all edges of the current triangle - if let Some(edges) = self.geometry_data.triangles.get(triangle_index).map(|t| t.get_edges()) { + if let Some(edges) = self.triangle(triangle_index).map(|t| t.get_edges()) { for edge in edges { // Add all edges to check for neighbors to expand @@ -367,8 +404,13 @@ impl Xenobalanus { continue; } + // Skip if triangle is blacklisted + if self.geometry_data.triangle_blacklist.contains(&neighbor_index) { + continue; + } + // Get neighbor triangle - if let Some(neighbor_triangle) = self.geometry_data.triangles.get(neighbor_index) { + if let Some(neighbor_triangle) = self.triangle(neighbor_index) { // Get neighbor triangle's terminal edge if let Some(neighbor_edge) = neighbor_triangle.terminal_edge { @@ -397,7 +439,7 @@ impl Xenobalanus { // Retain only those sets that meet the minimum area criteria void_polygons.retain(|set| { set.iter() - .filter_map(|&i| self.geometry_data.triangles[i].area) + .filter_map(|&i| Some(self.triangle_area(i))) .sum::() >= min_area }); @@ -454,142 +496,4 @@ impl Xenobalanus { clusters } -} - -#[cfg(test)] -mod tests { - use super::*; - - // --- Point --- - - #[test] - fn point_distance_3_4_5() { - let a = Point::new(0.0, 0.0); - let b = Point::new(3.0, 4.0); - assert!((a.distance(b) - 5.0).abs() < 1e-5); - } - - #[test] - fn point_bearing_east_is_zero() { - let origin = Point::new(0.0, 0.0); - let east = Point::new(1.0, 0.0); - // delta_y=0, delta_x=1 → atan2(0,1)=0° → bearing=0° - assert!((origin.bearing(east) - 0.0).abs() < 1e-4); - } - - // --- random_points --- - - #[test] - fn random_points_count_and_bounds() { - let mut xb = Xenobalanus::new(); - xb.random_points((0.0, 0.0), 10.0, 100); - let pts = xb.points(); - assert_eq!(pts.len(), 100); - for (x, y) in pts { - assert!(x >= -5.0 && x <= 5.0, "x={x} out of [-5, 5]"); - assert!(y >= -5.0 && y <= 5.0, "y={y} out of [-5, 5]"); - } - } - - // --- delaunay --- - - fn unit_square() -> Vec { - vec![ - Point::new(0.0, 0.0), - Point::new(1.0, 0.0), - Point::new(1.0, 1.0), - Point::new(0.0, 1.0), - ] - } - - #[test] - fn delaunay_four_points_two_triangles() { - let mut xb = Xenobalanus::new(); - xb.set_points(unit_square()); - xb.delaunay(); - // 4 convex points → 2 triangles → 6 indices - assert_eq!(xb.triangles_flat().len(), 6); - } - - // --- preprocess --- - - #[test] - fn preprocess_sequential_builds_edges() { - let mut xb = Xenobalanus::new(); - xb.set_points(unit_square()); - xb.delaunay(); - xb.preprocess(0, false); - // unit square Delaunay: 4 boundary edges + 1 diagonal = 5 - assert_eq!(xb.edge_lengths().len(), 5); - } - - #[test] - fn preprocess_parallel_matches_sequential() { - let mut xb_seq = Xenobalanus::new(); - xb_seq.set_points(unit_square()); - xb_seq.delaunay(); - xb_seq.preprocess(0, false); - - let mut xb_par = Xenobalanus::new(); - xb_par.set_points(unit_square()); - xb_par.delaunay(); - xb_par.preprocess(0, true); - - assert_eq!(xb_seq.edge_lengths().len(), xb_par.edge_lengths().len()); - } - - // --- dtscan --- - - #[test] - fn dtscan_finds_cluster_in_grid() { - let mut xb = Xenobalanus::new(); - // 3×3 grid with spacing 1.0; diagonal ≈ 1.414 - let pts: Vec = (0..3) - .flat_map(|i| (0..3).map(move |j| Point::new(i as f32, j as f32))) - .collect(); - xb.set_points(pts); - xb.delaunay(); - xb.preprocess(0, false); - // max_closeness=1.5 covers all edges (max diagonal ≈ 1.414) - let clusters = xb.dtscan(2, 1.5); - assert!(!clusters.is_empty()); - } - - // --- delfin --- - - #[test] - fn delfin_smoke_test() { - let mut xb = Xenobalanus::new(); - xb.random_points((0.0, 0.0), 100.0, 200); - xb.delaunay(); - xb.preprocess(2, false); - // just verify it runs without panic - let _voids = xb.delfin(0.0, 0.0); - } - - // --- readme example --- - - #[test] - fn readme_example_pipeline() { - // Mirrors the workflow shown in the README exactly. - let dots: u32 = 10000; - let side_length: f32 = 10000.0; - let mut xeno = Xenobalanus::new(); - xeno.random_points((0.0, 0.0), side_length, dots); - - xeno.delaunay(); - xeno.preprocess(0, false); - - let min_area: f32 = 1000.0; - let min_distance: f32 = 200.0; - let void_polygons: Vec> = xeno.delfin(min_area, min_distance); - - let min_pts: usize = 5; - let max_closeness: f32 = 100.5; - let clusters: Vec> = xeno.dtscan(min_pts, max_closeness); - - // With 10 000 uniform random points both algorithms should find results. - assert!(!void_polygons.is_empty(), "delfin found no voids"); - assert!(!clusters.is_empty(), "dtscan found no clusters"); - } } \ No newline at end of file