chore: relayout per PR discussion/feedback
All checks were successful
/ test (pull_request) Successful in 3m43s

This commit is contained in:
Glen Whitney 2025-11-21 13:09:05 -08:00
parent 4a3f47c8b5
commit 760e811ee4
6 changed files with 259 additions and 224 deletions

View file

@ -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,8 +763,8 @@ 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![

View file

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

View file

@ -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()
) )
} }
} }

View file

@ -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);

View file

@ -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)
in [(outer.clone(), "1"),
(sun.clone(), "-1"), (sun.clone(), "-1"),
(moon.clone(), "-1"), (moon.clone(), "-1"),
(chain_sphere_next.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));
} }

View file

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