mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-04 19:15:57 +00:00
Fill: a patch's edges are the body's own edges again, circles and ellipses included
A record edge on a circle or ellipse carries the NURBS form SMLib holds it as,
its interval on that form. The patch built its loop edges from the form alone
(sew::PartEdge had no place for it), so a fill's rim came back from SMLib as a
plain B-spline rather than the body's ellipse. PartEdge now carries the form;
assemble keeps a given form and labels the edge with what it is.
Test helpers that sampled an edge on `curve` with the form's interval picked
points off the edge ("no edge at …"); they sample `parameters()`. The fill's
own-edge check compares the curve's kind and the exact form it is parameterised
on: SMLib renormalises a conic's frame axes in the last bit on a round trip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -214,7 +214,7 @@ fn too_large_or_unsupported_is_refused() {
|
||||
.record()
|
||||
.edges
|
||||
.iter()
|
||||
.filter_map(|e| solidcraft_geom::brep::eval::CurveEval::new(&e.curve).ok()?.point(0.5 * (e.interval[0] + e.interval[1])))
|
||||
.filter_map(|e| solidcraft_geom::brep::eval::CurveEval::new(e.parameters()).ok()?.point(0.5 * (e.interval[0] + e.interval[1])))
|
||||
.find(|m| (m.z - 10.0).abs() < 1e-6)
|
||||
.unwrap();
|
||||
match chamfer_sides(&t, &[rim], 1.0, ChamferSide::Distance(2.0), false) {
|
||||
|
||||
@@ -1379,6 +1379,7 @@ mod tests {
|
||||
let vertices: Vec<[f64; 3]> = corners.iter().map(|&(i, j)| net[j * cu + i]).collect();
|
||||
let edges = (0..4)
|
||||
.map(|k| PartEdge {
|
||||
nurbs: None,
|
||||
curve: b::Curve::Line { start: vertices[k], end: vertices[(k + 1) % 4] },
|
||||
interval: [0.0, 1.0],
|
||||
vertices: [k as u32, ((k + 1) % 4) as u32],
|
||||
|
||||
+14
-10
@@ -709,7 +709,7 @@ fn patch_body(body: &Body, record: &b::Brep, chain: &[(usize, bool)], surface: b
|
||||
})?),
|
||||
Pcurves::Computed => None,
|
||||
};
|
||||
edges.push(PartEdge { curve: edge.parameters().clone(), interval: edge.interval, vertices: ends });
|
||||
edges.push(PartEdge { curve: edge.curve.clone(), nurbs: edge.nurbs.clone(), interval: edge.interval, vertices: ends });
|
||||
uses.push(PartUse { edge: k as u32, forward, given });
|
||||
}
|
||||
if !ccw {
|
||||
@@ -797,9 +797,9 @@ fn quilt_body(body: &Body, record: &b::Brep, chain: &[(usize, bool)], fill: &Iso
|
||||
};
|
||||
}
|
||||
let id = index(edges.len())?;
|
||||
// The curve the interval and the pcurve's parameter are on: a conic's
|
||||
// NURBS form.
|
||||
edges.push(PartEdge { curve: edge.parameters().clone(), interval: [lo, hi], vertices: ends });
|
||||
// The edge itself: the interval and the pcurve's parameter are on its
|
||||
// NURBS form (a conic's), which it keeps.
|
||||
edges.push(PartEdge { curve: edge.curve.clone(), nurbs: edge.nurbs.clone(), interval: [lo, hi], vertices: ends });
|
||||
origin.push(Some(e));
|
||||
piece_of.insert(key, id);
|
||||
id
|
||||
@@ -827,7 +827,7 @@ fn quilt_body(body: &Body, record: &b::Brep, chain: &[(usize, bool)], fill: &Iso
|
||||
b::Curve::Spline { knots: curve.knots.clone(), points: curve.points.iter().map(|p| [p.x, p.y, p.z]).collect() }
|
||||
}
|
||||
};
|
||||
edges.push(PartEdge { curve: spline, interval: [0.0, 1.0], vertices: [start, end] });
|
||||
edges.push(PartEdge { curve: spline, nurbs: None, interval: [0.0, 1.0], vertices: [start, end] });
|
||||
origin.push(None);
|
||||
spoke_of.insert(spoke, id);
|
||||
id
|
||||
@@ -976,11 +976,14 @@ mod tests {
|
||||
worst
|
||||
}
|
||||
|
||||
/// Whether every edge of `made` is one of `of`'s own (the same curve record on the same
|
||||
/// interval).
|
||||
/// Whether every edge of `made` is one of `of`'s own: the same kind of curve, on the same
|
||||
/// curve record it is parameterised on (its NURBS form where it has one), on the same
|
||||
/// interval. (A circle's or ellipse's frame is SMLib's again after a round trip, its axes
|
||||
/// renormalised to the last bit.)
|
||||
fn own_edges(made: &Body, of: &Body) -> bool {
|
||||
let own = record(of).edges;
|
||||
record(made).edges.iter().all(|e| own.iter().any(|o| o.curve == e.curve && o.interval == e.interval))
|
||||
let kind = |e: &b::Edge| std::mem::discriminant(&e.curve);
|
||||
record(made).edges.iter().all(|e| own.iter().any(|o| kind(o) == kind(e) && o.parameters() == e.parameters() && o.interval == e.interval))
|
||||
}
|
||||
|
||||
/// The fill's mesh: every normal finite, and no triangle folded over (all face the same way
|
||||
@@ -1109,9 +1112,10 @@ mod tests {
|
||||
let mut edges = Vec::new();
|
||||
for k in 0..n {
|
||||
let c = &top[k];
|
||||
edges.push(PartEdge { curve: c.clone(), interval: interval(c), vertices: [k as u32, ((k + 1) % n) as u32] });
|
||||
edges.push(PartEdge { curve: moved(c), interval: interval(c), vertices: [(n + k) as u32, (n + (k + 1) % n) as u32] });
|
||||
edges.push(PartEdge { nurbs: None, curve: c.clone(), interval: interval(c), vertices: [k as u32, ((k + 1) % n) as u32] });
|
||||
edges.push(PartEdge { nurbs: None, curve: moved(c), interval: interval(c), vertices: [(n + k) as u32, (n + (k + 1) % n) as u32] });
|
||||
edges.push(PartEdge {
|
||||
nurbs: None,
|
||||
curve: b::Curve::Line { start: arr(corners[k]), end: arr(corners[k] + drop) },
|
||||
interval: [0.0, 1.0],
|
||||
vertices: [k as u32, (n + k) as u32],
|
||||
|
||||
@@ -108,7 +108,7 @@ fn rim_points(body: &Body, per: usize) -> Vec<Vec3> {
|
||||
let r = body.record();
|
||||
let mut out = Vec::new();
|
||||
for e in r.edges.iter().filter(|e| e.uses.len() == 1) {
|
||||
let c = CurveEval::new(&e.curve).unwrap();
|
||||
let c = CurveEval::new(e.parameters()).unwrap();
|
||||
let [t0, t1] = e.interval;
|
||||
for k in 0..per {
|
||||
out.push(c.point(t0 + (t1 - t0) * (k as f64 + 0.5) / per as f64).unwrap());
|
||||
@@ -238,7 +238,7 @@ fn free_edge_points(body: &Body) -> Vec<Vec3> {
|
||||
.edges
|
||||
.iter()
|
||||
.filter(|e| e.uses.len() == 1)
|
||||
.map(|e| CurveEval::new(&e.curve).unwrap().point(0.5 * (e.interval[0] + e.interval[1])).unwrap())
|
||||
.map(|e| CurveEval::new(e.parameters()).unwrap().point(0.5 * (e.interval[0] + e.interval[1])).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
// In a fixed order.
|
||||
points.sort_by(|a, b| (a.z, a.y, a.x).partial_cmp(&(b.z, b.y, b.x)).unwrap());
|
||||
|
||||
@@ -286,7 +286,7 @@ impl Builder {
|
||||
}
|
||||
};
|
||||
let id = u32::try_from(self.edges.len()).map_err(|_| KernelError::Invalid("sew: edge budget exceeded".into()))?;
|
||||
self.edges.push(PartEdge { curve, interval, vertices: [va, vb] });
|
||||
self.edges.push(PartEdge { nurbs: None, curve, interval, vertices: [va, vb] });
|
||||
self.by_key.insert((ka, fa, kb, fb, mid, sheet), id);
|
||||
Ok((id, true))
|
||||
}
|
||||
@@ -1303,6 +1303,10 @@ pub(crate) struct PartUse {
|
||||
|
||||
pub(crate) struct PartEdge {
|
||||
pub curve: b::Curve,
|
||||
/// The NURBS form the edge is parameterised on, as a record edge keeps it
|
||||
/// ([`b::Edge::nurbs`]): given, `interval` is on it and `curve` only says what the edge is,
|
||||
/// so an edge taken from a record goes back as that same edge.
|
||||
pub nurbs: Option<b::Curve>,
|
||||
pub interval: [f64; 2],
|
||||
pub vertices: [u32; 2],
|
||||
}
|
||||
@@ -1366,8 +1370,8 @@ pub(crate) fn assemble(parts: Parts, solid: bool) -> Result<Assembled> {
|
||||
g
|
||||
};
|
||||
// Each edge's label and NURBS form (`form::edge`): a circle about a revolved face's axis on
|
||||
// that face's frame, so its form lies on the face's quarter grid; an edge the caller gave a
|
||||
// pcurve for keeps the parameter that pcurve is on.
|
||||
// that face's frame, so its form lies on the face's quarter grid; an edge given with its form
|
||||
// keeps it, and an edge the caller gave a pcurve for keeps the parameter that pcurve is on.
|
||||
let mut frames_of: Vec<Vec<b::Frame>> = vec![Vec::new(); edges.len()];
|
||||
for f in &faces {
|
||||
let Some(frame) = b::form::frame_of(&f.surface) else { continue };
|
||||
@@ -1380,6 +1384,13 @@ pub(crate) fn assemble(parts: Parts, solid: bool) -> Result<Assembled> {
|
||||
let mut labels: Vec<(b::Curve, [f64; 2])> = Vec::with_capacity(edges.len());
|
||||
let mut forms: Vec<Option<(b::Curve, [f64; 2])>> = Vec::with_capacity(edges.len());
|
||||
for ((e, frames), given) in edges.iter().zip(&frames_of).zip(&given_on) {
|
||||
if let Some(form) = &e.nurbs {
|
||||
let on_label =
|
||||
b::curve_interval(&e.curve, Some(form), e.interval).ok_or_else(|| fail("an edge's NURBS form does not evaluate".into()))?;
|
||||
labels.push((e.curve.clone(), on_label));
|
||||
forms.push(Some((form.clone(), e.interval)));
|
||||
continue;
|
||||
}
|
||||
if *given {
|
||||
labels.push((e.curve.clone(), e.interval));
|
||||
forms.push(None);
|
||||
|
||||
@@ -784,11 +784,16 @@ fn a_face_bounded_by_two_short_arcs_keeps_an_area_to_mesh() {
|
||||
let sq = [[3000.0, 0.0, 0.0], [5000.0, 0.0, 0.0], [5000.0, 2000.0, 0.0], [3000.0, 2000.0, 0.0]];
|
||||
let plane = Surface::Plane { origin: [0.0; 3], u_axis: [1.0, 0.0, 0.0], v_axis: [0.0, 1.0, 0.0] };
|
||||
let mut edges = vec![
|
||||
PartEdge { curve: circle(-c), interval: [top, PI - top], vertices: [0, 1] },
|
||||
PartEdge { curve: circle(c), interval: [PI + top, TAU - top], vertices: [1, 0] },
|
||||
PartEdge { nurbs: None, curve: circle(-c), interval: [top, PI - top], vertices: [0, 1] },
|
||||
PartEdge { nurbs: None, curve: circle(c), interval: [PI + top, TAU - top], vertices: [1, 0] },
|
||||
];
|
||||
for k in 0..4 {
|
||||
edges.push(PartEdge { curve: line(sq[k], sq[(k + 1) % 4]), interval: [0.0, 1.0], vertices: [2 + k as u32, 2 + ((k + 1) % 4) as u32] });
|
||||
edges.push(PartEdge {
|
||||
nurbs: None,
|
||||
curve: line(sq[k], sq[(k + 1) % 4]),
|
||||
interval: [0.0, 1.0],
|
||||
vertices: [2 + k as u32, 2 + ((k + 1) % 4) as u32],
|
||||
});
|
||||
}
|
||||
let uses = |es: &[u32]| es.iter().map(|&edge| PartUse { edge, forward: true, given: None }).collect::<Vec<_>>();
|
||||
let mut vertices = vec![[half, 0.0, 0.0], [-half, 0.0, 0.0]];
|
||||
|
||||
@@ -61,11 +61,13 @@ fn a_face_touching_its_own_edge_meshes() {
|
||||
let (h, dip) = (0.3175, 2.8e-7);
|
||||
let vertices = vec![[0.0, 0.0, 0.0], [5.0, 0.0, 0.0], [5.0, h, 0.0], [0.0, h, 0.0]];
|
||||
let line = |a: usize, b: usize| PartEdge {
|
||||
nurbs: None,
|
||||
curve: Curve::Line { start: vertices[a], end: vertices[b] },
|
||||
interval: [0.0, 1.0],
|
||||
vertices: [a as u32, b as u32],
|
||||
};
|
||||
let arch = PartEdge {
|
||||
nurbs: None,
|
||||
curve: Curve::Spline { knots: vec![0.0, 0.0, 0.0, 1.0, 1.0, 1.0], points: vec![[5.0, h, 0.0], [2.5, -h - 2.0 * dip, 0.0], [0.0, h, 0.0]] },
|
||||
interval: [0.0, 1.0],
|
||||
vertices: [2, 3],
|
||||
|
||||
Reference in New Issue
Block a user