mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 10:35:58 +00:00
kernel: how a body's material regions meet, read from the record
Brep::region_contacts lists each pair of material regions that shares a face, an edge (used by faces of both) or a vertex, with the most it shares; pairs not listed are apart. Body::region_contacts reports it for a boolean's result (lump positions as in Body::lumps), and Body::parts cuts a body into groups of lumps (lumps is parts of one each). Exact facts from the record only: no geometry, no decision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt (cherry picked from commit 64a1d688e68e5f0c5b1f9e4fe0d2facfaa4744be)
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
//! of its faces, edges and vertices was in the whole. A lump of a body is the part of one of its
|
||||
//! material regions; material inside one of its voids (an island) is a lump of its own. Records
|
||||
//! of bodies apart from each other join into one by [`Brep::compound`].
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{Brep, EdgeUseRef, Invalid, Loop, OrientedFace, Region, Result, Shell, require};
|
||||
|
||||
/// A record cut from a larger one, and the source index of each of its items.
|
||||
@@ -14,6 +16,26 @@ pub struct Part {
|
||||
pub vertices: Vec<u32>,
|
||||
}
|
||||
|
||||
/// The most two material regions of a record share, least first: a point, a curve, an area.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Contact {
|
||||
/// A vertex and no edge or face: they touch at a point.
|
||||
Vertex,
|
||||
/// An edge and no face: they touch along a curve.
|
||||
Edge,
|
||||
/// A face: they meet over an area.
|
||||
Face,
|
||||
}
|
||||
|
||||
/// Two material regions that meet (positions in [`Brep::material_regions`], the lower first),
|
||||
/// and the most they share.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct RegionContact {
|
||||
pub regions: [usize; 2],
|
||||
pub contact: Contact,
|
||||
}
|
||||
|
||||
/// First-appearance numbering of a subset of `n` source items.
|
||||
struct Renumber {
|
||||
new: Vec<Option<u32>>,
|
||||
@@ -45,6 +67,79 @@ impl Brep {
|
||||
self.regions.iter().enumerate().skip(1).filter(|(_, r)| !r.void).map(|(i, _)| i).collect()
|
||||
}
|
||||
|
||||
/// How the material regions meet, read from the record: each pair of regions that shares a
|
||||
/// face, an edge or a vertex, with the most they share. `regions` index
|
||||
/// [`Brep::material_regions`] (so a body's lumps); pairs not listed are apart. Regions share
|
||||
/// an edge where it is used by faces of both (more than two faces), a vertex where edges or
|
||||
/// vertex loops of both end at it. Sorted by pair.
|
||||
pub fn region_contacts(&self) -> Result<Vec<RegionContact>> {
|
||||
let material = self.material_regions();
|
||||
// The material regions (positions in `material`) each face, edge and vertex belongs to.
|
||||
let mut on_face: Vec<Vec<usize>> = vec![Vec::new(); self.faces.len()];
|
||||
let mut on_edge: Vec<Vec<usize>> = vec![Vec::new(); self.edges.len()];
|
||||
let mut on_vertex: Vec<Vec<usize>> = vec![Vec::new(); self.vertices.len()];
|
||||
let mark = |list: &mut Vec<Vec<usize>>, i: u32, k: usize| -> Result<()> {
|
||||
let owners = list.get_mut(i as usize).ok_or(Invalid("contact reference"))?;
|
||||
if owners.last() != Some(&k) {
|
||||
owners.push(k);
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
for (k, &r) in material.iter().enumerate() {
|
||||
let region = self.regions.get(r).ok_or(Invalid("region reference"))?;
|
||||
for &s in ®ion.shells {
|
||||
match self.shells.get(s as usize).ok_or(Invalid("shell reference"))? {
|
||||
Shell::Faces { faces, wires } => {
|
||||
for f in faces {
|
||||
mark(&mut on_face, f.face, k)?;
|
||||
for l in &self.faces.get(f.face as usize).ok_or(Invalid("face reference"))?.loops {
|
||||
match l {
|
||||
Loop::Edges { edges } => {
|
||||
for u in edges {
|
||||
mark(&mut on_edge, u.edge, k)?;
|
||||
}
|
||||
}
|
||||
Loop::Vertex { vertex } => mark(&mut on_vertex, *vertex, k)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
for &e in wires {
|
||||
mark(&mut on_edge, e, k)?;
|
||||
}
|
||||
}
|
||||
Shell::Wire { edges } => {
|
||||
for &e in edges {
|
||||
mark(&mut on_edge, e, k)?;
|
||||
}
|
||||
}
|
||||
Shell::Vertex { vertex } => mark(&mut on_vertex, *vertex, k)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
for (e, owners) in on_edge.iter().enumerate() {
|
||||
for &v in &self.edges.get(e).ok_or(Invalid("edge reference"))?.vertices {
|
||||
for &k in owners {
|
||||
let list = on_vertex.get_mut(v as usize).ok_or(Invalid("contact reference"))?;
|
||||
if !list.contains(&k) {
|
||||
list.push(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut pairs = std::collections::BTreeMap::<[usize; 2], Contact>::new();
|
||||
for (owners, contact) in [(&on_face, Contact::Face), (&on_edge, Contact::Edge), (&on_vertex, Contact::Vertex)] {
|
||||
for list in owners.iter().filter(|l| l.len() > 1) {
|
||||
for (i, &a) in list.iter().enumerate() {
|
||||
for &b in list.iter().skip(i + 1) {
|
||||
let most = pairs.entry([a.min(b), a.max(b)]).or_insert(contact);
|
||||
*most = (*most).max(contact);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(pairs.into_iter().map(|(regions, contact)| RegionContact { regions, contact }).collect())
|
||||
}
|
||||
|
||||
/// The part made of the material regions `regions` (indices into `self.regions`, bounded,
|
||||
/// not voids): each with its outer shell, its open and wire shells, and a void for each
|
||||
/// closed inner shell. An island inside one of those voids is in the part, inside it, when
|
||||
|
||||
@@ -1209,11 +1209,33 @@ impl Body {
|
||||
if self.is_mesh() || regions.len() < 2 {
|
||||
return Ok(vec![self.clone()]);
|
||||
}
|
||||
self.parts(&(0..regions.len()).map(|k| vec![k]).collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
/// How the body's lumps meet (positions as in [`Body::lumps`]): each pair that shares a face,
|
||||
/// an edge or a vertex, with the most it shares, read from the record. Pairs not listed are
|
||||
/// apart. Empty for a mesh body or a body in one piece.
|
||||
pub fn region_contacts(&self) -> Result<Vec<brep::part::RegionContact>> {
|
||||
if self.is_mesh() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
self.record.region_contacts().map_err(|e| KernelError::Invalid(format!("region contacts: {e}")))
|
||||
}
|
||||
|
||||
/// The body cut into parts: one per group of its lumps (positions as in [`Body::lumps`]),
|
||||
/// each the lumps listed with the voids inside them. The parts carry their history over
|
||||
/// `[self]`, as lumps do.
|
||||
pub fn parts(&self, groups: &[Vec<usize>]) -> Result<Vec<Body>> {
|
||||
let regions = self.record.material_regions();
|
||||
let counts = [(self.face_count(), self.edge_count())];
|
||||
regions
|
||||
.into_iter()
|
||||
.map(|r| {
|
||||
let part = self.record.part(&[r]).map_err(|e| KernelError::Failed(format!("lump: {e}")))?;
|
||||
groups
|
||||
.iter()
|
||||
.map(|group| {
|
||||
let picked = group
|
||||
.iter()
|
||||
.map(|&k| regions.get(k).copied().ok_or_else(|| KernelError::Invalid(format!("no lump {k}"))))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let part = self.record.part(&picked).map_err(|e| KernelError::Failed(format!("lump: {e}")))?;
|
||||
let sources = self
|
||||
.face_sources
|
||||
.as_ref()
|
||||
|
||||
Reference in New Issue
Block a user