forked from StudioInfinity/dyna3
chore: relayout per PR discussion/feedback
This commit is contained in:
parent
4a3f47c8b5
commit
760e811ee4
6 changed files with 259 additions and 224 deletions
|
|
@ -261,8 +261,8 @@ impl ProblemPoser for Sphere {
|
||||||
let index = self.column_index().expect(
|
let index = self.column_index().expect(
|
||||||
indexing_error("Sphere", &self.id, "it").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(
|
problem.guess
|
||||||
index, &self.representation.get_clone_untracked());
|
.set_column(index, &self.representation.get_clone_untracked());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -368,8 +368,8 @@ impl ProblemPoser for Point {
|
||||||
indexing_error("Point", &self.id, "it").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(
|
problem.guess
|
||||||
index, &self.representation.get_clone_untracked());
|
.set_column(index, &self.representation.get_clone_untracked());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -414,8 +414,8 @@ pub struct InversiveDistanceRegulator {
|
||||||
|
|
||||||
impl InversiveDistanceRegulator {
|
impl InversiveDistanceRegulator {
|
||||||
pub fn new(subjects: [Rc<dyn Element>; 2]) -> Self {
|
pub fn new(subjects: [Rc<dyn Element>; 2]) -> Self {
|
||||||
let representations = subjects.each_ref().map(
|
let representations = subjects.each_ref()
|
||||||
|subj| subj.representation());
|
.map(|subj| subj.representation());
|
||||||
let measurement = create_memo(move || {
|
let measurement = create_memo(move || {
|
||||||
representations[0].with(|rep_0|
|
representations[0].with(|rep_0|
|
||||||
representations[1].with(|rep_1|
|
representations[1].with(|rep_1|
|
||||||
|
|
@ -584,8 +584,11 @@ impl ProblemPoser for PointCoordinateRegulator {
|
||||||
}
|
}
|
||||||
if nset == Axis::CARDINALITY {
|
if nset == Axis::CARDINALITY {
|
||||||
let [x, y, z] = coords;
|
let [x, y, z] = coords;
|
||||||
problem.frozen.push(Point::NORM_COMPONENT,
|
problem.frozen.push(
|
||||||
col, point(x,y,z)[Point::NORM_COMPONENT]);
|
Point::NORM_COMPONENT,
|
||||||
|
col,
|
||||||
|
point(x,y,z)[Point::NORM_COMPONENT]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -684,8 +687,8 @@ impl Assembly {
|
||||||
let id = elt.id().clone();
|
let id = elt.id().clone();
|
||||||
let elt_rc = Rc::new(elt);
|
let elt_rc = Rc::new(elt);
|
||||||
self.elements.update(|elts| elts.insert(elt_rc.clone()));
|
self.elements.update(|elts| elts.insert(elt_rc.clone()));
|
||||||
self.elements_by_id.update(
|
self.elements_by_id
|
||||||
|elts_by_id| elts_by_id.insert(id, elt_rc.clone()));
|
.update(|elts_by_id| elts_by_id.insert(id, elt_rc.clone()));
|
||||||
|
|
||||||
// create and insert the element's default regulators
|
// create and insert the element's default regulators
|
||||||
for reg in elt_rc.default_regulators() {
|
for reg in elt_rc.default_regulators() {
|
||||||
|
|
@ -760,9 +763,9 @@ impl Assembly {
|
||||||
|
|
||||||
pub fn load_config(&self, config: &DMatrix<f64>) {
|
pub fn load_config(&self, config: &DMatrix<f64>) {
|
||||||
for elt in self.elements.get_clone_untracked() {
|
for elt in self.elements.get_clone_untracked() {
|
||||||
elt.representation().update(
|
elt.representation()
|
||||||
|rep| rep.set_column(
|
.update(|rep| rep.set_column(
|
||||||
0, &config.column(elt.column_index().unwrap()))
|
0, &config.column(elt.column_index().unwrap()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -907,19 +910,16 @@ impl Assembly {
|
||||||
if column_index < realized_dim {
|
if column_index < realized_dim {
|
||||||
// this element had a column index when we started, so by
|
// this element had a column index when we started, so by
|
||||||
// invariant (1), it's reflected in the tangent space
|
// invariant (1), it's reflected in the tangent space
|
||||||
let mut target_columns =
|
let mut target_columns
|
||||||
motion_proj.columns_mut(0, realized_dim);
|
= motion_proj.columns_mut(0, realized_dim);
|
||||||
target_columns += self.tangent.with(
|
target_columns += self.tangent
|
||||||
|tan| tan.proj(&elt_motion.velocity, column_index)
|
.with(|tan| tan.proj(&elt_motion.velocity, column_index));
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
// this element didn't have a column index when we started, so
|
// this element didn't have a column index when we started, so
|
||||||
// by invariant (2), it's unconstrained
|
// by invariant (2), it's unconstrained
|
||||||
let mut target_column = motion_proj.column_mut(column_index);
|
let mut target_column = motion_proj.column_mut(column_index);
|
||||||
let unif_to_std =
|
let unif_to_std = elt_motion.element.representation()
|
||||||
elt_motion.element.representation().with_untracked(
|
.with_untracked(|rep| local_unif_to_std(rep.as_view()));
|
||||||
|rep| local_unif_to_std(rep.as_view())
|
|
||||||
);
|
|
||||||
target_column += unif_to_std * elt_motion.velocity;
|
target_column += unif_to_std * elt_motion.velocity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -973,10 +973,9 @@ mod tests {
|
||||||
inversive distance regulator writes problem data")]
|
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]
|
||||||
|k| Rc::new(
|
.map(|k| Rc::new(Sphere::default(format!("sphere{k}"), k))
|
||||||
Sphere::default(format!("sphere{k}"), k)) as Rc<dyn Element>
|
as Rc<dyn Element>);
|
||||||
);
|
|
||||||
subjects[0].set_column_index(0);
|
subjects[0].set_column_index(0);
|
||||||
InversiveDistanceRegulator {
|
InversiveDistanceRegulator {
|
||||||
subjects: subjects,
|
subjects: subjects,
|
||||||
|
|
@ -1011,10 +1010,10 @@ mod tests {
|
||||||
// nudge the sphere repeatedly along the `z` axis
|
// nudge the sphere repeatedly along the `z` axis
|
||||||
const STEP_SIZE: f64 = 0.0025;
|
const STEP_SIZE: f64 = 0.0025;
|
||||||
const STEP_CNT: usize = 400;
|
const STEP_CNT: usize = 400;
|
||||||
let sphere = assembly.elements_by_id.with(
|
let sphere = assembly.elements_by_id
|
||||||
|elts_by_id| elts_by_id[sphere_id].clone());
|
.with(|elts_by_id| elts_by_id[sphere_id].clone());
|
||||||
let velocity =
|
let velocity
|
||||||
DVector::from_column_slice(&[0.0, 0.0, STEP_SIZE, 0.0]);
|
= DVector::from_column_slice(&[0.0, 0.0, STEP_SIZE, 0.0]);
|
||||||
for _ in 0..STEP_CNT {
|
for _ in 0..STEP_CNT {
|
||||||
assembly.deform(
|
assembly.deform(
|
||||||
vec![
|
vec![
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ impl SceneSpheres {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn len_i32(&self) -> i32 {
|
fn len_i32(&self) -> i32 {
|
||||||
self.representations.len().try_into().expect(
|
self.representations.len().try_into()
|
||||||
"Number of spheres must fit in a 32-bit integer")
|
.expect("Number of spheres must fit in a 32-bit integer")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push(
|
fn push(
|
||||||
|
|
@ -128,12 +128,14 @@ impl DisplayItem for Sphere {
|
||||||
const HIGHLIGHT: f32 = 0.2;
|
const HIGHLIGHT: f32 = 0.2;
|
||||||
|
|
||||||
let representation = self.representation.get_clone_untracked();
|
let representation = self.representation.get_clone_untracked();
|
||||||
let color =
|
let color = if selected {
|
||||||
if selected { self.color.map(|channel| 0.2 + 0.8*channel) }
|
self.color.map(|channel| 0.2 + 0.8*channel)
|
||||||
else { self.color };
|
} else {
|
||||||
let opacity =
|
self.color
|
||||||
if self.ghost.get() { GHOST_OPACITY }
|
};
|
||||||
else { DEFAULT_OPACITY };
|
let opacity = if self.ghost.get() {
|
||||||
|
GHOST_OPACITY
|
||||||
|
} else { DEFAULT_OPACITY };
|
||||||
let highlight = if selected { 1.0 } else { HIGHLIGHT };
|
let highlight = if selected { 1.0 } else { HIGHLIGHT };
|
||||||
scene.spheres.push(representation, color, opacity, highlight);
|
scene.spheres.push(representation, color, opacity, highlight);
|
||||||
}
|
}
|
||||||
|
|
@ -150,8 +152,8 @@ impl DisplayItem for Sphere {
|
||||||
// `a*u^2 + b*u + c` by the linear function `b*u + c`
|
// `a*u^2 + b*u + c` by the linear function `b*u + c`
|
||||||
const DEG_THRESHOLD: f64 = 1e-9;
|
const DEG_THRESHOLD: f64 = 1e-9;
|
||||||
|
|
||||||
let rep = self.representation.with_untracked(
|
let rep = self.representation
|
||||||
|rep| assembly_to_world * rep);
|
.with_untracked(|rep| assembly_to_world * rep);
|
||||||
let a = -rep[3] * dir.norm_squared();
|
let a = -rep[3] * dir.norm_squared();
|
||||||
let b = rep.rows_range(..3).dot(&dir);
|
let b = rep.rows_range(..3).dot(&dir);
|
||||||
let c = -rep[4];
|
let c = -rep[4];
|
||||||
|
|
@ -192,9 +194,9 @@ impl DisplayItem for Point {
|
||||||
const HIGHLIGHT: f32 = 0.5;
|
const HIGHLIGHT: f32 = 0.5;
|
||||||
|
|
||||||
let representation = self.representation.get_clone_untracked();
|
let representation = self.representation.get_clone_untracked();
|
||||||
let color =
|
let color = if selected {
|
||||||
if selected { self.color.map(|channel| 0.2 + 0.8*channel) }
|
self.color.map(|channel| 0.2 + 0.8*channel)
|
||||||
else { self.color };
|
} else { self.color };
|
||||||
let opacity = if self.ghost.get() { GHOST_OPACITY } else { 1.0 };
|
let opacity = if self.ghost.get() { GHOST_OPACITY } else { 1.0 };
|
||||||
let highlight = if selected { 1.0 } else { HIGHLIGHT };
|
let highlight = if selected { 1.0 } else { HIGHLIGHT };
|
||||||
scene.points.push(representation, color, opacity, highlight, selected);
|
scene.points.push(representation, color, opacity, highlight, selected);
|
||||||
|
|
@ -207,8 +209,8 @@ impl DisplayItem for Point {
|
||||||
assembly_to_world: &DMatrix<f64>,
|
assembly_to_world: &DMatrix<f64>,
|
||||||
pixel_size: f64,
|
pixel_size: f64,
|
||||||
) -> Option<f64> {
|
) -> Option<f64> {
|
||||||
let rep = self.representation.with_untracked(
|
let rep = self.representation
|
||||||
|rep| assembly_to_world * rep);
|
.with_untracked(|rep| assembly_to_world * rep);
|
||||||
if rep[2] < 0.0 {
|
if rep[2] < 0.0 {
|
||||||
// this constant should be kept synchronized with `point.frag`
|
// this constant should be kept synchronized with `point.frag`
|
||||||
const POINT_RADIUS_PX: f64 = 4.0;
|
const POINT_RADIUS_PX: f64 = 4.0;
|
||||||
|
|
@ -366,12 +368,12 @@ fn event_dir(event: &MouseEvent) -> (Vector3<f64>, f64) {
|
||||||
// this constant should be kept synchronized with `spheres.frag` and
|
// this constant should be kept synchronized with `spheres.frag` and
|
||||||
// `point.vert`
|
// `point.vert`
|
||||||
const FOCAL_SLOPE: f64 = 0.3;
|
const FOCAL_SLOPE: f64 = 0.3;
|
||||||
let horizontal = f64::from(event.client_x()) - rect.left();
|
let x_relative = f64::from(event.client_x()) - rect.left();
|
||||||
let vertical = rect.bottom() - f64::from(event.client_y());
|
let y_relative = rect.bottom() - f64::from(event.client_y());
|
||||||
(
|
(
|
||||||
Vector3::new(
|
Vector3::new(
|
||||||
FOCAL_SLOPE * (2.0*horizontal - width) / shortdim,
|
FOCAL_SLOPE * (2.0*x_relative - width) / shortdim,
|
||||||
FOCAL_SLOPE * (2.0*vertical - height) / shortdim,
|
FOCAL_SLOPE * (2.0*y_relative - height) / shortdim,
|
||||||
-1.0,
|
-1.0,
|
||||||
),
|
),
|
||||||
FOCAL_SLOPE * 2.0 / shortdim,
|
FOCAL_SLOPE * 2.0 / shortdim,
|
||||||
|
|
@ -455,8 +457,8 @@ pub fn Display() -> View {
|
||||||
let performance = window().unwrap().performance().unwrap();
|
let performance = window().unwrap().performance().unwrap();
|
||||||
|
|
||||||
// get the display canvas
|
// get the display canvas
|
||||||
let canvas =
|
let canvas
|
||||||
display.get().unchecked_into::<web_sys::HtmlCanvasElement>();
|
= display.get().unchecked_into::<web_sys::HtmlCanvasElement>();
|
||||||
let ctx = canvas
|
let ctx = canvas
|
||||||
.get_context("webgl2")
|
.get_context("webgl2")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
@ -469,8 +471,10 @@ pub fn Display() -> View {
|
||||||
|
|
||||||
// set blend mode
|
// set blend mode
|
||||||
ctx.enable(WebGl2RenderingContext::BLEND);
|
ctx.enable(WebGl2RenderingContext::BLEND);
|
||||||
ctx.blend_func(WebGl2RenderingContext::SRC_ALPHA,
|
ctx.blend_func(
|
||||||
WebGl2RenderingContext::ONE_MINUS_SRC_ALPHA);
|
WebGl2RenderingContext::SRC_ALPHA,
|
||||||
|
WebGl2RenderingContext::ONE_MINUS_SRC_ALPHA,
|
||||||
|
);
|
||||||
|
|
||||||
// set up the sphere rendering program
|
// set up the sphere rendering program
|
||||||
let sphere_program = set_up_program(
|
let sphere_program = set_up_program(
|
||||||
|
|
@ -500,19 +504,19 @@ pub fn Display() -> View {
|
||||||
// capped at 1024 elements
|
// capped at 1024 elements
|
||||||
console::log_2(
|
console::log_2(
|
||||||
&ctx.get_parameter(
|
&ctx.get_parameter(
|
||||||
WebGl2RenderingContext::MAX_FRAGMENT_UNIFORM_VECTORS).unwrap(),
|
WebGl2RenderingContext::MAX_FRAGMENT_UNIFORM_VECTORS
|
||||||
|
).unwrap(),
|
||||||
&JsValue::from("uniform vectors available"),
|
&JsValue::from("uniform vectors available"),
|
||||||
);
|
);
|
||||||
|
|
||||||
// find the sphere program's vertex attribute
|
// find the sphere program's vertex attribute
|
||||||
let viewport_position_attr =
|
let viewport_position_attr = ctx
|
||||||
ctx.get_attrib_location(&sphere_program, "position") as u32;
|
.get_attrib_location(&sphere_program, "position") as u32;
|
||||||
|
|
||||||
// find the sphere program's uniforms
|
// find the sphere program's uniforms
|
||||||
const SPHERE_MAX: usize = 200;
|
const SPHERE_MAX: usize = 200;
|
||||||
let sphere_cnt_loc = ctx.get_uniform_location(
|
let sphere_cnt_loc = ctx
|
||||||
&sphere_program, "sphere_cnt"
|
.get_uniform_location(&sphere_program, "sphere_cnt");
|
||||||
);
|
|
||||||
let sphere_sp_locs = get_uniform_array_locations::<SPHERE_MAX>(
|
let sphere_sp_locs = get_uniform_array_locations::<SPHERE_MAX>(
|
||||||
&ctx, &sphere_program, "sphere_list", Some("sp")
|
&ctx, &sphere_program, "sphere_list", Some("sp")
|
||||||
);
|
);
|
||||||
|
|
@ -525,18 +529,14 @@ pub fn Display() -> View {
|
||||||
let sphere_highlight_locs = get_uniform_array_locations::<SPHERE_MAX>(
|
let sphere_highlight_locs = get_uniform_array_locations::<SPHERE_MAX>(
|
||||||
&ctx, &sphere_program, "highlight_list", None
|
&ctx, &sphere_program, "highlight_list", None
|
||||||
);
|
);
|
||||||
let resolution_loc = ctx.get_uniform_location(
|
let resolution_loc = ctx
|
||||||
&sphere_program, "resolution"
|
.get_uniform_location(&sphere_program, "resolution");
|
||||||
);
|
let shortdim_loc = ctx
|
||||||
let shortdim_loc = ctx.get_uniform_location(
|
.get_uniform_location(&sphere_program, "shortdim");
|
||||||
&sphere_program, "shortdim"
|
let layer_threshold_loc = ctx
|
||||||
);
|
.get_uniform_location(&sphere_program, "layer_threshold");
|
||||||
let layer_threshold_loc = ctx.get_uniform_location(
|
let debug_mode_loc = ctx
|
||||||
&sphere_program, "layer_threshold"
|
.get_uniform_location(&sphere_program, "debug_mode");
|
||||||
);
|
|
||||||
let debug_mode_loc = ctx.get_uniform_location(
|
|
||||||
&sphere_program, "debug_mode"
|
|
||||||
);
|
|
||||||
|
|
||||||
// load the viewport vertex positions into a new vertex buffer object
|
// load the viewport vertex positions into a new vertex buffer object
|
||||||
const VERTEX_CNT: usize = 6;
|
const VERTEX_CNT: usize = 6;
|
||||||
|
|
@ -550,18 +550,18 @@ pub fn Display() -> View {
|
||||||
1.0, 1.0, 0.0,
|
1.0, 1.0, 0.0,
|
||||||
1.0, -1.0, 0.0,
|
1.0, -1.0, 0.0,
|
||||||
];
|
];
|
||||||
let viewport_position_buffer =
|
let viewport_position_buffer
|
||||||
load_new_buffer(&ctx, &viewport_positions);
|
= load_new_buffer(&ctx, &viewport_positions);
|
||||||
|
|
||||||
// find the point program's vertex attributes
|
// find the point program's vertex attributes
|
||||||
let point_position_attr =
|
let point_position_attr = ctx
|
||||||
ctx.get_attrib_location(&point_program, "position") as u32;
|
.get_attrib_location(&point_program, "position") as u32;
|
||||||
let point_color_attr =
|
let point_color_attr = ctx
|
||||||
ctx.get_attrib_location(&point_program, "color") as u32;
|
.get_attrib_location(&point_program, "color") as u32;
|
||||||
let point_highlight_attr =
|
let point_highlight_attr = ctx
|
||||||
ctx.get_attrib_location(&point_program, "highlight") as u32;
|
.get_attrib_location(&point_program, "highlight") as u32;
|
||||||
let point_selection_attr =
|
let point_selection_attr = ctx
|
||||||
ctx.get_attrib_location(&point_program, "selected") as u32;
|
.get_attrib_location(&point_program, "selected") as u32;
|
||||||
|
|
||||||
// set up a repainting routine
|
// set up a repainting routine
|
||||||
let (_, start_animation_loop, _) = create_raf(move || {
|
let (_, start_animation_loop, _) = create_raf(move || {
|
||||||
|
|
@ -625,8 +625,8 @@ pub fn Display() -> View {
|
||||||
let realization_successful = state.assembly.realization_status.with(
|
let realization_successful = state.assembly.realization_status.with(
|
||||||
|status| status.is_ok()
|
|status| status.is_ok()
|
||||||
);
|
);
|
||||||
let step_val =
|
let step_val = state.assembly
|
||||||
state.assembly.step.with_untracked(|step| step.value);
|
.step.with_untracked(|step| step.value);
|
||||||
let on_init_step = step_val.is_some_and(|n| n == 0.0);
|
let on_init_step = step_val.is_some_and(|n| n == 0.0);
|
||||||
let on_last_step = step_val.is_some_and(
|
let on_last_step = step_val.is_some_and(
|
||||||
|n| state.assembly.descent_history.with_untracked(
|
|n| state.assembly.descent_history.with_untracked(
|
||||||
|
|
@ -637,7 +637,8 @@ pub fn Display() -> View {
|
||||||
!realization_successful && on_init_step
|
!realization_successful && on_init_step
|
||||||
|| realization_successful && on_last_step;
|
|| realization_successful && on_last_step;
|
||||||
if on_manipulable_step
|
if on_manipulable_step
|
||||||
&& state.selection.with(|sel| sel.len() == 1) {
|
&& state.selection.with(|sel| sel.len() == 1)
|
||||||
|
{
|
||||||
let sel = state.selection.with(
|
let sel = state.selection.with(
|
||||||
|sel| sel.into_iter().next().unwrap().clone()
|
|sel| sel.into_iter().next().unwrap().clone()
|
||||||
);
|
);
|
||||||
|
|
@ -682,8 +683,8 @@ pub fn Display() -> View {
|
||||||
// measure mean frame interval
|
// measure mean frame interval
|
||||||
frames_since_last_sample += 1;
|
frames_since_last_sample += 1;
|
||||||
if frames_since_last_sample >= SAMPLE_PERIOD {
|
if frames_since_last_sample >= SAMPLE_PERIOD {
|
||||||
mean_frame_interval.set(
|
let SP64 = SAMPLE_PERIOD as f64;
|
||||||
(time - last_sample_time) / (SAMPLE_PERIOD as f64));
|
mean_frame_interval.set((time - last_sample_time) / SP64);
|
||||||
last_sample_time = time;
|
last_sample_time = time;
|
||||||
frames_since_last_sample = 0;
|
frames_since_last_sample = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -708,8 +709,8 @@ pub fn Display() -> View {
|
||||||
// set up the scene
|
// set up the scene
|
||||||
state.assembly.elements.with_untracked(
|
state.assembly.elements.with_untracked(
|
||||||
|elts| for elt in elts {
|
|elts| for elt in elts {
|
||||||
let selected =
|
let selected = state.selection
|
||||||
state.selection.with(|sel| sel.contains(elt));
|
.with(|sel| sel.contains(elt));
|
||||||
elt.show(&mut scene, selected);
|
elt.show(&mut scene, selected);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -724,8 +725,8 @@ pub fn Display() -> View {
|
||||||
ctx.enable_vertex_attrib_array(viewport_position_attr);
|
ctx.enable_vertex_attrib_array(viewport_position_attr);
|
||||||
|
|
||||||
// write the spheres in world coordinates
|
// write the spheres in world coordinates
|
||||||
let sphere_reps_world: Vec<_> =
|
let sphere_reps_world: Vec<_> = scene.spheres.representations
|
||||||
scene.spheres.representations.into_iter().map(
|
.into_iter().map(
|
||||||
|rep| (&asm_to_world * rep).cast::<f32>()
|
|rep| (&asm_to_world * rep).cast::<f32>()
|
||||||
).collect();
|
).collect();
|
||||||
|
|
||||||
|
|
@ -763,12 +764,14 @@ pub fn Display() -> View {
|
||||||
|
|
||||||
// bind the viewport vertex position buffer to the position
|
// bind the viewport vertex position buffer to the position
|
||||||
// attribute in the vertex shader
|
// attribute in the vertex shader
|
||||||
bind_to_attribute(&ctx, viewport_position_attr,
|
bind_to_attribute(
|
||||||
SPACE_DIM as i32, &viewport_position_buffer);
|
&ctx, viewport_position_attr,
|
||||||
|
SPACE_DIM as i32, &viewport_position_buffer,
|
||||||
|
);
|
||||||
|
|
||||||
// draw the scene
|
// draw the scene
|
||||||
ctx.draw_arrays(WebGl2RenderingContext::TRIANGLES, 0,
|
ctx.draw_arrays(
|
||||||
VERTEX_CNT as i32);
|
WebGl2RenderingContext::TRIANGLES, 0, VERTEX_CNT as i32);
|
||||||
|
|
||||||
// disable the sphere program's vertex attribute
|
// disable the sphere program's vertex attribute
|
||||||
ctx.disable_vertex_attrib_array(viewport_position_attr);
|
ctx.disable_vertex_attrib_array(viewport_position_attr);
|
||||||
|
|
@ -796,19 +799,28 @@ pub fn Display() -> View {
|
||||||
// load the point positions and colors into new buffers and
|
// load the point positions and colors into new buffers and
|
||||||
// bind them to the corresponding attributes in the vertex
|
// bind them to the corresponding attributes in the vertex
|
||||||
// shader
|
// shader
|
||||||
bind_new_buffer_to_attribute(&ctx, point_position_attr,
|
bind_new_buffer_to_attribute(
|
||||||
SPACE_DIM as i32, point_positions.as_slice());
|
&ctx, point_position_attr,
|
||||||
|
SPACE_DIM as i32, point_positions.as_slice(),
|
||||||
|
);
|
||||||
bind_new_buffer_to_attribute(&ctx, point_color_attr,
|
bind_new_buffer_to_attribute(&ctx, point_color_attr,
|
||||||
(COLOR_SIZE + 1) as i32,
|
(COLOR_SIZE + 1) as i32,
|
||||||
scene.points.colors_with_opacity.concat().as_slice());
|
scene.points.colors_with_opacity.concat().as_slice());
|
||||||
bind_new_buffer_to_attribute(&ctx, point_highlight_attr,
|
bind_new_buffer_to_attribute(
|
||||||
1i32, scene.points.highlights.as_slice());
|
&ctx, point_highlight_attr,
|
||||||
bind_new_buffer_to_attribute(&ctx, point_selection_attr,
|
1i32, scene.points.highlights.as_slice(),
|
||||||
1i32, scene.points.selections.as_slice());
|
);
|
||||||
|
bind_new_buffer_to_attribute(
|
||||||
|
&ctx, point_selection_attr,
|
||||||
|
1i32, scene.points.selections.as_slice(),
|
||||||
|
);
|
||||||
|
|
||||||
// draw the scene
|
// draw the scene
|
||||||
ctx.draw_arrays(WebGl2RenderingContext::POINTS, 0,
|
ctx.draw_arrays(
|
||||||
point_positions.ncols() as i32);
|
WebGl2RenderingContext::POINTS,
|
||||||
|
0,
|
||||||
|
point_positions.ncols() as i32,
|
||||||
|
);
|
||||||
|
|
||||||
// disable the point program's vertex attributes
|
// disable the point program's vertex attributes
|
||||||
ctx.disable_vertex_attrib_array(point_position_attr);
|
ctx.disable_vertex_attrib_array(point_position_attr);
|
||||||
|
|
@ -957,9 +969,9 @@ pub fn Display() -> View {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|elt| !elt.ghost().get());
|
.filter(|elt| !elt.ghost().get());
|
||||||
for elt in tangible_elts {
|
for elt in tangible_elts {
|
||||||
let target = assembly_to_world.with(
|
let hit = assembly_to_world.with(
|
||||||
|asm_to_world| elt.cast(dir, asm_to_world, pixel_size));
|
|asm_to_world| elt.cast(dir, asm_to_world, pixel_size));
|
||||||
match target {
|
match hit {
|
||||||
Some(depth) => match clicked {
|
Some(depth) => match clicked {
|
||||||
Some((_, best_depth)) => {
|
Some((_, best_depth)) => {
|
||||||
if depth < best_depth {
|
if depth < best_depth {
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,8 @@ fn RegulatorInput(regulator: Rc<dyn Regulator>) -> View {
|
||||||
placeholder = measurement.with(|result| result.to_string()),
|
placeholder = measurement.with(|result| result.to_string()),
|
||||||
bind:value = value,
|
bind:value = value,
|
||||||
on:change = move |_| {
|
on:change = move |_| {
|
||||||
let specification =
|
let specification
|
||||||
SpecifiedValue::try_from(value.get_clone_untracked());
|
= SpecifiedValue::try_from(value.get_clone_untracked());
|
||||||
valid.set(
|
valid.set(
|
||||||
match specification {
|
match specification {
|
||||||
Ok(set_pt) => {
|
Ok(set_pt) => {
|
||||||
|
|
@ -179,8 +179,10 @@ fn ElementOutlineItem(element: Rc<dyn Element>) -> View {
|
||||||
move |event: KeyboardEvent| {
|
move |event: KeyboardEvent| {
|
||||||
match event.key().as_str() {
|
match event.key().as_str() {
|
||||||
"Enter" => {
|
"Enter" => {
|
||||||
state.select(&element_for_handler,
|
state.select(
|
||||||
event.shift_key());
|
&element_for_handler,
|
||||||
|
event.shift_key(),
|
||||||
|
);
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
},
|
},
|
||||||
"ArrowRight" if regulated.get() => {
|
"ArrowRight" if regulated.get() => {
|
||||||
|
|
@ -210,8 +212,8 @@ fn ElementOutlineItem(element: Rc<dyn Element>) -> View {
|
||||||
let state_for_handler = state.clone();
|
let state_for_handler = state.clone();
|
||||||
let element_for_handler = element.clone();
|
let element_for_handler = element.clone();
|
||||||
move |event: MouseEvent| {
|
move |event: MouseEvent| {
|
||||||
state_for_handler.select(&element_for_handler,
|
state_for_handler.select(
|
||||||
event.shift_key());
|
&element_for_handler, event.shift_key());
|
||||||
event.stop_propagation();
|
event.stop_propagation();
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
}
|
}
|
||||||
|
|
@ -224,8 +226,8 @@ fn ElementOutlineItem(element: Rc<dyn Element>) -> View {
|
||||||
input(
|
input(
|
||||||
r#type = "checkbox",
|
r#type = "checkbox",
|
||||||
bind:checked = element.ghost(),
|
bind:checked = element.ghost(),
|
||||||
on:click =
|
on:click
|
||||||
|event: MouseEvent| event.stop_propagation()
|
= |event: MouseEvent| event.stop_propagation()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -225,8 +225,8 @@ void main() {
|
||||||
|
|
||||||
// highlight cusps
|
// highlight cusps
|
||||||
float cusp_cos = abs(dot(dir, frag.normal));
|
float cusp_cos = abs(dot(dir, frag.normal));
|
||||||
float cusp_threshold = 2.*sqrt(
|
float cusp_threshold
|
||||||
ixn_threshold * sphere_list[hit.id].lt.s);
|
= 2.*sqrt( ixn_threshold * sphere_list[hit.id].lt.s);
|
||||||
float cusp_highlight = highlight * (1. - smoothstep(
|
float cusp_highlight = highlight * (1. - smoothstep(
|
||||||
2./3.*cusp_threshold, 1.5*cusp_threshold, cusp_cos));
|
2./3.*cusp_threshold, 1.5*cusp_threshold, cusp_cos));
|
||||||
frag.color = mix(frag.color, vec4(1.), cusp_highlight);
|
frag.color = mix(frag.color, vec4(1.), cusp_highlight);
|
||||||
|
|
|
||||||
|
|
@ -167,36 +167,37 @@ fn load_low_curvature(assembly: &Assembly) {
|
||||||
let curvature = plane.regulators().with_untracked(
|
let curvature = plane.regulators().with_untracked(
|
||||||
|regs| regs.first().unwrap().clone()
|
|regs| regs.first().unwrap().clone()
|
||||||
);
|
);
|
||||||
curvature.set_point().set(
|
curvature.set_point()
|
||||||
SpecifiedValue::try_from("0".to_string()).unwrap());
|
.set(SpecifiedValue::try_from("0".to_string()).unwrap());
|
||||||
}
|
}
|
||||||
let all_perpendicular = [central.clone()].into_iter()
|
let all_perpendicular = [central.clone()].into_iter()
|
||||||
.chain(sides.clone())
|
.chain(sides.clone())
|
||||||
.chain(corners.clone());
|
.chain(corners.clone());
|
||||||
for sphere in all_perpendicular {
|
for sphere in all_perpendicular {
|
||||||
// make each side and packed sphere perpendicular to the assembly plane
|
// make each side and packed sphere perpendicular to the assembly plane
|
||||||
let right_angle = InversiveDistanceRegulator::new(
|
let right_angle
|
||||||
[sphere, assemb_plane.clone()]);
|
= InversiveDistanceRegulator::new([sphere, assemb_plane.clone()]);
|
||||||
right_angle.set_point.set(
|
right_angle.set_point
|
||||||
SpecifiedValue::try_from("0".to_string()).unwrap());
|
.set(SpecifiedValue::try_from("0".to_string()).unwrap());
|
||||||
assembly.insert_regulator(Rc::new(right_angle));
|
assembly.insert_regulator(Rc::new(right_angle));
|
||||||
}
|
}
|
||||||
for sphere in sides.clone().chain(corners.clone()) {
|
for sphere in sides.clone().chain(corners.clone()) {
|
||||||
// make each side and corner sphere tangent to the central sphere
|
// make each side and corner sphere tangent to the central sphere
|
||||||
let tangency = InversiveDistanceRegulator::new(
|
let tangency = InversiveDistanceRegulator::new([
|
||||||
[sphere.clone(), central.clone()]);
|
sphere.clone(), central.clone()
|
||||||
tangency.set_point.set(
|
]);
|
||||||
SpecifiedValue::try_from("-1".to_string()).unwrap());
|
tangency.set_point
|
||||||
|
.set(SpecifiedValue::try_from("-1".to_string()).unwrap());
|
||||||
assembly.insert_regulator(Rc::new(tangency));
|
assembly.insert_regulator(Rc::new(tangency));
|
||||||
}
|
}
|
||||||
for (side_index, side) in sides.enumerate() {
|
for (side_index, side) in sides.enumerate() {
|
||||||
// make each side tangent to the two adjacent corner spheres
|
// make each side tangent to the two adjacent corner spheres
|
||||||
for (corner_index, corner) in corners.clone().enumerate() {
|
for (corner_index, corner) in corners.clone().enumerate() {
|
||||||
if side_index != corner_index {
|
if side_index != corner_index {
|
||||||
let tangency = InversiveDistanceRegulator::new(
|
let tangency
|
||||||
[side.clone(), corner]);
|
= InversiveDistanceRegulator::new([side.clone(), corner]);
|
||||||
tangency.set_point.set(
|
tangency.set_point
|
||||||
SpecifiedValue::try_from("-1".to_string()).unwrap());
|
.set(SpecifiedValue::try_from("-1".to_string()).unwrap());
|
||||||
assembly.insert_regulator(Rc::new(tangency));
|
assembly.insert_regulator(Rc::new(tangency));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -226,13 +227,13 @@ fn load_pointed(assembly: &Assembly) {
|
||||||
let y = index_y as f64 - 0.5;
|
let y = index_y as f64 - 0.5;
|
||||||
let x32 = x as f32;
|
let x32 = x as f32;
|
||||||
let y32 = y as f32;
|
let y32 = y as f32;
|
||||||
let coords =
|
let color
|
||||||
[0.5*(1.0 + x32), 0.5*(1.0 + y32), 0.5*(1.0 - x32*y32)];
|
= [0.5*(1.0 + x32), 0.5*(1.0 + y32), 0.5*(1.0 - x32*y32)];
|
||||||
let _ = assembly.try_insert_element(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
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}"),
|
||||||
coords,
|
color,
|
||||||
engine::sphere(x, y, 0.0, 1.0),
|
engine::sphere(x, y, 0.0, 1.0),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
@ -241,7 +242,7 @@ fn load_pointed(assembly: &Assembly) {
|
||||||
Point::new(
|
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}"),
|
||||||
coords,
|
color,
|
||||||
engine::point(x, y, 0.0),
|
engine::point(x, y, 0.0),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
@ -332,7 +333,8 @@ fn load_tridiminished_icosahedron(assembly: &Assembly) {
|
||||||
COLOR_FACE,
|
COLOR_FACE,
|
||||||
engine::sphere_with_offset(
|
engine::sphere_with_offset(
|
||||||
frac_2_sqrt_6, -frac_1_sqrt_6, -frac_1_sqrt_6,
|
frac_2_sqrt_6, -frac_1_sqrt_6, -frac_1_sqrt_6,
|
||||||
-frac_1_sqrt_6, 0.0),
|
-frac_1_sqrt_6, 0.0
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
"face2".to_string(),
|
"face2".to_string(),
|
||||||
|
|
@ -340,7 +342,8 @@ fn load_tridiminished_icosahedron(assembly: &Assembly) {
|
||||||
COLOR_FACE,
|
COLOR_FACE,
|
||||||
engine::sphere_with_offset(
|
engine::sphere_with_offset(
|
||||||
-frac_1_sqrt_6, frac_2_sqrt_6, -frac_1_sqrt_6,
|
-frac_1_sqrt_6, frac_2_sqrt_6, -frac_1_sqrt_6,
|
||||||
-frac_1_sqrt_6, 0.0),
|
-frac_1_sqrt_6, 0.0
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
"face3".to_string(),
|
"face3".to_string(),
|
||||||
|
|
@ -348,7 +351,8 @@ fn load_tridiminished_icosahedron(assembly: &Assembly) {
|
||||||
COLOR_FACE,
|
COLOR_FACE,
|
||||||
engine::sphere_with_offset(
|
engine::sphere_with_offset(
|
||||||
-frac_1_sqrt_6, -frac_1_sqrt_6, frac_2_sqrt_6,
|
-frac_1_sqrt_6, -frac_1_sqrt_6, frac_2_sqrt_6,
|
||||||
-frac_1_sqrt_6, 0.0),
|
-frac_1_sqrt_6, 0.0
|
||||||
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
for face in faces {
|
for face in faces {
|
||||||
|
|
@ -373,10 +377,11 @@ fn load_tridiminished_icosahedron(assembly: &Assembly) {
|
||||||
let vertex_a = assembly.elements_by_id.with_untracked(
|
let vertex_a = assembly.elements_by_id.with_untracked(
|
||||||
|elts_by_id| elts_by_id[&format!("a{j}")].clone()
|
|elts_by_id| elts_by_id[&format!("a{j}")].clone()
|
||||||
);
|
);
|
||||||
let incidence_a = InversiveDistanceRegulator::new(
|
let incidence_a = InversiveDistanceRegulator::new([
|
||||||
[face.clone(), vertex_a.clone()]);
|
face.clone(), vertex_a.clone()
|
||||||
incidence_a.set_point.set(
|
]);
|
||||||
SpecifiedValue::try_from("0".to_string()).unwrap());
|
incidence_a.set_point
|
||||||
|
.set(SpecifiedValue::try_from("0".to_string()).unwrap());
|
||||||
assembly.insert_regulator(Rc::new(incidence_a));
|
assembly.insert_regulator(Rc::new(incidence_a));
|
||||||
|
|
||||||
// regulate the B-C vertex distances
|
// regulate the B-C vertex distances
|
||||||
|
|
@ -398,16 +403,18 @@ fn load_tridiminished_icosahedron(assembly: &Assembly) {
|
||||||
let vertex = assembly.elements_by_id.with_untracked(
|
let vertex = assembly.elements_by_id.with_untracked(
|
||||||
|elts_by_id| elts_by_id[&format!("{series}{k}")].clone()
|
|elts_by_id| elts_by_id[&format!("{series}{k}")].clone()
|
||||||
);
|
);
|
||||||
let incidence = InversiveDistanceRegulator::new(
|
let incidence = InversiveDistanceRegulator::new([
|
||||||
[face.clone(), vertex.clone()]);
|
face.clone(), vertex.clone()
|
||||||
incidence.set_point.set(
|
]);
|
||||||
SpecifiedValue::try_from("0".to_string()).unwrap());
|
incidence.set_point
|
||||||
|
.set(SpecifiedValue::try_from("0".to_string()).unwrap());
|
||||||
assembly.insert_regulator(Rc::new(incidence));
|
assembly.insert_regulator(Rc::new(incidence));
|
||||||
|
|
||||||
// regulate the A-B and A-C vertex distances
|
// regulate the A-B and A-C vertex distances
|
||||||
assembly.insert_regulator(
|
assembly.insert_regulator(
|
||||||
Rc::new(InversiveDistanceRegulator::new(
|
Rc::new(InversiveDistanceRegulator::new([
|
||||||
[vertex_a.clone(), vertex]))
|
vertex_a.clone(), vertex
|
||||||
|
]))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -523,16 +530,18 @@ fn load_dodecahedral_packing(assembly: &Assembly) {
|
||||||
|
|
||||||
// make each face sphere perpendicular to the substrate
|
// make each face sphere perpendicular to the substrate
|
||||||
for face in faces {
|
for face in faces {
|
||||||
let right_angle = InversiveDistanceRegulator::new(
|
let right_angle = InversiveDistanceRegulator::new([
|
||||||
[face, substrate.clone()]);
|
face, substrate.clone()
|
||||||
right_angle.set_point.set(
|
]);
|
||||||
SpecifiedValue::try_from("0".to_string()).unwrap());
|
right_angle.set_point
|
||||||
|
.set(SpecifiedValue::try_from("0".to_string()).unwrap());
|
||||||
assembly.insert_regulator(Rc::new(right_angle));
|
assembly.insert_regulator(Rc::new(right_angle));
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up the tangencies that define the packing
|
// set up the tangencies that define the packing
|
||||||
for [long_edge_plane, short_edge_plane]
|
for [long_edge_plane, short_edge_plane]
|
||||||
in [["a", "b"], ["b", "c"], ["c", "a"]] {
|
in [["a", "b"], ["b", "c"], ["c", "a"]]
|
||||||
|
{
|
||||||
for k in 0..2 {
|
for k in 0..2 {
|
||||||
let long_edge_ids = [
|
let long_edge_ids = [
|
||||||
format!("{long_edge_plane}{k}0"),
|
format!("{long_edge_plane}{k}0"),
|
||||||
|
|
@ -551,20 +560,20 @@ fn load_dodecahedral_packing(assembly: &Assembly) {
|
||||||
);
|
);
|
||||||
|
|
||||||
// set up the short-edge tangency
|
// set up the short-edge tangency
|
||||||
let short_tangency = InversiveDistanceRegulator::new(
|
let short_tangency
|
||||||
short_edge.clone());
|
= InversiveDistanceRegulator::new(short_edge.clone());
|
||||||
if k == 0 {
|
if k == 0 {
|
||||||
short_tangency.set_point.set(
|
short_tangency.set_point
|
||||||
SpecifiedValue::try_from("-1".to_string()).unwrap());
|
.set(SpecifiedValue::try_from("-1".to_string()).unwrap());
|
||||||
}
|
}
|
||||||
assembly.insert_regulator(Rc::new(short_tangency));
|
assembly.insert_regulator(Rc::new(short_tangency));
|
||||||
|
|
||||||
// set up the side tangencies
|
// set up the side tangencies
|
||||||
for i in 0..2 {
|
for i in 0..2 {
|
||||||
for j in 0..2 {
|
for j in 0..2 {
|
||||||
let side_tangency = InversiveDistanceRegulator::new(
|
let side_tangency = InversiveDistanceRegulator::new([
|
||||||
[long_edge[i].clone(), short_edge[j].clone()]
|
long_edge[i].clone(), short_edge[j].clone()
|
||||||
);
|
]);
|
||||||
if i == 0 && k == 0 {
|
if i == 0 && k == 0 {
|
||||||
side_tangency.set_point.set(
|
side_tangency.set_point.set(
|
||||||
SpecifiedValue::try_from("-1".to_string()).unwrap()
|
SpecifiedValue::try_from("-1".to_string()).unwrap()
|
||||||
|
|
@ -633,8 +642,8 @@ fn load_balanced(assembly: &Assembly) {
|
||||||
// initial configuration deliberately violates these constraints
|
// initial configuration deliberately violates these constraints
|
||||||
for inner in [a, b] {
|
for inner in [a, b] {
|
||||||
let tangency = InversiveDistanceRegulator::new([outer.clone(), inner]);
|
let tangency = InversiveDistanceRegulator::new([outer.clone(), inner]);
|
||||||
tangency.set_point.set(
|
tangency.set_point
|
||||||
SpecifiedValue::try_from("1".to_string()).unwrap());
|
.set(SpecifiedValue::try_from("1".to_string()).unwrap());
|
||||||
assembly.insert_regulator(Rc::new(tangency));
|
assembly.insert_regulator(Rc::new(tangency));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -799,10 +808,11 @@ fn load_radius_ratio(assembly: &Assembly) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// put the vertices on the faces
|
// put the vertices on the faces
|
||||||
let incidence_regulator = InversiveDistanceRegulator::new(
|
let incidence_regulator = InversiveDistanceRegulator::new([
|
||||||
[face_j.clone(), vertex_k.clone()]);
|
face_j.clone(), vertex_k.clone()
|
||||||
incidence_regulator.set_point.set(
|
]);
|
||||||
SpecifiedValue::try_from("0".to_string()).unwrap());
|
incidence_regulator.set_point
|
||||||
|
.set(SpecifiedValue::try_from("0".to_string()).unwrap());
|
||||||
assembly.insert_regulator(Rc::new(incidence_regulator));
|
assembly.insert_regulator(Rc::new(incidence_regulator));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -897,32 +907,37 @@ fn load_irisawa_hexlet(assembly: &Assembly) {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
for (chain_sphere, chain_sphere_next)
|
for (chain_sphere, chain_sphere_next)
|
||||||
in chain.clone().zip(chain.cycle().skip(1)) {
|
in chain.clone().zip(chain.cycle().skip(1))
|
||||||
for (other_sphere, inversive_distance) in [
|
{
|
||||||
(outer.clone(), "1"),
|
for (other_sphere, inversive_distance)
|
||||||
(sun.clone(), "-1"),
|
in [(outer.clone(), "1"),
|
||||||
(moon.clone(), "-1"),
|
(sun.clone(), "-1"),
|
||||||
(chain_sphere_next.clone(), "-1"),
|
(moon.clone(), "-1"),
|
||||||
|
(chain_sphere_next.clone(), "-1"),
|
||||||
] {
|
] {
|
||||||
let tangency = InversiveDistanceRegulator::new(
|
let tangency = InversiveDistanceRegulator::new([
|
||||||
[chain_sphere.clone(), other_sphere]);
|
chain_sphere.clone(), other_sphere
|
||||||
|
]);
|
||||||
tangency.set_point.set(
|
tangency.set_point.set(
|
||||||
SpecifiedValue::try_from(
|
SpecifiedValue::try_from(
|
||||||
inversive_distance.to_string()).unwrap());
|
inversive_distance.to_string()).unwrap()
|
||||||
|
);
|
||||||
assembly.insert_regulator(Rc::new(tangency));
|
assembly.insert_regulator(Rc::new(tangency));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let outer_sun_tangency = InversiveDistanceRegulator::new(
|
let outer_sun_tangency = InversiveDistanceRegulator::new([
|
||||||
[outer.clone(), sun]);
|
outer.clone(), sun
|
||||||
outer_sun_tangency.set_point.set(
|
]);
|
||||||
SpecifiedValue::try_from("1".to_string()).unwrap());
|
outer_sun_tangency.set_point
|
||||||
|
.set(SpecifiedValue::try_from("1".to_string()).unwrap());
|
||||||
assembly.insert_regulator(Rc::new(outer_sun_tangency));
|
assembly.insert_regulator(Rc::new(outer_sun_tangency));
|
||||||
|
|
||||||
let outer_moon_tangency = InversiveDistanceRegulator::new(
|
let outer_moon_tangency = InversiveDistanceRegulator::new([
|
||||||
[outer.clone(), moon]);
|
outer.clone(), moon
|
||||||
outer_moon_tangency.set_point.set(
|
]);
|
||||||
SpecifiedValue::try_from("1".to_string()).unwrap());
|
outer_moon_tangency.set_point
|
||||||
|
.set(SpecifiedValue::try_from("1".to_string()).unwrap());
|
||||||
assembly.insert_regulator(Rc::new(outer_moon_tangency));
|
assembly.insert_regulator(Rc::new(outer_moon_tangency));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,9 @@ pub fn point(x: f64, y: f64, z: f64) -> DVector<f64> {
|
||||||
|
|
||||||
// the sphere with the given center and radius, with inward-pointing normals
|
// the sphere with the given center and radius, with inward-pointing normals
|
||||||
pub fn sphere(center_x: f64, center_y: f64, center_z: f64, radius: f64)
|
pub fn sphere(center_x: f64, center_y: f64, center_z: f64, radius: f64)
|
||||||
-> DVector<f64>
|
-> DVector<f64> {
|
||||||
{
|
let center_norm_sq
|
||||||
let center_norm_sq =
|
= center_x * center_x + center_y * center_y + center_z * center_z;
|
||||||
center_x * center_x + center_y * center_y + center_z * center_z;
|
|
||||||
DVector::from_column_slice(&[
|
DVector::from_column_slice(&[
|
||||||
center_x / radius,
|
center_x / radius,
|
||||||
center_y / radius,
|
center_y / radius,
|
||||||
|
|
@ -27,8 +26,8 @@ pub fn sphere(center_x: f64, center_y: f64, center_z: f64, radius: f64)
|
||||||
// `off * dir` and normal `dir`, where `dir` is a unit vector. setting the
|
// `off * dir` and normal `dir`, where `dir` is a unit vector. setting the
|
||||||
// curvature to zero gives a plane
|
// curvature to zero gives a plane
|
||||||
pub fn sphere_with_offset(
|
pub fn sphere_with_offset(
|
||||||
dir_x: f64, dir_y: f64, dir_z: f64, off: f64, curv: f64) -> DVector<f64>
|
dir_x: f64, dir_y: f64, dir_z: f64, off: f64, curv: f64
|
||||||
{
|
) -> DVector<f64> {
|
||||||
let norm_sp = 1.0 + off * curv;
|
let norm_sp = 1.0 + off * curv;
|
||||||
DVector::from_column_slice(&[
|
DVector::from_column_slice(&[
|
||||||
norm_sp * dir_x,
|
norm_sp * dir_x,
|
||||||
|
|
@ -156,9 +155,7 @@ impl ConfigSubspace {
|
||||||
// space for `assembly_dim` elements. we consider an eigenvector to be part
|
// space for `assembly_dim` elements. we consider an eigenvector to be part
|
||||||
// of the kernel if its eigenvalue is smaller than the constant `THRESHOLD`
|
// of the kernel if its eigenvalue is smaller than the constant `THRESHOLD`
|
||||||
fn symmetric_kernel(
|
fn symmetric_kernel(
|
||||||
a: DMatrix<f64>,
|
a: DMatrix<f64>, proj_to_std: DMatrix<f64>, assembly_dim: usize
|
||||||
proj_to_std: DMatrix<f64>,
|
|
||||||
assembly_dim: usize,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// find a basis for the kernel. the basis is expressed in the projection
|
// find a basis for the kernel. the basis is expressed in the projection
|
||||||
// coordinates, and it's orthonormal with respect to the projection
|
// coordinates, and it's orthonormal with respect to the projection
|
||||||
|
|
@ -206,8 +203,7 @@ impl ConfigSubspace {
|
||||||
// projection coordinates, and the projection is done with respect to the
|
// projection coordinates, and the projection is done with respect to the
|
||||||
// projection inner product
|
// projection inner product
|
||||||
pub fn proj(&self, v: &DVectorView<f64>, column_index: usize)
|
pub fn proj(&self, v: &DVectorView<f64>, column_index: usize)
|
||||||
-> DMatrix<f64>
|
-> DMatrix<f64> {
|
||||||
{
|
|
||||||
if self.dim() == 0 {
|
if self.dim() == 0 {
|
||||||
const ELEMENT_DIM: usize = 5;
|
const ELEMENT_DIM: usize = 5;
|
||||||
DMatrix::zeros(ELEMENT_DIM, self.assembly_dim)
|
DMatrix::zeros(ELEMENT_DIM, self.assembly_dim)
|
||||||
|
|
@ -423,8 +419,8 @@ pub fn realize_gram(
|
||||||
for _ in 0..max_descent_steps {
|
for _ in 0..max_descent_steps {
|
||||||
// find the negative gradient of the loss function
|
// find the negative gradient of the loss function
|
||||||
let neg_grad = 4.0 * &*Q * &state.config * &state.err_proj;
|
let neg_grad = 4.0 * &*Q * &state.config * &state.err_proj;
|
||||||
let mut neg_grad_stacked =
|
let mut neg_grad_stacked = neg_grad.clone()
|
||||||
neg_grad.clone().reshape_generic(Dyn(total_dim), Const::<1>);
|
.reshape_generic(Dyn(total_dim), Const::<1>);
|
||||||
history.neg_grad.push(neg_grad.clone());
|
history.neg_grad.push(neg_grad.clone());
|
||||||
|
|
||||||
// find the negative Hessian of the loss function
|
// find the negative Hessian of the loss function
|
||||||
|
|
@ -489,8 +485,8 @@ pub fn realize_gram(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let base_step_stacked = hess_cholesky.solve(&neg_grad_stacked);
|
let base_step_stacked = hess_cholesky.solve(&neg_grad_stacked);
|
||||||
let base_step = base_step_stacked.reshape_generic(
|
let base_step = base_step_stacked
|
||||||
Dyn(element_dim), Dyn(assembly_dim));
|
.reshape_generic(Dyn(element_dim), Dyn(assembly_dim));
|
||||||
history.base_step.push(base_step.clone());
|
history.base_step.push(base_step.clone());
|
||||||
|
|
||||||
// use backtracking line search to find a better configuration
|
// use backtracking line search to find a better configuration
|
||||||
|
|
@ -520,11 +516,12 @@ pub fn realize_gram(
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the kernel of the Hessian. give it the uniform inner product
|
// find the kernel of the Hessian. give it the uniform inner product
|
||||||
let tangent =
|
let tangent
|
||||||
ConfigSubspace::symmetric_kernel(hess, unif_to_std, assembly_dim);
|
= ConfigSubspace::symmetric_kernel(hess, unif_to_std, assembly_dim);
|
||||||
|
|
||||||
Ok(ConfigNeighborhood {
|
Ok(ConfigNeighborhood {
|
||||||
#[cfg(feature = "dev")] config: state.config, nbhd: tangent
|
#[cfg(feature = "dev")] config: state.config,
|
||||||
|
nbhd: tangent,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err("Failed to reach target accuracy".to_string())
|
Err("Failed to reach target accuracy".to_string())
|
||||||
|
|
@ -625,7 +622,10 @@ pub mod examples {
|
||||||
// diagonal and hinge edges
|
// diagonal and hinge edges
|
||||||
for k in j..2 {
|
for k in j..2 {
|
||||||
problem.gram.push_sym(
|
problem.gram.push_sym(
|
||||||
block + j, block + k, if j == k { 0.0 } else { -0.5 });
|
block + j,
|
||||||
|
block + k,
|
||||||
|
if j == k { 0.0 } else { -0.5 }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// non-hinge edges
|
// non-hinge edges
|
||||||
|
|
@ -720,7 +720,9 @@ mod tests {
|
||||||
for j in 0..2 {
|
for j in 0..2 {
|
||||||
for k in j..2 {
|
for k in j..2 {
|
||||||
problem.gram.push_sym(
|
problem.gram.push_sym(
|
||||||
j, k, if (j, k) == (1, 1) { 1.0 } else { 0.0 });
|
j, k,
|
||||||
|
if (j, k) == (1, 1) { 1.0 } else { 0.0 }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
problem.frozen.push(3, 0, problem.guess[(3, 0)]);
|
problem.frozen.push(3, 0, problem.guess[(3, 0)]);
|
||||||
|
|
@ -747,8 +749,9 @@ mod tests {
|
||||||
|
|
||||||
// check against Irisawa's solution
|
// check against Irisawa's solution
|
||||||
let entry_tol = SCALED_TOL.sqrt();
|
let entry_tol = SCALED_TOL.sqrt();
|
||||||
let solution_diams =
|
let solution_diams = [
|
||||||
[30.0, 10.0, 6.0, 5.0, 15.0, 10.0, 3.75, 2.5, 2.0 + 8.0/11.0];
|
30.0, 10.0, 6.0, 5.0, 15.0, 10.0, 3.75, 2.5, 2.0 + 8.0/11.0
|
||||||
|
];
|
||||||
for (k, diam) in solution_diams.into_iter().enumerate() {
|
for (k, diam) in solution_diams.into_iter().enumerate() {
|
||||||
assert!((config[(3, k)] - 1.0 / diam).abs() < entry_tol);
|
assert!((config[(3, k)] - 1.0 / diam).abs() < entry_tol);
|
||||||
}
|
}
|
||||||
|
|
@ -816,18 +819,18 @@ mod tests {
|
||||||
let tol_sq = ((element_dim * assembly_dim) as f64)
|
let tol_sq = ((element_dim * assembly_dim) as f64)
|
||||||
* SCALED_TOL * SCALED_TOL;
|
* SCALED_TOL * SCALED_TOL;
|
||||||
for (motion_unif, motion_std)
|
for (motion_unif, motion_std)
|
||||||
in tangent_motions_unif.into_iter().zip(tangent_motions_std) {
|
in tangent_motions_unif.into_iter().zip(tangent_motions_std)
|
||||||
let motion_proj: DMatrix<_> =
|
{
|
||||||
motion_unif.column_iter().enumerate().map(
|
let motion_proj: DMatrix<_>
|
||||||
|(k, v)| tangent.proj(&v, k)
|
= motion_unif.column_iter().enumerate()
|
||||||
).sum();
|
.map(|(k, v)| tangent.proj(&v, k))
|
||||||
|
.sum();
|
||||||
assert!((motion_std - motion_proj).norm_squared() < tol_sq);
|
assert!((motion_std - motion_proj).norm_squared() < tol_sq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translation_motion_unif(vel: &Vector3<f64>, assembly_dim: usize)
|
fn translation_motion_unif(vel: &Vector3<f64>, assembly_dim: usize)
|
||||||
-> Vec<DVector<f64>>
|
-> Vec<DVector<f64>> {
|
||||||
{
|
|
||||||
let mut elt_motion = DVector::zeros(4);
|
let mut elt_motion = DVector::zeros(4);
|
||||||
elt_motion.fixed_rows_mut::<3>(0).copy_from(vel);
|
elt_motion.fixed_rows_mut::<3>(0).copy_from(vel);
|
||||||
iter::repeat(elt_motion).take(assembly_dim).collect()
|
iter::repeat(elt_motion).take(assembly_dim).collect()
|
||||||
|
|
@ -888,10 +891,12 @@ mod tests {
|
||||||
[
|
[
|
||||||
DVector::from_column_slice(&[0.0, 0.0, 5.0, 0.0]),
|
DVector::from_column_slice(&[0.0, 0.0, 5.0, 0.0]),
|
||||||
DVector::from_column_slice(&[0.0, 0.0, 1.0, 0.0]),
|
DVector::from_column_slice(&[0.0, 0.0, 1.0, 0.0]),
|
||||||
DVector::from_column_slice(
|
DVector::from_column_slice(&[
|
||||||
&[-vel_vert_x, -vel_vert_y, -3.0, 0.0]),
|
-vel_vert_x, -vel_vert_y, -3.0, 0.0
|
||||||
DVector::from_column_slice(
|
]),
|
||||||
&[vel_vert_x, vel_vert_y, -3.0, 0.0]),
|
DVector::from_column_slice(&[
|
||||||
|
vel_vert_x, vel_vert_y, -3.0, 0.0
|
||||||
|
]),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
).collect::<Vec<_>>(),
|
).collect::<Vec<_>>(),
|
||||||
|
|
@ -914,7 +919,8 @@ mod tests {
|
||||||
let tol_sq = ((element_dim * assembly_dim) as f64)
|
let tol_sq = ((element_dim * assembly_dim) as f64)
|
||||||
* SCALED_TOL * SCALED_TOL;
|
* SCALED_TOL * SCALED_TOL;
|
||||||
for (motion_unif, motion_std)
|
for (motion_unif, motion_std)
|
||||||
in tangent_motions_unif.into_iter().zip(tangent_motions_std) {
|
in tangent_motions_unif.into_iter().zip(tangent_motions_std)
|
||||||
|
{
|
||||||
let motion_proj: DMatrix<_> =
|
let motion_proj: DMatrix<_> =
|
||||||
motion_unif.into_iter().enumerate().map(
|
motion_unif.into_iter().enumerate().map(
|
||||||
|(k, v)| tangent.proj(&v.as_view(), k)
|
|(k, v)| tangent.proj(&v.as_view(), k)
|
||||||
|
|
@ -947,10 +953,10 @@ mod tests {
|
||||||
problem_orig.gram.push_sym(0, 0, 1.0);
|
problem_orig.gram.push_sym(0, 0, 1.0);
|
||||||
problem_orig.gram.push_sym(1, 1, 1.0);
|
problem_orig.gram.push_sym(1, 1, 1.0);
|
||||||
problem_orig.gram.push_sym(0, 1, 0.5);
|
problem_orig.gram.push_sym(0, 1, 0.5);
|
||||||
let Realization { result: result_orig, history: history_orig } =
|
let Realization { result: result_orig, history: history_orig }
|
||||||
realize_gram(&problem_orig, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110);
|
= realize_gram(&problem_orig, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110);
|
||||||
let ConfigNeighborhood { config: config_orig, nbhd: tangent_orig } =
|
let ConfigNeighborhood { config: config_orig, nbhd: tangent_orig }
|
||||||
result_orig.unwrap();
|
= result_orig.unwrap();
|
||||||
assert_eq!(config_orig, problem_orig.guess);
|
assert_eq!(config_orig, problem_orig.guess);
|
||||||
assert_eq!(history_orig.scaled_loss.len(), 1);
|
assert_eq!(history_orig.scaled_loss.len(), 1);
|
||||||
|
|
||||||
|
|
@ -968,10 +974,10 @@ mod tests {
|
||||||
frozen: problem_orig.frozen,
|
frozen: problem_orig.frozen,
|
||||||
guess: guess_tfm,
|
guess: guess_tfm,
|
||||||
};
|
};
|
||||||
let Realization { result: result_tfm, history: history_tfm } =
|
let Realization { result: result_tfm, history: history_tfm }
|
||||||
realize_gram(&problem_tfm, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110);
|
= realize_gram(&problem_tfm, SCALED_TOL, 0.5, 0.9, 1.1, 200, 110);
|
||||||
let ConfigNeighborhood { config: config_tfm, nbhd: tangent_tfm } =
|
let ConfigNeighborhood { config: config_tfm, nbhd: tangent_tfm }
|
||||||
result_tfm.unwrap();
|
= result_tfm.unwrap();
|
||||||
assert_eq!(config_tfm, problem_tfm.guess);
|
assert_eq!(config_tfm, problem_tfm.guess);
|
||||||
assert_eq!(history_tfm.scaled_loss.len(), 1);
|
assert_eq!(history_tfm.scaled_loss.len(), 1);
|
||||||
|
|
||||||
|
|
@ -982,8 +988,9 @@ mod tests {
|
||||||
|
|
||||||
// project the equivalent nudge to the tangent space of the solution
|
// project the equivalent nudge to the tangent space of the solution
|
||||||
// variety at the transformed solution
|
// variety at the transformed solution
|
||||||
let motion_tfm = DVector::from_column_slice(
|
let motion_tfm = DVector::from_column_slice(&[
|
||||||
&[FRAC_1_SQRT_2, 0.0, FRAC_1_SQRT_2, 0.0]);
|
FRAC_1_SQRT_2, 0.0, FRAC_1_SQRT_2, 0.0
|
||||||
|
]);
|
||||||
let motion_tfm_proj = tangent_tfm.proj(&motion_tfm.as_view(), 0);
|
let motion_tfm_proj = tangent_tfm.proj(&motion_tfm.as_view(), 0);
|
||||||
|
|
||||||
// take the transformation that sends the original solution to the
|
// take the transformation that sends the original solution to the
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue