diff --git a/README.md b/README.md index cf3e589..ac2771b 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,11 @@ Note that currently this is just the barest beginnings of the project, more of a ### Implementation goals -* Comfortable, intuitive UI +* Provide a comfortable, intuitive UI -* Able to run in browser (so implemented in WASM-compatible language) +* Allow execution in browser (so implemented in WASM-compatible language) -* Produce scalable graphics of 3D diagrams, and maybe STL files (or other fabricatable file format) as well. +* Produce scalable graphics of 3D diagrams, and maybe STL files (or other fabricatable file format) as well ## Prototype @@ -24,38 +24,40 @@ The latest prototype is in the folder `app-proto`. It includes both a user inter ### Install the prerequisites -1. Install [`rustup`](https://rust-lang.github.io/rustup/): the officially recommended Rust toolchain manager - - It's available on Ubuntu as a [Snap](https://snapcraft.io/rustup) -2. Call `rustup default stable` to "download the latest stable release of Rust and set it as your default toolchain" - - If you forget, the `rustup` [help system](https://github.com/rust-lang/rustup/blob/d9b3601c3feb2e88cf3f8ca4f7ab4fdad71441fd/src/errors.rs#L109-L112) will remind you -3. Call `rustup target add wasm32-unknown-unknown` to add the [most generic 32-bit WebAssembly target](https://doc.rust-lang.org/nightly/rustc/platform-support/wasm32-unknown-unknown.html) -4. Call `cargo install wasm-pack` to install the [WebAssembly toolchain](https://rustwasm.github.io/docs/wasm-pack/) -5. Call `cargo install trunk` to install the [Trunk](https://trunkrs.dev/) web-build tool +1. Install [`rustup`](https://rust-lang.github.io/rustup/): the officially recommended Rust toolchain manager. + - It's available on Ubuntu as a [Snap](https://snapcraft.io/rustup). +2. Call `rustup default stable` to "download the latest stable release of Rust and set it as your default toolchain". + - If you forget, the `rustup` [help system](https://github.com/rust-lang/rustup/blob/d9b3601c3feb2e88cf3f8ca4f7ab4fdad71441fd/src/errors.rs#L109-L112) will remind you. +3. Call `rustup target add wasm32-unknown-unknown` to add the [most generic 32-bit WebAssembly target](https://doc.rust-lang.org/nightly/rustc/platform-support/wasm32-unknown-unknown.html). +4. Call `cargo install wasm-pack` to install the [WebAssembly toolchain](https://rustwasm.github.io/docs/wasm-pack/). +5. Call `cargo install trunk` to install the [Trunk](https://trunkrs.dev/) web-build tool. + - In the future, `trunk` can be updated with the same command. (You may need the `--locked` flag if your ambient version of `rustc` does not match that required by `trunk`.) 6. Add the `.cargo/bin` folder in your home directory to your executable search path - - This lets you call Trunk, and other tools installed by Cargo, without specifying their paths - - On POSIX systems, the search path is stored in the `PATH` environment variable + - This lets you call Trunk, and other tools installed by Cargo, without specifying their paths. + - On POSIX systems, the search path is stored in the `PATH` environment variable. + - Alternatively, if you don't want to adjust your `PATH`, you can install `trunk` in another directory `DIR` via `cargo install --root DIR trunk`. ### Play with the prototype -1. From the `app-proto` folder, call `trunk serve --release` to build and serve the prototype - - The crates the prototype depends on will be downloaded and served automatically - - For a faster build, at the expense of a much slower prototype, you can call `trunk serve` without the `--release` flag +1. From the `app-proto` folder, call `trunk serve --release` to build and serve the prototype. + - The crates the prototype depends on will be downloaded and served automatically. + - For a faster build, at the expense of a much slower prototype, you can call `trunk serve` without the `--release` flag. - If you want to stay in the top-level folder, you can call `trunk serve --config app-proto [--release]` from there instead. -3. In a web browser, visit one of the URLs listed under the message `INFO 📡 server listening at:` - - Touching any file in the `app-proto` folder will make Trunk rebuild and live-reload the prototype -4. Press *ctrl+C* in the shell where Trunk is running to stop serving the prototype +3. In a web browser, visit one of the URLs listed under the message `INFO 📡 server listening at:`. + - Touching any file in the `app-proto` folder will make Trunk rebuild and live-reload the prototype. +4. Press *ctrl+C* in the shell where Trunk is running to stop serving the prototype. ### Run the engine on some example problems -1. Use `sh` to run the script `tools/run-examples.sh` - - The script is location-independent, so you can do this from anywhere in the dyna3 repository +1. Use `sh` to run the script `tools/run-examples.sh`. + - The script is location-independent, so you can do this from anywhere in the dyna3 repository. - The call from the top level of the repository is: ```bash sh tools/run-examples.sh ``` - - For each example problem, the engine will print the value of the loss function at each optimization step - - The first example that prints is the same as the Irisawa hexlet example from the Julia version of the engine prototype. If you go into `engine-proto/gram-test`, launch Julia, and then + - For each example problem, the engine will print the value of the loss function at each optimization step. + - The first example that prints is the same as the Irisawa hexlet example from the Julia version of the engine prototype. If you go into `engine-proto/gram-test`, launch Julia, and then execute ```julia include("irisawa-hexlet.jl") @@ -64,24 +66,24 @@ The latest prototype is in the folder `app-proto`. It includes both a user inter end ``` - you should see that it prints basically the same loss history until the last few steps, when the lower default precision of the Rust engine really starts to show + you should see that it prints basically the same loss history until the last few steps, when the lower default precision of the Rust engine really starts to show. ### Run the automated tests -1. Go into the `app-proto` folder -2. Call `cargo test` +1. Go into the `app-proto` folder. +2. Call `cargo test`. ### Deploy the prototype -1. From the `app-proto` folder, call `trunk build --release` - - Building in [release mode](https://doc.rust-lang.org/cargo/reference/profiles.html#release) produces an executable which is smaller and often much faster, but harder to debug and more time-consuming to build - - If you want to stay in the top-level folder, you can call `trunk build --config app-proto --release` from there instead +1. From the `app-proto` folder, call `trunk build --release`. + - Building in [release mode](https://doc.rust-lang.org/cargo/reference/profiles.html#release) produces an executable which is smaller and often much faster, but harder to debug and more time-consuming to build. + - If you want to stay in the top-level folder, you can call `trunk build --config app-proto --release` from there instead. 2. Use `sh` to run the packaging script `tools/package-for-deployment.sh`. - - The script is location-independent, so you can do this from anywhere in the dyna3 repository + - The script is location-independent, so you can do this from anywhere in the dyna3 repository. - The call from the top level of the repository is: ```bash sh tools/package-for-deployment.sh ``` - - This will overwrite or replace the files in `deploy/dyna3` + - This will overwrite or replace the files in `deploy/dyna3`. 3. Put the contents of `deploy/dyna3` in the folder on your server that the prototype will be served from. - - To simplify uploading, you might want to combine these files into an archive called `deploy/dyna3.zip`. Git has been set to ignore this path \ No newline at end of file + - To simplify uploading, you might want to combine these files into an archive called `deploy/dyna3.zip`. Git has been set to ignore this path. diff --git a/app-proto/main.css b/app-proto/main.css index ef8aaf9..a00d309 100644 --- a/app-proto/main.css +++ b/app-proto/main.css @@ -227,16 +227,6 @@ details[open]:has(li) .element-switch::after { border-radius: 8px; } -#distortion-bar { - display: flex; - margin-top: 8px; - gap: 8px; -} - -#distortion-gauge { - flex-grow: 1; -} - /* display */ #display { diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 0bde53d..0264b75 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -1,13 +1,11 @@ use enum_iterator::{all, Sequence}; use nalgebra::{DMatrix, DVector, DVectorView}; use std::{ - any::Any, cell::Cell, cmp::Ordering, collections::{BTreeMap, BTreeSet}, - f64::consts::SQRT_2, fmt, - fmt::{Debug, Formatter}, + fmt::{Debug, Display, Formatter}, hash::{Hash, Hasher}, rc::Rc, sync::{atomic, atomic::AtomicU64}, @@ -88,6 +86,14 @@ impl Ord for dyn Serial { } } +// Small helper function to generate consistent errors when there +// are indexing issues in a ProblemPoser +fn indexing_error(item: &str, name: &str, actor: &str) -> String { + format!( + "{item} \"{name}\" must be indexed before {actor} writes problem data" + ) +} + pub trait ProblemPoser { fn pose(&self, problem: &mut ConstraintProblem); } @@ -126,16 +132,11 @@ pub trait Element: Serial + ProblemPoser + DisplayItem { // be used carefully to preserve invariant (1), described in the comment on // the `tangent` field of the `Assembly` structure fn set_column_index(&self, index: usize); - - /* KLUDGE */ - fn is_point(&self) -> bool { - false - } } impl Debug for dyn Element { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - self.id().fmt(f) + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Debug::fmt(&self.id(), f) } } @@ -258,8 +259,7 @@ impl Serial for Sphere { impl ProblemPoser for Sphere { fn pose(&self, problem: &mut ConstraintProblem) { let index = self.column_index().expect( - format!("Sphere \"{}\" should be indexed before writing problem data", self.id).as_str() - ); + indexing_error("Sphere", &self.id, "it").as_str()); problem.gram.push_sym(index, index, 1.0); problem.guess.set_column(index, &self.representation.get_clone_untracked()); } @@ -353,10 +353,6 @@ impl Element for Point { fn set_column_index(&self, index: usize) { self.column_index.set(Some(index)); } - - fn is_point(&self) -> bool { - true - } } impl Serial for Point { @@ -368,25 +364,17 @@ impl Serial for Point { impl ProblemPoser for Point { fn pose(&self, problem: &mut ConstraintProblem) { let index = self.column_index().expect( - format!("Point \"{}\" should be indexed before writing problem data", self.id).as_str() - ); + indexing_error("Point", &self.id, "it").as_str()); problem.gram.push_sym(index, index, 0.0); problem.frozen.push(Self::WEIGHT_COMPONENT, index, 0.5); problem.guess.set_column(index, &self.representation.get_clone_untracked()); } } -pub trait Regulator: Any + Serial + ProblemPoser + OutlineItem { +pub trait Regulator: Serial + ProblemPoser + OutlineItem { fn subjects(&self) -> Vec>; fn measurement(&self) -> ReadSignal; fn set_point(&self) -> Signal; - fn soft(&self) -> Option> { - None - } - fn distortion(&self) -> Option> { /* KLUDGE */ - None - } - fn as_any(&self) -> &dyn Any; } impl Hash for dyn Regulator { @@ -419,8 +407,6 @@ pub struct InversiveDistanceRegulator { pub subjects: [Rc; 2], pub measurement: ReadSignal, pub set_point: Signal, - pub soft: Signal, - distortion: Option>, /* KLUDGE */ serial: u64, } @@ -436,24 +422,9 @@ impl InversiveDistanceRegulator { }); let set_point = create_signal(SpecifiedValue::from_empty_spec()); - let distortion = if subjects.iter().all(|subj| subj.is_point()) { - Some(create_memo(move || { - let set_point_opt = set_point.with(|set_pt| set_pt.value); - let measurement_val = measurement.get(); - match set_point_opt { - None => 0.0, - Some(set_point_val) => SQRT_2 * ( - (-measurement_val).sqrt() - (-set_point_val).sqrt() - ), - } - })) - } else { - None - }; - let soft = create_signal(false); let serial = Self::next_serial(); - Self { subjects, measurement, set_point, soft, distortion, serial } + Self { subjects, measurement, set_point, serial } } } @@ -469,18 +440,6 @@ impl Regulator for InversiveDistanceRegulator { fn set_point(&self) -> Signal { self.set_point } - - fn soft(&self) -> Option> { - Some(self.soft) - } - - fn distortion(&self) -> Option> { - self.distortion - } - - fn as_any(&self) -> &dyn Any { - self - } } impl Serial for InversiveDistanceRegulator { @@ -491,19 +450,14 @@ impl Serial for InversiveDistanceRegulator { impl ProblemPoser for InversiveDistanceRegulator { fn pose(&self, problem: &mut ConstraintProblem) { - let soft = self.soft.get_untracked(); self.set_point.with_untracked(|set_pt| { if let Some(val) = set_pt.value { let [row, col] = self.subjects.each_ref().map( |subj| subj.column_index().expect( - "Subjects should be indexed before inversive distance regulator writes problem data" - ) + indexing_error("Subject", subj.id(), + "inversive distance regulator").as_str()) ); - if soft { - problem.soft.push_sym(row, col, val); - } else { - problem.gram.push_sym(row, col, val); - } + problem.gram.push_sym(row, col, val); } }); } @@ -541,10 +495,6 @@ impl Regulator for HalfCurvatureRegulator { fn set_point(&self) -> Signal { self.set_point } - - fn as_any(&self) -> &dyn Any { - self - } } impl Serial for HalfCurvatureRegulator { @@ -558,8 +508,8 @@ impl ProblemPoser for HalfCurvatureRegulator { self.set_point.with_untracked(|set_pt| { if let Some(val) = set_pt.value { let col = self.subject.column_index().expect( - "Subject should be indexed before half-curvature regulator writes problem data" - ); + indexing_error("Subject", &self.subject.id, + "half-curvature regulator").as_str()); problem.frozen.push(Sphere::CURVATURE_COMPONENT, col, val); } }); @@ -567,11 +517,18 @@ impl ProblemPoser for HalfCurvatureRegulator { } #[derive(Clone, Copy, Sequence)] -pub enum Axis {X = 0, Y = 1, Z = 2} +pub enum Axis { X = 0, Y = 1, Z = 2 } impl Axis { - pub const N_AXIS: usize = (Axis::Z as usize) + 1; - pub const NAME: [&str; Axis::N_AXIS] = ["X", "Y", "Z"]; + fn name(&self) -> &'static str { + match self { Axis::X => "X", Axis::Y => "Y", Axis::Z => "Z" } + } +} + +impl Display for Axis { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } } pub struct PointCoordinateRegulator { @@ -597,21 +554,9 @@ impl Serial for PointCoordinateRegulator { } impl Regulator for PointCoordinateRegulator { - fn subjects(&self) -> Vec> { - vec![self.subject.clone()] - } - - fn measurement(&self) -> ReadSignal { - self.measurement - } - - fn set_point(&self) -> Signal { - self.set_point - } - - fn as_any(&self) -> &dyn Any { - self - } + fn subjects(&self) -> Vec> { vec![self.subject.clone()] } + fn measurement(&self) -> ReadSignal { self.measurement } + fn set_point(&self) -> Signal { self.set_point } } impl ProblemPoser for PointCoordinateRegulator { @@ -619,19 +564,20 @@ impl ProblemPoser for PointCoordinateRegulator { self.set_point.with_untracked(|set_pt| { if let Some(val) = set_pt.value { let col = self.subject.column_index().expect( - "Subject must be indexed before point-coordinate regulator poses."); + indexing_error("Subject", &self.subject.id, + "point-coordinate regulator").as_str()); problem.frozen.push(self.axis as usize, col, val); - // Check if all three coordinates have been frozen, and if so, - // freeze the coradius as well - let mut coords = [0.0; Axis::N_AXIS]; + // If all three of the subject's spatial coordinates have been + // frozen, then freeze its norm component: + let mut coords = [0.0; Axis::CARDINALITY]; let mut nset: usize = 0; for &MatrixEntry {index, value} in &(problem.frozen) { - if index.1 == col && index.0 < Axis::N_AXIS { + if index.1 == col && index.0 < Axis::CARDINALITY { nset += 1; coords[index.0] = value } } - if nset == Axis::N_AXIS { + if nset == Axis::CARDINALITY { let [x, y, z] = coords; problem.frozen.push( Point::NORM_COMPONENT, col, point(x,y,z)[Point::NORM_COMPONENT]); @@ -841,6 +787,7 @@ impl Assembly { /* DEBUG */ // log the Gram matrix console_log!("Gram matrix:\n{}", problem.gram); + console_log!("Frozen entries:\n{}", problem.frozen); /* DEBUG */ // log the initial configuration matrix @@ -848,7 +795,7 @@ impl Assembly { // look for a configuration with the given Gram matrix let Realization { result, history } = realize_gram( - &problem, 1.0e-20, 0.5, 0.9, 1.1, 400, 110 + &problem, 1.0e-12, 0.5, 0.9, 1.1, 200, 110 ); /* DEBUG */ @@ -1000,7 +947,8 @@ mod tests { use crate::engine; #[test] - #[should_panic(expected = "Sphere \"sphere\" should be indexed before writing problem data")] + #[should_panic(expected = + "Sphere \"sphere\" must be indexed before it writes problem data")] fn unindexed_element_test() { let _ = create_root(|| { let elt = Sphere::default("sphere".to_string(), 0); @@ -1009,7 +957,8 @@ mod tests { } #[test] - #[should_panic(expected = "Subjects should be indexed before inversive distance regulator writes problem data")] + #[should_panic(expected = "Subject \"sphere1\" must be indexed before \ + inversive distance regulator writes problem data")] fn unindexed_subject_test_inversive_distance() { let _ = create_root(|| { let subjects = [0, 1].map( @@ -1070,4 +1019,4 @@ mod tests { assert!((final_half_curv / INITIAL_HALF_CURV - 1.0).abs() < DRIFT_TOL); }); } -} \ No newline at end of file +} diff --git a/app-proto/src/components/diagnostics.rs b/app-proto/src/components/diagnostics.rs index bab6f35..51d58f1 100644 --- a/app-proto/src/components/diagnostics.rs +++ b/app-proto/src/components/diagnostics.rs @@ -111,94 +111,6 @@ fn StepInput() -> View { } } -#[component] -fn DistortionGauge() -> View { - let state = use_context::(); - let total_distortion = create_memo(move || { - state.assembly.regulators.with(|regs| { - let mut total = 0.0; - for reg in regs { - if let Some(distortion) = reg.distortion() { - total += distortion.get().abs(); - } - } - total - }) - }); - - view! { - div(id = "distortion-gauge") { - "Distortion: " (total_distortion.with(|distort| distort.to_string())) - } - } -} - -#[component] -fn PrintButton() -> View { - view! { - button( - on:click = |_| { - let state = use_context::(); - - // print the edge length distortions - let mut hard_distortion_table = String::new(); - let mut soft_distortion_table = String::new(); - let mut highest_distortion = f64::NEG_INFINITY; - let mut lowest_distortion = f64::INFINITY; - let mut largest_hard_distortion = f64::NEG_INFINITY; - state.assembly.regulators.with_untracked(|regs| { - for reg in regs { - if let Some(distortion) = reg.distortion() { - let distortion_val = distortion.get(); - let subjects = reg.subjects(); - let distortion_line = format!( - "{}, {}: {distortion_val}\n", - subjects[0].id(), - subjects[1].id(), - ); - match reg.soft() { - Some(soft) if soft.get() => { - soft_distortion_table += &distortion_line; - highest_distortion = highest_distortion.max(distortion_val); - lowest_distortion = lowest_distortion.min(distortion_val); - }, - _ => { - hard_distortion_table += &distortion_line; - largest_hard_distortion = largest_hard_distortion.max(distortion_val.abs()); - } - }; - } - } - }); - console_log!("\ - === Distortions of flexible edges (for labels) ===\n\n\ - --- Range ---\n\n\ - Highest: {highest_distortion}\n\ - Lowest: {lowest_distortion}\n\n\ - --- Table ---\n\n{soft_distortion_table}\n\ - === Distortions of rigid edges (for validation) ===\n\n\ - These values should be small relative to the ones for the flexible edges\n\n\ - --- Range ---\n\n\ - Largest absolute: {largest_hard_distortion}\n\n\ - --- Table ---\n\n{hard_distortion_table}\ - "); - - // print the vertex coordinates - let mut coords_table = String::new(); - state.assembly.elements.with_untracked(|elts| { - for elt in elts.iter().filter(|elt| elt.is_point()) { - let (x, y, z) = elt.representation().with( - |rep| (rep[0], rep[1], rep[2]) - ); - coords_table += &format!("{}: {x}, {y}, {z}\n", elt.id()); - } - }); - console_log!("=== Vertex coordinates ===\n\n{coords_table}"); - }, - ) { "Print" } - } -} - fn into_log10_time_point((step, value): (usize, f64)) -> Vec> { vec![ Some(step as f64), @@ -403,10 +315,6 @@ pub fn Diagnostics() -> View { } DiagnosticsPanel(name = "loss") { LossHistory {} } DiagnosticsPanel(name = "spectrum") { SpectrumHistory {} } - div(id = "distortion-bar") { - DistortionGauge {} - PrintButton {} - } } } } \ No newline at end of file diff --git a/app-proto/src/components/display.rs b/app-proto/src/components/display.rs index c4803dc..98be85e 100644 --- a/app-proto/src/components/display.rs +++ b/app-proto/src/components/display.rs @@ -597,16 +597,16 @@ pub fn Display() -> View { |status| status.is_ok() ); let step_val = state.assembly.step.with_untracked(|step| step.value); + let on_init_step = step_val.is_some_and(|n| n == 0.0); let on_last_step = step_val.is_some_and( |n| state.assembly.descent_history.with_untracked( |history| n as usize + 1 == history.config.len().max(1) ) ); - if - state.selection.with(|sel| sel.len() == 1) - && realization_successful - && on_last_step - { + let on_manipulable_step = + !realization_successful && on_init_step + || realization_successful && on_last_step; + if on_manipulable_step && state.selection.with(|sel| sel.len() == 1) { let sel = state.selection.with( |sel| sel.into_iter().next().unwrap().clone() ); diff --git a/app-proto/src/components/outline.rs b/app-proto/src/components/outline.rs index 79781fa..547b73b 100644 --- a/app-proto/src/components/outline.rs +++ b/app-proto/src/components/outline.rs @@ -6,7 +6,6 @@ use web_sys::{KeyboardEvent, MouseEvent, wasm_bindgen::JsCast}; use crate::{ AppState, assembly::{ - Axis, Element, HalfCurvatureRegulator, InversiveDistanceRegulator, @@ -123,10 +122,11 @@ impl OutlineItem for HalfCurvatureRegulator { impl OutlineItem for PointCoordinateRegulator { fn outline_item(self: Rc, _element: &Rc) -> View { + let name = format!("{} coordinate", self.axis); view! { li(class = "regulator") { - div(class = "regulator-label") { (Axis::NAME[self.axis as usize]) } - div(class = "regulator-type") { "Coordinate" } + div(class = "regulator-label") // for spacing + div(class = "regulator-type") { (name) } RegulatorInput(regulator = self) div(class = "status") } diff --git a/app-proto/src/components/test_assembly_chooser.rs b/app-proto/src/components/test_assembly_chooser.rs index 1e6e124..0d387d3 100644 --- a/app-proto/src/components/test_assembly_chooser.rs +++ b/app-proto/src/components/test_assembly_chooser.rs @@ -12,8 +12,6 @@ use crate::{ ElementColor, InversiveDistanceRegulator, Point, - PointCoordinateRegulator, - Regulator, Sphere, }, engine, @@ -884,2756 +882,6 @@ fn load_irisawa_hexlet(assembly: &Assembly) { assembly.insert_regulator(Rc::new(outer_moon_tangency)); } -fn regular_diagonals<'a, const N: usize>(vertex_ids: [&'a str; N]) -> Vec<(bool, f64, Vec<[&'a str; 2]>)> { - let ang = PI / (N as f64); - let ang_sin = ang.sin(); - (2..N-1).map(|sep| ( - false, - (sep as f64 * ang).sin() / ang_sin, - (0..N-sep).map(|k| [vertex_ids[k], vertex_ids[k + sep]]).collect() - )).collect() -} - -fn load_554_base(assembly: &Assembly) { - // create the vertices - const COLOR_A: ElementColor = [0.75_f32, 0.00_f32, 0.75_f32]; - const COLOR_B: ElementColor = [1.00_f32, 0.00_f32, 0.25_f32]; - const COLOR_C: ElementColor = [0.75_f32, 0.50_f32, 0.00_f32]; - const COLOR_D: ElementColor = [0.90_f32, 0.90_f32, 0.00_f32]; - const COLOR_E: ElementColor = [0.25_f32, 0.75_f32, 0.00_f32]; - const COLOR_F: ElementColor = [0.00_f32, 0.50_f32, 0.75_f32]; - const COLOR_G: ElementColor = [0.25_f32, 0.00_f32, 1.00_f32]; - let vertices = [ - Point::new( - "a_NE".to_string(), - "A-NE".to_string(), - COLOR_A, - engine::point(0.5, 0.5, 0.0), - ), - Point::new( - "a_NW".to_string(), - "A-NW".to_string(), - COLOR_A, - engine::point(-0.5, 0.5, 0.0), - ), - Point::new( - "a_SW".to_string(), - "A-SW".to_string(), - COLOR_A, - engine::point(-0.5, -0.5, 0.0), - ), - Point::new( - "a_SE".to_string(), - "A-SE".to_string(), - COLOR_A, - engine::point(0.5, -0.5, 0.0), - ), - Point::new( - "b_NE".to_string(), - "B-NE".to_string(), - COLOR_B, - engine::point(0.7, 0.7, 0.9), - ), - Point::new( - "b_NW".to_string(), - "B-NW".to_string(), - COLOR_B, - engine::point(-0.7, 0.7, 0.9), - ), - Point::new( - "b_SW".to_string(), - "B-SW".to_string(), - COLOR_B, - engine::point(-0.7, -0.7, 0.9), - ), - Point::new( - "b_SE".to_string(), - "B-SE".to_string(), - COLOR_B, - engine::point(0.7, -0.7, 0.9), - ), - Point::new( - "c_N".to_string(), - "C-N".to_string(), - COLOR_C, - engine::point(0.0, 0.8, 1.4), - ), - Point::new( - "c_W".to_string(), - "C-W".to_string(), - COLOR_C, - engine::point(-0.8, 0.0, 1.4), - ), - Point::new( - "c_S".to_string(), - "C-S".to_string(), - COLOR_C, - engine::point(0.0, -0.8, 1.4), - ), - Point::new( - "c_E".to_string(), - "C-E".to_string(), - COLOR_C, - engine::point(0.8, 0.0, 1.4), - ), - Point::new( - "d_NE".to_string(), - "D-NE".to_string(), - COLOR_D, - engine::point(0.1, 0.1, 1.8), - ), - Point::new( - "d_NW".to_string(), - "D-NW".to_string(), - COLOR_D, - engine::point(-0.1, 0.1, 1.8), - ), - Point::new( - "d_SW".to_string(), - "D-SW".to_string(), - COLOR_D, - engine::point(-0.1, -0.1, 1.8), - ), - Point::new( - "d_SE".to_string(), - "D-SE".to_string(), - COLOR_D, - engine::point(0.1, -0.1, 1.8), - ), - Point::new( - "e_N".to_string(), - "E-N".to_string(), - COLOR_E, - engine::point(0.0, 0.7, 2.3), - ), - Point::new( - "e_W".to_string(), - "E-W".to_string(), - COLOR_E, - engine::point(-0.7, 0.0, 2.3), - ), - Point::new( - "e_S".to_string(), - "E-S".to_string(), - COLOR_E, - engine::point(0.0, -0.7, 2.3), - ), - Point::new( - "e_E".to_string(), - "E-E".to_string(), - COLOR_E, - engine::point(0.7, 0.0, 2.3), - ), - Point::new( - "f_NE".to_string(), - "F-NE".to_string(), - COLOR_F, - engine::point(0.2, 0.2, 2.7), - ), - Point::new( - "f_NW".to_string(), - "F-NW".to_string(), - COLOR_F, - engine::point(-0.2, 0.2, 2.7), - ), - Point::new( - "f_SW".to_string(), - "F-SW".to_string(), - COLOR_F, - engine::point(-0.2, -0.2, 2.7), - ), - Point::new( - "f_SE".to_string(), - "F-SE".to_string(), - COLOR_F, - engine::point(0.2, -0.2, 2.7), - ), - Point::new( - "g_NNE".to_string(), - "G-NNE".to_string(), - COLOR_G, - engine::point(0.5, 1.2, 3.0), - ), - Point::new( - "g_NNW".to_string(), - "G-NNW".to_string(), - COLOR_G, - engine::point(-0.5, 1.2, 3.0), - ), - Point::new( - "g_WNW".to_string(), - "G-WNW".to_string(), - COLOR_G, - engine::point(-1.2, 0.5, 3.0), - ), - Point::new( - "g_WSW".to_string(), - "G-WSW".to_string(), - COLOR_G, - engine::point(-1.2, -0.5, 3.0), - ), - Point::new( - "g_SSW".to_string(), - "G-SSW".to_string(), - COLOR_G, - engine::point(-0.5, -1.2, 3.0), - ), - Point::new( - "g_SSE".to_string(), - "G-SSE".to_string(), - COLOR_G, - engine::point(0.5, -1.2, 3.0), - ), - Point::new( - "g_ESE".to_string(), - "G-ESE".to_string(), - COLOR_G, - engine::point(1.2, -0.5, 3.0), - ), - Point::new( - "g_ENE".to_string(), - "G-ENE".to_string(), - COLOR_G, - engine::point(1.2, 0.5, 3.0), - ), - ]; - for vertex in vertices { - let _ = assembly.try_insert_element(vertex); - } - - // fix the distances between adjacent vertices - let f_a = ["a_SE", "a_SW", "a_NW", "a_NE"]; - let f_abc_n = ["a_NW", "b_NW", "c_N", "b_NE", "a_NE"]; - let f_abc_w = ["a_SW", "b_SW", "c_W", "b_NW", "a_NW"]; - let f_abc_s = ["a_SE", "b_SE", "c_S", "b_SW", "a_SW"]; - let f_abc_e = ["a_NE", "b_NE", "c_E", "b_SE", "a_SE"]; - let f_bcd_ne = ["b_NE", "c_N", "d_NE", "c_E"]; - let f_bcd_nw = ["b_NW", "c_W", "d_NW", "c_N"]; - let f_bcd_sw = ["b_SW", "c_S", "d_SW", "c_W"]; - let f_bcd_se = ["b_SE", "c_E", "d_SE", "c_S"]; - let f_g = [ - "g_NNE", "g_NNW", "g_WNW", "g_WSW", - "g_SSW", "g_SSE", "g_ESE", "g_ENE", - ]; - let struts: Vec<_> = [ - (false, 1.0, vec![ - ["a_NE", "a_NW"], - ["a_NW", "a_SW"], - ["a_SW", "a_SE"], - ["a_SE", "a_NE"], - ["a_NE", "b_NE"], - ["a_NW", "b_NW"], - ["a_SW", "b_SW"], - ["a_SE", "b_SE"], - ["b_NE", "c_N"], - ["b_NW", "c_N"], - ["b_NW", "c_W"], - ["b_SW", "c_W"], - ["b_SW", "c_S"], - ["b_SE", "c_S"], - ["b_SE", "c_E"], - ["b_NE", "c_E"], - ["c_N", "d_NE"], - ["c_N", "d_NW"], - ["c_W", "d_NW"], - ["c_W", "d_SW"], - ["c_S", "d_SW"], - ["c_S", "d_SE"], - ["c_E", "d_SE"], - ["c_E", "d_NE"], - ["g_NNE", "g_NNW"], - ["g_NNW", "g_WNW"], - ["g_WNW", "g_WSW"], - ["g_WSW", "g_SSW"], - ["g_SSW", "g_SSE"], - ["g_SSE", "g_ESE"], - ["g_ESE", "g_ENE"], - ["g_ENE", "g_NNE"], - ]), - (true, 1.0, vec![ - ["d_NE", "e_N"], - ["d_NW", "e_N"], - ["d_NW", "e_W"], - ["d_SW", "e_W"], - ["d_SW", "e_S"], - ["d_SE", "e_S"], - ["d_SE", "e_E"], - ["d_NE", "e_E"], - ["c_N", "e_N"], - ["c_W", "e_W"], - ["c_S", "e_S"], - ["c_E", "e_E"], - ["e_N", "f_NE"], - ["e_N", "f_NW"], - ["e_W", "f_NW"], - ["e_W", "f_SW"], - ["e_S", "f_SW"], - ["e_S", "f_SE"], - ["e_E", "f_SE"], - ["e_E", "f_NE"], - ["d_NE", "f_NE"], - ["d_NW", "f_NW"], - ["d_SW", "f_SW"], - ["d_SE", "f_SE"], - ["f_NE", "g_ENE"], - ["f_NE", "g_NNE"], - ["f_NW", "g_NNW"], - ["f_NW", "g_WNW"], - ["f_SW", "g_WSW"], - ["f_SW", "g_SSW"], - ["f_SE", "g_SSE"], - ["f_SE", "g_ESE"], - ["e_N", "g_NNE"], - ["e_N", "g_NNW"], - ["e_W", "g_WNW"], - ["e_W", "g_WSW"], - ["e_S", "g_SSW"], - ["e_S", "g_SSE"], - ["e_E", "g_ESE"], - ["e_E", "g_ENE"], - ]), - ].into_iter() - .chain(regular_diagonals(f_a)) - .chain(regular_diagonals(f_bcd_ne)) - .chain(regular_diagonals(f_bcd_nw)) - .chain(regular_diagonals(f_bcd_sw)) - .chain(regular_diagonals(f_bcd_se)) - .chain(regular_diagonals(f_abc_n)) - .chain(regular_diagonals(f_abc_w)) - .chain(regular_diagonals(f_abc_s)) - .chain(regular_diagonals(f_abc_e)) - .chain(regular_diagonals(f_g)) - .collect(); - for (soft, length, vertex_pairs) in struts { - let inv_dist = Some(-0.5 * length * length); - for pair in vertex_pairs { - let adjacent_vertices = pair.map( - |id| assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[id].clone() - ) - ); - let distance = InversiveDistanceRegulator::new(adjacent_vertices); - distance.set_point.set(SpecifiedValue::from(inv_dist)); - distance.soft.set(soft); - assembly.insert_regulator(Rc::new(distance)); - } - } - - // create the faces - const COLOR_FACE: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32]; - let faces = [ - Sphere::new( - "f_a".to_string(), - "Face A".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 0.0, -1.0, 0.0, 0.0), - ), - Sphere::new( - "f_abc_N".to_string(), - "Face ABC-N".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 1.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_abc_W".to_string(), - "Face ABC-W".to_string(), - COLOR_FACE, - engine::sphere_with_offset(-1.0, 0.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_abc_S".to_string(), - "Face ABC-S".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, -1.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_abc_E".to_string(), - "Face ABC-E".to_string(), - COLOR_FACE, - engine::sphere_with_offset(1.0, 0.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_bcd_NE".to_string(), - "Face BCD-NE".to_string(), - COLOR_FACE, - engine::sphere_with_offset(FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.0, FRAC_1_SQRT_2, 0.0), - ), - Sphere::new( - "f_bcd_NW".to_string(), - "Face BCD-NW".to_string(), - COLOR_FACE, - engine::sphere_with_offset(-FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.0, FRAC_1_SQRT_2, 0.0), - ), - Sphere::new( - "f_bcd_SW".to_string(), - "Face BCD-SW".to_string(), - COLOR_FACE, - engine::sphere_with_offset(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.0, FRAC_1_SQRT_2, 0.0), - ), - Sphere::new( - "f_bcd_SE".to_string(), - "Face BCD-SE".to_string(), - COLOR_FACE, - engine::sphere_with_offset(FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.0, FRAC_1_SQRT_2, 0.0), - ), - Sphere::new( - "f_g".to_string(), - "Face G".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 0.0, 1.0, 3.0, 0.0), - ), - ]; - for face in faces { - face.ghost().set(true); - let _ = assembly.try_insert_element(face); - } - - // make the faces planar and make them pass through their vertices - let face_incidences = [ - ("f_a", Vec::from(f_a)), - ("f_abc_N", Vec::from(f_abc_n)), - ("f_abc_W", Vec::from(f_abc_w)), - ("f_abc_S", Vec::from(f_abc_s)), - ("f_abc_E", Vec::from(f_abc_e)), - ("f_bcd_NE", Vec::from(f_bcd_ne)), - ("f_bcd_NW", Vec::from(f_bcd_nw)), - ("f_bcd_SW", Vec::from(f_bcd_sw)), - ("f_bcd_SE", Vec::from(f_bcd_se)), - ("f_g", Vec::from(f_g)), - ]; - for (face_id, vertex_ids) in face_incidences { - // make the face planar - let face = assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[face_id].clone() - ); - let curvature_regulator = face.regulators().with_untracked( - |regs| regs.first().unwrap().clone() - ); - curvature_regulator.set_point().set(SpecifiedValue::from(Some(0.0))); - - // make the face pass through its vertices - for v_id in vertex_ids { - let vertex = assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[v_id].clone() - ); - let incidence = InversiveDistanceRegulator::new([face.clone(), vertex]); - incidence.set_point.set(SpecifiedValue::from(Some(0.0))); - assembly.insert_regulator(Rc::new(incidence)); - } - } -} - -fn load_554_aug1(assembly: &Assembly) { - // create the vertices - const COLOR_A: ElementColor = [0.75_f32, 0.00_f32, 0.75_f32]; - const COLOR_B: ElementColor = [1.00_f32, 0.00_f32, 0.25_f32]; - const COLOR_C: ElementColor = [0.75_f32, 0.50_f32, 0.00_f32]; - const COLOR_D: ElementColor = [0.90_f32, 0.90_f32, 0.00_f32]; - const COLOR_E: ElementColor = [0.25_f32, 0.75_f32, 0.00_f32]; - const COLOR_F: ElementColor = [0.00_f32, 0.50_f32, 0.75_f32]; - const COLOR_G: ElementColor = [0.25_f32, 0.00_f32, 1.00_f32]; - const GRAY: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32]; - let vertices = [ - Point::new( - "a_NE".to_string(), - "A-NE".to_string(), - COLOR_A, - engine::point(0.5, 0.5, 0.0), - ), - Point::new( - "a_NW".to_string(), - "A-NW".to_string(), - COLOR_A, - engine::point(-0.5, 0.5, 0.0), - ), - Point::new( - "a_SW".to_string(), - "A-SW".to_string(), - COLOR_A, - engine::point(-0.5, -0.5, 0.0), - ), - Point::new( - "a_SE".to_string(), - "A-SE".to_string(), - COLOR_A, - engine::point(0.5, -0.5, 0.0), - ), - Point::new( - "b_NE".to_string(), - "B-NE".to_string(), - COLOR_B, - engine::point(0.7, 0.7, 0.9), - ), - Point::new( - "b_NW".to_string(), - "B-NW".to_string(), - COLOR_B, - engine::point(-0.7, 0.7, 0.9), - ), - Point::new( - "b_SW".to_string(), - "B-SW".to_string(), - COLOR_B, - engine::point(-0.7, -0.7, 0.9), - ), - Point::new( - "b_SE".to_string(), - "B-SE".to_string(), - COLOR_B, - engine::point(0.7, -0.7, 0.9), - ), - Point::new( - "c_N".to_string(), - "C-N".to_string(), - COLOR_C, - engine::point(0.0, 0.8, 1.4), - ), - Point::new( - "c_W".to_string(), - "C-W".to_string(), - COLOR_C, - engine::point(-0.8, 0.0, 1.4), - ), - Point::new( - "c_S".to_string(), - "C-S".to_string(), - COLOR_C, - engine::point(0.0, -0.8, 1.4), - ), - Point::new( - "c_E".to_string(), - "C-E".to_string(), - COLOR_C, - engine::point(0.8, 0.0, 1.4), - ), - Point::new( - "y_NE".to_string(), - "Y-NE".to_string(), - GRAY, - engine::point(0.7, 0.7, 1.6), - ), - Point::new( - "y_NW".to_string(), - "Y-NW".to_string(), - GRAY, - engine::point(-0.7, 0.7, 1.6), - ), - Point::new( - "y_SW".to_string(), - "Y-SW".to_string(), - GRAY, - engine::point(-0.7, -0.7, 1.6), - ), - Point::new( - "y_SE".to_string(), - "Y-SE".to_string(), - GRAY, - engine::point(0.7, -0.7, 1.6), - ), - Point::new( - "d_NE".to_string(), - "D-NE".to_string(), - COLOR_D, - engine::point(0.1, 0.1, 1.8), - ), - Point::new( - "d_NW".to_string(), - "D-NW".to_string(), - COLOR_D, - engine::point(-0.1, 0.1, 1.8), - ), - Point::new( - "d_SW".to_string(), - "D-SW".to_string(), - COLOR_D, - engine::point(-0.1, -0.1, 1.8), - ), - Point::new( - "d_SE".to_string(), - "D-SE".to_string(), - COLOR_D, - engine::point(0.1, -0.1, 1.8), - ), - Point::new( - "e_N".to_string(), - "E-N".to_string(), - COLOR_E, - engine::point(0.0, 0.7, 2.3), - ), - Point::new( - "e_W".to_string(), - "E-W".to_string(), - COLOR_E, - engine::point(-0.7, 0.0, 2.3), - ), - Point::new( - "e_S".to_string(), - "E-S".to_string(), - COLOR_E, - engine::point(0.0, -0.7, 2.3), - ), - Point::new( - "e_E".to_string(), - "E-E".to_string(), - COLOR_E, - engine::point(0.7, 0.0, 2.3), - ), - Point::new( - "f_NE".to_string(), - "F-NE".to_string(), - COLOR_F, - engine::point(0.2, 0.2, 2.7), - ), - Point::new( - "f_NW".to_string(), - "F-NW".to_string(), - COLOR_F, - engine::point(-0.2, 0.2, 2.7), - ), - Point::new( - "f_SW".to_string(), - "F-SW".to_string(), - COLOR_F, - engine::point(-0.2, -0.2, 2.7), - ), - Point::new( - "f_SE".to_string(), - "F-SE".to_string(), - COLOR_F, - engine::point(0.2, -0.2, 2.7), - ), - Point::new( - "g_NNE".to_string(), - "G-NNE".to_string(), - COLOR_G, - engine::point(0.5, 1.2, 3.0), - ), - Point::new( - "g_NNW".to_string(), - "G-NNW".to_string(), - COLOR_G, - engine::point(-0.5, 1.2, 3.0), - ), - Point::new( - "g_WNW".to_string(), - "G-WNW".to_string(), - COLOR_G, - engine::point(-1.2, 0.5, 3.0), - ), - Point::new( - "g_WSW".to_string(), - "G-WSW".to_string(), - COLOR_G, - engine::point(-1.2, -0.5, 3.0), - ), - Point::new( - "g_SSW".to_string(), - "G-SSW".to_string(), - COLOR_G, - engine::point(-0.5, -1.2, 3.0), - ), - Point::new( - "g_SSE".to_string(), - "G-SSE".to_string(), - COLOR_G, - engine::point(0.5, -1.2, 3.0), - ), - Point::new( - "g_ESE".to_string(), - "G-ESE".to_string(), - COLOR_G, - engine::point(1.2, -0.5, 3.0), - ), - Point::new( - "g_ENE".to_string(), - "G-ENE".to_string(), - COLOR_G, - engine::point(1.2, 0.5, 3.0), - ), - ]; - for vertex in vertices { - let _ = assembly.try_insert_element(vertex); - } - - // fix the distances between adjacent vertices - let f_a = ["a_SE", "a_SW", "a_NW", "a_NE"]; - let f_abc_n = ["a_NW", "b_NW", "c_N", "b_NE", "a_NE"]; - let f_abc_w = ["a_SW", "b_SW", "c_W", "b_NW", "a_NW"]; - let f_abc_s = ["a_SE", "b_SE", "c_S", "b_SW", "a_SW"]; - let f_abc_e = ["a_NE", "b_NE", "c_E", "b_SE", "a_SE"]; - let f_g = [ - "g_NNE", "g_NNW", "g_WNW", "g_WSW", - "g_SSW", "g_SSE", "g_ESE", "g_ENE", - ]; - let struts: Vec<_> = [ - (false, 1.0, vec![ - ["a_NE", "a_NW"], - ["a_NW", "a_SW"], - ["a_SW", "a_SE"], - ["a_SE", "a_NE"], - ["a_NE", "b_NE"], - ["a_NW", "b_NW"], - ["a_SW", "b_SW"], - ["a_SE", "b_SE"], - ["b_NE", "c_N"], - ["b_NW", "c_N"], - ["b_NW", "c_W"], - ["b_SW", "c_W"], - ["b_SW", "c_S"], - ["b_SE", "c_S"], - ["b_SE", "c_E"], - ["b_NE", "c_E"], - ["g_NNE", "g_NNW"], - ["g_NNW", "g_WNW"], - ["g_WNW", "g_WSW"], - ["g_WSW", "g_SSW"], - ["g_SSW", "g_SSE"], - ["g_SSE", "g_ESE"], - ["g_ESE", "g_ENE"], - ["g_ENE", "g_NNE"], - ]), - (true, 1.0, vec![ - ["c_N", "d_NE"], - ["c_N", "d_NW"], - ["c_W", "d_NW"], - ["c_W", "d_SW"], - ["c_S", "d_SW"], - ["c_S", "d_SE"], - ["c_E", "d_SE"], - ["c_E", "d_NE"], - ["y_NE", "b_NE"], - ["y_NW", "b_NW"], - ["y_SW", "b_SW"], - ["y_SE", "b_SE"], - ["y_NE", "c_N"], - ["y_NW", "c_N"], - ["y_NW", "c_W"], - ["y_SW", "c_W"], - ["y_SW", "c_S"], - ["y_SE", "c_S"], - ["y_SE", "c_E"], - ["y_NE", "c_E"], - ["y_NE", "d_NE"], - ["y_NW", "d_NW"], - ["y_SW", "d_SW"], - ["y_SE", "d_SE"], - ["d_NE", "e_N"], - ["d_NW", "e_N"], - ["d_NW", "e_W"], - ["d_SW", "e_W"], - ["d_SW", "e_S"], - ["d_SE", "e_S"], - ["d_SE", "e_E"], - ["d_NE", "e_E"], - ["c_N", "e_N"], - ["c_W", "e_W"], - ["c_S", "e_S"], - ["c_E", "e_E"], - ["e_N", "f_NE"], - ["e_N", "f_NW"], - ["e_W", "f_NW"], - ["e_W", "f_SW"], - ["e_S", "f_SW"], - ["e_S", "f_SE"], - ["e_E", "f_SE"], - ["e_E", "f_NE"], - ["d_NE", "f_NE"], - ["d_NW", "f_NW"], - ["d_SW", "f_SW"], - ["d_SE", "f_SE"], - ["f_NE", "g_ENE"], - ["f_NE", "g_NNE"], - ["f_NW", "g_NNW"], - ["f_NW", "g_WNW"], - ["f_SW", "g_WSW"], - ["f_SW", "g_SSW"], - ["f_SE", "g_SSE"], - ["f_SE", "g_ESE"], - ["e_N", "g_NNE"], - ["e_N", "g_NNW"], - ["e_W", "g_WNW"], - ["e_W", "g_WSW"], - ["e_S", "g_SSW"], - ["e_S", "g_SSE"], - ["e_E", "g_ESE"], - ["e_E", "g_ENE"], - ]), - ].into_iter() - .chain(regular_diagonals(f_a)) - .chain(regular_diagonals(f_abc_n)) - .chain(regular_diagonals(f_abc_w)) - .chain(regular_diagonals(f_abc_s)) - .chain(regular_diagonals(f_abc_e)) - .chain(regular_diagonals(f_g)) - .collect(); - for (soft, length, vertex_pairs) in struts { - let inv_dist = Some(-0.5 * length * length); - for pair in vertex_pairs { - let adjacent_vertices = pair.map( - |id| assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[id].clone() - ) - ); - let distance = InversiveDistanceRegulator::new(adjacent_vertices); - distance.set_point.set(SpecifiedValue::from(inv_dist)); - distance.soft.set(soft); - assembly.insert_regulator(Rc::new(distance)); - } - } - - // create the faces - const COLOR_FACE: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32]; - let faces = [ - Sphere::new( - "f_a".to_string(), - "Face A".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 0.0, -1.0, 0.0, 0.0), - ), - Sphere::new( - "f_abc_N".to_string(), - "Face ABC-N".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 1.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_abc_W".to_string(), - "Face ABC-W".to_string(), - COLOR_FACE, - engine::sphere_with_offset(-1.0, 0.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_abc_S".to_string(), - "Face ABC-S".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, -1.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_abc_E".to_string(), - "Face ABC-E".to_string(), - COLOR_FACE, - engine::sphere_with_offset(1.0, 0.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_g".to_string(), - "Face G".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 0.0, 1.0, 3.0, 0.0), - ), - ]; - for face in faces { - face.ghost().set(true); - let _ = assembly.try_insert_element(face); - } - - // make the faces planar and make them pass through their vertices - let face_incidences = [ - ("f_a", Vec::from(f_a)), - ("f_abc_N", Vec::from(f_abc_n)), - ("f_abc_W", Vec::from(f_abc_w)), - ("f_abc_S", Vec::from(f_abc_s)), - ("f_abc_E", Vec::from(f_abc_e)), - ("f_g", Vec::from(f_g)), - ]; - for (face_id, vertex_ids) in face_incidences { - // make the face planar - let face = assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[face_id].clone() - ); - let curvature_regulator = face.regulators().with_untracked( - |regs| regs.first().unwrap().clone() - ); - curvature_regulator.set_point().set(SpecifiedValue::from(Some(0.0))); - - // make the face pass through its vertices - for v_id in vertex_ids { - let vertex = assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[v_id].clone() - ); - let incidence = InversiveDistanceRegulator::new([face.clone(), vertex]); - incidence.set_point.set(SpecifiedValue::from(Some(0.0))); - assembly.insert_regulator(Rc::new(incidence)); - } - } -} - -fn load_554_aug1_inner(assembly: &Assembly) { - // create the vertices - const COLOR_A: ElementColor = [0.75_f32, 0.00_f32, 0.75_f32]; - const COLOR_B: ElementColor = [1.00_f32, 0.00_f32, 0.25_f32]; - const COLOR_C: ElementColor = [0.75_f32, 0.50_f32, 0.00_f32]; - const COLOR_D: ElementColor = [0.90_f32, 0.90_f32, 0.00_f32]; - const COLOR_E: ElementColor = [0.25_f32, 0.75_f32, 0.00_f32]; - const COLOR_F: ElementColor = [0.00_f32, 0.50_f32, 0.75_f32]; - const COLOR_G: ElementColor = [0.25_f32, 0.00_f32, 1.00_f32]; - const GRAY: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32]; - let vertices = [ - Point::new( - "a_NE".to_string(), - "A-NE".to_string(), - COLOR_A, - engine::point(0.5, 0.5, 0.0), - ), - Point::new( - "a_NW".to_string(), - "A-NW".to_string(), - COLOR_A, - engine::point(-0.5, 0.5, 0.0), - ), - Point::new( - "a_SW".to_string(), - "A-SW".to_string(), - COLOR_A, - engine::point(-0.5, -0.5, 0.0), - ), - Point::new( - "a_SE".to_string(), - "A-SE".to_string(), - COLOR_A, - engine::point(0.5, -0.5, 0.0), - ), - Point::new( - "b_NE".to_string(), - "B-NE".to_string(), - COLOR_B, - engine::point(0.7, 0.7, 0.9), - ), - Point::new( - "b_NW".to_string(), - "B-NW".to_string(), - COLOR_B, - engine::point(-0.7, 0.7, 0.9), - ), - Point::new( - "b_SW".to_string(), - "B-SW".to_string(), - COLOR_B, - engine::point(-0.7, -0.7, 0.9), - ), - Point::new( - "b_SE".to_string(), - "B-SE".to_string(), - COLOR_B, - engine::point(0.7, -0.7, 0.9), - ), - Point::new( - "y_NE".to_string(), - "Y-NE".to_string(), - GRAY, - engine::point(0.1, 0.1, 1.0), - ), - Point::new( - "y_NW".to_string(), - "Y-NW".to_string(), - GRAY, - engine::point(-0.1, 0.1, 1.0), - ), - Point::new( - "y_SW".to_string(), - "Y-SW".to_string(), - GRAY, - engine::point(-0.1, -0.1, 1.0), - ), - Point::new( - "y_SE".to_string(), - "Y-SE".to_string(), - GRAY, - engine::point(0.1, -0.1, 1.0), - ), - Point::new( - "c_N".to_string(), - "C-N".to_string(), - COLOR_C, - engine::point(0.0, 0.8, 1.4), - ), - Point::new( - "c_W".to_string(), - "C-W".to_string(), - COLOR_C, - engine::point(-0.8, 0.0, 1.4), - ), - Point::new( - "c_S".to_string(), - "C-S".to_string(), - COLOR_C, - engine::point(0.0, -0.8, 1.4), - ), - Point::new( - "c_E".to_string(), - "C-E".to_string(), - COLOR_C, - engine::point(0.8, 0.0, 1.4), - ), - Point::new( - "d_NE".to_string(), - "D-NE".to_string(), - COLOR_D, - engine::point(0.1, 0.1, 1.8), - ), - Point::new( - "d_NW".to_string(), - "D-NW".to_string(), - COLOR_D, - engine::point(-0.1, 0.1, 1.8), - ), - Point::new( - "d_SW".to_string(), - "D-SW".to_string(), - COLOR_D, - engine::point(-0.1, -0.1, 1.8), - ), - Point::new( - "d_SE".to_string(), - "D-SE".to_string(), - COLOR_D, - engine::point(0.1, -0.1, 1.8), - ), - Point::new( - "e_N".to_string(), - "E-N".to_string(), - COLOR_E, - engine::point(0.0, 0.7, 2.3), - ), - Point::new( - "e_W".to_string(), - "E-W".to_string(), - COLOR_E, - engine::point(-0.7, 0.0, 2.3), - ), - Point::new( - "e_S".to_string(), - "E-S".to_string(), - COLOR_E, - engine::point(0.0, -0.7, 2.3), - ), - Point::new( - "e_E".to_string(), - "E-E".to_string(), - COLOR_E, - engine::point(0.7, 0.0, 2.3), - ), - Point::new( - "f_NE".to_string(), - "F-NE".to_string(), - COLOR_F, - engine::point(0.2, 0.2, 2.7), - ), - Point::new( - "f_NW".to_string(), - "F-NW".to_string(), - COLOR_F, - engine::point(-0.2, 0.2, 2.7), - ), - Point::new( - "f_SW".to_string(), - "F-SW".to_string(), - COLOR_F, - engine::point(-0.2, -0.2, 2.7), - ), - Point::new( - "f_SE".to_string(), - "F-SE".to_string(), - COLOR_F, - engine::point(0.2, -0.2, 2.7), - ), - Point::new( - "g_NNE".to_string(), - "G-NNE".to_string(), - COLOR_G, - engine::point(0.5, 1.2, 3.0), - ), - Point::new( - "g_NNW".to_string(), - "G-NNW".to_string(), - COLOR_G, - engine::point(-0.5, 1.2, 3.0), - ), - Point::new( - "g_WNW".to_string(), - "G-WNW".to_string(), - COLOR_G, - engine::point(-1.2, 0.5, 3.0), - ), - Point::new( - "g_WSW".to_string(), - "G-WSW".to_string(), - COLOR_G, - engine::point(-1.2, -0.5, 3.0), - ), - Point::new( - "g_SSW".to_string(), - "G-SSW".to_string(), - COLOR_G, - engine::point(-0.5, -1.2, 3.0), - ), - Point::new( - "g_SSE".to_string(), - "G-SSE".to_string(), - COLOR_G, - engine::point(0.5, -1.2, 3.0), - ), - Point::new( - "g_ESE".to_string(), - "G-ESE".to_string(), - COLOR_G, - engine::point(1.2, -0.5, 3.0), - ), - Point::new( - "g_ENE".to_string(), - "G-ENE".to_string(), - COLOR_G, - engine::point(1.2, 0.5, 3.0), - ), - ]; - for vertex in vertices { - let _ = assembly.try_insert_element(vertex); - } - - // fix the distances between adjacent vertices - let f_a = ["a_SE", "a_SW", "a_NW", "a_NE"]; - let f_abc_n = ["a_NW", "b_NW", "c_N", "b_NE", "a_NE"]; - let f_abc_w = ["a_SW", "b_SW", "c_W", "b_NW", "a_NW"]; - let f_abc_s = ["a_SE", "b_SE", "c_S", "b_SW", "a_SW"]; - let f_abc_e = ["a_NE", "b_NE", "c_E", "b_SE", "a_SE"]; - let f_g = [ - "g_NNE", "g_NNW", "g_WNW", "g_WSW", - "g_SSW", "g_SSE", "g_ESE", "g_ENE", - ]; - let struts: Vec<_> = [ - (false, 1.0, vec![ - ["a_NE", "a_NW"], - ["a_NW", "a_SW"], - ["a_SW", "a_SE"], - ["a_SE", "a_NE"], - ["a_NE", "b_NE"], - ["a_NW", "b_NW"], - ["a_SW", "b_SW"], - ["a_SE", "b_SE"], - ["b_NE", "c_N"], - ["b_NW", "c_N"], - ["b_NW", "c_W"], - ["b_SW", "c_W"], - ["b_SW", "c_S"], - ["b_SE", "c_S"], - ["b_SE", "c_E"], - ["b_NE", "c_E"], - ["g_NNE", "g_NNW"], - ["g_NNW", "g_WNW"], - ["g_WNW", "g_WSW"], - ["g_WSW", "g_SSW"], - ["g_SSW", "g_SSE"], - ["g_SSE", "g_ESE"], - ["g_ESE", "g_ENE"], - ["g_ENE", "g_NNE"], - ]), - (true, 1.0, vec![ - ["c_N", "d_NE"], - ["c_N", "d_NW"], - ["c_W", "d_NW"], - ["c_W", "d_SW"], - ["c_S", "d_SW"], - ["c_S", "d_SE"], - ["c_E", "d_SE"], - ["c_E", "d_NE"], - ["y_NE", "b_NE"], - ["y_NW", "b_NW"], - ["y_SW", "b_SW"], - ["y_SE", "b_SE"], - ["y_NE", "c_N"], - ["y_NW", "c_N"], - ["y_NW", "c_W"], - ["y_SW", "c_W"], - ["y_SW", "c_S"], - ["y_SE", "c_S"], - ["y_SE", "c_E"], - ["y_NE", "c_E"], - ["y_NE", "d_NE"], - ["y_NW", "d_NW"], - ["y_SW", "d_SW"], - ["y_SE", "d_SE"], - ["d_NE", "e_N"], - ["d_NW", "e_N"], - ["d_NW", "e_W"], - ["d_SW", "e_W"], - ["d_SW", "e_S"], - ["d_SE", "e_S"], - ["d_SE", "e_E"], - ["d_NE", "e_E"], - ["c_N", "e_N"], - ["c_W", "e_W"], - ["c_S", "e_S"], - ["c_E", "e_E"], - ["e_N", "f_NE"], - ["e_N", "f_NW"], - ["e_W", "f_NW"], - ["e_W", "f_SW"], - ["e_S", "f_SW"], - ["e_S", "f_SE"], - ["e_E", "f_SE"], - ["e_E", "f_NE"], - ["d_NE", "f_NE"], - ["d_NW", "f_NW"], - ["d_SW", "f_SW"], - ["d_SE", "f_SE"], - ["f_NE", "g_ENE"], - ["f_NE", "g_NNE"], - ["f_NW", "g_NNW"], - ["f_NW", "g_WNW"], - ["f_SW", "g_WSW"], - ["f_SW", "g_SSW"], - ["f_SE", "g_SSE"], - ["f_SE", "g_ESE"], - ["e_N", "g_NNE"], - ["e_N", "g_NNW"], - ["e_W", "g_WNW"], - ["e_W", "g_WSW"], - ["e_S", "g_SSW"], - ["e_S", "g_SSE"], - ["e_E", "g_ESE"], - ["e_E", "g_ENE"], - ]), - ].into_iter() - .chain(regular_diagonals(f_a)) - .chain(regular_diagonals(f_abc_n)) - .chain(regular_diagonals(f_abc_w)) - .chain(regular_diagonals(f_abc_s)) - .chain(regular_diagonals(f_abc_e)) - .chain(regular_diagonals(f_g)) - .collect(); - for (soft, length, vertex_pairs) in struts { - let inv_dist = Some(-0.5 * length * length); - for pair in vertex_pairs { - let adjacent_vertices = pair.map( - |id| assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[id].clone() - ) - ); - let distance = InversiveDistanceRegulator::new(adjacent_vertices); - distance.set_point.set(SpecifiedValue::from(inv_dist)); - distance.soft.set(soft); - assembly.insert_regulator(Rc::new(distance)); - } - } - - // create the faces - const COLOR_FACE: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32]; - let faces = [ - Sphere::new( - "f_a".to_string(), - "Face A".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 0.0, -1.0, 0.0, 0.0), - ), - Sphere::new( - "f_abc_N".to_string(), - "Face ABC-N".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 1.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_abc_W".to_string(), - "Face ABC-W".to_string(), - COLOR_FACE, - engine::sphere_with_offset(-1.0, 0.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_abc_S".to_string(), - "Face ABC-S".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, -1.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_abc_E".to_string(), - "Face ABC-E".to_string(), - COLOR_FACE, - engine::sphere_with_offset(1.0, 0.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_g".to_string(), - "Face G".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 0.0, 1.0, 3.0, 0.0), - ), - ]; - for face in faces { - face.ghost().set(true); - let _ = assembly.try_insert_element(face); - } - - // make the faces planar and make them pass through their vertices - let face_incidences = [ - ("f_a", Vec::from(f_a)), - ("f_abc_N", Vec::from(f_abc_n)), - ("f_abc_W", Vec::from(f_abc_w)), - ("f_abc_S", Vec::from(f_abc_s)), - ("f_abc_E", Vec::from(f_abc_e)), - ("f_g", Vec::from(f_g)), - ]; - for (face_id, vertex_ids) in face_incidences { - // make the face planar - let face = assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[face_id].clone() - ); - let curvature_regulator = face.regulators().with_untracked( - |regs| regs.first().unwrap().clone() - ); - curvature_regulator.set_point().set(SpecifiedValue::from(Some(0.0))); - - // make the face pass through its vertices - for v_id in vertex_ids { - let vertex = assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[v_id].clone() - ); - let incidence = InversiveDistanceRegulator::new([face.clone(), vertex]); - incidence.set_point.set(SpecifiedValue::from(Some(0.0))); - assembly.insert_regulator(Rc::new(incidence)); - } - } -} - -fn load_554_aug2(assembly: &Assembly) { - // create the vertices - const COLOR_A: ElementColor = [0.75_f32, 0.00_f32, 0.75_f32]; - const COLOR_Z: ElementColor = [1.00_f32, 0.40_f32, 0.60_f32]; - const COLOR_B: ElementColor = [1.00_f32, 0.00_f32, 0.25_f32]; - const COLOR_Y: ElementColor = [1.00_f32, 0.75_f32, 0.25_f32]; - const COLOR_C: ElementColor = [0.75_f32, 0.50_f32, 0.00_f32]; - const COLOR_D: ElementColor = [0.90_f32, 0.90_f32, 0.00_f32]; - const COLOR_E: ElementColor = [0.25_f32, 0.75_f32, 0.00_f32]; - const COLOR_F: ElementColor = [0.00_f32, 0.50_f32, 0.75_f32]; - const COLOR_G: ElementColor = [0.25_f32, 0.00_f32, 1.00_f32]; - let vertices = [ - Point::new( - "a_NE".to_string(), - "A-NE".to_string(), - COLOR_A, - engine::point(0.5, 0.5, 0.0), - ), - Point::new( - "a_NW".to_string(), - "A-NW".to_string(), - COLOR_A, - engine::point(-0.5, 0.5, 0.0), - ), - Point::new( - "a_SW".to_string(), - "A-SW".to_string(), - COLOR_A, - engine::point(-0.5, -0.5, 0.0), - ), - Point::new( - "a_SE".to_string(), - "A-SE".to_string(), - COLOR_A, - engine::point(0.5, -0.5, 0.0), - ), - Point::new( - "z_S".to_string(), - "Z-S".to_string(), - COLOR_Z, - engine::point(0.0, -0.4, 0.6), - ), - Point::new( - "z_E".to_string(), - "Z-E".to_string(), - COLOR_Z, - engine::point(0.4, 0.0, 0.6), - ), - Point::new( - "b_NE".to_string(), - "B-NE".to_string(), - COLOR_B, - engine::point(0.7, 0.7, 0.9), - ), - Point::new( - "b_NW".to_string(), - "B-NW".to_string(), - COLOR_B, - engine::point(-0.7, 0.7, 0.9), - ), - Point::new( - "b_SW".to_string(), - "B-SW".to_string(), - COLOR_B, - engine::point(-0.7, -0.7, 0.9), - ), - Point::new( - "b_SE".to_string(), - "B-SE".to_string(), - COLOR_B, - engine::point(0.7, -0.7, 0.9), - ), - Point::new( - "y_NE".to_string(), - "Y-NE".to_string(), - COLOR_Y, - engine::point(0.1, 0.1, 1.0), - ), - Point::new( - "y_NW".to_string(), - "Y-NW".to_string(), - COLOR_Y, - engine::point(-0.1, 0.1, 1.0), - ), - Point::new( - "y_SW".to_string(), - "Y-SW".to_string(), - COLOR_Y, - engine::point(-0.1, -0.1, 1.0), - ), - Point::new( - "y_SE".to_string(), - "Y-SE".to_string(), - COLOR_Y, - engine::point(0.1, -0.1, 1.0), - ), - Point::new( - "c_N".to_string(), - "C-N".to_string(), - COLOR_C, - engine::point(0.0, 0.8, 1.4), - ), - Point::new( - "c_W".to_string(), - "C-W".to_string(), - COLOR_C, - engine::point(-0.8, 0.0, 1.4), - ), - Point::new( - "c_S".to_string(), - "C-S".to_string(), - COLOR_C, - engine::point(0.0, -0.8, 1.4), - ), - Point::new( - "c_E".to_string(), - "C-E".to_string(), - COLOR_C, - engine::point(0.8, 0.0, 1.4), - ), - Point::new( - "d_NE".to_string(), - "D-NE".to_string(), - COLOR_D, - engine::point(0.1, 0.1, 1.8), - ), - Point::new( - "d_NW".to_string(), - "D-NW".to_string(), - COLOR_D, - engine::point(-0.1, 0.1, 1.8), - ), - Point::new( - "d_SW".to_string(), - "D-SW".to_string(), - COLOR_D, - engine::point(-0.1, -0.1, 1.8), - ), - Point::new( - "d_SE".to_string(), - "D-SE".to_string(), - COLOR_D, - engine::point(0.1, -0.1, 1.8), - ), - Point::new( - "e_N".to_string(), - "E-N".to_string(), - COLOR_E, - engine::point(0.0, 0.7, 2.3), - ), - Point::new( - "e_W".to_string(), - "E-W".to_string(), - COLOR_E, - engine::point(-0.7, 0.0, 2.3), - ), - Point::new( - "e_S".to_string(), - "E-S".to_string(), - COLOR_E, - engine::point(0.0, -0.7, 2.3), - ), - Point::new( - "e_E".to_string(), - "E-E".to_string(), - COLOR_E, - engine::point(0.7, 0.0, 2.3), - ), - Point::new( - "f_NE".to_string(), - "F-NE".to_string(), - COLOR_F, - engine::point(0.2, 0.2, 2.7), - ), - Point::new( - "f_NW".to_string(), - "F-NW".to_string(), - COLOR_F, - engine::point(-0.2, 0.2, 2.7), - ), - Point::new( - "f_SW".to_string(), - "F-SW".to_string(), - COLOR_F, - engine::point(-0.2, -0.2, 2.7), - ), - Point::new( - "f_SE".to_string(), - "F-SE".to_string(), - COLOR_F, - engine::point(0.2, -0.2, 2.7), - ), - Point::new( - "g_NNE".to_string(), - "G-NNE".to_string(), - COLOR_G, - engine::point(0.5, 1.2, 3.0), - ), - Point::new( - "g_NNW".to_string(), - "G-NNW".to_string(), - COLOR_G, - engine::point(-0.5, 1.2, 3.0), - ), - Point::new( - "g_WNW".to_string(), - "G-WNW".to_string(), - COLOR_G, - engine::point(-1.2, 0.5, 3.0), - ), - Point::new( - "g_WSW".to_string(), - "G-WSW".to_string(), - COLOR_G, - engine::point(-1.2, -0.5, 3.0), - ), - Point::new( - "g_SSW".to_string(), - "G-SSW".to_string(), - COLOR_G, - engine::point(-0.5, -1.2, 3.0), - ), - Point::new( - "g_SSE".to_string(), - "G-SSE".to_string(), - COLOR_G, - engine::point(0.5, -1.2, 3.0), - ), - Point::new( - "g_ESE".to_string(), - "G-ESE".to_string(), - COLOR_G, - engine::point(1.2, -0.5, 3.0), - ), - Point::new( - "g_ENE".to_string(), - "G-ENE".to_string(), - COLOR_G, - engine::point(1.2, 0.5, 3.0), - ), - ]; - for vertex in vertices { - let _ = assembly.try_insert_element(vertex); - } - - // fix the distances between adjacent vertices - let f_a = ["a_SE", "a_SW", "a_NW", "a_NE"]; - let f_abc_n = ["a_NW", "b_NW", "c_N", "b_NE", "a_NE"]; - let f_abc_w = ["a_SW", "b_SW", "c_W", "b_NW", "a_NW"]; - let f_g = [ - "g_NNE", "g_NNW", "g_WNW", "g_WSW", - "g_SSW", "g_SSE", "g_ESE", "g_ENE", - ]; - let struts: Vec<_> = [ - (false, 1.0, vec![ - ["a_NE", "a_NW"], - ["a_NW", "a_SW"], - ["a_SW", "a_SE"], - ["a_SE", "a_NE"], - ["a_NE", "b_NE"], - ["a_NW", "b_NW"], - ["a_SW", "b_SW"], - ["b_NE", "c_N"], - ["b_NW", "c_N"], - ["b_NW", "c_W"], - ["b_SW", "c_W"], - ["g_NNE", "g_NNW"], - ["g_NNW", "g_WNW"], - ["g_WNW", "g_WSW"], - ["g_WSW", "g_SSW"], - ["g_SSW", "g_SSE"], - ["g_SSE", "g_ESE"], - ["g_ESE", "g_ENE"], - ["g_ENE", "g_NNE"], - ]), - (true, 1.0, vec![ - ["a_SE", "b_SE"], - ["b_SW", "c_S"], - ["b_SE", "c_S"], - ["b_SE", "c_E"], - ["b_NE", "c_E"], - ["z_S", "a_SW"], - ["z_S", "a_SE"], - ["z_E", "a_SE"], - ["z_E", "a_NE"], - ["z_S", "b_SW"], - ["z_S", "b_SE"], - ["z_E", "b_SE"], - ["z_E", "b_NE"], - ["z_S", "c_S"], - ["z_E", "c_E"], - ["c_N", "d_NE"], - ["c_N", "d_NW"], - ["c_W", "d_NW"], - ["c_W", "d_SW"], - ["c_S", "d_SW"], - ["c_S", "d_SE"], - ["c_E", "d_SE"], - ["c_E", "d_NE"], - ["y_NE", "b_NE"], - ["y_NW", "b_NW"], - ["y_SW", "b_SW"], - ["y_SE", "b_SE"], - ["y_NE", "c_N"], - ["y_NW", "c_N"], - ["y_NW", "c_W"], - ["y_SW", "c_W"], - ["y_SW", "c_S"], - ["y_SE", "c_S"], - ["y_SE", "c_E"], - ["y_NE", "c_E"], - ["y_NE", "d_NE"], - ["y_NW", "d_NW"], - ["y_SW", "d_SW"], - ["y_SE", "d_SE"], - ["d_NE", "e_N"], - ["d_NW", "e_N"], - ["d_NW", "e_W"], - ["d_SW", "e_W"], - ["d_SW", "e_S"], - ["d_SE", "e_S"], - ["d_SE", "e_E"], - ["d_NE", "e_E"], - ["c_N", "e_N"], - ["c_W", "e_W"], - ["c_S", "e_S"], - ["c_E", "e_E"], - ["e_N", "f_NE"], - ["e_N", "f_NW"], - ["e_W", "f_NW"], - ["e_W", "f_SW"], - ["e_S", "f_SW"], - ["e_S", "f_SE"], - ["e_E", "f_SE"], - ["e_E", "f_NE"], - ["d_NE", "f_NE"], - ["d_NW", "f_NW"], - ["d_SW", "f_SW"], - ["d_SE", "f_SE"], - ["f_NE", "g_ENE"], - ["f_NE", "g_NNE"], - ["f_NW", "g_NNW"], - ["f_NW", "g_WNW"], - ["f_SW", "g_WSW"], - ["f_SW", "g_SSW"], - ["f_SE", "g_SSE"], - ["f_SE", "g_ESE"], - ["e_N", "g_NNE"], - ["e_N", "g_NNW"], - ["e_W", "g_WNW"], - ["e_W", "g_WSW"], - ["e_S", "g_SSW"], - ["e_S", "g_SSE"], - ["e_E", "g_ESE"], - ["e_E", "g_ENE"], - ]), - ].into_iter() - .chain(regular_diagonals(f_a)) - .chain(regular_diagonals(f_abc_n)) - .chain(regular_diagonals(f_abc_w)) - .chain(regular_diagonals(f_g)) - .collect(); - for (soft, length, vertex_pairs) in struts { - let inv_dist = Some(-0.5 * length * length); - for pair in vertex_pairs { - let adjacent_vertices = pair.map( - |id| assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[id].clone() - ) - ); - let distance = InversiveDistanceRegulator::new(adjacent_vertices); - distance.set_point.set(SpecifiedValue::from(inv_dist)); - distance.soft.set(soft); - assembly.insert_regulator(Rc::new(distance)); - } - } - - // create the faces - const COLOR_FACE: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32]; - let faces = [ - Sphere::new( - "f_a".to_string(), - "Face A".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 0.0, -1.0, 0.0, 0.0), - ), - Sphere::new( - "f_abc_N".to_string(), - "Face ABC-N".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 1.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_abc_W".to_string(), - "Face ABC-W".to_string(), - COLOR_FACE, - engine::sphere_with_offset(-1.0, 0.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_g".to_string(), - "Face G".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 0.0, 1.0, 3.0, 0.0), - ), - ]; - for face in faces { - face.ghost().set(true); - let _ = assembly.try_insert_element(face); - } - - // make the faces planar and make them pass through their vertices - let face_incidences = [ - ("f_a", Vec::from(f_a)), - ("f_abc_N", Vec::from(f_abc_n)), - ("f_abc_W", Vec::from(f_abc_w)), - ("f_g", Vec::from(f_g)), - ]; - for (face_id, vertex_ids) in face_incidences { - // make the face planar - let face = assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[face_id].clone() - ); - let curvature_regulator = face.regulators().with_untracked( - |regs| regs.first().unwrap().clone() - ); - curvature_regulator.set_point().set(SpecifiedValue::from(Some(0.0))); - - // make the face pass through its vertices - for v_id in vertex_ids { - let vertex = assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[v_id].clone() - ); - let incidence = InversiveDistanceRegulator::new([face.clone(), vertex]); - incidence.set_point.set(SpecifiedValue::from(Some(0.0))); - assembly.insert_regulator(Rc::new(incidence)); - } - } -} - -fn load_554_domed(assembly: &Assembly) { - // create the vertices - const COLOR_A: ElementColor = [0.75_f32, 0.00_f32, 0.75_f32]; - const COLOR_Z: ElementColor = [1.00_f32, 0.40_f32, 0.60_f32]; - const COLOR_B: ElementColor = [1.00_f32, 0.00_f32, 0.25_f32]; - const COLOR_Y: ElementColor = [1.00_f32, 0.75_f32, 0.25_f32]; - const COLOR_C: ElementColor = [0.75_f32, 0.50_f32, 0.00_f32]; - const COLOR_D: ElementColor = [0.90_f32, 0.90_f32, 0.00_f32]; - const COLOR_E: ElementColor = [0.25_f32, 0.75_f32, 0.00_f32]; - const COLOR_F: ElementColor = [0.00_f32, 0.50_f32, 0.75_f32]; - const COLOR_G: ElementColor = [0.25_f32, 0.00_f32, 1.00_f32]; - const COLOR_H: ElementColor = COLOR_A; - const COLOR_I: ElementColor = COLOR_B; - const COLOR_J: ElementColor = COLOR_C; - let vertices = [ - Point::new( - "a_NE".to_string(), - "A-NE".to_string(), - COLOR_A, - engine::point(0.5, 0.5, 0.0), - ), - Point::new( - "a_NW".to_string(), - "A-NW".to_string(), - COLOR_A, - engine::point(-0.5, 0.5, 0.0), - ), - Point::new( - "a_SW".to_string(), - "A-SW".to_string(), - COLOR_A, - engine::point(-0.5, -0.5, 0.0), - ), - Point::new( - "a_SE".to_string(), - "A-SE".to_string(), - COLOR_A, - engine::point(0.5, -0.5, 0.0), - ), - Point::new( - "z_S".to_string(), - "Z-S".to_string(), - COLOR_Z, - engine::point(0.0, -0.4, 0.6), - ), - Point::new( - "z_E".to_string(), - "Z-E".to_string(), - COLOR_Z, - engine::point(0.4, 0.0, 0.6), - ), - Point::new( - "b_NE".to_string(), - "B-NE".to_string(), - COLOR_B, - engine::point(0.7, 0.7, 0.9), - ), - Point::new( - "b_NW".to_string(), - "B-NW".to_string(), - COLOR_B, - engine::point(-0.7, 0.7, 0.9), - ), - Point::new( - "b_SW".to_string(), - "B-SW".to_string(), - COLOR_B, - engine::point(-0.7, -0.7, 0.9), - ), - Point::new( - "b_SE".to_string(), - "B-SE".to_string(), - COLOR_B, - engine::point(0.7, -0.7, 0.9), - ), - Point::new( - "y_NE".to_string(), - "Y-NE".to_string(), - COLOR_Y, - engine::point(0.1, 0.1, 1.0), - ), - Point::new( - "y_NW".to_string(), - "Y-NW".to_string(), - COLOR_Y, - engine::point(-0.1, 0.1, 1.0), - ), - Point::new( - "y_SW".to_string(), - "Y-SW".to_string(), - COLOR_Y, - engine::point(-0.1, -0.1, 1.0), - ), - Point::new( - "y_SE".to_string(), - "Y-SE".to_string(), - COLOR_Y, - engine::point(0.1, -0.1, 1.0), - ), - Point::new( - "c_N".to_string(), - "C-N".to_string(), - COLOR_C, - engine::point(0.0, 0.8, 1.4), - ), - Point::new( - "c_W".to_string(), - "C-W".to_string(), - COLOR_C, - engine::point(-0.8, 0.0, 1.4), - ), - Point::new( - "c_S".to_string(), - "C-S".to_string(), - COLOR_C, - engine::point(0.0, -0.8, 1.4), - ), - Point::new( - "c_E".to_string(), - "C-E".to_string(), - COLOR_C, - engine::point(0.8, 0.0, 1.4), - ), - Point::new( - "d_NE".to_string(), - "D-NE".to_string(), - COLOR_D, - engine::point(0.1, 0.1, 1.8), - ), - Point::new( - "d_NW".to_string(), - "D-NW".to_string(), - COLOR_D, - engine::point(-0.1, 0.1, 1.8), - ), - Point::new( - "d_SW".to_string(), - "D-SW".to_string(), - COLOR_D, - engine::point(-0.1, -0.1, 1.8), - ), - Point::new( - "d_SE".to_string(), - "D-SE".to_string(), - COLOR_D, - engine::point(0.1, -0.1, 1.8), - ), - Point::new( - "e_N".to_string(), - "E-N".to_string(), - COLOR_E, - engine::point(0.0, 0.7, 2.3), - ), - Point::new( - "e_W".to_string(), - "E-W".to_string(), - COLOR_E, - engine::point(-0.7, 0.0, 2.3), - ), - Point::new( - "e_S".to_string(), - "E-S".to_string(), - COLOR_E, - engine::point(0.0, -0.7, 2.3), - ), - Point::new( - "e_E".to_string(), - "E-E".to_string(), - COLOR_E, - engine::point(0.7, 0.0, 2.3), - ), - Point::new( - "f_NE".to_string(), - "F-NE".to_string(), - COLOR_F, - engine::point(0.2, 0.2, 2.7), - ), - Point::new( - "f_NW".to_string(), - "F-NW".to_string(), - COLOR_F, - engine::point(-0.2, 0.2, 2.7), - ), - Point::new( - "f_SW".to_string(), - "F-SW".to_string(), - COLOR_F, - engine::point(-0.2, -0.2, 2.7), - ), - Point::new( - "f_SE".to_string(), - "F-SE".to_string(), - COLOR_F, - engine::point(0.2, -0.2, 2.7), - ), - Point::new( - "g_NNE".to_string(), - "G-NNE".to_string(), - COLOR_G, - engine::point(0.5, 1.2, 3.0), - ), - Point::new( - "g_NNW".to_string(), - "G-NNW".to_string(), - COLOR_G, - engine::point(-0.5, 1.2, 3.0), - ), - Point::new( - "g_WNW".to_string(), - "G-WNW".to_string(), - COLOR_G, - engine::point(-1.2, 0.5, 3.0), - ), - Point::new( - "g_WSW".to_string(), - "G-WSW".to_string(), - COLOR_G, - engine::point(-1.2, -0.5, 3.0), - ), - Point::new( - "g_SSW".to_string(), - "G-SSW".to_string(), - COLOR_G, - engine::point(-0.5, -1.2, 3.0), - ), - Point::new( - "g_SSE".to_string(), - "G-SSE".to_string(), - COLOR_G, - engine::point(0.5, -1.2, 3.0), - ), - Point::new( - "g_ESE".to_string(), - "G-ESE".to_string(), - COLOR_G, - engine::point(1.2, -0.5, 3.0), - ), - Point::new( - "g_ENE".to_string(), - "G-ENE".to_string(), - COLOR_G, - engine::point(1.2, 0.5, 3.0), - ), - Point::new( - "h_N".to_string(), - "H-N".to_string(), - COLOR_H, - engine::point(0.0, 1.1, 3.6), - ), - Point::new( - "h_W".to_string(), - "H-W".to_string(), - COLOR_H, - engine::point(-1.1, 0.0, 3.6), - ), - Point::new( - "h_S".to_string(), - "H-S".to_string(), - COLOR_H, - engine::point(0.0, -1.1, 3.6), - ), - Point::new( - "h_E".to_string(), - "H-E".to_string(), - COLOR_H, - engine::point(1.1, 0.0, 3.6), - ), - Point::new( - "i_NE".to_string(), - "I-NE".to_string(), - COLOR_I, - engine::point(0.5, 0.5, 3.5), - ), - Point::new( - "i_NW".to_string(), - "I-NW".to_string(), - COLOR_I, - engine::point(-0.5, 0.5, 3.5), - ), - Point::new( - "i_SW".to_string(), - "I-SW".to_string(), - COLOR_I, - engine::point(-0.5, -0.5, 3.5), - ), - Point::new( - "i_SE".to_string(), - "I-SE".to_string(), - COLOR_I, - engine::point(0.5, -0.5, 3.5), - ), - Point::new( - "j".to_string(), - "J".to_string(), - COLOR_J, - engine::point(0.0, 0.0, 4.3), - ), - ]; - for vertex in vertices { - let _ = assembly.try_insert_element(vertex); - } - - // fix the distances between adjacent vertices - let f_a = ["a_SE", "a_SW", "a_NW", "a_NE"]; - let f_abc_n = ["a_NW", "b_NW", "c_N", "b_NE", "a_NE"]; - let f_abc_w = ["a_SW", "b_SW", "c_W", "b_NW", "a_NW"]; - let struts: Vec<_> = [ - (false, 1.0, vec![ - ["a_NE", "a_NW"], - ["a_NW", "a_SW"], - ["a_SW", "a_SE"], - ["a_SE", "a_NE"], - ["a_NE", "b_NE"], - ["a_NW", "b_NW"], - ["a_SW", "b_SW"], - ["b_NE", "c_N"], - ["b_NW", "c_N"], - ["b_NW", "c_W"], - ["b_SW", "c_W"], - ]), - (true, 1.0, vec![ - ["a_SE", "b_SE"], - ["b_SW", "c_S"], - ["b_SE", "c_S"], - ["b_SE", "c_E"], - ["b_NE", "c_E"], - ["z_S", "a_SW"], - ["z_S", "a_SE"], - ["z_E", "a_SE"], - ["z_E", "a_NE"], - ["z_S", "b_SW"], - ["z_S", "b_SE"], - ["z_E", "b_SE"], - ["z_E", "b_NE"], - ["z_S", "c_S"], - ["z_E", "c_E"], - ["c_N", "d_NE"], - ["c_N", "d_NW"], - ["c_W", "d_NW"], - ["c_W", "d_SW"], - ["c_S", "d_SW"], - ["c_S", "d_SE"], - ["c_E", "d_SE"], - ["c_E", "d_NE"], - ["y_NE", "b_NE"], - ["y_NW", "b_NW"], - ["y_SW", "b_SW"], - ["y_SE", "b_SE"], - ["y_NE", "c_N"], - ["y_NW", "c_N"], - ["y_NW", "c_W"], - ["y_SW", "c_W"], - ["y_SW", "c_S"], - ["y_SE", "c_S"], - ["y_SE", "c_E"], - ["y_NE", "c_E"], - ["y_NE", "d_NE"], - ["y_NW", "d_NW"], - ["y_SW", "d_SW"], - ["y_SE", "d_SE"], - ["d_NE", "e_N"], - ["d_NW", "e_N"], - ["d_NW", "e_W"], - ["d_SW", "e_W"], - ["d_SW", "e_S"], - ["d_SE", "e_S"], - ["d_SE", "e_E"], - ["d_NE", "e_E"], - ["c_N", "e_N"], - ["c_W", "e_W"], - ["c_S", "e_S"], - ["c_E", "e_E"], - ["e_N", "f_NE"], - ["e_N", "f_NW"], - ["e_W", "f_NW"], - ["e_W", "f_SW"], - ["e_S", "f_SW"], - ["e_S", "f_SE"], - ["e_E", "f_SE"], - ["e_E", "f_NE"], - ["d_NE", "f_NE"], - ["d_NW", "f_NW"], - ["d_SW", "f_SW"], - ["d_SE", "f_SE"], - ["f_NE", "g_ENE"], - ["f_NE", "g_NNE"], - ["f_NW", "g_NNW"], - ["f_NW", "g_WNW"], - ["f_SW", "g_WSW"], - ["f_SW", "g_SSW"], - ["f_SE", "g_SSE"], - ["f_SE", "g_ESE"], - ["e_N", "g_NNE"], - ["e_N", "g_NNW"], - ["e_W", "g_WNW"], - ["e_W", "g_WSW"], - ["e_S", "g_SSW"], - ["e_S", "g_SSE"], - ["e_E", "g_ESE"], - ["e_E", "g_ENE"], - ["g_NNE", "g_NNW"], - ["g_NNW", "g_WNW"], - ["g_WNW", "g_WSW"], - ["g_WSW", "g_SSW"], - ["g_SSW", "g_SSE"], - ["g_SSE", "g_ESE"], - ["g_ESE", "g_ENE"], - ["g_ENE", "g_NNE"], - ["g_NNE", "h_N"], - ["g_NNW", "h_N"], - ["g_WNW", "h_W"], - ["g_WSW", "h_W"], - ["g_SSW", "h_S"], - ["g_SSE", "h_S"], - ["g_ESE", "h_E"], - ["g_ENE", "h_E"], - ["h_N", "i_NE"], - ["h_N", "i_NW"], - ["h_W", "i_NW"], - ["h_W", "i_SW"], - ["h_S", "i_SW"], - ["h_S", "i_SE"], - ["h_E", "i_SE"], - ["h_E", "i_NE"], - ["g_NNE", "i_NE"], - ["g_NNW", "i_NW"], - ["g_WNW", "i_NW"], - ["g_WSW", "i_SW"], - ["g_SSW", "i_SW"], - ["g_SSE", "i_SE"], - ["g_ESE", "i_SE"], - ["g_ENE", "i_NE"], - ["i_NE", "i_NW"], - ["i_NW", "i_SW"], - ["i_SW", "i_SE"], - ["i_SE", "i_NE"], - ["i_NE", "j"], - ["i_NW", "j"], - ["i_SW", "j"], - ["i_SE", "j"], - ]), - ].into_iter() - .chain(regular_diagonals(f_a)) - .chain(regular_diagonals(f_abc_n)) - .chain(regular_diagonals(f_abc_w)) - .collect(); - for (soft, length, vertex_pairs) in struts { - let inv_dist = Some(-0.5 * length * length); - for pair in vertex_pairs { - let adjacent_vertices = pair.map( - |id| assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[id].clone() - ) - ); - let distance = InversiveDistanceRegulator::new(adjacent_vertices); - distance.set_point.set(SpecifiedValue::from(inv_dist)); - distance.soft.set(soft); - assembly.insert_regulator(Rc::new(distance)); - } - } - - // create the faces - const COLOR_FACE: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32]; - let faces = [ - Sphere::new( - "f_a".to_string(), - "Face A".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 0.0, -1.0, 0.0, 0.0), - ), - Sphere::new( - "f_abc_N".to_string(), - "Face ABC-N".to_string(), - COLOR_FACE, - engine::sphere_with_offset(0.0, 1.0, 0.0, 0.5, 0.0), - ), - Sphere::new( - "f_abc_W".to_string(), - "Face ABC-W".to_string(), - COLOR_FACE, - engine::sphere_with_offset(-1.0, 0.0, 0.0, 0.5, 0.0), - ), - ]; - for face in faces { - face.ghost().set(true); - let _ = assembly.try_insert_element(face); - } - - // make the faces planar and make them pass through their vertices - let face_incidences = [ - ("f_a", Vec::from(f_a)), - ("f_abc_N", Vec::from(f_abc_n)), - ("f_abc_W", Vec::from(f_abc_w)), - ]; - for (face_id, vertex_ids) in face_incidences { - // make the face planar - let face = assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[face_id].clone() - ); - let curvature_regulator = face.regulators().with_untracked( - |regs| regs.first().unwrap().clone() - ); - curvature_regulator.set_point().set(SpecifiedValue::from(Some(0.0))); - - // make the face pass through its vertices - for v_id in vertex_ids { - let vertex = assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[v_id].clone() - ); - let incidence = InversiveDistanceRegulator::new([face.clone(), vertex]); - incidence.set_point.set(SpecifiedValue::from(Some(0.0))); - assembly.insert_regulator(Rc::new(incidence)); - } - } -} - -fn load_554_domed_pinned(assembly: &Assembly) { - // create the vertices - const COLOR_A: ElementColor = [0.75_f32, 0.00_f32, 0.75_f32]; - const COLOR_Z: ElementColor = [1.00_f32, 0.40_f32, 0.60_f32]; - const COLOR_B: ElementColor = [1.00_f32, 0.00_f32, 0.25_f32]; - const COLOR_Y: ElementColor = [1.00_f32, 0.75_f32, 0.25_f32]; - const COLOR_C: ElementColor = [0.75_f32, 0.50_f32, 0.00_f32]; - const COLOR_D: ElementColor = [0.90_f32, 0.90_f32, 0.00_f32]; - const COLOR_E: ElementColor = [0.25_f32, 0.75_f32, 0.00_f32]; - const COLOR_F: ElementColor = [0.00_f32, 0.50_f32, 0.75_f32]; - const COLOR_G: ElementColor = [0.25_f32, 0.00_f32, 1.00_f32]; - const COLOR_H: ElementColor = COLOR_A; - const COLOR_I: ElementColor = COLOR_B; - const COLOR_J: ElementColor = COLOR_C; - let vertices = [ - Point::new( - "a_NE".to_string(), - "A-NE".to_string(), - COLOR_A, - engine::point(0.5, 0.5, 0.0), - ), - Point::new( - "a_NW".to_string(), - "A-NW".to_string(), - COLOR_A, - engine::point(-0.5, 0.5, 0.0), - ), - Point::new( - "a_SW".to_string(), - "A-SW".to_string(), - COLOR_A, - engine::point(-0.5, -0.5, 0.0), - ), - Point::new( - "a_SE".to_string(), - "A-SE".to_string(), - COLOR_A, - engine::point(0.5, -0.5, 0.0), - ), - Point::new( - "z_S".to_string(), - "Z-S".to_string(), - COLOR_Z, - engine::point(0.0, -0.4, 0.6), - ), - Point::new( - "z_E".to_string(), - "Z-E".to_string(), - COLOR_Z, - engine::point(0.4, 0.0, 0.6), - ), - Point::new( - "b_NE".to_string(), - "B-NE".to_string(), - COLOR_B, - engine::point(0.7, 0.7, 0.9), - ), - Point::new( - "b_NW".to_string(), - "B-NW".to_string(), - COLOR_B, - engine::point(-0.7, 0.7, 0.9), - ), - Point::new( - "b_SW".to_string(), - "B-SW".to_string(), - COLOR_B, - engine::point(-0.7, -0.7, 0.9), - ), - Point::new( - "b_SE".to_string(), - "B-SE".to_string(), - COLOR_B, - engine::point(0.7, -0.7, 0.9), - ), - Point::new( - "y_NE".to_string(), - "Y-NE".to_string(), - COLOR_Y, - engine::point(0.1, 0.1, 1.0), - ), - Point::new( - "y_NW".to_string(), - "Y-NW".to_string(), - COLOR_Y, - engine::point(-0.1, 0.1, 1.0), - ), - Point::new( - "y_SW".to_string(), - "Y-SW".to_string(), - COLOR_Y, - engine::point(-0.1, -0.1, 1.0), - ), - Point::new( - "y_SE".to_string(), - "Y-SE".to_string(), - COLOR_Y, - engine::point(0.1, -0.1, 1.0), - ), - Point::new( - "c_N".to_string(), - "C-N".to_string(), - COLOR_C, - engine::point(0.0, 0.8, 1.4), - ), - Point::new( - "c_W".to_string(), - "C-W".to_string(), - COLOR_C, - engine::point(-0.8, 0.0, 1.4), - ), - Point::new( - "c_S".to_string(), - "C-S".to_string(), - COLOR_C, - engine::point(0.0, -0.8, 1.4), - ), - Point::new( - "c_E".to_string(), - "C-E".to_string(), - COLOR_C, - engine::point(0.8, 0.0, 1.4), - ), - Point::new( - "d_NE".to_string(), - "D-NE".to_string(), - COLOR_D, - engine::point(0.1, 0.1, 1.8), - ), - Point::new( - "d_NW".to_string(), - "D-NW".to_string(), - COLOR_D, - engine::point(-0.1, 0.1, 1.8), - ), - Point::new( - "d_SW".to_string(), - "D-SW".to_string(), - COLOR_D, - engine::point(-0.1, -0.1, 1.8), - ), - Point::new( - "d_SE".to_string(), - "D-SE".to_string(), - COLOR_D, - engine::point(0.1, -0.1, 1.8), - ), - Point::new( - "e_N".to_string(), - "E-N".to_string(), - COLOR_E, - engine::point(0.0, 0.7, 2.3), - ), - Point::new( - "e_W".to_string(), - "E-W".to_string(), - COLOR_E, - engine::point(-0.7, 0.0, 2.3), - ), - Point::new( - "e_S".to_string(), - "E-S".to_string(), - COLOR_E, - engine::point(0.0, -0.7, 2.3), - ), - Point::new( - "e_E".to_string(), - "E-E".to_string(), - COLOR_E, - engine::point(0.7, 0.0, 2.3), - ), - Point::new( - "f_NE".to_string(), - "F-NE".to_string(), - COLOR_F, - engine::point(0.2, 0.2, 2.7), - ), - Point::new( - "f_NW".to_string(), - "F-NW".to_string(), - COLOR_F, - engine::point(-0.2, 0.2, 2.7), - ), - Point::new( - "f_SW".to_string(), - "F-SW".to_string(), - COLOR_F, - engine::point(-0.2, -0.2, 2.7), - ), - Point::new( - "f_SE".to_string(), - "F-SE".to_string(), - COLOR_F, - engine::point(0.2, -0.2, 2.7), - ), - Point::new( - "g_NNE".to_string(), - "G-NNE".to_string(), - COLOR_G, - engine::point(0.5, 1.2, 3.0), - ), - Point::new( - "g_NNW".to_string(), - "G-NNW".to_string(), - COLOR_G, - engine::point(-0.5, 1.2, 3.0), - ), - Point::new( - "g_WNW".to_string(), - "G-WNW".to_string(), - COLOR_G, - engine::point(-1.2, 0.5, 3.0), - ), - Point::new( - "g_WSW".to_string(), - "G-WSW".to_string(), - COLOR_G, - engine::point(-1.2, -0.5, 3.0), - ), - Point::new( - "g_SSW".to_string(), - "G-SSW".to_string(), - COLOR_G, - engine::point(-0.5, -1.2, 3.0), - ), - Point::new( - "g_SSE".to_string(), - "G-SSE".to_string(), - COLOR_G, - engine::point(0.5, -1.2, 3.0), - ), - Point::new( - "g_ESE".to_string(), - "G-ESE".to_string(), - COLOR_G, - engine::point(1.2, -0.5, 3.0), - ), - Point::new( - "g_ENE".to_string(), - "G-ENE".to_string(), - COLOR_G, - engine::point(1.2, 0.5, 3.0), - ), - Point::new( - "h_N".to_string(), - "H-N".to_string(), - COLOR_H, - engine::point(0.0, 0.6, 2.9), - ), - Point::new( - "h_W".to_string(), - "H-W".to_string(), - COLOR_H, - engine::point(-0.6, 0.0, 2.9), - ), - Point::new( - "h_S".to_string(), - "H-S".to_string(), - COLOR_H, - engine::point(0.0, -0.6, 2.9), - ), - Point::new( - "h_E".to_string(), - "H-E".to_string(), - COLOR_H, - engine::point(0.6, 0.0, 2.9), - ), - Point::new( - "i_NE".to_string(), - "I-NE".to_string(), - COLOR_I, - engine::point(0.5, 0.5, 3.5), - ), - Point::new( - "i_NW".to_string(), - "I-NW".to_string(), - COLOR_I, - engine::point(-0.5, 0.5, 3.5), - ), - Point::new( - "i_SW".to_string(), - "I-SW".to_string(), - COLOR_I, - engine::point(-0.5, -0.5, 3.5), - ), - Point::new( - "i_SE".to_string(), - "I-SE".to_string(), - COLOR_I, - engine::point(0.5, -0.5, 3.5), - ), - Point::new( - "j".to_string(), - "J".to_string(), - COLOR_J, - engine::point(0.0, 0.0, 3.0), - ), - ]; - for vertex in vertices { - let _ = assembly.try_insert_element(vertex); - } - - // fix the distances between adjacent vertices - let struts: Vec<_> = vec![ - (false, 1.0, vec![ - ["a_SE", "b_SE"], - ["b_SW", "c_S"], - ["b_SE", "c_S"], - ["b_SE", "c_E"], - ["b_NE", "c_E"], - ["z_S", "a_SW"], - ["z_S", "a_SE"], - ["z_E", "a_SE"], - ["z_E", "a_NE"], - ["z_S", "b_SW"], - ["z_S", "b_SE"], - ["z_E", "b_SE"], - ["z_E", "b_NE"], - ["z_S", "c_S"], - ["z_E", "c_E"], - ["c_N", "d_NE"], - ["c_N", "d_NW"], - ["c_W", "d_NW"], - ["c_W", "d_SW"], - ["c_S", "d_SW"], - ["c_S", "d_SE"], - ["c_E", "d_SE"], - ["c_E", "d_NE"], - ["y_NE", "b_NE"], - ["y_NW", "b_NW"], - ["y_SW", "b_SW"], - ["y_SE", "b_SE"], - ["y_NE", "c_N"], - ["y_NW", "c_N"], - ["y_NW", "c_W"], - ["y_SW", "c_W"], - ["y_SW", "c_S"], - ["y_SE", "c_S"], - ["y_SE", "c_E"], - ["y_NE", "c_E"], - ["y_NE", "d_NE"], - ["y_NW", "d_NW"], - ["y_SW", "d_SW"], - ["y_SE", "d_SE"], - ["d_NE", "e_N"], - ["d_NW", "e_N"], - ["d_NW", "e_W"], - ["d_SW", "e_W"], - ["d_SW", "e_S"], - ["d_SE", "e_S"], - ["d_SE", "e_E"], - ["d_NE", "e_E"], - ["c_N", "e_N"], - ["c_W", "e_W"], - ["c_S", "e_S"], - ["c_E", "e_E"], - ["e_N", "f_NE"], - ["e_N", "f_NW"], - ["e_W", "f_NW"], - ["e_W", "f_SW"], - ["e_S", "f_SW"], - ["e_S", "f_SE"], - ["e_E", "f_SE"], - ["e_E", "f_NE"], - ["d_NE", "f_NE"], - ["d_NW", "f_NW"], - ["d_SW", "f_SW"], - ["d_SE", "f_SE"], - ["f_NE", "g_ENE"], - ["f_NE", "g_NNE"], - ["f_NW", "g_NNW"], - ["f_NW", "g_WNW"], - ["f_SW", "g_WSW"], - ["f_SW", "g_SSW"], - ["f_SE", "g_SSE"], - ["f_SE", "g_ESE"], - ["e_N", "g_NNE"], - ["e_N", "g_NNW"], - ["e_W", "g_WNW"], - ["e_W", "g_WSW"], - ["e_S", "g_SSW"], - ["e_S", "g_SSE"], - ["e_E", "g_ESE"], - ["e_E", "g_ENE"], - ["g_NNE", "g_NNW"], - ["g_NNW", "g_WNW"], - ["g_WNW", "g_WSW"], - ["g_WSW", "g_SSW"], - ["g_SSW", "g_SSE"], - ["g_SSE", "g_ESE"], - ["g_ESE", "g_ENE"], - ["g_ENE", "g_NNE"], - ["g_NNE", "h_N"], - ["g_NNW", "h_N"], - ["g_WNW", "h_W"], - ["g_WSW", "h_W"], - ["g_SSW", "h_S"], - ["g_SSE", "h_S"], - ["g_ESE", "h_E"], - ["g_ENE", "h_E"], - ["h_N", "i_NE"], - ["h_N", "i_NW"], - ["h_W", "i_NW"], - ["h_W", "i_SW"], - ["h_S", "i_SW"], - ["h_S", "i_SE"], - ["h_E", "i_SE"], - ["h_E", "i_NE"], - ["g_NNE", "i_NE"], - ["g_NNW", "i_NW"], - ["g_WNW", "i_NW"], - ["g_WSW", "i_SW"], - ["g_SSW", "i_SW"], - ["g_SSE", "i_SE"], - ["g_ESE", "i_SE"], - ["g_ENE", "i_NE"], - ["i_NE", "i_NW"], - ["i_NW", "i_SW"], - ["i_SW", "i_SE"], - ["i_SE", "i_NE"], - ["i_NE", "j"], - ["i_NW", "j"], - ["i_SW", "j"], - ["i_SE", "j"], - ]), - ]; - for (soft, length, vertex_pairs) in struts { - let inv_dist = Some(-0.5 * length * length); - for pair in vertex_pairs { - let adjacent_vertices = pair.map( - |id| assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[id].clone() - ) - ); - let distance = InversiveDistanceRegulator::new(adjacent_vertices); - distance.set_point.set(SpecifiedValue::from(inv_dist)); - distance.soft.set(soft); - assembly.insert_regulator(Rc::new(distance)); - } - } - - // pin the vertices of the rigid faces - let phi = 0.5 + 1.25_f64.sqrt(); /* TO DO */ // replace with std::f64::consts::PHI when that gets stabilized - let phi_2 = 0.5 * phi; - let height_b = phi_2.sqrt(); - let height_c = (phi + 0.5).sqrt(); - let pinned_vertices = [ - ("a_NE", [0.5, 0.5, 0.0]), - ("a_NW", [-0.5, 0.5, 0.0]), - ("a_SW", [-0.5, -0.5, 0.0]), - ("a_SE", [0.5, -0.5, 0.0]), - ("b_NE", [phi_2, phi_2, height_b]), - ("b_NW", [-phi_2, phi_2, height_b]), - ("b_SW", [-phi_2, -phi_2, height_b]), - ("c_N", [0.0, 1.0, height_c]), - ("c_W", [-1.0, 0.0, height_c]), - ]; - for (id, coords) in pinned_vertices { - // get the point's coordinate regulators - let point = assembly.elements_by_id.with_untracked( - |elts_by_id| elts_by_id[id].clone() - ); - let coord_regs: Vec> = point.regulators().with_untracked( - |regs| regs.into_iter().filter_map( - /* KLUDGE */ - // there must be a better way to do the type-checking here - move |reg| reg - .as_any() - .downcast_ref::() - .map(|_| reg.clone()) - ).collect() - ); - - // set the coordinates - for reg in coord_regs { - let reg_downcast_ref = reg - .as_any() - .downcast_ref::(); - if let Some(coord_reg) = reg_downcast_ref { - let coord_val = SpecifiedValue::from( - Some(coords[coord_reg.axis as usize]) - ); - reg.set_point().set(coord_val); - } - } - } -} - // --- chooser --- /* DEBUG */ @@ -3670,12 +918,6 @@ pub fn TestAssemblyChooser() -> View { "off-center" => load_off_center(assembly), "radius-ratio" => load_radius_ratio(assembly), "irisawa-hexlet" => load_irisawa_hexlet(assembly), - "554-base" => load_554_base(assembly), - "554-aug1" => load_554_aug1(assembly), - "554-aug1-inner" => load_554_aug1_inner(assembly), - "554-aug2" => load_554_aug2(assembly), - "554-domed" => load_554_domed(assembly), - "554-domed-pinned" => load_554_domed_pinned(assembly), _ => (), }; }); @@ -3693,12 +935,6 @@ pub fn TestAssemblyChooser() -> View { option(value = "off-center") { "Off-center" } option(value = "radius-ratio") { "Radius ratio" } option(value = "irisawa-hexlet") { "Irisawa hexlet" } - option(value = "554-base") { "5-5-4 base" } - option(value = "554-aug1") { "5-5-4 once augmented" } - option(value = "554-aug1-inner") { "5-5-4 once augmented (inner)" } - option(value = "554-aug2") { "5-5-4 twice augmented" } - option(value = "554-domed") { "5-5-4 domed" } - option(value = "554-domed-pinned") { "5-5-4 domed (pinned)" } option(value = "empty") { "Empty" } } } diff --git a/app-proto/src/engine.rs b/app-proto/src/engine.rs index 4148ae2..0f26f02 100644 --- a/app-proto/src/engine.rs +++ b/app-proto/src/engine.rs @@ -1,7 +1,6 @@ use lazy_static::lazy_static; use nalgebra::{Const, DMatrix, DVector, DVectorView, Dyn, SymmetricEigen}; use std::fmt::{Display, Error, Formatter}; -use sycamore::prelude::console_log; /* DEBUG */ // --- elements --- @@ -47,7 +46,7 @@ pub fn project_sphere_to_normalized(rep: &mut DVector) { // normalize a point's representation vector by scaling pub fn project_point_to_normalized(rep: &mut DVector) { - rep.scale_mut(0.5 / rep[3]); //FIXME: This 3 should be Point::WEIGHT_COMPONENT + rep.scale_mut(0.5 / rep[3]); } // --- partial matrices --- @@ -241,7 +240,6 @@ impl DescentHistory { pub struct ConstraintProblem { pub gram: PartialMatrix, - pub soft: PartialMatrix, pub frozen: PartialMatrix, pub guess: DMatrix, } @@ -251,7 +249,6 @@ impl ConstraintProblem { const ELEMENT_DIM: usize = 5; Self { gram: PartialMatrix::new(), - soft: PartialMatrix::new(), frozen: PartialMatrix::new(), guess: DMatrix::::zeros(ELEMENT_DIM, element_count), } @@ -261,7 +258,6 @@ impl ConstraintProblem { pub fn from_guess(guess_columns: &[DVector]) -> Self { Self { gram: PartialMatrix::new(), - soft: PartialMatrix::new(), frozen: PartialMatrix::new(), guess: DMatrix::from_columns(guess_columns), } @@ -284,18 +280,14 @@ lazy_static! { struct SearchState { config: DMatrix, err_proj: DMatrix, - loss_hard: f64, loss: f64, } impl SearchState { - fn from_config(gram: &PartialMatrix, soft: &PartialMatrix, softness: f64, config: DMatrix) -> Self { - let config_gram = &(config.tr_mul(&*Q) * &config); - let err_proj_hard = gram.sub_proj(config_gram); - let err_proj = &err_proj_hard + softness * soft.sub_proj(config_gram); - let loss_hard = err_proj_hard.norm_squared(); + fn from_config(gram: &PartialMatrix, config: DMatrix) -> Self { + let err_proj = gram.sub_proj(&(config.tr_mul(&*Q) * &config)); let loss = err_proj.norm_squared(); - Self { config, err_proj, loss_hard, loss } + Self { config, err_proj, loss } } } @@ -339,8 +331,6 @@ pub fn local_unif_to_std(v: DVectorView) -> DMatrix { // use backtracking line search to find a better configuration fn seek_better_config( gram: &PartialMatrix, - soft: &PartialMatrix, - softness: f64, state: &SearchState, base_step: &DMatrix, base_target_improvement: f64, @@ -351,7 +341,7 @@ fn seek_better_config( let mut rate = 1.0; for backoff_steps in 0..max_backoff_steps { let trial_config = &state.config + rate * base_step; - let trial_state = SearchState::from_config(gram, soft, softness, trial_config); + let trial_state = SearchState::from_config(gram, trial_config); let improvement = state.loss - trial_state.loss; if improvement >= min_efficiency * rate * base_target_improvement { return Some((trial_state, backoff_steps)); @@ -386,7 +376,7 @@ pub fn realize_gram( max_backoff_steps: i32, ) -> Realization { // destructure the problem data - let ConstraintProblem { gram, soft, guess, frozen } = problem; + let ConstraintProblem { gram, guess, frozen } = problem; // start the descent history let mut history = DescentHistory::new(); @@ -413,18 +403,13 @@ pub fn realize_gram( let scale_adjustment = (gram.0.len() as f64).sqrt(); let tol = scale_adjustment * scaled_tol; - // set up constants and variables related to minimizing the soft loss - const GRAD_TOL: f64 = 1e-12; - let mut grad_size = f64::INFINITY; - let mut softness = 1.0; - // convert the frozen indices to stacked format let frozen_stacked: Vec = frozen.into_iter().map( |MatrixEntry { index: (row, col), .. }| col*element_dim + row ).collect(); // use a regularized Newton's method with backtracking - let mut state = SearchState::from_config(gram, soft, softness, frozen.freeze(guess)); + let mut state = SearchState::from_config(gram, frozen.freeze(guess)); let mut hess = DMatrix::zeros(element_dim, assembly_dim); for _ in 0..max_descent_steps { // find the negative gradient of the loss function @@ -441,7 +426,7 @@ pub fn realize_gram( let neg_d_err = basis_mat.tr_mul(&*Q) * &state.config + state.config.tr_mul(&*Q) * &basis_mat; - let neg_d_err_proj = gram.proj(&neg_d_err) + softness * soft.proj(&neg_d_err); + let neg_d_err_proj = gram.proj(&neg_d_err); let deriv_grad = 4.0 * &*Q * ( -&basis_mat * &state.err_proj + &state.config * &neg_d_err_proj @@ -470,13 +455,10 @@ pub fn realize_gram( hess[(k, k)] = 1.0; } - // stop if the hard loss is tolerably low and the total loss is close to - // stationary. we use `neg_grad_stacked` to measure the size of the - // gradient because it's been projected onto the frozen subspace + // stop if the loss is tolerably low history.config.push(state.config.clone()); - history.scaled_loss.push(state.loss_hard / scale_adjustment); - grad_size = neg_grad_stacked.norm_squared(); - if state.loss_hard < tol && grad_size < softness * GRAD_TOL { break; } + history.scaled_loss.push(state.loss / scale_adjustment); + if state.loss < tol { break; } // compute the Newton step /* TO DO */ @@ -500,7 +482,7 @@ pub fn realize_gram( // use backtracking line search to find a better configuration if let Some((better_state, backoff_steps)) = seek_better_config( - gram, soft, softness, &state, &base_step, neg_grad.dot(&base_step), + gram, &state, &base_step, neg_grad.dot(&base_step), min_efficiency, backoff, max_backoff_steps, ) { state = better_state; @@ -511,18 +493,8 @@ pub fn realize_gram( history, }; } - - // if we're near a minimum of the total loss, but the hard loss still - // isn't tolerably low, make the soft constraints softer - const SOFTNESS_BACKOFF_THRESHOLD: f64 = 1e-6; - const SOFTNESS_BACKOFF: f64 = 0.95; - if state.loss_hard >= tol && grad_size < softness * SOFTNESS_BACKOFF_THRESHOLD { - softness *= SOFTNESS_BACKOFF; - state = SearchState::from_config(gram, soft, softness, state.config); - console_log!("Softness decreased to {softness}"); - } } - let result = if state.loss_hard < tol && grad_size < softness * GRAD_TOL { + let result = if state.loss < tol { // express the uniform basis in the standard basis const UNIFORM_DIM: usize = 4; let total_dim_unif = UNIFORM_DIM * assembly_dim;