Compare commits

..

1 commit

Author SHA1 Message Date
2c8c09d20d feat: Point coordinate regulators (#118)
Implement regulators for the Euclidean coordinates of `Point` entities, automatically creating all three of them for each added point entity. When such a regulator is set, it freezes the corresponding representation coordinate to the set point. In addition, if all three coordinates of a given `Point` are set, the coradius coordinate (which holds the norm of the point) is frozen as well.

Note that a `PointCoordinateRegulator` must be created with a `Point` as the subject. This commit modifies `HalfCurvatureRegulator` analogously, so that it can only be created with a `Sphere`.
Co-authored-by: Glen Whitney <glen@studioinfinity.org>
Co-committed-by: Glen Whitney <glen@studioinfinity.org>
2025-10-13 22:52:02 +00:00
12 changed files with 342 additions and 1492 deletions

View file

@ -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
- 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.

View file

@ -1,2 +0,0 @@
[toolchain]
channel = "nightly"

View file

@ -1,12 +1,11 @@
use enum_iterator::{all, Sequence};
use nalgebra::{DMatrix, DVector, DVectorView};
use std::{
any::Any,
cell::Cell,
cmp::Ordering,
collections::{BTreeMap, BTreeSet},
fmt,
fmt::{Debug, Formatter},
fmt::{Debug, Display, Formatter},
hash::{Hash, Hasher},
rc::Rc,
sync::{atomic, atomic::AtomicU64},
@ -87,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);
}
@ -96,7 +103,7 @@ pub trait Element: Serial + ProblemPoser + DisplayItem {
fn default_id() -> String where Self: Sized;
// the default example of an element of this type
fn default(id: &str, id_num: u64) -> Self where Self: Sized;
fn default(id: String, id_num: u64) -> Self where Self: Sized;
// the default regulators that come with this element
fn default_regulators(self: Rc<Self>) -> Vec<Rc<dyn Regulator>> {
@ -128,8 +135,8 @@ pub trait Element: Serial + ProblemPoser + DisplayItem {
}
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)
}
}
@ -174,14 +181,14 @@ impl Sphere {
const CURVATURE_COMPONENT: usize = 3;
pub fn new(
id: &str,
label: &str,
id: String,
label: String,
color: ElementColor,
representation: DVector<f64>,
) -> Self {
Self {
id: id.to_string(),
label: label.to_string(),
id,
label,
color,
representation: create_signal(representation),
ghost: create_signal(false),
@ -197,11 +204,11 @@ impl Element for Sphere {
"sphere".to_string()
}
fn default(id: &str, id_num: u64) -> Self {
fn default(id: String, id_num: u64) -> Self {
Self::new(
id,
&format!("Sphere {id_num}"),
[0.75, 0.75, 0.75],
format!("Sphere {id_num}"),
[0.75_f32, 0.75_f32, 0.75_f32],
sphere(0.0, 0.0, 0.0, 1.0),
)
}
@ -252,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());
}
@ -275,14 +281,14 @@ impl Point {
const NORM_COMPONENT: usize = 4;
pub fn new(
id: &str,
label: &str,
id: String,
label: String,
color: ElementColor,
representation: DVector<f64>,
) -> Self {
Self {
id: id.to_string(),
label: label.to_string(),
id,
label,
color,
representation: create_signal(representation),
ghost: create_signal(false),
@ -298,10 +304,10 @@ impl Element for Point {
"point".to_string()
}
fn default(id: &str, id_num: u64) -> Self {
fn default(id: String, id_num: u64) -> Self {
Self::new(
id,
&format!("Point {id_num}"),
format!("Point {id_num}"),
[0.75_f32, 0.75_f32, 0.75_f32],
point(0.0, 0.0, 0.0),
)
@ -358,30 +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<Rc<dyn Element>>;
fn measurement(&self) -> ReadSignal<f64>;
fn set_point(&self) -> Signal<SpecifiedValue>;
fn set_to(&self, val: f64) {
self.set_point().set(SpecifiedValue::from(Some(val)))
}
// QUESTION: Why doesn't the following work? When I used it to
// set the coordinate regulators on pinned points, dyna3 would panic
// when trying to nudge unpinned points :-(
// fn set_to_current(&self) {
// self.set_point().set(
// SpecifiedValue::from(Some(self.measurement().get())),
// )
// }
fn as_any(&self) -> &dyn Any;
}
impl Hash for dyn Regulator {
@ -447,8 +440,6 @@ impl Regulator for InversiveDistanceRegulator {
fn set_point(&self) -> Signal<SpecifiedValue> {
self.set_point
}
fn as_any(&self) -> &dyn Any {self}
}
impl Serial for InversiveDistanceRegulator {
@ -463,8 +454,8 @@ impl ProblemPoser for InversiveDistanceRegulator {
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())
);
problem.gram.push_sym(row, col, val);
}
@ -504,8 +495,6 @@ impl Regulator for HalfCurvatureRegulator {
fn set_point(&self) -> Signal<SpecifiedValue> {
self.set_point
}
fn as_any(&self) -> &dyn Any {self}
}
impl Serial for HalfCurvatureRegulator {
@ -519,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);
}
});
@ -528,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 {
@ -561,7 +557,6 @@ impl Regulator for PointCoordinateRegulator {
fn subjects(&self) -> Vec<Rc<dyn Element>> { vec![self.subject.clone()] }
fn measurement(&self) -> ReadSignal<f64> { self.measurement }
fn set_point(&self) -> Signal<SpecifiedValue> { self.set_point }
fn as_any(&self) -> &dyn Any {self}
}
impl ProblemPoser for PointCoordinateRegulator {
@ -569,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]);
@ -714,12 +710,9 @@ impl Assembly {
}
// create and insert the default example of `T`
let _ = self.insert_element_unchecked(T::default(&id, id_num));
let _ = self.insert_element_unchecked(T::default(id, id_num));
}
// FIXME: insert_element and insert_regulator are not parallel;
// the former takes an element and calls Rc::new, inserting clones of
// the result, whereas the latter takes an Rc and inserts clones of that
pub fn insert_regulator(&self, regulator: Rc<dyn Regulator>) {
// add the regulator to the assembly's regulator list
self.regulators.update(
@ -757,11 +750,6 @@ impl Assembly {
});
}
// --- finding entities ---
pub fn find_element(&self, id: &str) -> Rc<dyn Element> {
self.elements_by_id.with_untracked( |elts| elts[id].clone() )
}
// --- updating the configuration ---
pub fn load_config(&self, config: &DMatrix<f64>) {
@ -799,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
@ -958,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);
@ -967,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(

View file

@ -1,12 +1,11 @@
use itertools::Itertools;
use std::rc::Rc;
use sycamore::prelude::*;
use web_sys::{KeyboardEvent, MouseEvent, wasm_bindgen::{JsCast,}};
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<Self>, _element: &Rc<dyn Element>) -> 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")
}
@ -261,31 +261,6 @@ pub fn Outline() -> View {
on:click = {
let state = use_context::<AppState>();
move |_| state.selection.update(|sel| sel.clear())
},
on:keydown = {
let rep_list = state.assembly.elements.map(
|elts| elts
.clone()
.into_iter()
.map(|elt| format!("{}: {}", elt.id(), elt.representation()))
.collect::<Vec<_>>()
);
move |event: KeyboardEvent| {
match event.key().as_str() {
"c" => {
console_log!("Dumping all coordinates");
rep_list.map(
|reps| {
for item in reps {
console_log!("{}", item);
}
}
);
},
_ => {},
}
}
}
) {
Keyed(

View file

@ -1,5 +1,5 @@
use itertools::izip;
use std::{f64::consts::{FRAC_1_SQRT_2, PI, SQRT_2}, rc::Rc};
use std::{f64::consts::{FRAC_1_SQRT_2, PI}, rc::Rc};
use nalgebra::Vector3;
use sycamore::prelude::*;
use web_sys::{console, wasm_bindgen::JsValue};
@ -12,80 +12,13 @@ use crate::{
ElementColor,
InversiveDistanceRegulator,
Point,
PointCoordinateRegulator,
Regulator,
Sphere,
},
engine,
engine::{DescentHistory, point},
engine::DescentHistory,
specified::SpecifiedValue,
};
// Convenience: macro to allow elision of const array lengths
// adapted from https://stackoverflow.com/a/59905715
macro_rules! const_array {
($name: ident: $ty: ty = $value: expr) => {
const $name: [$ty; $value.len()] = $value;
}
}
// Convenience: use sqrt() as a function to get the square root of
// anything that can be losslessly converted to f64, since we happen
// to always want our sqrts to be f64.
// RUST KVETCHES: (I) Why is this so convoluted?
// In particular, what would be so bad about allowing
// const fn sqrt<T: Into<f64>>(x: T) -> f64 { (x as f64).sqrt() }
// ???
// (II) Oh dear, sqrt is not computable in a const context, so we have to
// roll our own. I hope I get it right! I definitely don't know how to ensure
// that the final double is correctly rounded :-(
const SIXTH: f64 = 1./6.;
const FOURTH: f64 = 1./4.;
const fn invsqrt(xin: f64) -> f64 {
if !xin.is_finite() { return f64::NAN; }
let mut log4 = 0;
let mut x = xin;
while x < 1. { x *= 4.; log4 -= 1; }
while x > 4. { x *= FOURTH; log4 += 1; }
let mut next = 1.1 - SIXTH * x;
let mut diff = 1.;
while diff > 4. * f64::EPSILON { // termination condition??
let last = next;
next = last * (1.5 - 0.5 * x * last * last);
diff = next - last;
if diff < 0. { diff *= -1.; }
}
while log4 > 0 { next *= 0.5; log4 -= 1; }
while log4 < 0 { next *= 2.; log4 += 1; }
return next;
}
#[const_trait]
trait ConstSqrt {fn sqr(self) -> f64;}
impl const ConstSqrt for f64 {fn sqr(self) -> f64 {self * invsqrt(self)}}
impl const ConstSqrt for i32 {fn sqr(self) -> f64 {(self as f64).sqr()}}
const fn sqrt<T: const ConstSqrt>(x: T) -> f64 {x.sqr()}
// RUST KVETCHES: (I) It is annoying that we must redundantly specify
// the type of the well-typed RHS to assign it to a const.
// See https://github.com/rust-lang/rfcs/pull/3546
// Can we fix this in husht? Seems like we will need to be able to do full
// rust type inference in the transpiler; how will we accomplish that?
// (2) It is very annoying that there doesn't seem to be any way to specify
// that we want an expression like `5.0 / 2` to be evaluated by converting
// the 2 to a float, because of the "orphan rule". Can we fix this in husht?
// Again, it seems we would need full rust type inference.
// FIXME: replace with std::f64::consts::PHI when that gets stabilized
const PHI: f64 = (1. + sqrt(5)) / 2.;
const fn gray(level: f32) -> ElementColor { [level, level, level] }
const GRAY: ElementColor = gray(0.75);
const RED: ElementColor = [1., 0., 0.25];
const GREEN: ElementColor = [0.25, 1., 0.];
const BLUE: ElementColor = [0., 0.25, 1.];
// --- loaders ---
/* DEBUG */
@ -96,37 +29,49 @@ const BLUE: ElementColor = [0., 0.25, 1.];
fn load_general(assembly: &Assembly) {
let _ = assembly.try_insert_element(
Sphere::new(
"gemini_a", "Castor", [1.00, 0.25, 0.00],
String::from("gemini_a"),
String::from("Castor"),
[1.00_f32, 0.25_f32, 0.00_f32],
engine::sphere(0.5, 0.5, 0.0, 1.0),
)
);
let _ = assembly.try_insert_element(
Sphere::new(
"gemini_b", "Pollux", BLUE,
String::from("gemini_b"),
String::from("Pollux"),
[0.00_f32, 0.25_f32, 1.00_f32],
engine::sphere(-0.5, -0.5, 0.0, 1.0),
)
);
let _ = assembly.try_insert_element(
Sphere::new(
"ursa_major", "Ursa major", [0.25, 0.00, 1.00],
String::from("ursa_major"),
String::from("Ursa major"),
[0.25_f32, 0.00_f32, 1.00_f32],
engine::sphere(-0.5, 0.5, 0.0, 0.75),
)
);
let _ = assembly.try_insert_element(
Sphere::new(
"ursa_minor", "Ursa minor", GREEN,
String::from("ursa_minor"),
String::from("Ursa minor"),
[0.25_f32, 1.00_f32, 0.00_f32],
engine::sphere(0.5, -0.5, 0.0, 0.5),
)
);
let _ = assembly.try_insert_element(
Sphere::new(
"moon_deimos", "Deimos", [0.75, 0.75, 0.00],
String::from("moon_deimos"),
String::from("Deimos"),
[0.75_f32, 0.75_f32, 0.00_f32],
engine::sphere(0.0, 0.15, 1.0, 0.25),
)
);
let _ = assembly.try_insert_element(
Sphere::new(
"moon_phobos", "Phobos", [0.00, 0.75, 0.50],
String::from("moon_phobos"),
String::from("Phobos"),
[0.00_f32, 0.75_f32, 0.50_f32],
engine::sphere(0.0, -0.15, -1.0, 0.25),
)
);
@ -134,66 +79,88 @@ fn load_general(assembly: &Assembly) {
fn load_low_curvature(assembly: &Assembly) {
// create the spheres
const A: f64 = sqrt(0.75);
let a = 0.75_f64.sqrt();
let _ = assembly.try_insert_element(
Sphere::new(
"central", "Central", GRAY,
"central".to_string(),
"Central".to_string(),
[0.75_f32, 0.75_f32, 0.75_f32],
engine::sphere(0.0, 0.0, 0.0, 1.0),
)
);
let _ = assembly.try_insert_element(
Sphere::new(
"assemb_plane", "Assembly plane", GRAY,
"assemb_plane".to_string(),
"Assembly plane".to_string(),
[0.75_f32, 0.75_f32, 0.75_f32],
engine::sphere_with_offset(0.0, 0.0, 1.0, 0.0, 0.0),
)
);
let _ = assembly.try_insert_element(
Sphere::new(
"side1", "Side 1", RED,
"side1".to_string(),
"Side 1".to_string(),
[1.00_f32, 0.00_f32, 0.25_f32],
engine::sphere_with_offset(1.0, 0.0, 0.0, 1.0, 0.0),
)
);
let _ = assembly.try_insert_element(
Sphere::new(
"side2", "Side 2", GREEN,
engine::sphere_with_offset(-0.5, A, 0.0, 1.0, 0.0),
"side2".to_string(),
"Side 2".to_string(),
[0.25_f32, 1.00_f32, 0.00_f32],
engine::sphere_with_offset(-0.5, a, 0.0, 1.0, 0.0),
)
);
let _ = assembly.try_insert_element(
Sphere::new(
"side3", "Side 3", BLUE,
engine::sphere_with_offset(-0.5, -A, 0.0, 1.0, 0.0),
"side3".to_string(),
"Side 3".to_string(),
[0.00_f32, 0.25_f32, 1.00_f32],
engine::sphere_with_offset(-0.5, -a, 0.0, 1.0, 0.0),
)
);
let _ = assembly.try_insert_element(
Sphere::new(
"corner1", "Corner 1", GRAY,
"corner1".to_string(),
"Corner 1".to_string(),
[0.75_f32, 0.75_f32, 0.75_f32],
engine::sphere(-4.0/3.0, 0.0, 0.0, 1.0/3.0),
)
);
let _ = assembly.try_insert_element(
Sphere::new(
"corner2", "Corner 2", GRAY,
engine::sphere(2.0/3.0, -4.0/3.0 * A, 0.0, 1.0/3.0),
"corner2".to_string(),
"Corner 2".to_string(),
[0.75_f32, 0.75_f32, 0.75_f32],
engine::sphere(2.0/3.0, -4.0/3.0 * a, 0.0, 1.0/3.0),
)
);
let _ = assembly.try_insert_element(
Sphere::new(
"corner3", "Corner 3", GRAY,
engine::sphere(2.0/3.0, 4.0/3.0 * A, 0.0, 1.0/3.0),
String::from("corner3"),
String::from("Corner 3"),
[0.75_f32, 0.75_f32, 0.75_f32],
engine::sphere(2.0/3.0, 4.0/3.0 * a, 0.0, 1.0/3.0),
)
);
// impose the desired tangencies and make the sides planar
let index_range = 1..=3;
let [central, assemb_plane] = ["central", "assemb_plane"].map(
|id| assembly.find_element(id)
|id| assembly.elements_by_id.with_untracked(
|elts_by_id| elts_by_id[id].clone()
)
);
let sides = index_range.clone().map(
|k| assembly.find_element(&format!("side{k}"))
|k| assembly.elements_by_id.with_untracked(
|elts_by_id| elts_by_id[&format!("side{k}")].clone()
)
);
let corners = index_range.map(
|k| assembly.find_element(&format!("corner{k}"))
|k| assembly.elements_by_id.with_untracked(
|elts_by_id| elts_by_id[&format!("corner{k}")].clone()
)
);
for plane in [assemb_plane.clone()].into_iter().chain(sides.clone()) {
// fix the curvature of each plane
@ -232,16 +199,16 @@ fn load_low_curvature(assembly: &Assembly) {
fn load_pointed(assembly: &Assembly) {
let _ = assembly.try_insert_element(
Point::new(
"point_front",
"Front point",
format!("point_front"),
format!("Front point"),
[0.75_f32, 0.75_f32, 0.75_f32],
engine::point(0.0, 0.0, FRAC_1_SQRT_2),
)
);
let _ = assembly.try_insert_element(
Point::new(
"point_back",
"Back point",
format!("point_back"),
format!("Back point"),
[0.75_f32, 0.75_f32, 0.75_f32],
engine::point(0.0, 0.0, -FRAC_1_SQRT_2),
)
@ -253,8 +220,8 @@ fn load_pointed(assembly: &Assembly) {
let _ = assembly.try_insert_element(
Sphere::new(
&format!("sphere{index_x}{index_y}"),
&format!("Sphere {index_x}{index_y}"),
format!("sphere{index_x}{index_y}"),
format!("Sphere {index_x}{index_y}"),
[0.5*(1.0 + x) as f32, 0.5*(1.0 + y) as f32, 0.5*(1.0 - x*y) as f32],
engine::sphere(x, y, 0.0, 1.0),
)
@ -262,8 +229,8 @@ fn load_pointed(assembly: &Assembly) {
let _ = assembly.try_insert_element(
Point::new(
&format!("point{index_x}{index_y}"),
&format!("Point {index_x}{index_y}"),
format!("point{index_x}{index_y}"),
format!("Point {index_x}{index_y}"),
[0.5*(1.0 + x) as f32, 0.5*(1.0 + y) as f32, 0.5*(1.0 - x*y) as f32],
engine::point(x, y, 0.0),
)
@ -281,42 +248,91 @@ fn load_pointed(assembly: &Assembly) {
// A-C -0.25 * φ^2 = -0.6545084971874737
fn load_tridiminished_icosahedron(assembly: &Assembly) {
// create the vertices
const COLOR_A: ElementColor = [1.00, 0.25, 0.25];
const COLOR_B: ElementColor = [0.75, 0.75, 0.75];
const COLOR_C: ElementColor = [0.25, 0.50, 1.00];
const COLOR_A: ElementColor = [1.00_f32, 0.25_f32, 0.25_f32];
const COLOR_B: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32];
const COLOR_C: ElementColor = [0.25_f32, 0.50_f32, 1.00_f32];
let vertices = [
Point::new("a1", "A₁", COLOR_A, point( 0.25, 0.75, 0.75)),
Point::new("a2", "A₂", COLOR_A, point( 0.75, 0.25, 0.75)),
Point::new("a3", "A₃", COLOR_A, point( 0.75, 0.75, 0.25)),
Point::new("b1", "B₁", COLOR_B, point( 0.75, -0.25, -0.25)),
Point::new("b2", "B₂", COLOR_B, point(-0.25, 0.75, -0.25)),
Point::new("b3", "B₃", COLOR_B, point(-0.25, -0.25, 0.75)),
Point::new("c1", "C₁", COLOR_C, point( 0.0, -1.0, -1.0)),
Point::new("c2", "C₂", COLOR_C, point(-1.0, 0.0, -1.0)),
Point::new("c3", "C₃", COLOR_C, point(-1.0, -1.0, 0.0)),
Point::new(
"a1".to_string(),
"A₁".to_string(),
COLOR_A,
engine::point(0.25, 0.75, 0.75),
),
Point::new(
"a2".to_string(),
"A₂".to_string(),
COLOR_A,
engine::point(0.75, 0.25, 0.75),
),
Point::new(
"a3".to_string(),
"A₃".to_string(),
COLOR_A,
engine::point(0.75, 0.75, 0.25),
),
Point::new(
"b1".to_string(),
"B₁".to_string(),
COLOR_B,
engine::point(0.75, -0.25, -0.25),
),
Point::new(
"b2".to_string(),
"B₂".to_string(),
COLOR_B,
engine::point(-0.25, 0.75, -0.25),
),
Point::new(
"b3".to_string(),
"B₃".to_string(),
COLOR_B,
engine::point(-0.25, -0.25, 0.75),
),
Point::new(
"c1".to_string(),
"C₁".to_string(),
COLOR_C,
engine::point(0.0, -1.0, -1.0),
),
Point::new(
"c2".to_string(),
"C₂".to_string(),
COLOR_C,
engine::point(-1.0, 0.0, -1.0),
),
Point::new(
"c3".to_string(),
"C₃".to_string(),
COLOR_C,
engine::point(-1.0, -1.0, 0.0),
),
];
for vertex in vertices {
let _ = assembly.try_insert_element(vertex);
}
// create the faces
const SQRT_1_6: f64 = invsqrt(6.);
const SQRT_2_3: f64 = 2. * SQRT_1_6;
const COLOR_FACE: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32];
let frac_1_sqrt_6 = 1.0 / 6.0_f64.sqrt();
let frac_2_sqrt_6 = 2.0 * frac_1_sqrt_6;
let faces = [
Sphere::new(
"face1", "Face 1", GRAY,
engine::sphere_with_offset(
SQRT_2_3, -SQRT_1_6, -SQRT_1_6, -SQRT_1_6, 0.0),
"face1".to_string(),
"Face 1".to_string(),
COLOR_FACE,
engine::sphere_with_offset(frac_2_sqrt_6, -frac_1_sqrt_6, -frac_1_sqrt_6, -frac_1_sqrt_6, 0.0),
),
Sphere::new(
"face2", "Face 2", GRAY,
engine::sphere_with_offset(
-SQRT_1_6, SQRT_2_3, -SQRT_1_6, -SQRT_1_6, 0.0),
"face2".to_string(),
"Face 2".to_string(),
COLOR_FACE,
engine::sphere_with_offset(-frac_1_sqrt_6, frac_2_sqrt_6, -frac_1_sqrt_6, -frac_1_sqrt_6, 0.0),
),
Sphere::new(
"face3", "Face 3", GRAY,
engine::sphere_with_offset(
-SQRT_1_6, -SQRT_1_6, SQRT_2_3, -SQRT_1_6, 0.0),
"face3".to_string(),
"Face 3".to_string(),
COLOR_FACE,
engine::sphere_with_offset(-frac_1_sqrt_6, -frac_1_sqrt_6, frac_2_sqrt_6, -frac_1_sqrt_6, 0.0),
),
];
for face in faces {
@ -397,7 +413,9 @@ fn load_dodecahedral_packing(assembly: &Assembly) {
// add the substrate
let _ = assembly.try_insert_element(
Sphere::new(
"substrate", "Substrate", GRAY,
"substrate".to_string(),
"Substrate".to_string(),
[0.75_f32, 0.75_f32, 0.75_f32],
engine::sphere(0.0, 0.0, 0.0, 1.0),
)
);
@ -416,16 +434,17 @@ fn load_dodecahedral_packing(assembly: &Assembly) {
const COLOR_A: ElementColor = [1.00_f32, 0.25_f32, 0.00_f32];
const COLOR_B: ElementColor = [1.00_f32, 0.00_f32, 0.25_f32];
const COLOR_C: ElementColor = [0.25_f32, 0.00_f32, 1.00_f32];
const PHI_INV: f64 = 1.0 / PHI;
const COORD_SCALE: f64 = sqrt(PHI + 2.0);
const_array!(FACE_SCALES: f64 = [PHI_INV, (13.0 / 12.0) / COORD_SCALE]);
const_array!(FACE_RADII: f64 = [PHI_INV, 5.0 / 12.0]);
let phi = 0.5 + 1.25_f64.sqrt(); /* TO DO */ // replace with std::f64::consts::PHI when that gets stabilized
let phi_inv = 1.0 / phi;
let coord_scale = (phi + 2.0).sqrt();
let face_scales = [phi_inv, (13.0 / 12.0) / coord_scale];
let face_radii = [phi_inv, 5.0 / 12.0];
let mut faces = Vec::<Rc<dyn Element>>::new();
let subscripts = ["", ""];
for j in 0..2 {
for k in 0..2 {
let small_coord = FACE_SCALES[k] * if j > 0 {1.} else {-1.};
let big_coord = FACE_SCALES[k] * PHI * if k > 0 {1.} else {-1.};
let small_coord = face_scales[k] * (2.0*(j as f64) - 1.0);
let big_coord = face_scales[k] * (2.0*(k as f64) - 1.0) * phi;
let id_num = format!("{j}{k}");
let label_sub = format!("{}{}", subscripts[j], subscripts[k]);
@ -434,10 +453,10 @@ fn load_dodecahedral_packing(assembly: &Assembly) {
let id_a = format!("a{id_num}");
let _ = assembly.try_insert_element(
Sphere::new(
&id_a,
&format!("A{label_sub}"),
id_a.clone(),
format!("A{label_sub}"),
COLOR_A,
engine::sphere(0.0, small_coord, big_coord, FACE_RADII[k]),
engine::sphere(0.0, small_coord, big_coord, face_radii[k]),
)
);
faces.push(
@ -450,10 +469,10 @@ fn load_dodecahedral_packing(assembly: &Assembly) {
let id_b = format!("b{id_num}");
let _ = assembly.try_insert_element(
Sphere::new(
&id_b,
&format!("B{label_sub}"),
id_b.clone(),
format!("B{label_sub}"),
COLOR_B,
engine::sphere(small_coord, big_coord, 0.0, FACE_RADII[k]),
engine::sphere(small_coord, big_coord, 0.0, face_radii[k]),
)
);
faces.push(
@ -466,10 +485,10 @@ fn load_dodecahedral_packing(assembly: &Assembly) {
let id_c = format!("c{id_num}");
let _ = assembly.try_insert_element(
Sphere::new(
&id_c,
&format!("C{label_sub}"),
id_c.clone(),
format!("C{label_sub}"),
COLOR_C,
engine::sphere(big_coord, 0.0, small_coord, FACE_RADII[k]),
engine::sphere(big_coord, 0.0, small_coord, face_radii[k]),
)
);
faces.push(
@ -537,9 +556,23 @@ fn load_balanced(assembly: &Assembly) {
const R_INNER: f64 = 4.0;
let spheres = [
Sphere::new(
"outer","Outer", GRAY, engine::sphere(0.0, 0.0, 0.0, R_OUTER)),
Sphere::new("a", "A", RED, engine::sphere(0.0, 4.0, 0.0, R_INNER)),
Sphere::new("b", "B", BLUE, engine::sphere(0.0, -4.0, 0.0, R_INNER)),
"outer".to_string(),
"Outer".to_string(),
[0.75_f32, 0.75_f32, 0.75_f32],
engine::sphere(0.0, 0.0, 0.0, R_OUTER),
),
Sphere::new(
"a".to_string(),
"A".to_string(),
[1.00_f32, 0.00_f32, 0.25_f32],
engine::sphere(0.0, 4.0, 0.0, R_INNER),
),
Sphere::new(
"b".to_string(),
"B".to_string(),
[0.00_f32, 0.25_f32, 1.00_f32],
engine::sphere(0.0, -4.0, 0.0, R_INNER),
),
];
for sphere in spheres {
let _ = assembly.try_insert_element(sphere);
@ -547,7 +580,9 @@ fn load_balanced(assembly: &Assembly) {
// get references to the spheres
let [outer, a, b] = ["outer", "a", "b"].map(
|id| assembly.find_element(id)
|id| assembly.elements_by_id.with_untracked(
|elts_by_id| elts_by_id[id].clone()
)
);
// fix the diameters of the outer, sun, and moon spheres
@ -579,22 +614,32 @@ fn load_balanced(assembly: &Assembly) {
fn load_off_center(assembly: &Assembly) {
// create a point almost at the origin and a sphere centered on the origin
let _ = assembly.try_insert_element(
Point::new("point", "Point", GRAY, point(1e-9, 0.0, 0.0)),
Point::new(
"point".to_string(),
"Point".to_string(),
[0.75_f32, 0.75_f32, 0.75_f32],
engine::point(1e-9, 0.0, 0.0),
),
);
let _ = assembly.try_insert_element(
Sphere::new(
"sphere", "Sphere", GRAY, engine::sphere(0.0, 0.0, 0.0, 1.0)),
"sphere".to_string(),
"Sphere".to_string(),
[0.75_f32, 0.75_f32, 0.75_f32],
engine::sphere(0.0, 0.0, 0.0, 1.0),
),
);
// get references to the elements
let point_and_sphere = ["point", "sphere"].map(
|id| assembly.find_element(id)
|id| assembly.elements_by_id.with_untracked(
|elts_by_id| elts_by_id[id].clone()
)
);
// put the point on the sphere
let incidence = InversiveDistanceRegulator::new(point_and_sphere);
incidence.set_to(0.);
incidence.set_point.set(SpecifiedValue::try_from("0".to_string()).unwrap());
assembly.insert_regulator(Rc::new(incidence));
}
@ -607,13 +652,18 @@ fn load_radius_ratio(assembly: &Assembly) {
let index_range = 1..=4;
// create the spheres
const GRAY: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32];
let spheres = [
Sphere::new(
"sphere_faces", "Insphere", GRAY,
"sphere_faces".to_string(),
"Insphere".to_string(),
GRAY,
engine::sphere(0.0, 0.0, 0.0, 0.5),
),
Sphere::new(
"sphere_vertices", "Circumsphere", GRAY,
"sphere_vertices".to_string(),
"Circumsphere".to_string(),
GRAY,
engine::sphere(0.0, 0.0, 0.0, 0.25),
),
];
@ -639,8 +689,8 @@ fn load_radius_ratio(assembly: &Assembly) {
).map(
|(k, color, representation)| {
Point::new(
&format!("v{k}"),
&format!("Vertex {k}"),
format!("v{k}"),
format!("Vertex {k}"),
color,
representation,
)
@ -670,8 +720,8 @@ fn load_radius_ratio(assembly: &Assembly) {
).map(
|(k, color, representation)| {
Sphere::new(
&format!("f{k}"),
&format!("Face {k}"),
format!("f{k}"),
format!("Face {k}"),
color,
representation,
)
@ -684,18 +734,27 @@ fn load_radius_ratio(assembly: &Assembly) {
// impose the constraints
for j in index_range.clone() {
let [face_j, vertex_j] = [format!("f{j}"),format!("v{j}")]
.map(|id| assembly.find_element(&id));
let [face_j, vertex_j] = [
format!("f{j}"),
format!("v{j}"),
].map(
|id| assembly.elements_by_id.with_untracked(
|elts_by_id| elts_by_id[&id].clone()
)
);
// make the faces planar
let curvature_regulator = face_j.regulators().with_untracked(
|regs| regs.first().unwrap().clone()
);
curvature_regulator.set_to(0.);
curvature_regulator.set_point().set(
SpecifiedValue::try_from("0".to_string()).unwrap()
);
for k in index_range.clone().filter(|&index| index != j) {
let vertex_k = assembly.find_element(&format!("v{k}"));
let vertex_k = assembly.elements_by_id.with_untracked(
|elts_by_id| elts_by_id[&format!("v{k}")].clone()
);
// fix the distances between the vertices
if j < k {
@ -707,7 +766,7 @@ fn load_radius_ratio(assembly: &Assembly) {
// put the vertices on the faces
let incidence_regulator = InversiveDistanceRegulator::new([face_j.clone(), vertex_k.clone()]);
incidence_regulator.set_to(0.);
incidence_regulator.set_point.set(SpecifiedValue::try_from("0".to_string()).unwrap());
assembly.insert_regulator(Rc::new(incidence_regulator));
}
}
@ -733,26 +792,32 @@ fn load_radius_ratio(assembly: &Assembly) {
fn load_irisawa_hexlet(assembly: &Assembly) {
let index_range = 1..=6;
let colors = [
[1.00, 0.00, 0.25],
[1.00, 0.25, 0.00],
[0.75, 0.75, 0.00],
[0.25, 1.00, 0.00],
[0.00, 0.25, 1.00],
[0.25, 0.00, 1.00],
[1.00_f32, 0.00_f32, 0.25_f32],
[1.00_f32, 0.25_f32, 0.00_f32],
[0.75_f32, 0.75_f32, 0.00_f32],
[0.25_f32, 1.00_f32, 0.00_f32],
[0.00_f32, 0.25_f32, 1.00_f32],
[0.25_f32, 0.00_f32, 1.00_f32],
].into_iter();
// create the spheres
let spheres = [
Sphere::new(
"outer", "Outer", gray(0.5),
"outer".to_string(),
"Outer".to_string(),
[0.5_f32, 0.5_f32, 0.5_f32],
engine::sphere(0.0, 0.0, 0.0, 1.5),
),
Sphere::new(
"sun", "Sun", GRAY,
"sun".to_string(),
"Sun".to_string(),
[0.75_f32, 0.75_f32, 0.75_f32],
engine::sphere(0.0, -0.75, 0.0, 0.75),
),
Sphere::new(
"moon", "Moon", gray(0.25),
"moon".to_string(),
"Moon".to_string(),
[0.25_f32, 0.25_f32, 0.25_f32],
engine::sphere(0.0, 0.75, 0.0, 0.75),
),
].into_iter().chain(
@ -760,8 +825,8 @@ fn load_irisawa_hexlet(assembly: &Assembly) {
|(k, color)| {
let ang = (k as f64) * PI/3.0;
Sphere::new(
&format!("chain{k}"),
&format!("Chain {k}"),
format!("chain{k}"),
format!("Chain {k}"),
color,
engine::sphere(1.0 * ang.sin(), 0.0, 1.0 * ang.cos(), 0.5),
)
@ -773,7 +838,9 @@ fn load_irisawa_hexlet(assembly: &Assembly) {
}
// put the outer sphere in ghost mode and fix its curvature
let outer = assembly.find_element("outer");
let outer = assembly.elements_by_id.with_untracked(
|elts_by_id| elts_by_id["outer"].clone()
);
outer.ghost().set(true);
let outer_curvature_regulator = outer.regulators().with_untracked(
|regs| regs.first().unwrap().clone()
@ -783,9 +850,15 @@ fn load_irisawa_hexlet(assembly: &Assembly) {
);
// impose the desired tangencies
let [sun, moon] = ["sun", "moon"].map(|id| assembly.find_element(id));
let [outer, sun, moon] = ["outer", "sun", "moon"].map(
|id| assembly.elements_by_id.with_untracked(
|elts_by_id| elts_by_id[id].clone()
)
);
let chain = index_range.map(
|k| assembly.find_element(&format!("chain{k}"))
|k| assembly.elements_by_id.with_untracked(
|elts_by_id| elts_by_id[&format!("chain{k}")].clone()
)
);
for (chain_sphere, chain_sphere_next) in chain.clone().zip(chain.cycle().skip(1)) {
for (other_sphere, inversive_distance) in [
@ -809,205 +882,6 @@ fn load_irisawa_hexlet(assembly: &Assembly) {
assembly.insert_regulator(Rc::new(outer_moon_tangency));
}
const HPHI: f64 = PHI / 2.; // "half phi"
const RTHPHI: f64 = sqrt(HPHI); // "root half phi"
const RTPHIPH: f64 = sqrt(PHI + 0.5); // "root phi plus (half)"
const P: bool = true; // Pinned
const F: bool = false; // Free
// Initial data for the vertices commmon to 554aug2 and 554domed.
// Used the Vectornaut near_miss branch final positions for 554aug2,
// to the 3 decimal places currently shown.
const_array!(ACRON554_COMMON: (&str, [f64; 3], bool, usize, &str) = [
// id, coordinates, Pin/Free, level, earlier neighbor IDs.
("A_NE", [ 0.5, 0.5, 0.], P, 0, ""),
("A_NW", [-0.5, 0.5, 0.], P, 0, ""),
("A_SE", [ 0.5, -0.5, 0.], P, 0, ""),
("A_SW", [-0.5, -0.5, 0.], P, 0, ""),
("Z_E", [ 0.229, -0.002, 0.822], F, 1, "A_NE,A_SE"),
("Z_S", [ 0.002, -0.229, 0.822], F, 1, "A_SE,A_SW"),
("B_NE", [ HPHI, HPHI, RTHPHI], P, 2, "Z_E"),
("B_NW", [-HPHI, HPHI, RTHPHI], P, 2, ""),
("B_SW", [-HPHI, -HPHI, RTHPHI], P, 2, "Z_S"),
("B_SE", [ 0.812, -0.812, 0.897], F, 2, "A_SE,Z_E,Z_S"),
("Y_NE", [ 0.11, 0.104, 1.019], F, 3, "B_NE"),
("Y_NW", [-0.104, 0.104, 1.019], F, 3, "B_NW"),
("Y_SE", [ 0.11, -0.11, 1.018], F, 3, "B_SE"),
("Y_SW", [-0.104, -0.11, 1.019], F, 3, "B_SW"),
("C_N", [ 0., 1., RTPHIPH], P, 4, "Y_NE,Y_NW"),
("C_W", [-1., 0., RTPHIPH], P, 4, "Y_NW,Y_SW"),
("C_E", [ 1.003, -0.003, 1.454], F, 4, "Z_E,B_NE,B_SE,Y_NE,Y_SE"),
("C_S", [ 0.003, -1.003, 1.454], F, 4, "Z_S,B_SE,B_SW,Y_SE,Y_SW"),
("D_NE", [ 0.195, 0.186, 2.012], F, 5, "Y_NE,C_N,C_E"),
("D_NW", [-0.186, 0.186, 2.012], F, 5, "Y_NW,C_N,C_W"),
("D_SE", [ 0.195, -0.195, 2.011], F, 5, "Y_SE,C_E,C_S"),
("D_SW", [-0.186, -0.195, 2.012], F, 5, "Y_SW,C_W,C_S"),
("E_N", [ 0.005, 1.062, 2.455], F, 6, "C_N,D_NE,D_NW"),
("E_W", [-1.063, -0.005, 2.455], F, 6, "C_W,D_NW,D_SW"),
("E_E", [ 1.072, -0.005, 2.452], F, 6, "C_E,D_NE,D_SE"),
("E_S", [ 0.005, -1.072, 2.452], F, 6, "C_S,D_SE,D_SW"),
("F_NE", [ 0.286, 0.275, 3.003], F, 7, "D_NE,E_N,E_E"),
("F_NW", [-0.275, 0.275, 3.004], F, 7, "D_NW,E_N,E_W"),
("F_SE", [ 0.286, -0.286, 3.003], F, 7, "D_SE,E_E,E_S"),
("F_SW", [-0.275, -0.286, 3.003], F, 7, "D_SW,E_W,E_S"),
// The following must be in order around the octagon (in some orientation)
("G_1", [ 0.506, 1.201, 3.309], F, 8, "E_N,F_NE"),
("G_2", [ 1.213, 0.494, 3.307], F, 8, "E_E,F_NE"),
("G_4", [ 1.213, -0.506, 3.306], F, 8, "E_E,F_SE"),
("G_5", [ 0.506, -1.213, 3.306], F, 8, "E_S,F_SE"),
("G_7", [-0.494, -1.213, 3.307], F, 8, "E_S,F_SW"),
("G_8", [-1.201, -0.506, 3.309], F, 8, "E_W,F_SW"),
("G_10", [-1.201, 0.494, 3.311], F, 8, "E_W,F_NW"),
("G_11", [-0.494, 1.201, 3.311], F, 8, "E_N,F_NW"),
]);
const_array!(LEVEL_COLORS: ElementColor = [
[0.75, 0., 0.75],
[1., 0.4, 0.6],
[1., 0., 0.25],
[1., 0.75, 0.25],
[0.75, 0.5, 0.],
[0.9, 0.9, 0.],
[0.25, 0.75, 0.],
[0., 0.5, 0.75],
[0.25, 0., 1.],
[0.6, 0.15, 0.6],
[0.9, 0.5, 0.6],
[0.85, 0.15, 0.35]
]);
fn load_554aug2(assembly: &Assembly) {
// first a plane for the octagon
let oct_id = "f_g";
let engsph = engine::sphere_with_offset(
0., 0., 1., ACRON554_COMMON[37].1[2], 0.);
let octaface = Sphere::new(oct_id, "Octagon", [0.7, 0.7, 0.7], engsph);
octaface.ghost().set(true);
assembly.try_insert_element(octaface);
let face_rc = assembly.find_element(oct_id);
let face_curvature = face_rc.regulators().with_untracked(
|regs| regs.first().unwrap().clone()
);
face_curvature.set_to(0.);
// Octagon vertices and side/diagonal lengths
let mut oct_verts = Vec::<Rc<dyn Element>>::new();
const OCT_LONG: usize = 4;
const OCT_N_DIAG: usize = 5;
const OCT_N: usize = 8;
const OCT_DIST: [f64; OCT_N_DIAG] = [0., // dummy at start
-0.5,
-0.5*(2. + SQRT_2),
-0.5*(3. + 2.*SQRT_2),
-0.5*(4. + 2.*SQRT_2)
];
// Now process the acron data
for (id, v, pinned, l, neighbors) in ACRON554_COMMON {
let pt = Point::new(id, id, LEVEL_COLORS[l], point(v[0], v[1], v[2]));
assembly.try_insert_element(pt);
// QUESTION: Would there be a way to insert an Rc<dyn Element> into
// an assembly to avoid the need to re-lookup pt in the assembly
// after just inserting it? Or could/should try_insert_element return
// the Rc<dyn Element> ?
let pt_rc = assembly.find_element(id);
if pinned { // regulate each coordinate to its given value
let mut freeze = Vec::<Rc<dyn Regulator>>::new();
// filter the three coordinate regulators into freeze
pt_rc.regulators().with_untracked( |regs| {
for reg in regs {
if let Some(_pcr) = reg
.as_any().downcast_ref::<PointCoordinateRegulator>() {
freeze.push(reg.clone())
}
}
});
// now set them to their originally specified values.
let mut coord: usize = 0;
for reg in freeze {
reg.set_to(v[coord]);
coord += 1;
}
}
// If part of the octagon, make incident to the plane:
if l == 8 {
let oct_index = oct_verts.len();
oct_verts.push(pt_rc.clone());
let incidence = InversiveDistanceRegulator::new(
[face_rc.clone(), pt_rc.clone()]);
// incidence.set_to(0.0);
assembly.insert_regulator(Rc::new(incidence));
// And regulate the length to the other vertices of the octagon
for offset in 1..OCT_N_DIAG {
if offset <= oct_index {
let dist = InversiveDistanceRegulator::new(
[oct_verts[oct_index - offset].clone(), pt_rc.clone()]);
if offset == 1 { dist.set_to(OCT_DIST[offset]); }
assembly.insert_regulator(Rc::new(dist));
}
if offset < OCT_LONG && oct_index + offset >= OCT_N {
let forward = oct_index + offset - OCT_N;
let dist = InversiveDistanceRegulator::new(
[oct_verts[forward].clone(), pt_rc.clone()]);
if offset == 1 { dist.set_to(OCT_DIST[offset]); }
assembly.insert_regulator(Rc::new(dist));
}
}
}
// Finally, add any specified neighbors
for id in neighbors.split(",") {
if id.len() == 0 { continue; }
let strut = InversiveDistanceRegulator::new(
[assembly.find_element(id), pt_rc.clone()]);
strut.set_to(-0.5);
assembly.insert_regulator(Rc::new(strut));
}
}
}
const_array!(ACRON554_INDOME: (&str, [f64; 3], bool, usize, &str) = [
// id, coordinates, Pin/Free, level, earlier neighbor IDs.
("H_E", [ 0.359, -0.006, 3.161], F, 9, "G_2,G_4"),
("H_N", [ 0.005, 0.348, 3.158], F, 9, "G_1,G_11"),
("H_S", [ 0.006, -0.359, 3.159], F, 9, "G_5,G_7"),
("H_W", [-0.347, -0.005, 3.163], F, 9, "G_8,G_10"),
("I_NE", [ 0.507, 0.493, 4.015], F, 10, "G_1,G_2,H_E,H_N"),
("I_NW", [-0.493, 0.493, 4.013], F, 10, "G_10,G_11,H_N,H_W,I_NE"),
("I_SE", [ 0.507, -0.507, 4.012], F, 10, "G_4,G_5,H_E,H_S,I_NE"),
("I_SW", [-0.493, -0.507, 4.015], F, 10, "G_7,G_8,H_S,H_W,I_NW,I_SE"),
("J", [ 0.004, -0.010, 3.303], F, 11, "I_NE,I_NW,I_SE,I_SW"),
]);
const_array!(ACRON554_OUTDOME: (&str, [f64; 3], bool, usize, &str) = [
// id, coordinates, Pin/Free, level, earlier neighbor IDs.
("H_E", [ 1.361, -0.007, 4.16], F, 9, "G_2,G_4"),
("H_N", [ 0.007, 1.346, 4.164], F, 9, "G_1,G_11"),
("H_S", [ 0.007, -1.361, 4.16], F, 9, "G_5,G_7"),
("H_W", [-1.346, -0.007, 4.164], F, 9, "G_8,G_10"),
("I_NE", [ 0.507, 0.493, 4.015], F, 10, "G_1,G_2,H_E,H_N"),
("I_NW", [-0.493, 0.493, 4.013], F, 10, "G_10,G_11,H_N,H_W,I_NE"),
("I_SE", [ 0.507, -0.507, 4.012], F, 10, "G_4,G_5,H_E,H_S,I_NE"),
("I_SW", [-0.493, -0.507, 4.015], F, 10, "G_7,G_8,H_S,H_W,I_NW,I_SE"),
("J", [ 0.008, -0.008, 4.722], F, 11, "I_NE,I_NW,I_SE,I_SW"),
]);
fn load_554domed(assembly: &Assembly) {
load_554aug2(assembly);
// Now process the additional data
for (id, v, _pinned, l, neighbors) in ACRON554_INDOME {
let pt = Point::new(id, id, LEVEL_COLORS[l], point(v[0], v[1], v[2]));
assembly.try_insert_element(pt);
let pt_rc = assembly.find_element(id);
// Add any specified neighbors
for id in neighbors.split(",") {
if id.len() == 0 { continue; }
let strut = InversiveDistanceRegulator::new(
[assembly.find_element(id), pt_rc.clone()]);
strut.set_to(-0.5);
assembly.insert_regulator(Rc::new(strut));
}
}
}
// --- chooser ---
/* DEBUG */
@ -1044,15 +918,11 @@ pub fn TestAssemblyChooser() -> View {
"off-center" => load_off_center(assembly),
"radius-ratio" => load_radius_ratio(assembly),
"irisawa-hexlet" => load_irisawa_hexlet(assembly),
"aug554" => load_554aug2(assembly),
"domed554" => load_554domed(assembly),
_ => (),
};
});
});
// FIXME: Non-DRY -- should not need to reiterate thie list of assembly
// labels
// build the chooser
view! {
select(bind:value = assembly_name) {
@ -1065,8 +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 = "aug554") { "McNeill acron 554" }
option(value = "domed554") { "Domed acron 554" }
option(value = "empty") { "Empty" }
}
}

View file

@ -46,7 +46,7 @@ pub fn project_sphere_to_normalized(rep: &mut DVector<f64>) {
// normalize a point's representation vector by scaling
pub fn project_point_to_normalized(rep: &mut DVector<f64>) {
rep.scale_mut(0.5 / rep[3]); //FIXME: This 3 should be Point::WEIGHT_COMPONENT
rep.scale_mut(0.5 / rep[3]);
}
// --- partial matrices ---

View file

@ -1,3 +1 @@
#![feature(const_trait_impl)]
pub mod engine;

View file

@ -1,5 +1,3 @@
#![feature(const_trait_impl)]
mod assembly;
mod components;
mod engine;

View file

@ -1,378 +0,0 @@
A_NE:
┌ ┐
│ 0.5 │
│ 0.5 │
│ 0 │
│ 0.5 │
│ 0.25 │
└ ┘
A_NW:
┌ ┐
│ -0.5 │
│ 0.5 │
│ 0 │
│ 0.5 │
│ 0.25 │
└ ┘
A_SE:
┌ ┐
│ 0.5 │
│ -0.5 │
│ 0 │
│ 0.5 │
│ 0.25 │
└ ┘
A_SW:
┌ ┐
│ -0.5 │
│ -0.5 │
│ 0 │
│ 0.5 │
│ 0.25 │
└ ┘
Z_E:
┌ ┐
│ 0.22634143549538555 │
│ 0.000032427771744506665 │
│ 0.8216513780396574 │
│ 0.5 │
│ 0.363170717377161 │
└ ┘
Z_S:
┌ ┐
│ -0.000032427771744220924 │
│ -0.22634143549538618 │
│ 0.8216513780396576 │
│ 0.5 │
│ 0.36317071737716133 │
└ ┘
B_NE:
┌ ┐
│ 0.8090169943749475 │
│ 0.8090169943749475 │
│ 0.8994537199739336 │
│ 0.5 │
│ 1.0590169943749475 │
└ ┘
B_NW:
┌ ┐
│ -0.8090169943749475 │
│ 0.8090169943749475 │
│ 0.8994537199739336 │
│ 0.5 │
│ 1.0590169943749475 │
└ ┘
B_SW:
┌ ┐
│ -0.8090169943749475 │
│ -0.8090169943749475 │
│ 0.8994537199739336 │
│ 0.5 │
│ 1.0590169943749475 │
└ ┘
B_SE:
┌ ┐
│ 0.8089673881587364 │
│ -0.8089673881587335 │
│ 0.8995105586041169 │
│ 0.5 │
│ 1.0589799582637938 │
└ ┘
Y_NE:
┌ ┐
│ 0.10684030416478518 │
│ 0.1069861535711018 │
│ 1.0181778574241942 │
│ 0.5 │
│ 0.5297735192825956 │
└ ┘
Y_NW:
┌ ┐
│ -0.10693925692260356 │
│ 0.10693925692260356 │
│ 1.0182278452645257 │
│ 0.5 │
│ 0.5298299819167654 │
└ ┘
Y_SE:
┌ ┐
│ 0.10684605297358556 │
│ -0.10684605297358302 │
│ 1.0181767492960683 │
│ 0.5 │
│ 0.5297580227088338 │
└ ┘
Y_SW:
┌ ┐
│ -0.10698615357110258 │
│ -0.10684030416478382 │
│ 1.0181778574241929 │
│ 0.5 │
│ 0.5297735192825943 │
└ ┘
C_N:
┌ ┐
│ 0 │
│ 1 │
│ 1.4553466902253547 │
│ 0.5 │
│ 1.5590169943749472 │
└ ┘
C_W:
┌ ┐
│ -1 │
│ 0 │
│ 1.4553466902253547 │
│ 0.5 │
│ 1.5590169943749472 │
└ ┘
C_E:
┌ ┐
│ 0.9997685166344451 │
│ 0.000045591796741388215 │
│ 1.4555126531079483 │
│ 0.5 │
│ 1.5590202705588938 │
└ ┘
C_S:
┌ ┐
│ -0.00004559179673933571 │
│ -0.9997685166344433 │
│ 1.4555126531079468 │
│ 0.5 │
│ 1.5590202705588896 │
└ ┘
D_NE:
┌ ┐
│ 0.19050130249898212 │
│ 0.19080920821293734 │
│ 2.0111349254444733 │
│ 0.5 │
│ 2.0586841044725492 │
└ ┘
D_NW:
┌ ┐
│ -0.19087531658436754 │
│ 0.1908753165843677 │
│ 2.0111018697108243 │
│ 0.5 │
│ 2.058720870516732 │
└ ┘
D_SE:
┌ ┐
│ 0.1904710244638656 │
│ -0.1904710244638643 │
│ 2.011170408319603 │
│ 0.5 │
│ 2.0586783801164676 │
└ ┘
D_SW:
┌ ┐
│ -0.19080920821293662 │
│ -0.19050130249898098 │
│ 2.011134925444472 │
│ 0.5 │
│ 2.0586841044725475 │
└ ┘
E_N:
┌ ┐
│ -0.0001774225161991373 │
│ 1.0673561656973407 │
│ 2.453087108367921 │
│ 0.5 │
│ 3.5784389787247153 │
└ ┘
E_W:
┌ ┐
│ -1.0673561656973403 │
│ 0.00017742251619961636 │
│ 2.4530871083679195 │
│ 0.5 │
│ 3.578438978724712 │
└ ┘
E_E:
┌ ┐
│ 1.0669548967200053 │
│ 0.0001843766584768728 │
│ 2.4532662028373866 │
│ 0.5 │
│ 3.5784510553790545 │
└ ┘
E_S:
┌ ┐
│ -0.00018437665847651806 │
│ -1.0669548967200044 │
│ 2.453266202837385 │
│ 0.5 │
│ 3.57845105537905 │
└ ┘
F_NE:
┌ ┐
│ 0.28011657185554717 │
│ 0.280597474289424 │
│ 3.003049209443169 │
│ 0.5 │
│ 4.587753011062896 │
└ ┘
F_NW:
┌ ┐
│ -0.280598407806719 │
│ 0.28059840780671874 │
│ 3.002988046197754 │
│ 0.5 │
│ 4.587705454166589 │
└ ┘
F_SE:
┌ ┐
│ 0.28011643423019444 │
│ -0.2801164342301949 │
│ 3.0031051243209257 │
│ 0.5 │
│ 4.587785491049536 │
└ ┘
F_SW:
┌ ┐
│ -0.2805974742894247 │
│ -0.28011657185554784 │
│ 3.003049209443168 │
│ 0.5 │
│ 4.587753011062893 │
└ ┘
G_1:
┌ ┐
│ 0.4997377212516245 │
│ 1.2073683664308739 │
│ 3.3077679987306667 │
│ 0.5 │
│ 6.324404048677748 │
└ ┘
G_2:
┌ ┐
│ 1.2068449832232118 │
│ 0.5002622274345682 │
│ 3.307880423181695 │
│ 0.5 │
│ 6.3244041808325075 │
└ ┘
G_4:
┌ ┐
│ 1.2068444751045393 │
│ -0.4997377595067899 │
│ 3.307966697528916 │
│ 0.5 │
│ 6.324427820224868 │
└ ┘
G_5:
┌ ┐
│ 0.4997377595067881 │
│ -1.2068444751045408 │
│ 3.307966697528914 │
│ 0.5 │
│ 6.324427820224864 │
└ ┘
G_7:
┌ ┐
│ -0.5002622274345697 │
│ -1.2068449832232129 │
│ 3.307880423181691 │
│ 0.5 │
│ 6.324404180832497 │
└ ┘
G_8:
┌ ┐
│ -1.2073683664308756 │
│ -0.4997377212516256 │
│ 3.307767998730663 │
│ 0.5 │
│ 6.3244040486777395 │
└ ┘
G_10:
┌ ┐
│ -1.2073693671510268 │
│ 0.50026206687508 │
│ 3.307678829535478 │
│ 0.5 │
│ 6.3243704479462215 │
└ ┘
G_11:
┌ ┐
│ -0.5002620668750811 │
│ 1.2073693671510257 │
│ 3.30767882953548 │
│ 0.5 │
│ 6.324370447946225 │
└ ┘

View file

@ -1,422 +0,0 @@
A_NE:
┌ ┐
│ 0.5 │
│ 0.5 │
│ 0 │
│ 0.5 │
│ 0.25 │
└ ┘
A_NW:
┌ ┐
│ -0.5 │
│ 0.5 │
│ 0 │
│ 0.5 │
│ 0.25 │
└ ┘
A_SE:
┌ ┐
│ 0.5 │
│ -0.5 │
│ 0 │
│ 0.5 │
│ 0.25 │
└ ┘
A_SW:
┌ ┐
│ -0.5 │
│ -0.5 │
│ 0 │
│ 0.5 │
│ 0.25 │
└ ┘
Z_E:
┌ ┐
│ 0.226403187618189 │
│ -0.0000011852849153538265 │
│ 0.8216719444221399 │
│ 0.5 │
│ 0.36320159380866957 │
└ ┘
Z_S:
┌ ┐
│ 0.0000012064271737704876 │
│ -0.22640322646306607 │
│ 0.8216719573565728 │
│ 0.5 │
│ 0.36320161323110667 │
└ ┘
B_NE:
┌ ┐
│ 0.8090169943749475 │
│ 0.8090169943749475 │
│ 0.8994537199739336 │
│ 0.5 │
│ 1.0590169943749475 │
└ ┘
B_NW:
┌ ┐
│ -0.8090169943749475 │
│ 0.8090169943749475 │
│ 0.8994537199739336 │
│ 0.5 │
│ 1.0590169943749475 │
└ ┘
B_SW:
┌ ┐
│ -0.8090169943749475 │
│ -0.8090169943749475 │
│ 0.8994537199739336 │
│ 0.5 │
│ 1.0590169943749475 │
└ ┘
B_SE:
┌ ┐
│ 0.8090190229759617 │
│ -0.8090188141637347 │
│ 0.899451950040933 │
│ 0.5 │
│ 1.059018649731349 │
└ ┘
Y_NE:
┌ ┐
│ 0.10639264310226318 │
│ 0.10730337799321536 │
│ 1.0174285282252145 │
│ 0.5 │
│ 0.5289971090900759 │
└ ┘
Y_NW:
┌ ┐
│ -0.10692454007698927 │
│ 0.10692454007699025 │
│ 1.0183290894713781 │
│ 0.5 │
│ 0.5299299245035725 │
└ ┘
Y_SE:
┌ ┐
│ 0.10619251333984216 │
│ -0.10758965782463996 │
│ 1.0179033510994517 │
│ 0.5 │
│ 0.5294898085465076 │
└ ┘
Y_SW:
┌ ┐
│ -0.1067218861162948 │
│ -0.10720833408227096 │
│ 1.018815416393646 │
│ 0.5 │
│ 0.5304340206102979 │
└ ┘
C_N:
┌ ┐
│ 0 │
│ 1 │
│ 1.4553466902253547 │
│ 0.5 │
│ 1.5590169943749472 │
└ ┘
C_W:
┌ ┐
│ -1 │
│ 0 │
│ 1.4553466902253547 │
│ 0.5 │
│ 1.5590169943749472 │
└ ┘
C_E:
┌ ┐
│ 0.9987752175493758 │
│ -0.0000014991453936079486 │
│ 1.4557639853989686 │
│ 0.5 │
│ 1.558400358790791 │
└ ┘
C_S:
┌ ┐
│ 0.0000016241779451142522 │
│ -1.0006558417024733 │
│ 1.4551187604443314 │
│ 0.5 │
│ 1.5593413623171548 │
└ ┘
D_NE:
┌ ┐
│ 0.18880014858596228 │
│ 0.1900242678455326 │
│ 2.0105891402812603 │
│ 0.5 │
│ 2.057111349663646 │
└ ┘
D_NW:
┌ ┐
│ -0.19098138154171115 │
│ 0.19098137963576622 │
│ 2.0112375319589524 │
│ 0.5 │
│ 2.0590124438119553 │
└ ┘
D_SE:
┌ ┐
│ 0.18930742120763205 │
│ -0.19118865645940125 │
│ 2.0109297224500113 │
│ 0.5 │
│ 2.0581147310902232 │
└ ┘
D_SW:
┌ ┐
│ -0.19150073518747784 │
│ -0.19215789298794494 │
│ 2.011588230853374 │
│ 0.5 │
│ 2.060041842611764 │
└ ┘
E_N:
┌ ┐
│ 0.0018705386847266854 │
│ 1.067093230632768 │
│ 2.4530916457713374 │
│ 0.5 │
│ 3.5781750425903667 │
└ ┘
E_W:
┌ ┐
│ -1.0677431983248686 │
│ 0.0010069929693730891 │
│ 2.453048970298719 │
│ 0.5 │
│ 3.578762902144111 │
└ ┘
E_E:
┌ ┐
│ 1.0651802756853244 │
│ 0.0009763264191362732 │
│ 2.4535562533844497 │
│ 0.5 │
│ 3.5772741307914204 │
└ ┘
E_S:
┌ ┐
│ 0.0019014550057512374 │
│ -1.0684657533845878 │
│ 2.4528152065600635 │
│ 0.5 │
│ 3.5789625604223154 │
└ ┘
F_NE:
┌ ┐
│ 0.2768627273275836 │
│ 0.27828736361573514 │
│ 3.0027865275597208 │
│ 0.5 │
│ 4.58541185017254 │
└ ┘
F_NW:
┌ ┐
│ -0.28103002630311136 │
│ 0.2812882756471644 │
│ 3.0030716277259866 │
│ 0.5 │
│ 4.588270112066947 │
└ ┘
F_SE:
┌ ┐
│ 0.2784445942560668 │
│ -0.28049951604978735 │
│ 3.002936318943545 │
│ 0.5 │
│ 4.586918980412344 │
└ ┘
F_SW:
┌ ┐
│ -0.28267553068657514 │
│ -0.2835640963165625 │
│ 3.0032197706602255 │
│ 0.5 │
│ 4.58982149472019 │
└ ┘
G_1:
┌ ┐
│ 0.49499953176717715 │
│ 1.2038466206487632 │
│ 3.3122320493386623 │
│ 0.5 │
│ 6.332576199448855 │
└ ┘
G_2:
┌ ┐
│ 1.202713808441931 │
│ 0.49734958577973293 │
│ 3.3107015388161924 │
│ 0.5 │
│ 6.327310911161432 │
└ ┘
G_4:
┌ ┐
│ 1.205139169495155 │
│ -0.5026367636047644 │
│ 3.3060733731956917 │
│ 0.5 │
│ 6.3175626265818385 │
└ ┘
G_5:
┌ ┐
│ 0.49496884524644963 │
│ -1.2066437505221832 │
│ 3.3117630879125723 │
│ 0.5 │
│ 6.334379009718901 │
└ ┘
G_7:
┌ ┐
│ -0.5049809193563459 │
│ -1.2113397181531034 │
│ 3.3029076805907547 │
│ 0.5 │
│ 6.315774407531318 │
└ ┘
G_8:
┌ ┐
│ -1.2105141090163352 │
│ -0.502666189603216 │
│ 3.305064063797819 │
│ 0.5 │
│ 6.320733099502539 │
└ ┘
G_10:
┌ ┐
│ -1.2079897553472638 │
│ 0.49731945797605365 │
│ 3.309789835947903 │
│ 0.5 │
│ 6.330637311024589 │
└ ┘
G_11:
┌ ┐
│ -0.5049515565196006 │
│ 1.2084435744379876 │
│ 3.303474805253526 │
│ 0.5 │
│ 6.314128853476387 │
└ ┘
H_E:
┌ ┐
│ 0.3493418610082748 │
│ -0.004066882149056481 │
│ 3.1680910206723154 │
│ 0.5 │
│ 5.079428493907433 │
└ ┘
H_N:
┌ ┐
│ -0.007700036653444471 │
│ 0.35115095882911657 │
│ 3.1700941994675693 │
│ 0.5 │
│ 5.086431758305634 │
└ ┘
H_S:
┌ ┐
│ -0.007865804611166411 │
│ -0.3529429622292672 │
│ 3.176292181037656 │
│ 0.5 │
│ 5.10673131179707 │
└ ┘
H_W:
┌ ┐
│ -0.35269124233601623 │
│ -0.004232365263278458 │
│ 3.179750192542026 │
│ 0.5 │
│ 5.117610155394313 │
└ ┘
I_NE:
┌ ┐
│ 0.49805064236295854 │
│ 0.4976522130513484 │
│ 4.020243412022774 │
│ 0.5 │
│ 8.329034633323353 │
└ ┘
I_NW:
┌ ┐
│ -0.5060555022841527 │
│ 0.5128022974270082 │
│ 4.021863319771454 │
│ 0.5 │
│ 8.34722145884986 │
└ ┘
I_SE:
┌ ┐
│ 0.5060495556329386 │
│ -0.5018683273402639 │
│ 4.021107068324604 │
│ 0.5 │
│ 8.338630006433696 │
└ ┘
I_SW:
┌ ┐
│ -0.5143110146278468 │
│ -0.5172754412791255 │
│ 4.0227602162137135 │
│ 0.5 │
│ 8.357344731885544 │
└ ┘
J:
┌ ┐
│ -0.012852519748535315 │
│ -0.006830373978588703 │
│ 3.3242009136904245 │
│ 0.5 │
│ 5.525261774194571 │
└ ┘

View file

@ -1,85 +0,0 @@
from math import fabs, nan, sqrt
import sys
vertices = {}
current_id = ""
for line in sys.stdin:
text = line.strip()
if text.endswith(":"):
new_id = text.strip(":")
vertices[new_id] = []
if current_id: print(current_id, vertices[current_id])
current_id = new_id
continue
remainder = text.strip("┌ ┐└┘│")
if len(remainder): vertices[current_id].append(float(remainder))
print(len(vertices), "vertices found")
P = True
F = False
HPHI = nan
RTHPHI = nan
RTPHIPH = nan
# Taken verbatim from test_assembly_chooser.rs
acron_data = [
("A_NE", [ 0.5, 0.5, 0.], P, 0, ""),
("A_NW", [-0.5, 0.5, 0.], P, 0, ""),
("A_SE", [ 0.5, -0.5, 0.], P, 0, ""),
("A_SW", [-0.5, -0.5, 0.], P, 0, ""),
("Z_E", [ 0.229, -0.002, 0.821], F, 1, "A_NE,A_SE"),
("Z_S", [ 0.002, -0.229, 0.821], F, 1, "A_SE,A_SW"),
("B_NE", [ HPHI, HPHI, RTHPHI], P, 2, "Z_E"),
("B_NW", [-HPHI, HPHI, RTHPHI], P, 2, ""),
("B_SW", [-HPHI, -HPHI, RTHPHI], P, 2, "Z_S"),
("B_SE", [ 0.812, -0.812, 0.89], F, 2, "A_SE,Z_E,Z_S"),
("Y_NE", [ 0.11, 0.103, 1.019], F, 3, "B_NE"),
("Y_NW", [-0.103, 0.103, 1.02], F, 3, "B_NW"),
("Y_SE", [ 0.11, -0.11, 1.017], F, 3, "B_SE"),
("Y_SW", [-0.103, -0.11, 1.019], F, 3, "B_SW"),
("C_N", [ 0., 1., RTPHIPH], P, 4, "Y_NE,Y_NW"),
("C_W", [-1., 0., RTPHIPH], P, 4, "Y_NW,Y_SW"),
("C_E", [ 1.006, -0.006, 1.45], F, 4, "B_NE,B_SE,Y_NE,Y_SE"),
("C_S", [ 0.006, -1.006, 1.45], F, 4, "B_SE,B_SW,Y_SE,Y_SW"),
("D_NE", [ 0.2, 0.181, 2.011], F, 5, "Y_NE,C_N,C_E"),
("D_NW", [-0.181, 0.181, 2.014], F, 5, "Y_NW,C_N,C_W"),
("D_SE", [ 0.2, -0.2, 2.009], F, 5, "Y_SE,C_E,C_S"),
("D_SW", [-0.181, -0.2, 2.011], F, 5, "Y_SW,C_W,C_S"),
("E_N", [ 0.012, 1.055, 2.46], F, 6, "C_N,D_NE,D_NW"),
("E_W", [-1.055, -0.012, 2.46], F, 6, "C_W,D_NW,D_SW"),
("E_E", [ 1.079, -0.012, 2.447], F, 6, "C_E,D_NE,D_SE"),
("E_S", [ 0.012, -1.079, 2.447], F, 6, "C_S,D_SE,D_SW"),
("F_NE", [ 0.296, 0.265, 3.003], F, 7, "D_NE,E_N,E_E"),
("F_NW", [-0.265, 0.265, 3.007], F, 7, "D_NW,E_N,E_W"),
("F_SE", [ 0.296, -0.296, 3.0], F, 7, "D_SE,E_E,E_S"),
("F_SW", [-0.265, -0.296, 3.003], F, 7, "D_SW,E_W,E_S"),
# The following must be in order around the octagon (in some orientation)
("G_1", [ 0.517, 1.19, 3.312], F, 8, "E_N,F_NE"),
("G_2", [ 1.224, 0.483, 3.304], F, 8, "E_E,F_NE"),
("G_4", [ 1.224, -0.517, 3.298], F, 8, "E_E,F_SE"),
("G_5", [ 0.517, -1.224, 3.298], F, 8, "E_S,F_SE"),
("G_7", [-0.483, -1.224, 3.304], F, 8, "E_S,F_SW"),
("G_8", [-1.19, -0.517, 3.312], F, 8, "E_W,F_SW"),
("G_10", [-1.19, 0.483, 3.318], F, 8, "E_W,F_NW"),
("G_11", [-0.483, 1.19, 3.318], F, 8, "E_N,F_NW"),
]
E = 0.0
n_struts = 11 # for the pinned vertices, which all have length exactly 1
for vi in range(0, len(acron_data)):
start_id = acron_data[vi][0]
start = vertices[start_id]
ends = acron_data[vi][4].split(",")
if acron_data[vi][3] == 8:
if start_id != "G_1":
ends.append(acron_data[vi-1][0])
if vi+1 == len(acron_data):
ends.append("G_1")
for end_id in ends:
if not(end_id): continue
end = vertices[end_id]
dist = sqrt(
(end[0]-start[0])**2 + (end[1]-start[1])**2 + (end[2]-start[2])**2)
print(f"{start_id}-{end_id}: {dist} {dist-1}")
E += fabs(dist-1)
n_struts += 1
print(n_struts, "unit edges")
print(f"----> Total distortion E={E}")

View file

@ -1,95 +0,0 @@
from math import fabs, nan, sqrt
import sys
vertices = {}
current_id = ""
for line in sys.stdin:
text = line.strip()
if text.endswith(":"):
new_id = text.strip(":")
vertices[new_id] = []
if current_id: print(current_id, vertices[current_id])
current_id = new_id
continue
remainder = text.strip("┌ ┐└┘│")
if len(remainder): vertices[current_id].append(float(remainder))
print(len(vertices), "vertices found")
P = True
F = False
HPHI = nan
RTHPHI = nan
RTPHIPH = nan
# Taken verbatim from test_assembly_chooser.rs
acron_data = [
("A_NE", [ 0.5, 0.5, 0.], P, 0, ""),
("A_NW", [-0.5, 0.5, 0.], P, 0, ""),
("A_SE", [ 0.5, -0.5, 0.], P, 0, ""),
("A_SW", [-0.5, -0.5, 0.], P, 0, ""),
("Z_E", [ 0.229, -0.002, 0.821], F, 1, "A_NE,A_SE"),
("Z_S", [ 0.002, -0.229, 0.821], F, 1, "A_SE,A_SW"),
("B_NE", [ HPHI, HPHI, RTHPHI], P, 2, "Z_E"),
("B_NW", [-HPHI, HPHI, RTHPHI], P, 2, ""),
("B_SW", [-HPHI, -HPHI, RTHPHI], P, 2, "Z_S"),
("B_SE", [ 0.812, -0.812, 0.89], F, 2, "A_SE,Z_E,Z_S"),
("Y_NE", [ 0.11, 0.103, 1.019], F, 3, "B_NE"),
("Y_NW", [-0.103, 0.103, 1.02], F, 3, "B_NW"),
("Y_SE", [ 0.11, -0.11, 1.017], F, 3, "B_SE"),
("Y_SW", [-0.103, -0.11, 1.019], F, 3, "B_SW"),
("C_N", [ 0., 1., RTPHIPH], P, 4, "Y_NE,Y_NW"),
("C_W", [-1., 0., RTPHIPH], P, 4, "Y_NW,Y_SW"),
("C_E", [ 1.006, -0.006, 1.45], F, 4, "B_NE,B_SE,Y_NE,Y_SE"),
("C_S", [ 0.006, -1.006, 1.45], F, 4, "B_SE,B_SW,Y_SE,Y_SW"),
("D_NE", [ 0.2, 0.181, 2.011], F, 5, "Y_NE,C_N,C_E"),
("D_NW", [-0.181, 0.181, 2.014], F, 5, "Y_NW,C_N,C_W"),
("D_SE", [ 0.2, -0.2, 2.009], F, 5, "Y_SE,C_E,C_S"),
("D_SW", [-0.181, -0.2, 2.011], F, 5, "Y_SW,C_W,C_S"),
("E_N", [ 0.012, 1.055, 2.46], F, 6, "C_N,D_NE,D_NW"),
("E_W", [-1.055, -0.012, 2.46], F, 6, "C_W,D_NW,D_SW"),
("E_E", [ 1.079, -0.012, 2.447], F, 6, "C_E,D_NE,D_SE"),
("E_S", [ 0.012, -1.079, 2.447], F, 6, "C_S,D_SE,D_SW"),
("F_NE", [ 0.296, 0.265, 3.003], F, 7, "D_NE,E_N,E_E"),
("F_NW", [-0.265, 0.265, 3.007], F, 7, "D_NW,E_N,E_W"),
("F_SE", [ 0.296, -0.296, 3.0], F, 7, "D_SE,E_E,E_S"),
("F_SW", [-0.265, -0.296, 3.003], F, 7, "D_SW,E_W,E_S"),
# The following must be in order around the octagon (in some orientation)
("G_1", [ 0.517, 1.19, 3.312], F, 8, "E_N,F_NE"),
("G_2", [ 1.224, 0.483, 3.304], F, 8, "E_E,F_NE"),
("G_4", [ 1.224, -0.517, 3.298], F, 8, "E_E,F_SE"),
("G_5", [ 0.517, -1.224, 3.298], F, 8, "E_S,F_SE"),
("G_7", [-0.483, -1.224, 3.304], F, 8, "E_S,F_SW"),
("G_8", [-1.19, -0.517, 3.312], F, 8, "E_W,F_SW"),
("G_10", [-1.19, 0.483, 3.318], F, 8, "E_W,F_NW"),
("G_11", [-0.483, 1.19, 3.318], F, 8, "E_N,F_NW"),
# Additional data
("H_E", [ 0.359, -0.006, 3.161], F, 9, "G_2,G_4"),
("H_N", [ 0.005, 0.348, 3.158], F, 9, "G_1,G_11"),
("H_S", [ 0.006, 0.359, 3.159], F, 9, "G_5,G_7"),
("H_W", [-0.347, -0.005, 3.163], F, 9, "G_8,G_10"),
("I_NE", [ 0.507, 0.493, 4.015], F, 10, "G_1,G_2,H_E,H_N"),
("I_NW", [-0.493, 0.493, 4.013], F, 10, "G_10,G_11,H_N,H_W"),
("I_SE", [ 0.507, -0.507, 4.012], F, 10, "G_4,G_5,H_E,H_S"),
("I_SW", [-0.493, -0.507, 4.015], F, 10, "G_7,G_8,H_S,H_W"),
("J", [ 0.004, -0.010, 3.303], F, 11, "I_NE,I_NW,I_SE,I_SW"),
]
E = 0.0
n_struts = 11 # for the pinned vertices, which all have length exactly 1
for vi in range(0, len(acron_data)):
start_id = acron_data[vi][0]
start = vertices[start_id]
ends = acron_data[vi][4].split(",")
if acron_data[vi][3] == 8:
if start_id != "G_1":
ends.append(acron_data[vi-1][0])
if vi+1 == len(acron_data) or not(acron_data[vi+1][0].startswith("G")):
ends.append("G_1")
for end_id in ends:
if not(end_id): continue
end = vertices[end_id]
dist = sqrt(
(end[0]-start[0])**2 + (end[1]-start[1])**2 + (end[2]-start[2])**2)
print(f"{start_id}-{end_id}: {dist} {dist-1}")
E += fabs(dist-1)
n_struts += 1
print(n_struts, "unit edges")
print(f"----> Total distortion E={E}")