diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index b4b5329..278a8f9 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -317,36 +317,50 @@ impl Assembly { console::log_1(&JsValue::from("The assembly is rigid")); } - const ELEMENT_DIM: usize = 5; - let assembly_dim = self.elements.with(|elts| elts.len()); - let mut motion_proj = DMatrix::zeros(ELEMENT_DIM, assembly_dim); - let tan_assembly_dim = self.tangent.with(|tan| tan.assembly_dim()); - - // project the element motions onto the tangent space of the solution - // variety, and sum them to get a deformation of the whole assembly - let mut next_column_index = tan_assembly_dim; - for elt_motion in motion { - match self.elements.with_untracked(|elts| elts[elt_motion.key].column_index) { - Some(column_index) => { - let mut target_columns = motion_proj.columns_mut(0, tan_assembly_dim); - target_columns += self.tangent.with( - |tan| tan.proj(&elt_motion.velocity, column_index) - ) - }, - None => { - // give the element a column index, even though it's never - // been through a realization, and add its motion to that - // column of the projected motion. this temporarily breaks - // invariant (1), but the invariant will be restored when we - // realize the assembly at the end of the deformation - let mut target_column = motion_proj.column_mut(next_column_index); - target_column += elt_motion.velocity; - self.elements.update_silent( - |elts| elts[elt_motion.key].column_index = Some(next_column_index) - ); + // give a column index to each moving element that doesn't have one yet. + // this temporarily breaks invariant (1), but the invariant will be + // restored when we realize the assembly at the end of the deformation. + // in the process, we find out how many matrix columns we'll need to + // hold the deformation + let realized_dim = self.tangent.with(|tan| tan.assembly_dim()); + let motion_dim = self.elements.update_silent(|elts| { + let mut next_column_index = realized_dim; + for elt_motion in motion.iter() { + let moving_elt = &mut elts[elt_motion.key]; + if moving_elt.column_index.is_none() { + moving_elt.column_index = Some(next_column_index); next_column_index += 1; } } + next_column_index + }); + + // project the element motions onto the tangent space of the solution + // variety and sum them to get a deformation of the whole assembly. the + // matrix `motion_proj` that holds the deformation has extra columns for + // any moving elements that aren't reflected in the saved tangent space + const ELEMENT_DIM: usize = 5; + let mut motion_proj = DMatrix::zeros(ELEMENT_DIM, motion_dim); + for elt_motion in motion { + // we can unwrap the column index because we know that every moving + // element has one at this point + let column_index = self.elements.with_untracked( + |elts| elts[elt_motion.key].column_index.unwrap() + ); + + if column_index < realized_dim { + // this element had a column index when we started, so by + // invariant (1), it's reflected in the tangent space + let mut target_columns = motion_proj.columns_mut(0, realized_dim); + target_columns += self.tangent.with( + |tan| tan.proj(&elt_motion.velocity, column_index) + ); + } else { + // this element didn't have a column index when we started, so + // by invariant (2), it's unconstrained + let mut target_column = motion_proj.column_mut(column_index); + target_column += elt_motion.velocity; + } } // step each element along the mass shell geodesic that matches its