diff --git a/experiments/2025-03-18/.gitignore b/experiments/2025-03-18/.gitignore new file mode 100644 index 000000000..3d7ba23b0 --- /dev/null +++ b/experiments/2025-03-18/.gitignore @@ -0,0 +1,5 @@ +# Cargo +/target + +# Fornjot +/*.3mf diff --git a/experiments/2025-03-18/.vscode/settings.json b/experiments/2025-03-18/.vscode/settings.json new file mode 100644 index 000000000..8a43eb89a --- /dev/null +++ b/experiments/2025-03-18/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.formatOnSave": true, + "rust-analyzer.check.command": "clippy", + "nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix" +} \ No newline at end of file diff --git a/experiments/2025-03-18/Cargo.lock b/experiments/2025-03-18/Cargo.lock new file mode 100644 index 000000000..c6a91b37d --- /dev/null +++ b/experiments/2025-03-18/Cargo.lock @@ -0,0 +1,2932 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ab_glyph" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags 2.9.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys 0.6.0+11769913", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +dependencies = [ + "serde", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytemuck" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.9.0", + "log", + "polling", + "rustix", + "slab", + "thiserror 1.0.69", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cc" +version = "1.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cosmic-text" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2" +dependencies = [ + "bitflags 2.9.0", + "fontdb", + "log", + "rangemap", + "rayon", + "rustc-hash 1.1.0", + "rustybuzz", + "self_cell", + "swash", + "sys-locale", + "ttf-parser 0.21.1", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "document-features" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "etagere" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "euclid" +version = "0.22.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fj" +version = "0.0.0" +dependencies = [ + "anyhow", + "bytemuck", + "geo", + "glam", + "glyphon", + "iter_fixed", + "itertools", + "pollster", + "spade", + "threemf", + "wgpu", + "winit", +] + +[[package]] +name = "flate2" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[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 = "font-types" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3971f9a5ca983419cdc386941ba3b9e1feba01a0ab888adf78739feb2798492" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "fontconfig-parser" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7" +dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser 0.20.0", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "geo" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34f0e6e028c581e82e6822a68869514e94c25e7f8ea669a2d8595bdf7461ccc5" +dependencies = [ + "float_next_after", + "geo-types", + "geographiclib-rs", + "i_overlay", + "log", + "num-traits", + "robust", + "rstar", +] + +[[package]] +name = "geo-types" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd1157f0f936bf0cd68dec91e8f7c311afe60295574d62b70d4861a1bfdf2d9" +dependencies = [ + "approx", + "num-traits", + "rstar", + "serde", +] + +[[package]] +name = "geographiclib-rs" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e5ed84f8089c70234b0a8e0aedb6dc733671612ddc0d37c6066052f9781960" +dependencies = [ + "libm", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glam" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17fcdf9683c406c2fc4d124afd29c0d595e22210d633cbdb8695ba9935ab1dc6" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "glow" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "glyphon" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc83cc30ed66a0a9e77b7974c91360a13cad5e7ab65aa51e299c383749a018ab" +dependencies = [ + "cosmic-text", + "etagere", + "lru", + "rustc-hash 2.1.1", + "wgpu", +] + +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.9.0", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "gpu-allocator" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +dependencies = [ + "log", + "presser", + "thiserror 1.0.69", + "windows", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" +dependencies = [ + "bitflags 2.9.0", + "gpu-descriptor-types", + "hashbrown", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "i_float" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775f9961a8d2f879725da8aff789bb20a3ddf297473e0c90af75e69313919490" +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 = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01882ce5ed786bf6e8f5167f171a4026cd129ce17d9ff5cbf1e6749b98628ece" +dependencies = [ + "i_float", + "i_key_sort", + "i_shape", + "i_tree", +] + +[[package]] +name = "i_shape" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27dbe9e5238d6b9c694c08415bf00fb370b089949bd818ab01f41f8927b8774c" +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 = "indexmap" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "iter_fixed" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "592ff74cdc6a923b2ae357dad7db2f5dcbf5d4ace9afcf3ab7c7f20b209fd949" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.9.0", + "libc", + "redox_syscall 0.5.10", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + +[[package]] +name = "log" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + +[[package]] +name = "metal" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" +dependencies = [ + "bitflags 2.9.0", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", + "paste", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +dependencies = [ + "adler2", +] + +[[package]] +name = "naga" +version = "24.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e380993072e52eef724eddfcde0ed013b0c023c3f0417336ed041aa9f076994e" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags 2.9.0", + "cfg_aliases", + "codespan-reporting", + "hexf-parse", + "indexmap", + "log", + "rustc-hash 1.1.0", + "spirv", + "strum", + "termcolor", + "thiserror 2.0.12", + "unicode-xid", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.9.0", + "jni-sys", + "log", + "ndk-sys 0.6.0+11769913", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.9.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.9.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.9.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.9.0", + "block2", + "dispatch", + "libc", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.9.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.9.0", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.9.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.9.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" + +[[package]] +name = "orbclient" +version = "0.3.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43" +dependencies = [ + "libredox", +] + +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +dependencies = [ + "ttf-parser 0.25.1", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.10", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "polling" +version = "3.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" + +[[package]] +name = "quick-xml" +version = "0.37.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "range-alloc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" + +[[package]] +name = "rangemap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "read-fonts" +version = "0.22.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69aacb76b5c29acfb7f90155d39759a29496aebb49395830e928a9703d2eec2f" +dependencies = [ + "bytemuck", + "font-types", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "robust" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30" + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rstar" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421400d13ccfd26dfa5858199c30a5d76f9c54e0dba7575273025b43c5175dbb" +dependencies = [ + "heapless", + "num-traits", + "smallvec", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "rustybuzz" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +dependencies = [ + "bitflags 2.9.0", + "bytemuck", + "libm", + "smallvec", + "ttf-parser 0.21.1", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sctk-adwaita" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] +name = "self_cell" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "skrifa" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1c44ad1f6c5bdd4eefed8326711b7dbda9ea45dfd36068c427d332aa382cbe" +dependencies = [ + "bytemuck", + "read-fonts", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" + +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.9.0", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix", + "thiserror 1.0.69", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "spade" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ece03ff43cd2a9b57ebf776ea5e78bd30b3b4185a619f041079f4109f385034" +dependencies = [ + "hashbrown", + "num-traits", + "robust", + "smallvec", +] + +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "svg_fmt" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce5d813d71d82c4cbc1742135004e4a79fd870214c155443451c139c9470a0aa" + +[[package]] +name = "swash" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd59f3f359ddd2c95af4758c18270eddd9c730dde98598023cdabff472c2ca2" +dependencies = [ + "skrifa", + "yazi", + "zeno", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "threemf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d946000a45b7cd15c3e64d781b2ea4270f30f82c2712de3267195d4fb24475" +dependencies = [ + "quick-xml", + "serde", + "thiserror 2.0.12", + "zip", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" + +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" + +[[package]] +name = "unicode-ccc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "unicode-script" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wayland-backend" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" +dependencies = [ + "bitflags 2.9.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.9.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d" +dependencies = [ + "rustix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" +dependencies = [ + "bitflags 2.9.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3" +dependencies = [ + "bitflags 2.9.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" +dependencies = [ + "bitflags 2.9.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wgpu" +version = "24.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47f55718f85c2fa756edffa0e7f0e0a60aba463d1362b57e23123c58f035e4b6" +dependencies = [ + "arrayvec", + "bitflags 2.9.0", + "cfg_aliases", + "document-features", + "js-sys", + "log", + "naga", + "parking_lot", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "24.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671c25545d479b47d3f0a8e373aceb2060b67c6eb841b24ac8c32348151c7a0c" +dependencies = [ + "arrayvec", + "bit-vec", + "bitflags 2.9.0", + "cfg_aliases", + "document-features", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 2.0.12", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "24.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4317a17171dc20e6577bf606796794580accae0716a69edbc7388c86a3ec9f23" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.9.0", + "block", + "bytemuck", + "cfg_aliases", + "core-graphics-types", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "js-sys", + "khronos-egl", + "libc", + "libloading", + "log", + "metal", + "naga", + "ndk-sys 0.5.0+25.2.9519653", + "objc", + "once_cell", + "ordered-float", + "parking_lot", + "profiling", + "range-alloc", + "raw-window-handle", + "renderdoc-sys", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 2.0.12", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "windows", + "windows-core", +] + +[[package]] +name = "wgpu-types" +version = "24.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" +dependencies = [ + "bitflags 2.9.0", + "js-sys", + "log", + "web-sys", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winit" +version = "0.30.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0" +dependencies = [ + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.9.0", + "block2", + "bytemuck", + "calloop", + "cfg_aliases", + "concurrent-queue", + "core-foundation", + "core-graphics", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "memmap2", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "orbclient", + "percent-encoding", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "tracing", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.52.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +dependencies = [ + "memchr", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading", + "once_cell", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + +[[package]] +name = "xcursor" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.9.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "xml-rs" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" + +[[package]] +name = "yazi" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" + +[[package]] +name = "zeno" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938cc23ac49778ac8340e366ddc422b2227ea176edb447e23fc0627608dddadd" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "flate2", + "indexmap", + "memchr", + "thiserror 2.0.12", + "zopfli", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", +] diff --git a/experiments/2025-03-18/Cargo.toml b/experiments/2025-03-18/Cargo.toml new file mode 100644 index 000000000..bb544eb6d --- /dev/null +++ b/experiments/2025-03-18/Cargo.toml @@ -0,0 +1,28 @@ +[workspace] + +[package] +name = "fj" +edition = "2024" + +[dependencies] +anyhow = "*" +glyphon = "*" +iter_fixed = "*" +itertools = "*" +pollster = "*" +spade = "*" +threemf = "*" +wgpu = "*" +winit = "*" + +[dependencies.bytemuck] +version = "*" +features = ["derive"] + +[dependencies.geo] +version = "*" +default-features = false + +[dependencies.glam] +version = "*" +features = ["bytemuck"] diff --git a/experiments/2025-03-18/README.md b/experiments/2025-03-18/README.md new file mode 100644 index 000000000..1fe7d0a44 --- /dev/null +++ b/experiments/2025-03-18/README.md @@ -0,0 +1,44 @@ +# Fornjot - Experiment 2025-03-18 + +## About + +This experiment is packaged as a single application. Run it with `cargo run`. +This should open a window and also create a 3MF file in this directory. + +## Context + +It has become clear, that Fornjot's current architecture is at a local maximum. +I also think it is too complicated for what it does, and suspect that a simpler +architecture would serve us much better going forward. + +While it's certainly not impossible to address this piecemeal, through +incremental improvements (which is the approach that I usually prefer), I don't +think this is the best course of action. + +Because while I don't consider the architecture to be very good, it is still +consistent and self-reinforcing. Whenever I try to simplify one aspect, I run +into the problem that it's there for a reason; that other aspects of the +architecture depend on it being the way it is. + +And while I haven't figured out yet, how to break out of this situation, I do +have quite a few unproven ideas on how an improved architecture would look like, +redesigned from the ground up using the experience I've gained over the last few +years. + +This experiment is the third in a series meant to prove out those ideas. The +results should provide a clearer picture of what is possible, and how the +current architecture can be evolved. + +## Setup + +This experiment builds on [the second one](../2024-12-09/). There are two +objectives: + +- Simplify the `Object` trait by removing as much functionality as possible. + Compensate for the loss of insight into an object's structure by experimenting + with other means of providing required debug information. +- Expand the existing b-rep primitives, adding support for curved surfaces. + +## Result + +The experiment is still ongoing. diff --git a/experiments/2025-03-18/shell.nix b/experiments/2025-03-18/shell.nix new file mode 100644 index 000000000..2fb5abdf2 --- /dev/null +++ b/experiments/2025-03-18/shell.nix @@ -0,0 +1,12 @@ +{ pkgs ? import { } }: + +let + libPath = with pkgs; lib.makeLibraryPath [ + libxkbcommon + vulkan-loader + wayland + ]; +in +pkgs.mkShell { + LD_LIBRARY_PATH = "${libPath}"; +} diff --git a/experiments/2025-03-18/src/app.rs b/experiments/2025-03-18/src/app.rs new file mode 100644 index 000000000..918d70fa8 --- /dev/null +++ b/experiments/2025-03-18/src/app.rs @@ -0,0 +1,134 @@ +use std::{collections::BTreeSet, sync::Arc}; + +use winit::{ + application::ApplicationHandler, + event::{ElementState, KeyEvent, WindowEvent}, + event_loop::{ActiveEventLoop, EventLoop}, + keyboard::{Key, NamedKey}, + window::{Window, WindowAttributes, WindowId}, +}; + +use crate::{object::HandleAny, render::Renderer, view::OperationView}; + +pub fn run(shape: HandleAny) -> anyhow::Result<()> { + let view = OperationView::new(shape); + + let event_loop = EventLoop::new()?; + + let mut app = App { + view, + window: None, + renderer: None, + pressed_keys: BTreeSet::new(), + }; + event_loop.run_app(&mut app)?; + + Ok(()) +} + +struct App { + view: OperationView, + window: Option>, + renderer: Option, + pressed_keys: BTreeSet, +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let (window, renderer) = match init(event_loop) { + Ok(ok) => ok, + Err(err) => { + eprintln!("Initialization error: `{err:?}`"); + event_loop.exit(); + return; + } + }; + + self.window = Some(window); + self.renderer = Some(renderer); + } + + fn window_event( + &mut self, + event_loop: &ActiveEventLoop, + _: WindowId, + event: WindowEvent, + ) { + let (Some(window), Some(renderer)) = + (self.window.as_ref(), self.renderer.as_mut()) + else { + return; + }; + + match event { + WindowEvent::CloseRequested => { + event_loop.exit(); + } + WindowEvent::KeyboardInput { + event: + KeyEvent { + logical_key: Key::Named(NamedKey::Escape), + .. + }, + .. + } => { + event_loop.exit(); + } + WindowEvent::KeyboardInput { + event: + KeyEvent { + logical_key, state, .. + }, + .. + } => { + match state { + ElementState::Pressed => { + if self.pressed_keys.contains(&logical_key) { + return; + } + } + ElementState::Released => { + self.pressed_keys.remove(&logical_key); + return; + } + } + + match logical_key { + Key::Named(NamedKey::ArrowRight) => { + self.view.selected_mut().select_last(); + } + Key::Named(NamedKey::ArrowLeft) => { + self.view.parent_of_selected_mut().select_none(); + } + Key::Named(NamedKey::ArrowDown) => { + self.view.parent_of_selected_mut().select_next(); + } + Key::Named(NamedKey::ArrowUp) => { + self.view.parent_of_selected_mut().select_previous(); + } + _ => {} + } + + window.request_redraw(); + } + WindowEvent::RedrawRequested => { + if let Err(err) = renderer.render(&self.view) { + eprintln!("Render error: {err}"); + } + } + _ => {} + } + } +} + +fn init( + event_loop: &ActiveEventLoop, +) -> anyhow::Result<(Arc, Renderer)> { + let window = { + let window = event_loop.create_window(WindowAttributes::default())?; + Arc::new(window) + }; + let renderer = pollster::block_on(Renderer::new(window.clone()))?; + + Ok((window, renderer)) +} diff --git a/experiments/2025-03-18/src/export.rs b/experiments/2025-03-18/src/export.rs new file mode 100644 index 000000000..04fa23aa2 --- /dev/null +++ b/experiments/2025-03-18/src/export.rs @@ -0,0 +1,45 @@ +use std::{collections::BTreeMap, fs::File}; + +use crate::object::Object; + +pub fn export(op: &dyn Object) -> anyhow::Result<()> { + let tri_mesh = op.tri_mesh(); + + let mut indices_by_vertex = BTreeMap::new(); + + let mut points = Vec::new(); + let mut triangles = Vec::new(); + + for triangle in tri_mesh.external_triangles() { + let triangle = triangle.points.map(|point| { + *indices_by_vertex.entry(point).or_insert_with(|| { + let index = points.len(); + points.push(point); + index + }) + }); + + triangles.push(triangle); + } + + let mesh = threemf::Mesh { + vertices: threemf::model::Vertices { + vertex: points + .into_iter() + .map(|point| point.coords.components.map(|coord| coord.value())) + .map(|[x, y, z]| threemf::model::Vertex { x, y, z }) + .collect(), + }, + triangles: threemf::model::Triangles { + triangle: triangles + .into_iter() + .map(|[v1, v2, v3]| threemf::model::Triangle { v1, v2, v3 }) + .collect(), + }, + }; + + let output = File::create("output.3mf")?; + threemf::write(output, mesh)?; + + Ok(()) +} diff --git a/experiments/2025-03-18/src/extra/mod.rs b/experiments/2025-03-18/src/extra/mod.rs new file mode 100644 index 000000000..27a91256e --- /dev/null +++ b/experiments/2025-03-18/src/extra/mod.rs @@ -0,0 +1 @@ +pub mod triangulate; diff --git a/experiments/2025-03-18/src/extra/triangulate.rs b/experiments/2025-03-18/src/extra/triangulate.rs new file mode 100644 index 000000000..1484a9c58 --- /dev/null +++ b/experiments/2025-03-18/src/extra/triangulate.rs @@ -0,0 +1,146 @@ +use std::{ + collections::{BTreeSet, VecDeque}, + mem, +}; + +use geo::{Contains, Coord, LineString, Polygon}; +use spade::Triangulation; + +use crate::{ + geometry::{MeshTriangle, TriMesh, Triangle}, + math::Point, + topology::face::Face, +}; + +pub fn triangulate(face: &Face) -> TriMesh { + let points = points(face); + let triangles = triangles(&points); + + let polygon = polygon(&points); + let triangles_in_face = triangles + .into_iter() + .filter(|triangle| { + let points = triangle.map(|point| point.point_surface); + let triangle = Triangle { points }; + + let [x, y] = triangle.center().coords.components.map(|s| s.value()); + polygon.contains(&Coord { x, y }) + }) + .map(|triangle| { + let points = triangle.map(|point| point.point_vertex); + MeshTriangle { + inner: Triangle { points }, + is_internal: face.is_internal, + } + }); + + let mut mesh = TriMesh::new(); + mesh.triangles.extend(triangles_in_face); + + mesh +} + +fn points(face: &Face) -> Vec { + face.half_edges + .iter() + .map(|half_edge| { + // Here, we project a 3D point (from the vertex) into the face's + // surface, creating a 2D point. Through the surface, this 2D + // point has a position in 3D space. + // + // But this position isn't necessarily going to be the same as + // the position of the original 3D point, due to numerical + // inaccuracy. + // + // This doesn't matter. Neither does the fact, that other faces + // might share the same vertices and project them into their own + // surfaces, creating more redundancy. + // + // The reason that it doesn't, is that we're using the projected + // 2D points _only_ for this local triangulation. Once that + // tells us how the different 3D points must connect, we use the + // original 3D points to build those triangles. We never convert + // the 2D points back into 3D. + let point_surface = + face.surface.geometry.project_point(half_edge.start.point); + + TriangulationPoint { + point_surface, + point_vertex: half_edge.start.point, + } + }) + .collect() +} + +fn triangles(points: &[TriangulationPoint]) -> Vec<[TriangulationPoint; 3]> { + let mut triangulation = spade::ConstrainedDelaunayTriangulation::<_>::new(); + + // We're passing duplicate points to the triangulation here. It doesn't seem + // to mind though. + triangulation + .add_constraint_edges(points.iter().copied(), true) + .unwrap(); + + triangulation + .inner_faces() + .map(|triangle| triangle.vertices().map(|vertex| *vertex.data())) + .collect() +} + +fn polygon(points: &[TriangulationPoint]) -> Polygon { + // This is a placeholder implementation that is probably not well-tested and + // probably doesn't support polygons with multiple holes. + + let mut line_strings = VecDeque::new(); + let mut current_line_string = Vec::new(); + let mut visited_points = BTreeSet::new(); + + for point in points { + if visited_points.contains(point) { + line_strings.push_back(mem::take(&mut current_line_string)); + continue; + } + + let [x, y] = point.point_surface.coords.components.map(|s| s.value()); + current_line_string.push(Coord { x, y }); + visited_points.insert(point); + } + + let (exterior, interiors) = if let Some(exterior) = line_strings.pop_front() + { + line_strings.push_back(mem::take(&mut current_line_string)); + + let exterior = LineString::new(exterior); + let interiors = line_strings + .into_iter() + .filter_map(|line_string| { + (!line_string.is_empty()) + .then_some(LineString::new(line_string)) + }) + .collect(); + + (exterior, interiors) + } else { + let exterior = LineString::new(current_line_string); + let interiors = Vec::new(); + + (exterior, interiors) + }; + + Polygon::new(exterior, interiors) +} + +#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] +struct TriangulationPoint { + point_surface: Point<2>, + point_vertex: Point<3>, +} + +impl spade::HasPosition for TriangulationPoint { + type Scalar = f64; + + fn position(&self) -> spade::Point2 { + let [x, y] = self.point_surface.coords.components.map(|s| s.value()); + spade::Point2 { x, y } + } +} diff --git a/experiments/2025-03-18/src/geometry/mod.rs b/experiments/2025-03-18/src/geometry/mod.rs new file mode 100644 index 000000000..16c612bd3 --- /dev/null +++ b/experiments/2025-03-18/src/geometry/mod.rs @@ -0,0 +1,11 @@ +mod sketch; +mod surface; +mod tri_mesh; +mod triangle; + +pub use self::{ + sketch::Sketch, + surface::SurfaceGeometry, + tri_mesh::{MeshTriangle, TriMesh}, + triangle::Triangle, +}; diff --git a/experiments/2025-03-18/src/geometry/sketch.rs b/experiments/2025-03-18/src/geometry/sketch.rs new file mode 100644 index 000000000..3b6626e14 --- /dev/null +++ b/experiments/2025-03-18/src/geometry/sketch.rs @@ -0,0 +1,66 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use itertools::Itertools; + +use crate::{ + math::Point, + object::Handle, + topology::{ + face::Face, half_edge::HalfEdge, surface::Surface, vertex::Vertex, + }, +}; + +pub struct Sketch { + pub points: Vec>, +} + +impl Sketch { + pub fn to_face(&self, surface: Handle) -> Face { + let mut vertices_by_local_point: BTreeMap<_, Vec<_>> = BTreeMap::new(); + let vertices = self + .points + .iter() + .copied() + .map(|point| { + let point = surface.geometry.point_from_local(point); + let vertex = Handle::new(Vertex::new(point)); + + vertices_by_local_point + .entry(point) + .or_default() + .push(vertex.clone()); + + vertex + }) + .collect::>(); + + let mut coincident_vertices = BTreeSet::new(); + for vertices in vertices_by_local_point.into_values() { + if vertices.len() > 1 { + coincident_vertices.extend(vertices); + } + } + + let half_edges = vertices.into_iter().circular_tuple_windows().map( + |(start, end)| { + let is_internal = coincident_vertices.contains(&start) + && coincident_vertices.contains(&end); + + Handle::new(HalfEdge { start, is_internal }) + }, + ); + + Face::new(surface, half_edges, false) + } +} + +impl From for Sketch +where + I: IntoIterator, + P: Into>, +{ + fn from(points: I) -> Self { + let points = points.into_iter().map(Into::into).collect(); + Self { points } + } +} diff --git a/experiments/2025-03-18/src/geometry/surface.rs b/experiments/2025-03-18/src/geometry/surface.rs new file mode 100644 index 000000000..bc31ec04a --- /dev/null +++ b/experiments/2025-03-18/src/geometry/surface.rs @@ -0,0 +1,26 @@ +use crate::math::{Plane, Point, Vector}; + +pub trait SurfaceGeometry { + fn point_from_local(&self, point: Point<2>) -> Point<3>; + fn project_point(&self, point: Point<3>) -> Point<2>; + fn flip(&self) -> Box; + fn translate(&self, offset: Vector<3>) -> Box; +} + +impl SurfaceGeometry for Plane { + fn point_from_local(&self, point: Point<2>) -> Point<3> { + self.point_from_local(point) + } + + fn project_point(&self, point: Point<3>) -> Point<2> { + self.project_point(point) + } + + fn flip(&self) -> Box { + Box::new((*self).flip()) + } + + fn translate(&self, offset: Vector<3>) -> Box { + Box::new((*self).translate(offset)) + } +} diff --git a/experiments/2025-03-18/src/geometry/tri_mesh.rs b/experiments/2025-03-18/src/geometry/tri_mesh.rs new file mode 100644 index 000000000..6932dc0c9 --- /dev/null +++ b/experiments/2025-03-18/src/geometry/tri_mesh.rs @@ -0,0 +1,35 @@ +use super::Triangle; + +#[derive(Debug)] +pub struct TriMesh { + pub triangles: Vec, +} + +impl TriMesh { + pub fn new() -> Self { + Self { + triangles: Vec::new(), + } + } + + pub fn merge(mut self, other: Self) -> Self { + self.triangles.extend(other.triangles); + self + } + + pub fn all_triangles(&self) -> impl Iterator> { + self.triangles.iter().map(|triangle| triangle.inner) + } + + pub fn external_triangles(&self) -> impl Iterator> { + self.triangles.iter().filter_map(|triangle| { + (!triangle.is_internal).then_some(triangle.inner) + }) + } +} + +#[derive(Debug)] +pub struct MeshTriangle { + pub inner: Triangle<3>, + pub is_internal: bool, +} diff --git a/experiments/2025-03-18/src/geometry/triangle.rs b/experiments/2025-03-18/src/geometry/triangle.rs new file mode 100644 index 000000000..81432d79b --- /dev/null +++ b/experiments/2025-03-18/src/geometry/triangle.rs @@ -0,0 +1,51 @@ +use std::fmt; + +use crate::{ + math::Point, + object::{HandleAny, Object}, +}; + +use super::{MeshTriangle, TriMesh}; + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Triangle { + pub points: [Point; 3], +} + +impl Triangle { + pub fn center(&self) -> Point { + let [a, b, c] = self.points; + let coords = (a.coords + b.coords + c.coords) / 3.; + Point { coords } + } +} + +impl From<[P; 3]> for Triangle +where + P: Into>, +{ + fn from(points: [P; 3]) -> Self { + Self { + points: points.map(Into::into), + } + } +} + +impl Object for Triangle<3> { + fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Triangle") + } + + fn tri_mesh(&self) -> TriMesh { + TriMesh { + triangles: vec![MeshTriangle { + inner: *self, + is_internal: false, + }], + } + } + + fn children(&self) -> Vec { + Vec::new() + } +} diff --git a/experiments/2025-03-18/src/main.rs b/experiments/2025-03-18/src/main.rs new file mode 100644 index 000000000..af5d0d531 --- /dev/null +++ b/experiments/2025-03-18/src/main.rs @@ -0,0 +1,22 @@ +#![allow(clippy::module_inception)] + +mod app; +mod export; +mod extra; +mod geometry; +mod math; +mod model; +mod object; +mod operations; +mod render; +mod topology; +mod view; + +fn main() -> anyhow::Result<()> { + let model = model::model(); + + export::export(&model)?; + app::run(model)?; + + Ok(()) +} diff --git a/experiments/2025-03-18/src/math/bivector.rs b/experiments/2025-03-18/src/math/bivector.rs new file mode 100644 index 000000000..9ed8b6978 --- /dev/null +++ b/experiments/2025-03-18/src/math/bivector.rs @@ -0,0 +1,7 @@ +use super::Vector; + +#[derive(Clone, Copy, Debug)] +pub struct Bivector { + pub a: Vector, + pub b: Vector, +} diff --git a/experiments/2025-03-18/src/math/mod.rs b/experiments/2025-03-18/src/math/mod.rs new file mode 100644 index 000000000..8864ae2c8 --- /dev/null +++ b/experiments/2025-03-18/src/math/mod.rs @@ -0,0 +1,10 @@ +mod bivector; +mod plane; +mod point; +mod scalar; +mod vector; + +pub use self::{ + bivector::Bivector, plane::Plane, point::Point, scalar::Scalar, + vector::Vector, +}; diff --git a/experiments/2025-03-18/src/math/plane.rs b/experiments/2025-03-18/src/math/plane.rs new file mode 100644 index 000000000..13582c3d6 --- /dev/null +++ b/experiments/2025-03-18/src/math/plane.rs @@ -0,0 +1,80 @@ +use super::{Bivector, Point, Vector}; + +#[derive(Clone, Copy, Debug)] +pub struct Plane { + pub origin: Point<3>, + pub coords: Bivector<3>, +} + +impl Plane { + pub fn from_points([a, b, c]: [Point<3>; 3]) -> Self { + Self { + origin: a, + coords: Bivector { a: b - a, b: c - a }, + } + } + + pub fn u(&self) -> Vector<3> { + self.coords.a + } + + pub fn v(&self) -> Vector<3> { + self.coords.b + } + + pub fn normal(&self) -> Vector<3> { + self.u().cross(self.v()).normalize() + } + + pub fn point_from_local(&self, point: impl Into>) -> Point<3> { + let [u, v] = point.into().coords.components; + self.origin + self.coords.a * u + self.coords.b * v + } + + pub fn project_point(&self, point: impl Into>) -> Point<2> { + let point = point.into(); + let origin_to_point = point - self.origin; + + let min_distance_plane_to_point = origin_to_point.dot(&self.normal()); + let point_in_plane = + point - self.normal() * min_distance_plane_to_point; + let origin_to_point_in_plane = point_in_plane - self.origin; + + let u = origin_to_point_in_plane.dot(&self.u()); + let v = origin_to_point_in_plane.dot(&self.v()); + + Point::from([u, v]) + } + + pub fn flip(mut self) -> Self { + self.coords.b = -self.coords.b; + self + } + + pub fn translate(self, offset: impl Into>) -> Self { + Self { + origin: self.origin + offset, + coords: self.coords, + } + } +} + +#[cfg(test)] +mod tests { + use crate::math::{Bivector, Point, Vector}; + + use super::Plane; + + #[test] + fn project_point() { + let plane = Plane { + origin: Point::from([1., 1., 1.]), + coords: Bivector { + a: Vector::from([1., 0., 0.]), + b: Vector::from([0., 1., 0.]), + }, + }; + + assert_eq!(plane.project_point([2., 2., 2.]), Point::from([1., 1.])); + } +} diff --git a/experiments/2025-03-18/src/math/point.rs b/experiments/2025-03-18/src/math/point.rs new file mode 100644 index 000000000..37dd2f655 --- /dev/null +++ b/experiments/2025-03-18/src/math/point.rs @@ -0,0 +1,49 @@ +use std::ops; + +use super::Vector; + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Point { + pub coords: Vector, +} + +impl From for Point +where + V: Into>, +{ + fn from(coords: V) -> Self { + Self { + coords: coords.into(), + } + } +} + +impl ops::Add for Point +where + V: Into>, +{ + type Output = Self; + + fn add(self, other: V) -> Self::Output { + let other = other.into(); + let coords = self.coords + other; + Self { coords } + } +} + +impl ops::Sub> for Point { + type Output = Vector; + + fn sub(self, other: Point) -> Self::Output { + self.coords - other.coords + } +} + +impl ops::Sub> for Point { + type Output = Point; + + fn sub(self, other: Vector) -> Self::Output { + let coords = self.coords - other; + Self { coords } + } +} diff --git a/experiments/2025-03-18/src/math/scalar.rs b/experiments/2025-03-18/src/math/scalar.rs new file mode 100644 index 000000000..45ee52fb0 --- /dev/null +++ b/experiments/2025-03-18/src/math/scalar.rs @@ -0,0 +1,116 @@ +use std::{cmp::Ordering, ops}; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Scalar { + value: f64, +} + +impl Scalar { + pub fn new(value: f64) -> Self { + if value.is_nan() { + panic!("`Scalar` value must not be NaN"); + } + if value.is_infinite() { + panic!("`Scalar` value must not be infinite. Value: `{value}`"); + } + + Self { value } + } + + pub fn zero() -> Self { + Self::new(0.) + } + + pub fn value(&self) -> f64 { + self.value + } + + pub fn sqrt(self) -> Self { + let value = self.value().sqrt(); + Self::new(value) + } +} + +impl Eq for Scalar {} + +impl Ord for Scalar { + fn cmp(&self, other: &Self) -> Ordering { + let Some(ordering) = self.value.partial_cmp(&other.value) else { + unreachable!( + "Failed to compare `Scalar` values `{}` and `{}`", + self.value, other.value + ); + }; + + ordering + } +} + +impl PartialOrd for Scalar { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl From for Scalar { + fn from(value: f64) -> Self { + Self::new(value) + } +} + +impl ops::Add for Scalar +where + S: Into, +{ + type Output = Self; + + fn add(self, other: S) -> Self::Output { + let value = self.value() + other.into().value(); + Self::new(value) + } +} + +impl ops::Div for Scalar +where + S: Into, +{ + type Output = Self; + + fn div(self, other: S) -> Self::Output { + let value = self.value() / other.into().value(); + Self::new(value) + } +} + +impl ops::Mul for Scalar +where + S: Into, +{ + type Output = Self; + + fn mul(self, other: S) -> Self::Output { + let value = self.value() * other.into().value(); + Self::new(value) + } +} + +impl ops::Neg for Scalar { + type Output = Self; + + fn neg(self) -> Self::Output { + let value = -self.value(); + Self::new(value) + } +} + +impl ops::Sub for Scalar +where + S: Into, +{ + type Output = Self; + + fn sub(self, other: S) -> Self::Output { + let value = self.value() - other.into().value(); + Self::new(value) + } +} diff --git a/experiments/2025-03-18/src/math/vector.rs b/experiments/2025-03-18/src/math/vector.rs new file mode 100644 index 000000000..343cfe164 --- /dev/null +++ b/experiments/2025-03-18/src/math/vector.rs @@ -0,0 +1,135 @@ +use std::ops; + +use iter_fixed::IntoIteratorFixed; + +use super::Scalar; + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Vector { + pub components: [Scalar; D], +} + +impl Vector { + pub fn magnitude(&self) -> Scalar { + self.dot(self).sqrt() + } + + pub fn normalize(self) -> Self { + self / self.magnitude() + } + + pub fn dot(&self, other: &Self) -> Scalar { + self.components + .into_iter() + .zip(other.components) + .map(|(a, b)| a * b) + .reduce(|a, b| a + b) + .unwrap_or(Scalar::zero()) + } +} + +impl Vector<3> { + pub fn cross(self, other: Self) -> Self { + let [ax, ay, az] = self.components; + let [bx, by, bz] = other.components; + + Self { + components: [ + ay * bz - az * by, + az * bx - ax * bz, + ax * by - ay * bx, + ], + } + } +} + +impl From<[S; D]> for Vector +where + S: Into, +{ + fn from(components: [S; D]) -> Self { + Self { + components: components.map(Into::into), + } + } +} + +impl ops::Add for Vector +where + V: Into>, +{ + type Output = Self; + + fn add(self, other: V) -> Self::Output { + let other = other.into(); + + let components = self + .components + .into_iter_fixed() + .zip(other.components) + .map(|(a, b)| a + b) + .collect(); + + Self { components } + } +} + +impl ops::Div for Vector +where + S: Into, +{ + type Output = Self; + + fn div(self, scalar: S) -> Self::Output { + let scalar = scalar.into(); + let components = self.components.map(|component| component / scalar); + Self { components } + } +} + +impl ops::Mul for Vector +where + S: Into, +{ + type Output = Self; + + fn mul(self, scalar: S) -> Self::Output { + let scalar = scalar.into(); + + let components = self + .components + .into_iter_fixed() + .map(|v| v * scalar) + .collect(); + + Self { components } + } +} + +impl ops::Neg for Vector { + type Output = Self; + + fn neg(self) -> Self::Output { + self * -1. + } +} + +impl ops::Sub for Vector +where + V: Into>, +{ + type Output = Self; + + fn sub(self, other: V) -> Self::Output { + let other = other.into(); + + let components = self + .components + .into_iter_fixed() + .zip(other.components) + .map(|(a, b)| a - b) + .collect(); + + Self { components } + } +} diff --git a/experiments/2025-03-18/src/model.rs b/experiments/2025-03-18/src/model.rs new file mode 100644 index 000000000..1407d31f6 --- /dev/null +++ b/experiments/2025-03-18/src/model.rs @@ -0,0 +1,46 @@ +use crate::{ + geometry::Sketch, + math::{Bivector, Plane, Point, Vector}, + object::{Handle, HandleAny}, + operations::sweep::SweepExt, + topology::surface::Surface, +}; + +pub fn model() -> HandleAny { + let top = { + let sketch = Sketch::from([ + // outer boundary + [-1., -1.], + [1., -1.], + [1., 1.], + [-1., 1.], + // connection to inner boundary + [-1., -1.], + // inner boundary + [-0.5, -0.5], + [-0.5, 0.5], + [0.5, 0.5], + [0.5, -0.5], + // connection to outer boundary + [-0.5, -0.5], + // half-edge between last and first vertex is implicit, so we're done here + ]); + + let surface = Handle::new(Surface { + geometry: Box::new(Plane { + origin: Point::from([0., 0., 1.]), + coords: Bivector { + a: Vector::from([1., 0., 0.]), + b: Vector::from([0., 1., 0.]), + }, + }), + }); + + let face = sketch.to_face(surface); + Handle::new(face) + }; + + let solid = top.sweep([0., 0., -2.]); + + HandleAny::new(solid) +} diff --git a/experiments/2025-03-18/src/object/handle.rs b/experiments/2025-03-18/src/object/handle.rs new file mode 100644 index 000000000..d0375f13b --- /dev/null +++ b/experiments/2025-03-18/src/object/handle.rs @@ -0,0 +1,74 @@ +use std::{cmp::Ordering, fmt, ops::Deref, rc::Rc}; + +use super::{HandleAny, Object}; + +pub struct Handle { + inner: Rc, +} + +impl Handle { + pub fn new(inner: T) -> Self { + Self { + inner: Rc::new(inner), + } + } +} + +impl Handle +where + T: Object + 'static, +{ + pub fn to_any(&self) -> HandleAny { + self.clone().into_any() + } + + pub fn into_any(self) -> HandleAny { + HandleAny { inner: self.inner } + } +} + +impl Clone for Handle { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } +} + +impl Deref for Handle { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl Eq for Handle {} + +impl Ord for Handle { + fn cmp(&self, other: &Self) -> Ordering { + Rc::as_ptr(&self.inner) + .cast::<()>() + .cmp(&Rc::as_ptr(&other.inner).cast::<()>()) + } +} + +impl PartialEq for Handle { + fn eq(&self, other: &Self) -> bool { + Rc::ptr_eq(&self.inner, &other.inner) + } +} + +impl PartialOrd for Handle { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl fmt::Debug for Handle { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Handle") + .field("inner", &Rc::as_ptr(&self.inner)) + .finish() + } +} diff --git a/experiments/2025-03-18/src/object/handle_any.rs b/experiments/2025-03-18/src/object/handle_any.rs new file mode 100644 index 000000000..25cdebbdc --- /dev/null +++ b/experiments/2025-03-18/src/object/handle_any.rs @@ -0,0 +1,33 @@ +use std::{fmt, rc::Rc}; + +use crate::geometry::TriMesh; + +use super::Object; + +#[derive(Clone)] +pub struct HandleAny { + pub(super) inner: Rc, +} + +impl HandleAny { + pub fn new(op: impl Object + 'static) -> Self { + Self { inner: Rc::new(op) } + } +} + +impl Object for HandleAny { + fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.inner.display(f)?; + write!(f, " ({:?})", Rc::as_ptr(&self.inner))?; + + Ok(()) + } + + fn tri_mesh(&self) -> TriMesh { + self.inner.tri_mesh() + } + + fn children(&self) -> Vec { + self.inner.children() + } +} diff --git a/experiments/2025-03-18/src/object/mod.rs b/experiments/2025-03-18/src/object/mod.rs new file mode 100644 index 000000000..d696f968e --- /dev/null +++ b/experiments/2025-03-18/src/object/mod.rs @@ -0,0 +1,5 @@ +mod handle; +mod handle_any; +mod traits; + +pub use self::{handle::Handle, handle_any::HandleAny, traits::Object}; diff --git a/experiments/2025-03-18/src/object/traits.rs b/experiments/2025-03-18/src/object/traits.rs new file mode 100644 index 000000000..160001e1c --- /dev/null +++ b/experiments/2025-03-18/src/object/traits.rs @@ -0,0 +1,28 @@ +use std::fmt; + +use crate::geometry::TriMesh; + +use super::HandleAny; + +pub trait Object { + fn display(&self, f: &mut fmt::Formatter) -> fmt::Result; + fn tri_mesh(&self) -> TriMesh; + fn children(&self) -> Vec; + + fn label(&self) -> OperationDisplay + where + Self: Sized, + { + OperationDisplay { op: self as &_ } + } +} + +pub struct OperationDisplay<'r> { + pub op: &'r dyn Object, +} + +impl fmt::Display for OperationDisplay<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.op.display(f) + } +} diff --git a/experiments/2025-03-18/src/operations/connect.rs b/experiments/2025-03-18/src/operations/connect.rs new file mode 100644 index 000000000..24d2ac365 --- /dev/null +++ b/experiments/2025-03-18/src/operations/connect.rs @@ -0,0 +1,76 @@ +use crate::{ + math::Plane, + object::Handle, + topology::{ + face::Face, half_edge::HalfEdge, solid::Solid, surface::Surface, + }, +}; + +pub trait ConnectExt { + /// # Connect two faces by creating a side wall of faces from their vertices + /// + /// ## Panics + /// + /// Panics, if the two faces provided do not have the same number of + /// half-edges. + /// + /// Panics, if an internal half-edge of one face would connect to an + /// external half-edge of the other. + /// + /// ## Implementation Note + /// + /// This method has very particular (and undocumented) requirements about + /// the orientation of the two faces relative to each other, and will + /// happily generate invalid geometry, if those undocumented requirements + /// aren't met. + /// + /// It should be seen as more of a placeholder for a real implementation of + /// this operation. + fn connect(self, other: Self) -> Solid; +} + +impl ConnectExt for Handle { + fn connect(self, other: Self) -> Solid { + assert_eq!( + self.half_edges.len(), + other.half_edges.len(), + "Can only connect faces that have the same number of vertices.", + ); + + let side_faces = self + .half_edges_with_end_vertex() + .zip(other.half_edges_with_end_vertex()) + .map(|((q, r), (t, s))| { + let is_internal = match [q.is_internal, t.is_internal] { + [true, true] => true, + [false, false] => false, + _ => { + panic!( + "Trying to connect an internal half-edge of one \ + face to an external half-edge of another" + ); + } + }; + + let surface = Handle::new(Surface { + geometry: Box::new(Plane::from_points( + [&q.start, r, s].map(|vertex| vertex.point), + )), + }); + let face = Face::new( + surface, + [&q.start, r, s, &t.start].map(|vertex| { + Handle::new(HalfEdge { + start: vertex.clone(), + is_internal: false, + }) + }), + is_internal, + ); + Handle::new(face) + }) + .collect::>(); + + Solid::new([self, other].into_iter().chain(side_faces)) + } +} diff --git a/experiments/2025-03-18/src/operations/flip.rs b/experiments/2025-03-18/src/operations/flip.rs new file mode 100644 index 000000000..04cc56eb1 --- /dev/null +++ b/experiments/2025-03-18/src/operations/flip.rs @@ -0,0 +1,25 @@ +use crate::{ + object::Handle, + topology::{face::Face, surface::Surface}, +}; + +pub trait FlipExt { + fn flip(&self) -> Self; +} + +impl FlipExt for Face { + fn flip(&self) -> Self { + Face::new( + Handle::new(self.surface.flip()), + self.half_edges.clone(), + self.is_internal, + ) + } +} + +impl FlipExt for Surface { + fn flip(&self) -> Self { + let geometry = self.geometry.flip(); + Self { geometry } + } +} diff --git a/experiments/2025-03-18/src/operations/mod.rs b/experiments/2025-03-18/src/operations/mod.rs new file mode 100644 index 000000000..7d1d75085 --- /dev/null +++ b/experiments/2025-03-18/src/operations/mod.rs @@ -0,0 +1,4 @@ +pub mod connect; +pub mod flip; +pub mod sweep; +pub mod translate; diff --git a/experiments/2025-03-18/src/operations/sweep.rs b/experiments/2025-03-18/src/operations/sweep.rs new file mode 100644 index 000000000..3e25eeb7f --- /dev/null +++ b/experiments/2025-03-18/src/operations/sweep.rs @@ -0,0 +1,31 @@ +use crate::{ + math::Vector, + object::Handle, + topology::{face::Face, solid::Solid}, +}; + +use super::{connect::ConnectExt, flip::FlipExt, translate::TranslateExt}; + +pub trait SweepExt { + /// # Sweep a face along a path, creating a solid + /// + /// ## Implementation Note + /// + /// This method has very particular (and undocumented) requirements about + /// the orientation of the two faces relative to each other, and will + /// happily generate invalid geometry, if those undocumented requirements + /// aren't met. + /// + /// It should be seen as more of a placeholder for a real implementation of + /// this operation. + fn sweep(self, path: impl Into>) -> Solid; +} + +impl SweepExt for Handle { + fn sweep(self, path: impl Into>) -> Solid { + let bottom = self; + let top = Handle::new(bottom.flip().translate(path)); + + top.connect(bottom) + } +} diff --git a/experiments/2025-03-18/src/operations/translate.rs b/experiments/2025-03-18/src/operations/translate.rs new file mode 100644 index 000000000..9091d7724 --- /dev/null +++ b/experiments/2025-03-18/src/operations/translate.rs @@ -0,0 +1,51 @@ +use crate::{ + math::Vector, + object::Handle, + topology::{ + face::Face, half_edge::HalfEdge, surface::Surface, vertex::Vertex, + }, +}; + +pub trait TranslateExt { + fn translate(&self, offset: impl Into>) -> Self; +} + +impl TranslateExt for Face { + fn translate(&self, offset: impl Into>) -> Self { + let offset = offset.into(); + + Face::new( + Handle::new(self.surface.translate(offset)), + self.half_edges + .iter() + .map(|half_edge| Handle::new(half_edge.translate(offset))), + self.is_internal, + ) + } +} + +impl TranslateExt for HalfEdge { + fn translate(&self, offset: impl Into>) -> Self { + let start = self.start.translate(offset); + + HalfEdge { + start: Handle::new(start), + is_internal: self.is_internal, + } + } +} + +impl TranslateExt for Surface { + fn translate(&self, offset: impl Into>) -> Self { + let offset = offset.into(); + let geometry = self.geometry.translate(offset); + Self { geometry } + } +} + +impl TranslateExt for Vertex { + fn translate(&self, offset: impl Into>) -> Self { + let offset = offset.into(); + Vertex::new(self.point + offset) + } +} diff --git a/experiments/2025-03-18/src/render/geometry.rs b/experiments/2025-03-18/src/render/geometry.rs new file mode 100644 index 000000000..144f302bd --- /dev/null +++ b/experiments/2025-03-18/src/render/geometry.rs @@ -0,0 +1,71 @@ +use glam::Vec3; +use wgpu::util::DeviceExt; + +use crate::object::Object; + +use super::vertex::Vertex; + +pub struct Geometry { + pub vertices: wgpu::Buffer, + pub indices: wgpu::Buffer, + pub num_indices: u32, +} + +impl Geometry { + pub fn new(device: &wgpu::Device, operation: &dyn Object) -> Self { + let tri_mesh = operation.tri_mesh(); + + let mut indices = Vec::new(); + let mut vertices = Vec::new(); + + for triangle in tri_mesh.all_triangles() { + let triangle = triangle.points.each_ref().map(|point| { + Vec3::from( + point.coords.components.map(|coord| coord.value() as f32), + ) + }); + let normal = { + let [a, b, c] = triangle; + + let ab = b - a; + let ac = c - a; + + ab.cross(ac) + }; + + for point in triangle { + let index = vertices.len() as u32; + let vertex = Vertex { + position: point.into(), + normal: normal.into(), + }; + + indices.push(index); + vertices.push(vertex); + } + } + + let Ok(num_indices) = indices.len().try_into() else { + panic!("Unsupported number of indices: `{}`", indices.len()); + }; + + let vertices = + device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&vertices), + usage: wgpu::BufferUsages::VERTEX, + }); + let indices = + device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&indices), + usage: wgpu::BufferUsages::INDEX, + }); + + Self { + vertices, + indices, + num_indices, + } + } +} diff --git a/experiments/2025-03-18/src/render/mod.rs b/experiments/2025-03-18/src/render/mod.rs new file mode 100644 index 000000000..35c43f2e4 --- /dev/null +++ b/experiments/2025-03-18/src/render/mod.rs @@ -0,0 +1,8 @@ +mod geometry; +mod pipeline; +mod renderer; +mod text; +mod uniforms; +mod vertex; + +pub use self::renderer::Renderer; diff --git a/experiments/2025-03-18/src/render/pipeline.rs b/experiments/2025-03-18/src/render/pipeline.rs new file mode 100644 index 000000000..22ce834ac --- /dev/null +++ b/experiments/2025-03-18/src/render/pipeline.rs @@ -0,0 +1,145 @@ +use std::f32::consts::PI; + +use glam::{Mat4, Vec3}; +use wgpu::util::DeviceExt; + +use super::{geometry::Geometry, uniforms::Uniforms, vertex::Vertex}; + +pub struct Pipeline { + render_pipeline: wgpu::RenderPipeline, + bind_group: wgpu::BindGroup, +} + +impl Pipeline { + pub fn new( + device: &wgpu::Device, + surface_configuration: &wgpu::SurfaceConfiguration, + ) -> Self { + let aspect_ratio = surface_configuration.width as f32 + / surface_configuration.height as f32; + let uniforms = + device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&[Uniforms::from_transform( + default_transform(aspect_ratio), + )]), + usage: wgpu::BufferUsages::UNIFORM, + }); + + let bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: None, + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }], + }); + + let pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[&bind_group_layout], + push_constant_ranges: &[], + }); + + let shader_module = device.create_shader_module(wgpu::include_wgsl!( + "shaders/triangles.wgsl" + )); + + let render_pipeline = + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader_module, + entry_point: Some("vertex"), + compilation_options: + wgpu::PipelineCompilationOptions::default(), + buffers: &[wgpu::VertexBufferLayout { + array_stride: size_of::() + as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: Vertex::ATTRIBUTES, + }], + }, + fragment: Some(wgpu::FragmentState { + module: &shader_module, + entry_point: Some("fragment"), + compilation_options: + wgpu::PipelineCompilationOptions::default(), + targets: &[Some(wgpu::ColorTargetState { + format: surface_configuration.format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::all(), + })], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + unclipped_depth: false, + polygon_mode: wgpu::PolygonMode::Fill, + conservative: false, + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: wgpu::TextureFormat::Depth32Float, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState::default(), + multiview: None, + cache: None, + }); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: uniforms.as_entire_binding(), + }], + }); + + Pipeline { + render_pipeline, + bind_group, + } + } + + pub fn draw( + &self, + render_pass: &mut wgpu::RenderPass, + geometry: &Geometry, + ) { + if geometry.num_indices > 0 { + render_pass.set_index_buffer( + geometry.indices.slice(..), + wgpu::IndexFormat::Uint32, + ); + render_pass.set_vertex_buffer(0, geometry.vertices.slice(..)); + render_pass.set_pipeline(&self.render_pipeline); + render_pass.set_bind_group(0, &self.bind_group, &[]); + render_pass.draw_indexed(0..geometry.num_indices, 0, 0..1); + } + } +} + +fn default_transform(aspect_ratio: f32) -> Mat4 { + let fov_y_radians = std::f32::consts::PI / 2.; + let z_near = 0.1; + let z_far = 10.; + + Mat4::perspective_rh(fov_y_radians, aspect_ratio, z_near, z_far) + * Mat4::from_translation(Vec3::new(0., 0., -4.)) + * Mat4::from_rotation_x(-PI / 4.) + * Mat4::from_rotation_z(PI / 4.) +} diff --git a/experiments/2025-03-18/src/render/renderer.rs b/experiments/2025-03-18/src/render/renderer.rs new file mode 100644 index 000000000..fdcb123e1 --- /dev/null +++ b/experiments/2025-03-18/src/render/renderer.rs @@ -0,0 +1,146 @@ +use std::sync::Arc; + +use anyhow::anyhow; +use winit::window::Window; + +use crate::view::OperationView; + +use super::{geometry::Geometry, pipeline::Pipeline, text::TextRenderer}; + +pub struct Renderer { + pub surface: wgpu::Surface<'static>, + pub device: wgpu::Device, + pub queue: wgpu::Queue, + pub surface_config: wgpu::SurfaceConfiguration, + pub pipeline: Pipeline, + pub depth_view: wgpu::TextureView, + pub text_renderer: TextRenderer, +} + +impl Renderer { + pub async fn new(window: Arc) -> anyhow::Result { + let instance = wgpu::Instance::default(); + let surface = instance.create_surface(window.clone())?; + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + compatible_surface: Some(&surface), + ..Default::default() + }) + .await + .ok_or_else(|| anyhow!("Failed to request adapter"))?; + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + required_features: wgpu::Features::default(), + required_limits: wgpu::Limits::default(), + memory_hints: wgpu::MemoryHints::default(), + }, + None, + ) + .await?; + + let size = window.inner_size(); + let surface_config = surface + .get_default_config(&adapter, size.width, size.height) + .ok_or_else(|| anyhow!("Failed to get default surface config"))?; + surface.configure(&device, &surface_config); + + let pipeline = Pipeline::new(&device, &surface_config); + + let depth_view = { + let depth_texture = + device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: surface_config.width, + height: surface_config.height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Depth32Float, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT + | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }); + + depth_texture.create_view(&wgpu::TextureViewDescriptor::default()) + }; + + let text_renderer = TextRenderer::new( + &device, + &queue, + &surface_config, + window.scale_factor() as f32, + ); + + Ok(Self { + surface, + device, + queue, + surface_config, + pipeline, + depth_view, + text_renderer, + }) + } + + pub fn render(&mut self, operations: &OperationView) -> anyhow::Result<()> { + let selected_operation = operations.selected(); + + let geometry = Geometry::new(&self.device, selected_operation); + + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + let frame = self.surface.get_current_texture().unwrap(); + let color_view = frame + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + + { + let mut render_pass = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some( + wgpu::RenderPassColorAttachment { + view: &color_view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::WHITE), + store: wgpu::StoreOp::Store, + }, + }, + )], + depth_stencil_attachment: Some( + wgpu::RenderPassDepthStencilAttachment { + view: &self.depth_view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: wgpu::StoreOp::Store, + }), + stencil_ops: None, + }, + ), + timestamp_writes: None, + occlusion_query_set: None, + }); + + self.pipeline.draw(&mut render_pass, &geometry); + self.text_renderer.render( + operations, + &self.device, + &self.queue, + &self.surface_config, + &mut render_pass, + )?; + } + + self.queue.submit(Some(encoder.finish())); + frame.present(); + + Ok(()) + } +} diff --git a/experiments/2025-03-18/src/render/shaders/triangles.wgsl b/experiments/2025-03-18/src/render/shaders/triangles.wgsl new file mode 100644 index 000000000..90e89a92f --- /dev/null +++ b/experiments/2025-03-18/src/render/shaders/triangles.wgsl @@ -0,0 +1,32 @@ +struct Uniforms { + transform: mat4x4, + transform_for_normals: mat4x4, +}; + +@group(0) @binding(0) +var uniforms: Uniforms; + +struct VertexInput { + @location(0) position: vec3, + @location(1) normal: vec3, +} + +struct VertexOutput { + @builtin(position) position: vec4, + @location(0) normal: vec3, +} + +@vertex +fn vertex(in: VertexInput) -> VertexOutput { + var out: VertexOutput; + out.position = uniforms.transform * vec4(in.position, 1.0); + out.normal = (uniforms.transform_for_normals * vec4(in.normal, 0.0)).xyz; + + return out; +} + +@fragment +fn fragment(in: VertexOutput) -> @location(0) vec4 { + var color = vec4(in.normal, 1.0); + return color; +} diff --git a/experiments/2025-03-18/src/render/text.rs b/experiments/2025-03-18/src/render/text.rs new file mode 100644 index 000000000..bf2e3f369 --- /dev/null +++ b/experiments/2025-03-18/src/render/text.rs @@ -0,0 +1,141 @@ +use std::fmt::Write; + +use crate::{object::Object, view::OperationView}; + +pub struct TextRenderer { + text_atlas: glyphon::TextAtlas, + viewport: glyphon::Viewport, + text_renderer: glyphon::TextRenderer, + font_system: glyphon::FontSystem, + swash_cache: glyphon::SwashCache, + scale_factor: f32, +} + +impl TextRenderer { + pub fn new( + device: &wgpu::Device, + queue: &wgpu::Queue, + surface_config: &wgpu::SurfaceConfiguration, + scale_factor: f32, + ) -> Self { + let cache = glyphon::Cache::new(device); + let swash_cache = glyphon::SwashCache::new(); + + let mut text_atlas = glyphon::TextAtlas::new( + device, + queue, + &cache, + surface_config.format, + ); + + let mut viewport = glyphon::Viewport::new(device, &cache); + viewport.update( + queue, + glyphon::Resolution { + width: surface_config.width, + height: surface_config.height, + }, + ); + + let text_renderer = glyphon::TextRenderer::new( + &mut text_atlas, + device, + wgpu::MultisampleState::default(), + Some(wgpu::DepthStencilState { + format: wgpu::TextureFormat::Depth32Float, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState::default(), + }), + ); + + let font_system = glyphon::FontSystem::new(); + + Self { + text_atlas, + viewport, + text_renderer, + font_system, + swash_cache, + scale_factor, + } + } + + pub fn render( + &mut self, + operations: &OperationView, + device: &wgpu::Device, + queue: &wgpu::Queue, + surface_config: &wgpu::SurfaceConfiguration, + render_pass: &mut wgpu::RenderPass, + ) -> anyhow::Result<()> { + let mut buffer = glyphon::Buffer::new( + &mut self.font_system, + glyphon::Metrics { + font_size: 16., + line_height: 16., + }, + ); + + for (op, selected, indent_level) in operations.operations() { + let mut attrs = glyphon::Attrs::new(); + + if selected { + attrs = attrs.color(glyphon::Color::rgb(0, 127, 0)); + } + + let mut line = String::new(); + + for _ in 0..indent_level { + write!(line, "\t")?; + } + + write!(line, "{}", op.label())?; + + buffer.lines.push(glyphon::BufferLine::new( + line, + glyphon::cosmic_text::LineEnding::Lf, + glyphon::AttrsList::new(attrs), + glyphon::Shaping::Advanced, + )); + } + + buffer.shape_until_scroll(&mut self.font_system, false); + + let text_area = glyphon::TextArea { + buffer: &buffer, + left: 0., + top: 0., + scale: self.scale_factor, + bounds: glyphon::TextBounds { + left: 0, + top: 0, + right: surface_config.width as i32, + bottom: surface_config.height as i32, + }, + default_color: glyphon::Color::rgb(0, 0, 0), + custom_glyphs: &[], + }; + + self.text_renderer + .prepare( + device, + queue, + &mut self.font_system, + &mut self.text_atlas, + &self.viewport, + [text_area], + &mut self.swash_cache, + ) + .unwrap(); + + self.text_renderer.render( + &self.text_atlas, + &self.viewport, + render_pass, + )?; + + Ok(()) + } +} diff --git a/experiments/2025-03-18/src/render/uniforms.rs b/experiments/2025-03-18/src/render/uniforms.rs new file mode 100644 index 000000000..c533b0738 --- /dev/null +++ b/experiments/2025-03-18/src/render/uniforms.rs @@ -0,0 +1,19 @@ +use glam::Mat4; + +#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] +pub struct Uniforms { + pub transform: Mat4, + pub transform_for_normals: Mat4, +} + +impl Uniforms { + pub fn from_transform(transform: Mat4) -> Self { + let transform_for_normals = transform.inverse().transpose(); + + Self { + transform, + transform_for_normals, + } + } +} diff --git a/experiments/2025-03-18/src/render/vertex.rs b/experiments/2025-03-18/src/render/vertex.rs new file mode 100644 index 000000000..9003fdea4 --- /dev/null +++ b/experiments/2025-03-18/src/render/vertex.rs @@ -0,0 +1,13 @@ +#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] +pub struct Vertex { + pub position: [f32; 3], + pub normal: [f32; 3], +} + +impl Vertex { + pub const ATTRIBUTES: &[wgpu::VertexAttribute] = &wgpu::vertex_attr_array![ + 0 => Float32x3, + 1 => Float32x3, + ]; +} diff --git a/experiments/2025-03-18/src/topology/face.rs b/experiments/2025-03-18/src/topology/face.rs new file mode 100644 index 000000000..5fadd4d55 --- /dev/null +++ b/experiments/2025-03-18/src/topology/face.rs @@ -0,0 +1,58 @@ +use std::fmt; + +use itertools::Itertools; + +use crate::{ + extra::triangulate::triangulate, + geometry::TriMesh, + object::{Handle, HandleAny, Object}, +}; + +use super::{half_edge::HalfEdge, surface::Surface, vertex::Vertex}; + +#[derive(Debug)] +pub struct Face { + pub surface: Handle, + pub half_edges: Vec>, + pub is_internal: bool, +} + +impl Face { + pub fn new( + surface: Handle, + half_edges: impl IntoIterator>, + is_internal: bool, + ) -> Self { + Self { + surface, + half_edges: half_edges.into_iter().collect(), + is_internal, + } + } + + pub fn half_edges_with_end_vertex( + &self, + ) -> impl Iterator, &Handle)> { + self.half_edges + .iter() + .circular_tuple_windows() + .map(|(a, b)| (a, &b.start)) + } +} + +impl Object for Face { + fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Face") + } + + fn tri_mesh(&self) -> TriMesh { + triangulate(self) + } + + fn children(&self) -> Vec { + self.half_edges + .iter() + .map(|vertex| vertex.to_any()) + .collect() + } +} diff --git a/experiments/2025-03-18/src/topology/half_edge.rs b/experiments/2025-03-18/src/topology/half_edge.rs new file mode 100644 index 000000000..9bf0a5849 --- /dev/null +++ b/experiments/2025-03-18/src/topology/half_edge.rs @@ -0,0 +1,27 @@ +use std::fmt; + +use crate::{ + geometry::TriMesh, + object::{Handle, HandleAny, Object}, +}; + +use super::vertex::Vertex; + +pub struct HalfEdge { + pub start: Handle, + pub is_internal: bool, +} + +impl Object for HalfEdge { + fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "HalfEdge") + } + + fn tri_mesh(&self) -> TriMesh { + TriMesh::new() + } + + fn children(&self) -> Vec { + vec![self.start.to_any()] + } +} diff --git a/experiments/2025-03-18/src/topology/mod.rs b/experiments/2025-03-18/src/topology/mod.rs new file mode 100644 index 000000000..00bb5a849 --- /dev/null +++ b/experiments/2025-03-18/src/topology/mod.rs @@ -0,0 +1,5 @@ +pub mod face; +pub mod half_edge; +pub mod solid; +pub mod surface; +pub mod vertex; diff --git a/experiments/2025-03-18/src/topology/solid.rs b/experiments/2025-03-18/src/topology/solid.rs new file mode 100644 index 000000000..0c0938d14 --- /dev/null +++ b/experiments/2025-03-18/src/topology/solid.rs @@ -0,0 +1,41 @@ +use std::fmt; + +use crate::{ + geometry::TriMesh, + object::{Handle, HandleAny, Object}, +}; + +use super::face::Face; + +#[derive(Clone)] +pub struct Solid { + faces: Vec>, +} + +impl Solid { + pub fn new(faces: impl IntoIterator>) -> Self { + Self { + faces: faces.into_iter().collect(), + } + } +} + +impl Object for Solid { + fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Solid") + } + + fn tri_mesh(&self) -> TriMesh { + let mut tri_mesh = TriMesh::new(); + + for face in &self.faces { + tri_mesh = tri_mesh.merge(face.tri_mesh()); + } + + tri_mesh + } + + fn children(&self) -> Vec { + self.faces.iter().map(|face| face.to_any()).collect() + } +} diff --git a/experiments/2025-03-18/src/topology/surface.rs b/experiments/2025-03-18/src/topology/surface.rs new file mode 100644 index 000000000..7b3f58def --- /dev/null +++ b/experiments/2025-03-18/src/topology/surface.rs @@ -0,0 +1,15 @@ +use std::fmt; + +use crate::geometry::SurfaceGeometry; + +pub struct Surface { + pub geometry: Box, +} + +impl fmt::Debug for Surface { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Surface") + .field("geometry", &"Box") + .finish() + } +} diff --git a/experiments/2025-03-18/src/topology/vertex.rs b/experiments/2025-03-18/src/topology/vertex.rs new file mode 100644 index 000000000..65a7a8b03 --- /dev/null +++ b/experiments/2025-03-18/src/topology/vertex.rs @@ -0,0 +1,33 @@ +use std::fmt; + +use crate::{ + geometry::TriMesh, + math::Point, + object::{HandleAny, Object}, +}; + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Vertex { + pub point: Point<3>, +} + +impl Vertex { + pub fn new(point: impl Into>) -> Self { + let point = point.into(); + Self { point } + } +} + +impl Object for Vertex { + fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Vertex") + } + + fn tri_mesh(&self) -> TriMesh { + TriMesh::new() + } + + fn children(&self) -> Vec { + Vec::new() + } +} diff --git a/experiments/2025-03-18/src/view.rs b/experiments/2025-03-18/src/view.rs new file mode 100644 index 000000000..35a4e1a05 --- /dev/null +++ b/experiments/2025-03-18/src/view.rs @@ -0,0 +1,136 @@ +use std::{fmt, iter}; + +use crate::{ + geometry::TriMesh, + object::{HandleAny, Object}, +}; + +#[derive(Clone)] +pub struct OperationView { + operation: HandleAny, + children: Vec, + selected: Option, +} + +impl OperationView { + pub fn new(operation: HandleAny) -> Self { + let children = + operation.children().into_iter().map(Self::new).collect(); + + Self { + operation, + children, + selected: None, + } + } + + pub fn operations(&self) -> impl Iterator { + self.operations_inner(true, 0) + } + + fn operations_inner( + &self, + selected: bool, + indent_level: usize, + ) -> Box + '_> { + let self_ = iter::once((self, selected, indent_level)); + + if self.selected.is_some() { + Box::new(self_.chain(self.children.iter().enumerate().flat_map( + move |(i, view)| { + let selected = Some(i) == self.selected; + view.operations_inner(selected, indent_level + 1) + }, + ))) + } else { + Box::new(self_) + } + } + + pub fn select_last(&mut self) { + self.selected = Some(self.last_index()); + } + + pub fn select_next(&mut self) { + if let Some(selected) = self.selected { + self.selected = Some(usize::min(selected + 1, self.last_index())); + } + } + + pub fn select_previous(&mut self) { + if let Some(selected) = self.selected { + self.selected = Some(selected.saturating_sub(1)); + } + } + + pub fn select_none(&mut self) { + self.selected = None; + } + + pub fn selected(&self) -> &Self { + self.selected + .and_then(|selected| self.children.get(selected)) + .map(|child| child.selected()) + .unwrap_or(self) + } + + pub fn selected_mut(&mut self) -> &mut Self { + let Some(selected) = self.selected else { + return self; + }; + + // The way this is done, first checking for `is_none` and then + // `unwrap`ing below, is really ugly. But the borrow checker is forcing + // my hand. + // + // I've tried several variations of matching, and it can't see that in + // the `None` case, `self` no longer needs to be borrowed, preventing me + // from returning it. + + if self.children.get_mut(selected).is_none() { + return self; + }; + + self.children.get_mut(selected).unwrap().selected_mut() + } + + pub fn parent_of_selected_mut(&mut self) -> &mut Self { + let Some(selected) = self.selected else { + return self; + }; + + // The same comment in `selected_mut` applies here too. Plus, some ugly + // duplication. + + if self.children.get_mut(selected).is_none() { + return self; + }; + + if self.children.get_mut(selected).unwrap().selected.is_none() { + self + } else { + self.children + .get_mut(selected) + .unwrap() + .parent_of_selected_mut() + } + } + + fn last_index(&self) -> usize { + self.children.len().saturating_sub(1) + } +} + +impl Object for OperationView { + fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.operation.display(f) + } + + fn tri_mesh(&self) -> TriMesh { + self.operation.tri_mesh() + } + + fn children(&self) -> Vec { + self.operation.children() + } +}