forked from 1b-agent/fieldcraft-upstream
model: exterior edges of 2D elements
The boundary of a 2D model, which general contact takes by default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
6da3055a1c
commit
2ca5f84113
1 file changed
+47
-10
+47
-10
@@ -219,27 +219,47 @@ impl Model {
|
||||
remove_existing(&mut self.contacts, "contact", name).map(|_| ())
|
||||
}
|
||||
|
||||
/// The exterior edges of the 2D elements (the edges no other 2D element shares), in
|
||||
/// element order: the boundary of a 2D model, which general contact takes by default.
|
||||
pub fn exterior_edges(&self) -> Vec<crate::ElementEdge> {
|
||||
self.unshared(2, |kind| kind.edges(), 2, |element, i| crate::ElementEdge { element, edge: i })
|
||||
}
|
||||
|
||||
/// The exterior faces of the solid elements (the faces no other solid element shares), in
|
||||
/// element and face order: the surface of general contact without a named set.
|
||||
/// element order.
|
||||
pub fn exterior_faces(&self) -> Vec<crate::ElementFace> {
|
||||
let mut keyed: Vec<([u32; 4], crate::ElementFace)> = Vec::new();
|
||||
for (label, e) in self.elements.iter().filter(|(_, e)| e.kind.dimension() == 3) {
|
||||
for (i, local) in e.kind.faces().iter().enumerate() {
|
||||
// A face is known by its sorted corner nodes (three or four).
|
||||
let corners = if local.len() == 6 || local.len() == 3 { 3 } else { 4 };
|
||||
// A face is known by its sorted corner nodes (three or four).
|
||||
self.unshared(3, |kind| kind.faces(), 4, |element, face| crate::ElementFace { element, face })
|
||||
}
|
||||
|
||||
/// The sides (`sides` of each element kind) of the elements of `dimension` that no other
|
||||
/// such element shares, each known by its sorted corners (at most `corners`; triangles of
|
||||
/// quadratic kinds by their first three), made by `make` from the element and the 1-based
|
||||
/// side number, sorted.
|
||||
fn unshared<T: Ord>(
|
||||
&self,
|
||||
dimension: u8,
|
||||
sides: impl Fn(crate::ElementKind) -> &'static [&'static [usize]],
|
||||
corners: usize,
|
||||
make: impl Fn(u32, u8) -> T,
|
||||
) -> Vec<T> {
|
||||
let mut keyed: Vec<([u32; 4], u32, u8)> = Vec::new();
|
||||
for (label, e) in self.elements.iter().filter(|(_, e)| e.kind.dimension() == dimension) {
|
||||
for (i, local) in sides(e.kind).iter().enumerate() {
|
||||
let count = if local.len() == 6 || local.len() == 3 { 3 } else { 4 }.min(corners);
|
||||
let mut key = [u32::MAX; 4];
|
||||
for (k, n) in key.iter_mut().zip(local.iter().take(corners)) {
|
||||
for (k, n) in key.iter_mut().zip(local.iter().take(count)) {
|
||||
*k = e.nodes.get(*n).copied().unwrap_or(u32::MAX);
|
||||
}
|
||||
key.sort_unstable();
|
||||
keyed.push((key, crate::ElementFace { element: *label, face: i as u8 + 1 }));
|
||||
keyed.push((key, *label, i as u8 + 1));
|
||||
}
|
||||
}
|
||||
keyed.sort_unstable();
|
||||
let mut out: Vec<crate::ElementFace> = keyed
|
||||
let mut out: Vec<T> = keyed
|
||||
.chunk_by(|a, b| a.0 == b.0)
|
||||
.filter_map(|run| match run {
|
||||
[(_, face)] => Some(*face),
|
||||
[(_, element, side)] => Some(make(*element, *side)),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
@@ -356,6 +376,23 @@ mod tests {
|
||||
assert!(Model::default().exterior_faces().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exterior_edges_are_those_of_one_2d_element() {
|
||||
// Two quads sharing an edge and a triangle on the second's top edge.
|
||||
let mut m = Model::default();
|
||||
for (i, xy) in [[0., 0.], [1., 0.], [2., 0.], [0., 1.], [1., 1.], [2., 1.], [1.5, 2.]].iter().enumerate() {
|
||||
m.set_node(i as u32 + 1, [xy[0], xy[1], 0.0]).unwrap();
|
||||
}
|
||||
m.set_element(1, crate::ElementKind::Quad4, vec![1, 2, 5, 4]).unwrap();
|
||||
m.set_element(2, crate::ElementKind::Quad4, vec![2, 3, 6, 5]).unwrap();
|
||||
m.set_element(3, crate::ElementKind::Tri3, vec![5, 6, 7]).unwrap();
|
||||
let edges = m.exterior_edges();
|
||||
let of = |e: u32| edges.iter().filter(|f| f.element == e).map(|f| f.edge).collect::<Vec<_>>();
|
||||
assert_eq!(of(1), [1, 3, 4]);
|
||||
assert_eq!(of(2), [1, 2]);
|
||||
assert_eq!(of(3), [2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_model_holds_a_bounded_number_of_contacts() {
|
||||
let mut m = model();
|
||||
|
||||
Reference in new issue
Block a user