forked from StudioInfinity/dyna3
Add a point element
Also add a new test assembly, "Pointed," to try out the new element.
This commit is contained in:
parent
873de78f2d
commit
1945086586
3 changed files with 179 additions and 51 deletions
|
@ -1,11 +1,11 @@
|
||||||
use std::rc::Rc;
|
use std::{f64::consts::FRAC_1_SQRT_2, rc::Rc};
|
||||||
use sycamore::prelude::*;
|
use sycamore::prelude::*;
|
||||||
use web_sys::{console, wasm_bindgen::JsValue};
|
use web_sys::{console, wasm_bindgen::JsValue};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
engine,
|
engine,
|
||||||
AppState,
|
AppState,
|
||||||
assembly::{Assembly, InversiveDistanceRegulator, Sphere}
|
assembly::{Assembly, InversiveDistanceRegulator, Point, Sphere}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
|
@ -133,6 +133,46 @@ fn load_low_curv_assemb(assembly: &Assembly) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_pointed_assemb(assembly: &Assembly) {
|
||||||
|
let _ = assembly.try_insert_element(
|
||||||
|
Point::new(
|
||||||
|
format!("point_front"),
|
||||||
|
format!("Front point"),
|
||||||
|
engine::point(0.0, 0.0, FRAC_1_SQRT_2)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
let _ = assembly.try_insert_element(
|
||||||
|
Point::new(
|
||||||
|
format!("point_back"),
|
||||||
|
format!("Back point"),
|
||||||
|
engine::point(0.0, 0.0, -FRAC_1_SQRT_2)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
for index_x in 0..=1 {
|
||||||
|
for index_y in 0..=1 {
|
||||||
|
let x = index_x as f64 - 0.5;
|
||||||
|
let y = index_y as f64 - 0.5;
|
||||||
|
|
||||||
|
let _ = assembly.try_insert_element(
|
||||||
|
Sphere::new(
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
let _ = assembly.try_insert_element(
|
||||||
|
Point::new(
|
||||||
|
format!("point{index_x}{index_y}"),
|
||||||
|
format!("Point {index_x}{index_y}"),
|
||||||
|
engine::point(x, y, 0.0)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn AddRemove() -> View {
|
pub fn AddRemove() -> View {
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
|
@ -158,6 +198,7 @@ pub fn AddRemove() -> View {
|
||||||
match name.as_str() {
|
match name.as_str() {
|
||||||
"general" => load_gen_assemb(assembly),
|
"general" => load_gen_assemb(assembly),
|
||||||
"low-curv" => load_low_curv_assemb(assembly),
|
"low-curv" => load_low_curv_assemb(assembly),
|
||||||
|
"pointed" => load_pointed_assemb(assembly),
|
||||||
_ => ()
|
_ => ()
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -199,6 +240,7 @@ pub fn AddRemove() -> View {
|
||||||
select(bind:value=assembly_name) { /* DEBUG */ // example assembly chooser
|
select(bind:value=assembly_name) { /* DEBUG */ // example assembly chooser
|
||||||
option(value="general") { "General" }
|
option(value="general") { "General" }
|
||||||
option(value="low-curv") { "Low-curvature" }
|
option(value="low-curv") { "Low-curvature" }
|
||||||
|
option(value="pointed") { "Pointed" }
|
||||||
option(value="empty") { "Empty" }
|
option(value="empty") { "Empty" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use nalgebra::{DMatrix, DVector, DVectorView};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
use std::{
|
use std::{
|
||||||
|
any::{Any, TypeId},
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
collections::BTreeSet,
|
collections::BTreeSet,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
@ -16,6 +17,7 @@ use crate::{
|
||||||
Q,
|
Q,
|
||||||
change_half_curvature,
|
change_half_curvature,
|
||||||
local_unif_to_std,
|
local_unif_to_std,
|
||||||
|
point,
|
||||||
realize_gram,
|
realize_gram,
|
||||||
sphere,
|
sphere,
|
||||||
ConfigSubspace,
|
ConfigSubspace,
|
||||||
|
@ -197,6 +199,87 @@ impl ProblemPoser for Sphere {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Point {
|
||||||
|
pub id: String,
|
||||||
|
pub label: String,
|
||||||
|
pub representation: Signal<DVector<f64>>,
|
||||||
|
pub regulators: Signal<BTreeSet<RegulatorKey>>,
|
||||||
|
pub serial: u64,
|
||||||
|
column_index: Cell<Option<usize>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Point {
|
||||||
|
const WEIGHT_COMPONENT: usize = 3;
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
id: String,
|
||||||
|
label: String,
|
||||||
|
representation: DVector<f64>
|
||||||
|
) -> Point {
|
||||||
|
Point {
|
||||||
|
id,
|
||||||
|
label,
|
||||||
|
representation: create_signal(representation),
|
||||||
|
regulators: create_signal(BTreeSet::default()),
|
||||||
|
serial: Self::next_serial(),
|
||||||
|
column_index: None.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Element for Point {
|
||||||
|
fn default_id() -> String {
|
||||||
|
"point".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default(id: String, id_num: u64) -> Point {
|
||||||
|
Point::new(
|
||||||
|
id,
|
||||||
|
format!("Point {id_num}"),
|
||||||
|
point(0.0, 0.0, 0.0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> &String {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn label(&self) -> &String {
|
||||||
|
&self.label
|
||||||
|
}
|
||||||
|
|
||||||
|
fn representation(&self) -> Signal<DVector<f64>> {
|
||||||
|
self.representation
|
||||||
|
}
|
||||||
|
|
||||||
|
fn regulators(&self) -> Signal<BTreeSet<RegulatorKey>> {
|
||||||
|
self.regulators
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serial(&self) -> u64 {
|
||||||
|
self.serial
|
||||||
|
}
|
||||||
|
|
||||||
|
fn column_index(&self) -> Option<usize> {
|
||||||
|
self.column_index.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_column_index(&self, index: usize) {
|
||||||
|
self.column_index.set(Some(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProblemPoser for Point {
|
||||||
|
fn pose(&self, problem: &mut ConstraintProblem, _elts: &Slab<Rc<dyn Element>>) {
|
||||||
|
let index = self.column_index().expect(
|
||||||
|
format!("Point \"{}\" should be indexed before writing problem data", self.id).as_str()
|
||||||
|
);
|
||||||
|
problem.gram.push_sym(index, index, 0.0);
|
||||||
|
problem.frozen.push(Point::WEIGHT_COMPONENT, index, 0.5);
|
||||||
|
problem.guess.set_column(index, &self.representation.get_clone_untracked());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Regulator: ProblemPoser + OutlineItem {
|
pub trait Regulator: ProblemPoser + OutlineItem {
|
||||||
fn subjects(&self) -> Vec<ElementKey>;
|
fn subjects(&self) -> Vec<ElementKey>;
|
||||||
fn measurement(&self) -> ReadSignal<f64>;
|
fn measurement(&self) -> ReadSignal<f64>;
|
||||||
|
@ -617,8 +700,7 @@ impl Assembly {
|
||||||
// step the assembly along the deformation. this changes the elements'
|
// step the assembly along the deformation. this changes the elements'
|
||||||
// normalizations, so we restore those afterward
|
// normalizations, so we restore those afterward
|
||||||
/* KLUDGE */
|
/* KLUDGE */
|
||||||
// since our test assemblies only include spheres, we assume that every
|
// for now, we only restore the normalizations of spheres
|
||||||
// element is on the 1 mass shell
|
|
||||||
for (_, elt) in self.elements.get_clone_untracked() {
|
for (_, elt) in self.elements.get_clone_untracked() {
|
||||||
elt.representation().update_silent(|rep| {
|
elt.representation().update_silent(|rep| {
|
||||||
match elt.column_index() {
|
match elt.column_index() {
|
||||||
|
@ -626,13 +708,15 @@ impl Assembly {
|
||||||
// step the assembly along the deformation
|
// step the assembly along the deformation
|
||||||
*rep += motion_proj.column(column_index);
|
*rep += motion_proj.column(column_index);
|
||||||
|
|
||||||
// restore normalization by contracting toward the last
|
if elt.type_id() == TypeId::of::<Sphere>() {
|
||||||
// coordinate axis
|
// restore normalization by contracting toward the
|
||||||
let q_sp = rep.fixed_rows::<3>(0).norm_squared();
|
// last coordinate axis
|
||||||
let half_q_lt = -2.0 * rep[3] * rep[4];
|
let q_sp = rep.fixed_rows::<3>(0).norm_squared();
|
||||||
let half_q_lt_sq = half_q_lt * half_q_lt;
|
let half_q_lt = -2.0 * rep[3] * rep[4];
|
||||||
let scaling = half_q_lt + (q_sp + half_q_lt_sq).sqrt();
|
let half_q_lt_sq = half_q_lt * half_q_lt;
|
||||||
rep.fixed_rows_mut::<4>(0).scale_mut(1.0 / scaling);
|
let scaling = half_q_lt + (q_sp + half_q_lt_sq).sqrt();
|
||||||
|
rep.fixed_rows_mut::<4>(0).scale_mut(1.0 / scaling);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
console::log_1(&JsValue::from(
|
console::log_1(&JsValue::from(
|
||||||
|
|
|
@ -14,7 +14,10 @@ use web_sys::{
|
||||||
wasm_bindgen::{JsCast, JsValue}
|
wasm_bindgen::{JsCast, JsValue}
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{AppState, assembly::{ElementKey, ElementColor, ElementMotion, Sphere}};
|
use crate::{
|
||||||
|
AppState,
|
||||||
|
assembly::{ElementKey, ElementColor, ElementMotion, Point, Sphere}
|
||||||
|
};
|
||||||
|
|
||||||
// --- scene data ---
|
// --- scene data ---
|
||||||
|
|
||||||
|
@ -129,6 +132,18 @@ impl DisplayItem for Sphere {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DisplayItem for Point {
|
||||||
|
fn show(&self, scene: &mut Scene, _selected: bool) {
|
||||||
|
let representation = self.representation.get_clone_untracked();
|
||||||
|
scene.points.representations.push(representation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SCAFFOLDING */
|
||||||
|
fn cast(&self, _dir: Vector3<f64>, _assembly_to_world: &DMatrix<f64>) -> Option<f64> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- WebGL utilities ---
|
// --- WebGL utilities ---
|
||||||
|
|
||||||
fn compile_shader(
|
fn compile_shader(
|
||||||
|
@ -551,7 +566,7 @@ pub fn Display() -> View {
|
||||||
};
|
};
|
||||||
let asm_to_world = &location * &orientation;
|
let asm_to_world = &location * &orientation;
|
||||||
|
|
||||||
// get the spheres
|
// set up the scene
|
||||||
state.assembly.elements.with_untracked(
|
state.assembly.elements.with_untracked(
|
||||||
|elts| for (key, elt) in elts {
|
|elts| for (key, elt) in elts {
|
||||||
let selected = state.selection.with(|sel| sel.contains(&key));
|
let selected = state.selection.with(|sel| sel.contains(&key));
|
||||||
|
@ -560,39 +575,16 @@ pub fn Display() -> View {
|
||||||
);
|
);
|
||||||
let sphere_cnt = scene.spheres.len_i32();
|
let sphere_cnt = scene.spheres.len_i32();
|
||||||
|
|
||||||
// write the spheres in world coordinates
|
|
||||||
let sphere_reps_world: Vec<_> = scene.spheres.representations.into_iter().map(
|
|
||||||
|rep| (&asm_to_world * rep).cast::<f32>()
|
|
||||||
).collect();
|
|
||||||
|
|
||||||
/* SCAFFOLDING */
|
|
||||||
// get the points
|
|
||||||
scene.points.representations.append({
|
|
||||||
use crate::engine::point;
|
|
||||||
&mut vec![
|
|
||||||
point(0.0, 0.0, 0.0),
|
|
||||||
point(0.5, 0.5, 0.0),
|
|
||||||
point(-0.5, -0.5, 0.0),
|
|
||||||
point(-0.5, 0.5, 0.0),
|
|
||||||
point(0.5, -0.5, 0.0),
|
|
||||||
point(0.0, 0.15, 1.0),
|
|
||||||
point(0.0, -0.15, -1.0)
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
// write the points in world coordinates
|
|
||||||
let asm_to_world_sp = asm_to_world.rows(0, SPACE_DIM);
|
|
||||||
let point_positions = DMatrix::from_columns(
|
|
||||||
&scene.points.representations.into_iter().map(
|
|
||||||
|rep| &asm_to_world_sp * rep
|
|
||||||
).collect::<Vec<_>>().as_slice()
|
|
||||||
).cast::<f32>();
|
|
||||||
|
|
||||||
// --- draw the spheres ---
|
// --- draw the spheres ---
|
||||||
|
|
||||||
// use the sphere rendering program
|
// use the sphere rendering program
|
||||||
ctx.use_program(Some(&sphere_program));
|
ctx.use_program(Some(&sphere_program));
|
||||||
|
|
||||||
|
// write the spheres in world coordinates
|
||||||
|
let sphere_reps_world: Vec<_> = scene.spheres.representations.into_iter().map(
|
||||||
|
|rep| (&asm_to_world * rep).cast::<f32>()
|
||||||
|
).collect();
|
||||||
|
|
||||||
// set the resolution
|
// set the resolution
|
||||||
let width = canvas.width() as f32;
|
let width = canvas.width() as f32;
|
||||||
let height = canvas.height() as f32;
|
let height = canvas.height() as f32;
|
||||||
|
@ -635,16 +627,26 @@ pub fn Display() -> View {
|
||||||
|
|
||||||
// --- draw the points ---
|
// --- draw the points ---
|
||||||
|
|
||||||
// use the point rendering program
|
if !scene.points.representations.is_empty() {
|
||||||
ctx.use_program(Some(&point_program));
|
// use the point rendering program
|
||||||
|
ctx.use_program(Some(&point_program));
|
||||||
// load the point positions into a new buffer and bind it to the
|
|
||||||
// position attribute in the vertex shader
|
// write the points in world coordinates
|
||||||
let point_position_buffer = load_new_buffer(&ctx, point_positions.as_slice());
|
let asm_to_world_sp = asm_to_world.rows(0, SPACE_DIM);
|
||||||
bind_to_attribute(&ctx, point_position_attr, SPACE_DIM as i32, &point_position_buffer);
|
let point_positions = DMatrix::from_columns(
|
||||||
|
&scene.points.representations.into_iter().map(
|
||||||
// draw the scene
|
|rep| &asm_to_world_sp * rep
|
||||||
ctx.draw_arrays(WebGl2RenderingContext::POINTS, 0, point_positions.ncols() as i32);
|
).collect::<Vec<_>>().as_slice()
|
||||||
|
).cast::<f32>();
|
||||||
|
|
||||||
|
// load the point positions into a new buffer and bind it to the
|
||||||
|
// position attribute in the vertex shader
|
||||||
|
let point_position_buffer = load_new_buffer(&ctx, point_positions.as_slice());
|
||||||
|
bind_to_attribute(&ctx, point_position_attr, SPACE_DIM as i32, &point_position_buffer);
|
||||||
|
|
||||||
|
// draw the scene
|
||||||
|
ctx.draw_arrays(WebGl2RenderingContext::POINTS, 0, point_positions.ncols() as i32);
|
||||||
|
}
|
||||||
|
|
||||||
// --- update the display state ---
|
// --- update the display state ---
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue