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 web_sys::{console, wasm_bindgen::JsValue};
|
||||
|
||||
use crate::{
|
||||
engine,
|
||||
AppState,
|
||||
assembly::{Assembly, InversiveDistanceRegulator, Sphere}
|
||||
assembly::{Assembly, InversiveDistanceRegulator, Point, Sphere}
|
||||
};
|
||||
|
||||
/* 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]
|
||||
pub fn AddRemove() -> View {
|
||||
/* DEBUG */
|
||||
|
@ -158,6 +198,7 @@ pub fn AddRemove() -> View {
|
|||
match name.as_str() {
|
||||
"general" => load_gen_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
|
||||
option(value="general") { "General" }
|
||||
option(value="low-curv") { "Low-curvature" }
|
||||
option(value="pointed") { "Pointed" }
|
||||
option(value="empty") { "Empty" }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use nalgebra::{DMatrix, DVector, DVectorView};
|
|||
use rustc_hash::FxHashMap;
|
||||
use slab::Slab;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::Cell,
|
||||
collections::BTreeSet,
|
||||
rc::Rc,
|
||||
|
@ -16,6 +17,7 @@ use crate::{
|
|||
Q,
|
||||
change_half_curvature,
|
||||
local_unif_to_std,
|
||||
point,
|
||||
realize_gram,
|
||||
sphere,
|
||||
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 {
|
||||
fn subjects(&self) -> Vec<ElementKey>;
|
||||
fn measurement(&self) -> ReadSignal<f64>;
|
||||
|
@ -617,8 +700,7 @@ impl Assembly {
|
|||
// step the assembly along the deformation. this changes the elements'
|
||||
// normalizations, so we restore those afterward
|
||||
/* KLUDGE */
|
||||
// since our test assemblies only include spheres, we assume that every
|
||||
// element is on the 1 mass shell
|
||||
// for now, we only restore the normalizations of spheres
|
||||
for (_, elt) in self.elements.get_clone_untracked() {
|
||||
elt.representation().update_silent(|rep| {
|
||||
match elt.column_index() {
|
||||
|
@ -626,13 +708,15 @@ impl Assembly {
|
|||
// step the assembly along the deformation
|
||||
*rep += motion_proj.column(column_index);
|
||||
|
||||
// restore normalization by contracting toward the last
|
||||
// coordinate axis
|
||||
let q_sp = rep.fixed_rows::<3>(0).norm_squared();
|
||||
let half_q_lt = -2.0 * rep[3] * rep[4];
|
||||
let half_q_lt_sq = half_q_lt * half_q_lt;
|
||||
let scaling = half_q_lt + (q_sp + half_q_lt_sq).sqrt();
|
||||
rep.fixed_rows_mut::<4>(0).scale_mut(1.0 / scaling);
|
||||
if elt.type_id() == TypeId::of::<Sphere>() {
|
||||
// restore normalization by contracting toward the
|
||||
// last coordinate axis
|
||||
let q_sp = rep.fixed_rows::<3>(0).norm_squared();
|
||||
let half_q_lt = -2.0 * rep[3] * rep[4];
|
||||
let half_q_lt_sq = half_q_lt * half_q_lt;
|
||||
let scaling = half_q_lt + (q_sp + half_q_lt_sq).sqrt();
|
||||
rep.fixed_rows_mut::<4>(0).scale_mut(1.0 / scaling);
|
||||
}
|
||||
},
|
||||
None => {
|
||||
console::log_1(&JsValue::from(
|
||||
|
|
|
@ -14,7 +14,10 @@ use web_sys::{
|
|||
wasm_bindgen::{JsCast, JsValue}
|
||||
};
|
||||
|
||||
use crate::{AppState, assembly::{ElementKey, ElementColor, ElementMotion, Sphere}};
|
||||
use crate::{
|
||||
AppState,
|
||||
assembly::{ElementKey, ElementColor, ElementMotion, Point, Sphere}
|
||||
};
|
||||
|
||||
// --- 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 ---
|
||||
|
||||
fn compile_shader(
|
||||
|
@ -551,7 +566,7 @@ pub fn Display() -> View {
|
|||
};
|
||||
let asm_to_world = &location * &orientation;
|
||||
|
||||
// get the spheres
|
||||
// set up the scene
|
||||
state.assembly.elements.with_untracked(
|
||||
|elts| for (key, elt) in elts {
|
||||
let selected = state.selection.with(|sel| sel.contains(&key));
|
||||
|
@ -560,39 +575,16 @@ pub fn Display() -> View {
|
|||
);
|
||||
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 ---
|
||||
|
||||
// use the sphere rendering 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
|
||||
let width = canvas.width() as f32;
|
||||
let height = canvas.height() as f32;
|
||||
|
@ -635,16 +627,26 @@ pub fn Display() -> View {
|
|||
|
||||
// --- draw the points ---
|
||||
|
||||
// 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
|
||||
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);
|
||||
if !scene.points.representations.is_empty() {
|
||||
// use the point rendering program
|
||||
ctx.use_program(Some(&point_program));
|
||||
|
||||
// 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>();
|
||||
|
||||
// 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 ---
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue