feat: Point coordinate regulators (#118)
All checks were successful
/ test (push) Successful in 3m43s
All checks were successful
/ test (push) Successful in 3m43s
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>
This commit is contained in:
parent
978f70aac7
commit
2c8c09d20d
6 changed files with 180 additions and 49 deletions
64
README.md
64
README.md
|
@ -12,11 +12,11 @@ Note that currently this is just the barest beginnings of the project, more of a
|
||||||
|
|
||||||
### Implementation goals
|
### 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
|
## Prototype
|
||||||
|
|
||||||
|
@ -24,38 +24,40 @@ The latest prototype is in the folder `app-proto`. It includes both a user inter
|
||||||
|
|
||||||
### Install the prerequisites
|
### Install the prerequisites
|
||||||
|
|
||||||
1. Install [`rustup`](https://rust-lang.github.io/rustup/): the officially recommended Rust toolchain manager
|
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)
|
- 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"
|
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
|
- 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)
|
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/)
|
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
|
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
|
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
|
- 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
|
- 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
|
### Play with the prototype
|
||||||
|
|
||||||
1. From the `app-proto` folder, call `trunk serve --release` to build and serve 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
|
- 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
|
- 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.
|
- 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:`
|
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
|
- 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
|
4. Press *ctrl+C* in the shell where Trunk is running to stop serving the prototype.
|
||||||
|
|
||||||
### Run the engine on some example problems
|
### Run the engine on some example problems
|
||||||
|
|
||||||
1. Use `sh` to run the script `tools/run-examples.sh`
|
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 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:
|
- The call from the top level of the repository is:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sh tools/run-examples.sh
|
sh tools/run-examples.sh
|
||||||
```
|
```
|
||||||
- For each example problem, the engine will print the value of the loss function at each optimization step
|
- 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
|
- 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
|
```julia
|
||||||
include("irisawa-hexlet.jl")
|
include("irisawa-hexlet.jl")
|
||||||
|
@ -64,24 +66,24 @@ The latest prototype is in the folder `app-proto`. It includes both a user inter
|
||||||
end
|
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
|
### Run the automated tests
|
||||||
|
|
||||||
1. Go into the `app-proto` folder
|
1. Go into the `app-proto` folder.
|
||||||
2. Call `cargo test`
|
2. Call `cargo test`.
|
||||||
|
|
||||||
### Deploy the prototype
|
### Deploy the prototype
|
||||||
|
|
||||||
1. From the `app-proto` folder, call `trunk build --release`
|
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
|
- 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
|
- 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`.
|
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:
|
- The call from the top level of the repository is:
|
||||||
```bash
|
```bash
|
||||||
sh tools/package-for-deployment.sh
|
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.
|
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.
|
||||||
|
|
21
app-proto/Cargo.lock
generated
21
app-proto/Cargo.lock
generated
|
@ -255,6 +255,7 @@ dependencies = [
|
||||||
"charming",
|
"charming",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"dyna3",
|
"dyna3",
|
||||||
|
"enum-iterator",
|
||||||
"itertools",
|
"itertools",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
@ -271,6 +272,26 @@ version = "1.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum-iterator"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4549325971814bda7a44061bf3fe7e487d447cba01e4220a4b454d630d7a016"
|
||||||
|
dependencies = [
|
||||||
|
"enum-iterator-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum-iterator-derive"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "685adfa4d6f3d765a26bc5dbc936577de9abf756c1feeb3089b01dd395034842"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
|
@ -10,6 +10,7 @@ default = ["console_error_panic_hook"]
|
||||||
dev = []
|
dev = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
enum-iterator = "2.3.0"
|
||||||
itertools = "0.13.0"
|
itertools = "0.13.0"
|
||||||
js-sys = "0.3.70"
|
js-sys = "0.3.70"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
use enum_iterator::{all, Sequence};
|
||||||
use nalgebra::{DMatrix, DVector, DVectorView};
|
use nalgebra::{DMatrix, DVector, DVectorView};
|
||||||
use std::{
|
use std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::{BTreeMap, BTreeSet},
|
collections::{BTreeMap, BTreeSet},
|
||||||
fmt,
|
fmt,
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Display, Formatter},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{atomic, atomic::AtomicU64},
|
sync::{atomic, atomic::AtomicU64},
|
||||||
|
@ -26,6 +27,7 @@ use crate::{
|
||||||
ConfigSubspace,
|
ConfigSubspace,
|
||||||
ConstraintProblem,
|
ConstraintProblem,
|
||||||
DescentHistory,
|
DescentHistory,
|
||||||
|
MatrixEntry,
|
||||||
Realization,
|
Realization,
|
||||||
},
|
},
|
||||||
specified::SpecifiedValue,
|
specified::SpecifiedValue,
|
||||||
|
@ -84,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 {
|
pub trait ProblemPoser {
|
||||||
fn pose(&self, problem: &mut ConstraintProblem);
|
fn pose(&self, problem: &mut ConstraintProblem);
|
||||||
}
|
}
|
||||||
|
@ -125,8 +135,8 @@ pub trait Element: Serial + ProblemPoser + DisplayItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for dyn Element {
|
impl Debug for dyn Element {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
self.id().fmt(f)
|
Debug::fmt(&self.id(), f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,8 +259,7 @@ impl Serial for Sphere {
|
||||||
impl ProblemPoser for Sphere {
|
impl ProblemPoser for Sphere {
|
||||||
fn pose(&self, problem: &mut ConstraintProblem) {
|
fn pose(&self, problem: &mut ConstraintProblem) {
|
||||||
let index = self.column_index().expect(
|
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.gram.push_sym(index, index, 1.0);
|
||||||
problem.guess.set_column(index, &self.representation.get_clone_untracked());
|
problem.guess.set_column(index, &self.representation.get_clone_untracked());
|
||||||
}
|
}
|
||||||
|
@ -269,6 +278,7 @@ pub struct Point {
|
||||||
|
|
||||||
impl Point {
|
impl Point {
|
||||||
const WEIGHT_COMPONENT: usize = 3;
|
const WEIGHT_COMPONENT: usize = 3;
|
||||||
|
const NORM_COMPONENT: usize = 4;
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
id: String,
|
id: String,
|
||||||
|
@ -302,6 +312,15 @@ impl Element for Point {
|
||||||
point(0.0, 0.0, 0.0),
|
point(0.0, 0.0, 0.0),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_regulators(self: Rc<Self>) -> Vec<Rc<dyn Regulator>> {
|
||||||
|
all::<Axis>()
|
||||||
|
.map(|axis| {
|
||||||
|
Rc::new(PointCoordinateRegulator::new(self.clone(), axis))
|
||||||
|
as Rc::<dyn Regulator>
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn id(&self) -> &String {
|
fn id(&self) -> &String {
|
||||||
&self.id
|
&self.id
|
||||||
|
@ -345,8 +364,7 @@ impl Serial for Point {
|
||||||
impl ProblemPoser for Point {
|
impl ProblemPoser for Point {
|
||||||
fn pose(&self, problem: &mut ConstraintProblem) {
|
fn pose(&self, problem: &mut ConstraintProblem) {
|
||||||
let index = self.column_index().expect(
|
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.gram.push_sym(index, index, 0.0);
|
||||||
problem.frozen.push(Self::WEIGHT_COMPONENT, index, 0.5);
|
problem.frozen.push(Self::WEIGHT_COMPONENT, index, 0.5);
|
||||||
problem.guess.set_column(index, &self.representation.get_clone_untracked());
|
problem.guess.set_column(index, &self.representation.get_clone_untracked());
|
||||||
|
@ -436,8 +454,8 @@ impl ProblemPoser for InversiveDistanceRegulator {
|
||||||
if let Some(val) = set_pt.value {
|
if let Some(val) = set_pt.value {
|
||||||
let [row, col] = self.subjects.each_ref().map(
|
let [row, col] = self.subjects.each_ref().map(
|
||||||
|subj| subj.column_index().expect(
|
|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);
|
problem.gram.push_sym(row, col, val);
|
||||||
}
|
}
|
||||||
|
@ -446,14 +464,14 @@ impl ProblemPoser for InversiveDistanceRegulator {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HalfCurvatureRegulator {
|
pub struct HalfCurvatureRegulator {
|
||||||
pub subject: Rc<dyn Element>,
|
pub subject: Rc<Sphere>,
|
||||||
pub measurement: ReadSignal<f64>,
|
pub measurement: ReadSignal<f64>,
|
||||||
pub set_point: Signal<SpecifiedValue>,
|
pub set_point: Signal<SpecifiedValue>,
|
||||||
serial: u64,
|
serial: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HalfCurvatureRegulator {
|
impl HalfCurvatureRegulator {
|
||||||
pub fn new(subject: Rc<dyn Element>) -> Self {
|
pub fn new(subject: Rc<Sphere>) -> Self {
|
||||||
let measurement = subject.representation().map(
|
let measurement = subject.representation().map(
|
||||||
|rep| rep[Sphere::CURVATURE_COMPONENT]
|
|rep| rep[Sphere::CURVATURE_COMPONENT]
|
||||||
);
|
);
|
||||||
|
@ -490,14 +508,85 @@ impl ProblemPoser for HalfCurvatureRegulator {
|
||||||
self.set_point.with_untracked(|set_pt| {
|
self.set_point.with_untracked(|set_pt| {
|
||||||
if let Some(val) = set_pt.value {
|
if let Some(val) = set_pt.value {
|
||||||
let col = self.subject.column_index().expect(
|
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);
|
problem.frozen.push(Sphere::CURVATURE_COMPONENT, col, val);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Sequence)]
|
||||||
|
pub enum Axis { X = 0, Y = 1, Z = 2 }
|
||||||
|
|
||||||
|
impl Axis {
|
||||||
|
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 {
|
||||||
|
pub subject: Rc<Point>,
|
||||||
|
pub axis: Axis,
|
||||||
|
pub measurement: ReadSignal<f64>,
|
||||||
|
pub set_point: Signal<SpecifiedValue>,
|
||||||
|
serial: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointCoordinateRegulator {
|
||||||
|
pub fn new(subject: Rc<Point>, axis: Axis) -> Self {
|
||||||
|
let measurement = subject.representation().map(
|
||||||
|
move |rep| rep[axis as usize]
|
||||||
|
);
|
||||||
|
let set_point = create_signal(SpecifiedValue::from_empty_spec());
|
||||||
|
Self { subject, axis, measurement, set_point, serial: Self::next_serial() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serial for PointCoordinateRegulator {
|
||||||
|
fn serial(&self) -> u64 { self.serial }
|
||||||
|
}
|
||||||
|
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProblemPoser for PointCoordinateRegulator {
|
||||||
|
fn pose(&self, problem: &mut ConstraintProblem) {
|
||||||
|
self.set_point.with_untracked(|set_pt| {
|
||||||
|
if let Some(val) = set_pt.value {
|
||||||
|
let col = self.subject.column_index().expect(
|
||||||
|
indexing_error("Subject", &self.subject.id,
|
||||||
|
"point-coordinate regulator").as_str());
|
||||||
|
problem.frozen.push(self.axis as usize, col, val);
|
||||||
|
// 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::CARDINALITY {
|
||||||
|
nset += 1;
|
||||||
|
coords[index.0] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nset == Axis::CARDINALITY {
|
||||||
|
let [x, y, z] = coords;
|
||||||
|
problem.frozen.push(
|
||||||
|
Point::NORM_COMPONENT, col, point(x,y,z)[Point::NORM_COMPONENT]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// the velocity is expressed in uniform coordinates
|
// the velocity is expressed in uniform coordinates
|
||||||
pub struct ElementMotion<'a> {
|
pub struct ElementMotion<'a> {
|
||||||
pub element: Rc<dyn Element>,
|
pub element: Rc<dyn Element>,
|
||||||
|
@ -698,6 +787,7 @@ impl Assembly {
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
// log the Gram matrix
|
// log the Gram matrix
|
||||||
console_log!("Gram matrix:\n{}", problem.gram);
|
console_log!("Gram matrix:\n{}", problem.gram);
|
||||||
|
console_log!("Frozen entries:\n{}", problem.frozen);
|
||||||
|
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
// log the initial configuration matrix
|
// log the initial configuration matrix
|
||||||
|
@ -857,7 +947,8 @@ mod tests {
|
||||||
use crate::engine;
|
use crate::engine;
|
||||||
|
|
||||||
#[test]
|
#[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() {
|
fn unindexed_element_test() {
|
||||||
let _ = create_root(|| {
|
let _ = create_root(|| {
|
||||||
let elt = Sphere::default("sphere".to_string(), 0);
|
let elt = Sphere::default("sphere".to_string(), 0);
|
||||||
|
@ -866,7 +957,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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() {
|
fn unindexed_subject_test_inversive_distance() {
|
||||||
let _ = create_root(|| {
|
let _ = create_root(|| {
|
||||||
let subjects = [0, 1].map(
|
let subjects = [0, 1].map(
|
||||||
|
@ -927,4 +1019,4 @@ mod tests {
|
||||||
assert!((final_half_curv / INITIAL_HALF_CURV - 1.0).abs() < DRIFT_TOL);
|
assert!((final_half_curv / INITIAL_HALF_CURV - 1.0).abs() < DRIFT_TOL);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use crate::{
|
||||||
Element,
|
Element,
|
||||||
HalfCurvatureRegulator,
|
HalfCurvatureRegulator,
|
||||||
InversiveDistanceRegulator,
|
InversiveDistanceRegulator,
|
||||||
|
PointCoordinateRegulator,
|
||||||
Regulator,
|
Regulator,
|
||||||
},
|
},
|
||||||
specified::SpecifiedValue
|
specified::SpecifiedValue
|
||||||
|
@ -119,6 +120,20 @@ 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") // for spacing
|
||||||
|
div(class = "regulator-type") { (name) }
|
||||||
|
RegulatorInput(regulator = self)
|
||||||
|
div(class = "status")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// a list item that shows an element in an outline view of an assembly
|
// a list item that shows an element in an outline view of an assembly
|
||||||
#[component(inline_props)]
|
#[component(inline_props)]
|
||||||
fn ElementOutlineItem(element: Rc<dyn Element>) -> View {
|
fn ElementOutlineItem(element: Rc<dyn Element>) -> View {
|
||||||
|
|
|
@ -52,8 +52,8 @@ pub fn project_point_to_normalized(rep: &mut DVector<f64>) {
|
||||||
// --- partial matrices ---
|
// --- partial matrices ---
|
||||||
|
|
||||||
pub struct MatrixEntry {
|
pub struct MatrixEntry {
|
||||||
index: (usize, usize),
|
pub index: (usize, usize),
|
||||||
value: f64,
|
pub value: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PartialMatrix(Vec<MatrixEntry>);
|
pub struct PartialMatrix(Vec<MatrixEntry>);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue