mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 02:25:55 +00:00
Sketch projection: a projected face's curves meet at the face's vertices
A face's edges are projected exactly: lines, and arcs read off SMLib's NURBS within the planar-edge tolerance (Body::planar_edges). An SMLib shell's offset corner is a NURBS whose arc came out with its centre 2e-6 mm off and its ends up to 6e-7 mm from the lines they meet, beyond the link's merge tolerance (1e-7): the inner loop of the sensor enclosure's case top projected as sixteen loose curves, the offset of one found no chain to follow, and the Lid lip extrude found no profile l13..a16 (the five acceptance scenarios, action_matrix an_axis_through_a_corner_round_that_a_parameter_moves). Each edge's pieces now end on the edge's vertices where they lie within the edge's and the reading's tolerance of them (the vertex is the point both edges share), and an arc whose ends moved takes the centre nearest its own that is equally far from both, so the sketch keeps both ends on its circle. Body::planar_edges_tolerance names the reading tolerance planar_edges uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -426,7 +426,10 @@ fn projected_edges(body: &crate::ModelBody, mesh: &Mesh, plane: &Plane, edges: &
|
||||
let mut out = Vec::new();
|
||||
for (e, exact) in edges.iter().zip(exact_edges(body, plane, edges)) {
|
||||
match exact {
|
||||
Some(g) => out.extend(g),
|
||||
Some(mut g) => {
|
||||
at_vertices(body, mesh, plane, *e, &mut g);
|
||||
out.extend(g);
|
||||
}
|
||||
None => {
|
||||
if let Some(pts) = mesh.edges.get(*e) {
|
||||
out.extend(fit_world(plane, pts));
|
||||
@@ -437,6 +440,51 @@ fn projected_edges(body: &crate::ModelBody, mesh: &Mesh, plane: &Plane, edges: &
|
||||
out
|
||||
}
|
||||
|
||||
/// Edge `e`'s exact pieces `g` with their outer ends on the edge's vertices, where they lie
|
||||
/// within the edge's and the reading's tolerance of them. An edge's curve need only pass within
|
||||
/// its tolerance of its vertices, and an arc is read off a NURBS within the planar-edge
|
||||
/// tolerance: one SMLib made (a shell's offset corner) ended 2e-6 mm from the line it meets, and
|
||||
/// the two ends, not taken for one point, left the loop open, so an offset of one curve found
|
||||
/// no chain to follow. The vertex is the point both edges share.
|
||||
fn at_vertices(body: &crate::ModelBody, mesh: &Mesh, plane: &Plane, e: usize, g: &mut [LinkGeom]) {
|
||||
let record = body.body.record();
|
||||
let Some(edge) = record.edges.get(e) else { return };
|
||||
let Some((first, last)) = mesh.edges.get(e).and_then(|pts| pts.first().zip(pts.last())) else { return };
|
||||
// The pieces are read within the planar-edge tolerance of the edge's curve, which passes
|
||||
// within its own tolerance of the vertices.
|
||||
let tol = edge.vertices.iter().filter_map(|v| record.vertices.get(*v as usize)).map(|v| v.tolerance).fold(edge.tolerance, f64::max)
|
||||
+ body.body.planar_edges_tolerance();
|
||||
// Each vertex moves the piece end nearest it (the pieces' order and direction need not be
|
||||
// the edge's).
|
||||
for v in [plane.to_local(*first), plane.to_local(*last)] {
|
||||
let mut best: Option<(f64, &mut Vec2)> = None;
|
||||
for piece in g.iter_mut() {
|
||||
if let LinkGeom::Line(a, b) | LinkGeom::Arc { a, b, .. } | LinkGeom::Conic { a, b, .. } = piece {
|
||||
for p in [a, b] {
|
||||
let d = p.dist(v);
|
||||
if d <= tol && best.as_ref().is_none_or(|(bd, _)| d < *bd) {
|
||||
best = Some((d, p));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((_, p)) = best {
|
||||
*p = v;
|
||||
}
|
||||
}
|
||||
// An arc is its centre and two ends on one circle: with its ends moved, its centre moves to
|
||||
// the nearest point equally far from both (on their bisector), or the sketch would move an
|
||||
// end back off the vertex onto the circle through the other.
|
||||
for piece in g.iter_mut() {
|
||||
if let LinkGeom::Arc { c, a, b } = piece
|
||||
&& let Some(across) = Vec2::new(a.y - b.y, b.x - a.x).normalized()
|
||||
{
|
||||
let mid = (*a + *b) * 0.5;
|
||||
*c = mid + across * (*c - mid).dot(across);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A boundary segment as the link geometry that is the same curve.
|
||||
fn link_geom(s: &Seg2) -> Option<LinkGeom> {
|
||||
Some(match *s {
|
||||
|
||||
@@ -1198,6 +1198,13 @@ impl Body {
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// How far [`Body::planar_edges`] lets a piece it reads as a line or an arc lie from the
|
||||
/// edge's curve (a NURBS within this of a circle is that arc), so how far a piece's ends may
|
||||
/// lie from the edge's vertices.
|
||||
pub fn planar_edges_tolerance(&self) -> f64 {
|
||||
(self.size() * 1e-7).max(1e-9) * 10.0
|
||||
}
|
||||
|
||||
/// Edges `indices` (record edge indices) in `plane`'s coordinates, exactly: lines, circular
|
||||
/// arcs, and free-form curves as the Bézier spans they are, for a sketch to project them.
|
||||
/// `None` for an edge that doesn't lie in a plane parallel to `plane`, isn't read exactly or
|
||||
@@ -1206,7 +1213,7 @@ impl Body {
|
||||
if self.is_mesh() {
|
||||
return vec![None; indices.len()];
|
||||
}
|
||||
let tol = (self.size() * 1e-7).max(1e-9) * 10.0;
|
||||
let tol = self.planar_edges_tolerance();
|
||||
indices
|
||||
.iter()
|
||||
.map(|i| {
|
||||
|
||||
Reference in New Issue
Block a user