mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 18:45:55 +00:00
kernel: one canonicaliser; one tensor sheet
sew::assemble and surfaces::Parts::canonical each lay out their record as its parts stand (regions, shells, faces, edges with no uses, vertices) and end in Brep::canonical; their own ordering walks are deleted. spline_conv::tensor_face (no callers left) is deleted in favour of surfaces::tensor_sheet, and fill's copy of the spline surface record in favour of surfaces::spline_surface. (not type-checked: the kernel stops at name resolution in step_*, build, sweep_path and surface_ruled) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
This commit is contained in:
@@ -383,7 +383,7 @@ pub(crate) fn patch_loop(body: &Body, at: &[Vec3], crown: f64) -> Result<Body> {
|
||||
Err(e) => return Err(spline_error(e)),
|
||||
};
|
||||
let projection = Projection { origin: fill.origin, axes: fill.axes };
|
||||
(surface_record(&fill.surface)?, fill.counter_clockwise, Some(projection))
|
||||
(crate::surfaces::spline_surface(&fill.surface), fill.counter_clockwise, Some(projection))
|
||||
}
|
||||
};
|
||||
let pcurves = match &pcurve {
|
||||
@@ -604,21 +604,6 @@ impl Projection {
|
||||
}
|
||||
}
|
||||
|
||||
/// A geom NURBS surface as a record: a plain spline where every weight is one.
|
||||
fn surface_record(s: &solidcraft_geom::bspline::NurbsSurface) -> Result<b::Surface> {
|
||||
let [ku, kv] = s.knots().clone();
|
||||
let rows = s.net().into_u_rows();
|
||||
if rows.iter().flatten().all(|h| h.w == 1.0) {
|
||||
Ok(b::Surface::Spline { u_knots: ku, v_knots: kv, points: rows.iter().map(|r| r.iter().map(|h| [h.x, h.y, h.z]).collect()).collect() })
|
||||
} else {
|
||||
Ok(b::Surface::RationalSpline {
|
||||
u_knots: ku,
|
||||
v_knots: kv,
|
||||
homogeneous: rows.iter().map(|r| r.iter().map(|h| [h.x, h.y, h.z, h.w]).collect()).collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The one-face sheet on `surface` bounded by the loop `chain` of `record`'s own edges, run
|
||||
/// counter-clockwise about the surface's natural normal (`ccw`: the chain already does), each
|
||||
/// edge's pcurve the exact projection where the surface is a height field. Its history is over
|
||||
@@ -770,7 +755,7 @@ fn quilt_body(body: &Body, record: &b::Brep, chain: &[(usize, bool)], fill: &Iso
|
||||
}
|
||||
}
|
||||
}
|
||||
faces.push(PartFace { surface: surface_record(&patch.surface)?, forward: true, loops: vec![uses] });
|
||||
faces.push(PartFace { surface: crate::surfaces::spline_surface(&patch.surface), forward: true, loops: vec![uses] });
|
||||
}
|
||||
let made = assemble(Parts { vertices, edges, faces, tolerance: tol }, false)?;
|
||||
let smlib = solidcraft_smlib::Brep::from_record(&made.brep, true).map_err(|e| KernelError::Failed(format!("patch: SMLib: {e}")))?;
|
||||
|
||||
+43
-93
@@ -1231,115 +1231,65 @@ pub(crate) fn assemble(parts: Parts, solid: bool) -> Result<Assembled> {
|
||||
let o = around.ok_or_else(|| fail("a cavity lies in no solid".into()))?;
|
||||
cavities.get_mut(o).ok_or_else(|| fail("cavity".into()))?.push(k);
|
||||
}
|
||||
// Regions, and the shells numbered by first appearance in them.
|
||||
let mut shell_group: Vec<usize> = Vec::new();
|
||||
let mut shell_of: HashMap<usize, u32> = HashMap::new();
|
||||
let mut number = |g: usize, shell_group: &mut Vec<usize>| -> u32 {
|
||||
*shell_of.entry(g).or_insert_with(|| {
|
||||
shell_group.push(g);
|
||||
(shell_group.len() - 1) as u32
|
||||
})
|
||||
};
|
||||
// The record as its parts stand (shell k is group k, face i part face i, edges and vertices
|
||||
// as given), then put in canonical order.
|
||||
let index = |i: usize| u32::try_from(i).map_err(|_| fail("index".into()));
|
||||
let open: Vec<usize> = (0..groups.len()).filter(|k| !closed.get(*k).copied().unwrap_or(false)).collect();
|
||||
let infinite: Vec<u32> = outers.iter().chain(&open).map(|g| number(*g, &mut shell_group)).collect();
|
||||
let mut regions = vec![b::Region { shells: infinite, void: false }];
|
||||
let mut regions = vec![b::Region { shells: outers.iter().chain(&open).map(|g| index(*g)).collect::<Result<_>>()?, void: false }];
|
||||
for &o in &outers {
|
||||
let mut shells = vec![number(o, &mut shell_group)];
|
||||
let mut shells = vec![index(o)?];
|
||||
for &c in cavities.get(o).into_iter().flatten() {
|
||||
shells.push(number(c, &mut shell_group));
|
||||
shells.push(index(c)?);
|
||||
}
|
||||
regions.push(b::Region { shells, void: false });
|
||||
}
|
||||
for &o in &outers {
|
||||
for &c in cavities.get(o).into_iter().flatten() {
|
||||
regions.push(b::Region { shells: vec![number(c, &mut shell_group)], void: true });
|
||||
regions.push(b::Region { shells: vec![index(c)?], void: true });
|
||||
}
|
||||
}
|
||||
// Faces in first appearance walking shells; edges walking faces → loops → uses (backwards on
|
||||
// a face whose side in its shell is reversed), with their uses; vertices walking edges.
|
||||
let index = |i: usize| u32::try_from(i).map_err(|_| fail("index".into()));
|
||||
let mut face_new = vec![usize::MAX; nf];
|
||||
let mut face_order = Vec::with_capacity(nf);
|
||||
for g in &shell_group {
|
||||
for &f in groups.get(*g).into_iter().flatten() {
|
||||
if face_new.get(f) == Some(&usize::MAX) {
|
||||
if let Some(slot) = face_new.get_mut(f) {
|
||||
*slot = face_order.len();
|
||||
}
|
||||
face_order.push(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut edge_new: Vec<Option<usize>> = vec![None; edges.len()];
|
||||
let mut edge_order = Vec::with_capacity(edges.len());
|
||||
let mut uses: Vec<Vec<b::EdgeUseRef>> = vec![Vec::new(); edges.len()];
|
||||
for (fnew, &f) in face_order.iter().enumerate() {
|
||||
let forward = faces.get(f).is_some_and(|x| x.forward);
|
||||
for (li, l) in loops.get(f).into_iter().flatten().enumerate() {
|
||||
let n = l.len();
|
||||
for k in 0..n {
|
||||
let p = if forward { k } else { n - 1 - k };
|
||||
let &(e, fwd, _) = l.get(p).ok_or_else(|| fail("edge reference".into()))?;
|
||||
let slot = edge_new.get_mut(e as usize).ok_or_else(|| fail("edge reference".into()))?;
|
||||
if slot.is_none() {
|
||||
*slot = Some(edge_order.len());
|
||||
edge_order.push(e as usize);
|
||||
}
|
||||
let at = b::EdgeUseRef { face: index(fnew)?, r#loop: index(li)?, position: index(p)?, upper: !fwd };
|
||||
uses.get_mut(e as usize).ok_or_else(|| fail("edge reference".into()))?.push(at);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut vertex_new: Vec<Option<u32>> = vec![None; vertices.len()];
|
||||
let mut out_vertices = Vec::new();
|
||||
let mut out_edges = Vec::with_capacity(edge_order.len());
|
||||
for &e in &edge_order {
|
||||
let edge = edges.get(e).ok_or_else(|| fail("edge reference".into()))?;
|
||||
let mut ends = [0u32; 2];
|
||||
for (end, v) in ends.iter_mut().zip(edge.vertices) {
|
||||
let slot = vertex_new.get_mut(v as usize).ok_or_else(|| fail("vertex reference".into()))?;
|
||||
*end = match slot {
|
||||
Some(n) => *n,
|
||||
None => {
|
||||
let n = index(out_vertices.len())?;
|
||||
let point = *vertices.get(v as usize).ok_or_else(|| fail("vertex reference".into()))?;
|
||||
out_vertices.push(b::Vertex { point, tolerance: tol });
|
||||
*slot = Some(n);
|
||||
n
|
||||
}
|
||||
};
|
||||
}
|
||||
let edge_uses = uses.get(e).cloned().unwrap_or_default();
|
||||
out_edges.push(b::Edge { curve: edge.curve.clone(), interval: edge.interval, vertices: ends, tolerance: tol, uses: edge_uses });
|
||||
}
|
||||
let emap = |e: u32| edge_new.get(e as usize).copied().flatten().ok_or_else(|| fail("edge reference".into())).and_then(index);
|
||||
let mut out_faces = Vec::with_capacity(nf);
|
||||
for &f in &face_order {
|
||||
let (Some(face), Some(ls), Some(pc), Some(domain)) = (faces.get(f), loops.get(f), pcs.get(f), domains.get(f)) else {
|
||||
return Err(fail("face reference".into()));
|
||||
};
|
||||
let shells = groups
|
||||
.iter()
|
||||
.map(|g| {
|
||||
let faces = g
|
||||
.iter()
|
||||
.map(|&f| Ok(b::OrientedFace { face: index(f)?, forward: faces.get(f).is_some_and(|x| x.forward) }))
|
||||
.collect::<Result<_>>()?;
|
||||
Ok(b::Shell::Faces { faces, wires: Vec::new() })
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
let mut record_faces = Vec::with_capacity(nf);
|
||||
for (f, ((face, ls), (pc, domain))) in faces.iter().zip(&loops).zip(pcs.iter().zip(&domains)).enumerate() {
|
||||
let mut record_loops = Vec::with_capacity(ls.len());
|
||||
for (l, c) in ls.iter().zip(pc) {
|
||||
let mut oriented = Vec::with_capacity(l.len());
|
||||
for (&(e, forward, _), pcurve) in l.iter().zip(c) {
|
||||
oriented.push(b::OrientedEdge { edge: emap(e)?, forward, pcurve: pcurve.clone() });
|
||||
}
|
||||
let oriented = l.iter().zip(c).map(|(&(edge, forward, _), pcurve)| b::OrientedEdge { edge, forward, pcurve: pcurve.clone() }).collect();
|
||||
record_loops.push(b::Loop::Edges { edges: oriented });
|
||||
}
|
||||
out_faces.push(b::Face { surface: face.surface.clone(), domain: *domain, loops: record_loops });
|
||||
}
|
||||
let mut shells = Vec::with_capacity(shell_group.len());
|
||||
for g in &shell_group {
|
||||
let mut oriented = Vec::new();
|
||||
for &f in groups.get(*g).into_iter().flatten() {
|
||||
let face = face_new.get(f).copied().ok_or_else(|| fail("face reference".into())).and_then(index)?;
|
||||
oriented.push(b::OrientedFace { face, forward: faces.get(f).is_some_and(|x| x.forward) });
|
||||
if record_loops.is_empty() {
|
||||
return Err(fail(format!("face {f} has no loop")));
|
||||
}
|
||||
shells.push(b::Shell::Faces { faces: oriented, wires: Vec::new() });
|
||||
record_faces.push(b::Face { surface: face.surface.clone(), domain: *domain, loops: record_loops });
|
||||
}
|
||||
let brep = b::Brep { version: b::VERSION, tolerance: tol, regions, shells, faces: out_faces, edges: out_edges, vertices: out_vertices };
|
||||
brep.validate().map_err(|e| KernelError::Failed(format!("sew: the record is invalid: {e}")))?;
|
||||
Ok(Assembled { brep, faces: face_new, edges: edge_new })
|
||||
let record_edges = edges
|
||||
.iter()
|
||||
.map(|e| b::Edge { curve: e.curve.clone(), interval: e.interval, vertices: e.vertices, tolerance: tol, uses: Vec::new() })
|
||||
.collect();
|
||||
let record_vertices = vertices.iter().map(|point| b::Vertex { point: *point, tolerance: tol }).collect();
|
||||
let loose = b::Brep { version: b::VERSION, tolerance: tol, regions, shells, faces: record_faces, edges: record_edges, vertices: record_vertices };
|
||||
let part = loose.canonical().map_err(|e| KernelError::Failed(format!("sew: the record is invalid: {e}")))?;
|
||||
let mut face_new = vec![usize::MAX; nf];
|
||||
for (c, &f) in part.faces.iter().enumerate() {
|
||||
if let Some(slot) = face_new.get_mut(f as usize) {
|
||||
*slot = c;
|
||||
}
|
||||
}
|
||||
let mut edge_new = vec![None; edges.len()];
|
||||
for (c, &e) in part.edges.iter().enumerate() {
|
||||
if let Some(slot) = edge_new.get_mut(e as usize) {
|
||||
*slot = Some(c);
|
||||
}
|
||||
}
|
||||
Ok(Assembled { brep: part.brep, faces: face_new, edges: edge_new })
|
||||
}
|
||||
|
||||
/// A face's parameter box: around its pcurves' control points (a B-spline lies in their hull),
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
//! geom's B-spline curves and surfaces as schema 4 records, weights kept (homogeneous control
|
||||
//! points carried over as they are), and a whole tensor surface as a one-face sheet.
|
||||
//! points carried over as they are). A whole tensor surface as a one-face sheet is
|
||||
//! `surfaces::tensor_sheet`.
|
||||
|
||||
use solidcraft_geom::Vec3;
|
||||
use solidcraft_geom::brep::{Curve, Curve2, Surface};
|
||||
use solidcraft_geom::brep::{Curve, Surface};
|
||||
use solidcraft_geom::bspline::{HPoint, NurbsSurface};
|
||||
|
||||
use crate::assemble::{AFace, AUse, Assembly};
|
||||
use crate::body::Body;
|
||||
use crate::{KernelError, Result};
|
||||
|
||||
fn fail(m: impl std::fmt::Display) -> KernelError {
|
||||
KernelError::Failed(format!("spline geometry: {m}"))
|
||||
}
|
||||
|
||||
/// A homogeneous control point as the record's `[x*w, y*w, z*w, w]`.
|
||||
pub(crate) fn h4(h: HPoint) -> [f64; 4] {
|
||||
[h.x, h.y, h.z, h.w]
|
||||
@@ -34,48 +26,3 @@ pub(crate) fn surface(s: &NurbsSurface) -> Surface {
|
||||
let [ku, kv] = s.knots();
|
||||
nurbs_surface(ku, kv, (0..nu).map(|i| (0..nv).map(|j| h4(s.point(i, j))).collect()).collect())
|
||||
}
|
||||
|
||||
/// The active interval `[knots[degree], knots[count]]` of a direction with `count` points.
|
||||
fn active(knots: &[f64], count: usize) -> Result<[f64; 2]> {
|
||||
let degree = knots.len().checked_sub(count + 1).ok_or_else(|| fail("knots and points disagree"))?;
|
||||
match (knots.get(degree), knots.get(count)) {
|
||||
(Some(a), Some(b)) if a < b => Ok([*a, *b]),
|
||||
_ => Err(fail("an empty knot interval")),
|
||||
}
|
||||
}
|
||||
|
||||
/// One face of a whole tensor surface as a sheet: its four boundary curves (the net's first and
|
||||
/// last rows and columns) as edges sharing the corners, each with its exact pcurve (an edge's
|
||||
/// parameter is the surface's along it). This is what a patch built as a single control net
|
||||
/// (`bridge`, `boundary`, the T-spline sheet) hands to the B-rep.
|
||||
pub(crate) fn tensor_face(s: &NurbsSurface) -> Result<Body> {
|
||||
let [nu, nv] = s.count();
|
||||
if nu < 2 || nv < 2 {
|
||||
return Err(fail("a face needs at least 2 x 2 control points"));
|
||||
}
|
||||
let [ku, kv] = s.knots();
|
||||
let ([u0, u1], [v0, v1]) = (active(ku, nu)?, active(kv, nv)?);
|
||||
let corners = [s.point(0, 0), s.point(nu - 1, 0), s.point(nu - 1, nv - 1), s.point(0, nv - 1)];
|
||||
let corners: Vec<Vec3> = corners.iter().map(|c| c.project().ok_or_else(|| fail("a corner has no position"))).collect::<Result<_>>()?;
|
||||
let diag = corners.iter().flat_map(|a| corners.iter().map(move |b| a.dist(*b))).fold(0.0, f64::max);
|
||||
let mut asm = Assembly::sheet((diag * 1e-7).max(1e-9));
|
||||
let v: Vec<u32> = corners.iter().map(|p| asm.vertex(*p)).collect::<Result<_>>()?;
|
||||
let (Some(&a), Some(&b), Some(&c), Some(&d)) = (v.first(), v.get(1), v.get(2), v.get(3)) else { return Err(fail("corners")) };
|
||||
let row = |j: usize| -> Vec<HPoint> { (0..nu).map(|i| s.point(i, j)).collect() };
|
||||
let column = |i: usize| -> Vec<HPoint> { (0..nv).map(|j| s.point(i, j)).collect() };
|
||||
let line = |t: [f64; 2], p: [f64; 2], q: [f64; 2]| Curve2::Spline { knots: vec![t[0], t[0], t[1], t[1]], points: vec![p, q] };
|
||||
// Bottom (v0) and top (v1) run along u, right (u1) and left (u0) along v, each from its
|
||||
// lower corner; the loop runs counter-clockwise in (u, v).
|
||||
let bottom = asm.edge(nurbs_curve(ku, &row(0)), [u0, u1], a, b)?;
|
||||
let right = asm.edge(nurbs_curve(kv, &column(nu - 1)), [v0, v1], b, c)?;
|
||||
let top = asm.edge(nurbs_curve(ku, &row(nv - 1)), [u0, u1], d, c)?;
|
||||
let left = asm.edge(nurbs_curve(kv, &column(0)), [v0, v1], a, d)?;
|
||||
let loop_ = vec![
|
||||
AUse { edge: bottom, forward: true, given: Some(line([u0, u1], [u0, v0], [u1, v0])) },
|
||||
AUse { edge: right, forward: true, given: Some(line([v0, v1], [u1, v0], [u1, v1])) },
|
||||
AUse { edge: top, forward: false, given: Some(line([u0, u1], [u0, v1], [u1, v1])) },
|
||||
AUse { edge: left, forward: false, given: Some(line([v0, v1], [u0, v0], [u0, v1])) },
|
||||
];
|
||||
asm.face(AFace { surface: surface(s), natural_out: true, loops: vec![loop_], shell: 0, domain: Some([[u0, u1], [v0, v1]]) });
|
||||
Body::from_record(asm.finish()?.record)
|
||||
}
|
||||
|
||||
@@ -194,88 +194,18 @@ impl Parts {
|
||||
return Err(invalid("no faces"));
|
||||
}
|
||||
let shells = self.shells()?;
|
||||
let order: Vec<usize> = shells.iter().flatten().copied().collect();
|
||||
let face = |fi: usize| self.faces.get(fi).ok_or_else(|| invalid("a face reference"));
|
||||
let mut edge_new: Vec<Option<u32>> = vec![None; self.edges.len()];
|
||||
let mut edges_old: Vec<usize> = Vec::new();
|
||||
let mut uses: Vec<Vec<b::EdgeUseRef>> = Vec::new();
|
||||
for (nf, &fi) in order.iter().enumerate() {
|
||||
let (f, forward) = face(fi)?;
|
||||
for (li, l) in f.loops.iter().enumerate() {
|
||||
let b::Loop::Edges { edges } = l else { continue };
|
||||
let k = edges.len();
|
||||
for step in 0..k {
|
||||
let p = if *forward { step } else { k - 1 - step };
|
||||
let Some(u) = edges.get(p) else { continue };
|
||||
let slot = edge_new.get_mut(u.edge as usize).ok_or_else(|| invalid("an edge reference"))?;
|
||||
let ne = match *slot {
|
||||
Some(ne) => ne,
|
||||
None => {
|
||||
let ne = index(edges_old.len())?;
|
||||
*slot = Some(ne);
|
||||
edges_old.push(u.edge as usize);
|
||||
uses.push(Vec::new());
|
||||
ne
|
||||
}
|
||||
};
|
||||
let at = b::EdgeUseRef { face: index(nf)?, r#loop: index(li)?, position: index(p)?, upper: !u.forward };
|
||||
if let Some(list) = uses.get_mut(ne as usize) {
|
||||
list.push(at);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut vertex_new: Vec<Option<u32>> = vec![None; self.vertices.len()];
|
||||
let mut vertices: Vec<b::Vertex> = Vec::new();
|
||||
let mut visit = |v: u32, vertices: &mut Vec<b::Vertex>| -> Result<u32> {
|
||||
let slot = vertex_new.get_mut(v as usize).ok_or_else(|| invalid("a vertex reference"))?;
|
||||
if let Some(nv) = *slot {
|
||||
return Ok(nv);
|
||||
}
|
||||
let nv = index(vertices.len())?;
|
||||
*slot = Some(nv);
|
||||
vertices.push(self.vertices.get(v as usize).cloned().ok_or_else(|| invalid("a vertex reference"))?);
|
||||
Ok(nv)
|
||||
};
|
||||
let mut edges = Vec::with_capacity(edges_old.len());
|
||||
for (old, uses) in edges_old.iter().zip(uses) {
|
||||
let e = self.edges.get(*old).ok_or_else(|| invalid("an edge reference"))?;
|
||||
let ends = [visit(e.vertices[0], &mut vertices)?, visit(e.vertices[1], &mut vertices)?];
|
||||
edges.push(b::Edge { vertices: ends, uses, ..e.clone() });
|
||||
}
|
||||
let mut faces = Vec::with_capacity(order.len());
|
||||
for &fi in &order {
|
||||
let (f, _) = face(fi)?;
|
||||
let mut loops = Vec::with_capacity(f.loops.len());
|
||||
for l in &f.loops {
|
||||
loops.push(match l {
|
||||
b::Loop::Edges { edges } => b::Loop::Edges {
|
||||
edges: edges
|
||||
.iter()
|
||||
.map(|u| {
|
||||
let edge = edge_new.get(u.edge as usize).copied().flatten().ok_or_else(|| invalid("an edge reference"))?;
|
||||
Ok(b::OrientedEdge { edge, ..u.clone() })
|
||||
})
|
||||
.collect::<Result<_>>()?,
|
||||
},
|
||||
b::Loop::Vertex { vertex } => b::Loop::Vertex { vertex: visit(*vertex, &mut vertices)? },
|
||||
});
|
||||
}
|
||||
faces.push(b::Face { loops, ..f.clone() });
|
||||
}
|
||||
let mut numbered = 0usize;
|
||||
let mut shell_records = Vec::with_capacity(shells.len());
|
||||
// The record as the parts stand (shell k as `shells` groups it), then in canonical order.
|
||||
let mut regions = vec![b::Region { shells: Vec::new(), void: false }];
|
||||
let mut shell_records = Vec::with_capacity(shells.len());
|
||||
for (s, members) in shells.iter().enumerate() {
|
||||
let mut count: std::collections::HashMap<u32, usize> = std::collections::HashMap::new();
|
||||
let mut list = Vec::with_capacity(members.len());
|
||||
for &fi in members {
|
||||
let (f, forward) = face(fi)?;
|
||||
let (f, forward) = self.faces.get(fi).ok_or_else(|| invalid("a face reference"))?;
|
||||
for e in Parts::uses_of(f) {
|
||||
*count.entry(e).or_default() += 1;
|
||||
}
|
||||
list.push(b::OrientedFace { face: index(numbered)?, forward: *forward });
|
||||
numbered += 1;
|
||||
list.push(b::OrientedFace { face: index(fi)?, forward: *forward });
|
||||
}
|
||||
shell_records.push(b::Shell::Faces { faces: list, wires: Vec::new() });
|
||||
let s = index(s)?;
|
||||
@@ -286,9 +216,21 @@ impl Parts {
|
||||
regions.push(b::Region { shells: vec![s], void: false });
|
||||
}
|
||||
}
|
||||
let brep = Brep { version: b::VERSION, tolerance: self.tolerance, regions, shells: shell_records, faces, edges, vertices };
|
||||
brep.validate().map_err(|e| failed(format!("faces into a record: {e}")))?;
|
||||
Ok(Canonical { brep, faces: order, edges: edges_old })
|
||||
let loose = Brep {
|
||||
version: b::VERSION,
|
||||
tolerance: self.tolerance,
|
||||
regions,
|
||||
shells: shell_records,
|
||||
faces: self.faces.iter().map(|(f, _)| f.clone()).collect(),
|
||||
edges: self.edges.iter().map(|e| b::Edge { uses: Vec::new(), ..e.clone() }).collect(),
|
||||
vertices: self.vertices.clone(),
|
||||
};
|
||||
let part = loose.canonical().map_err(|e| failed(format!("faces into a record: {e}")))?;
|
||||
Ok(Canonical {
|
||||
brep: part.brep,
|
||||
faces: part.faces.iter().map(|f| *f as usize).collect(),
|
||||
edges: part.edges.iter().map(|e| *e as usize).collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user