mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 02:25:55 +00:00
sew: a circle on a revolved face is labelled on that face's frame
The bend's rim arcs carried frames of their own (x toward the arc's start), a fraction of a quarter from the bend cylinder's, so their forms were off the cylinder's quarter grid and no straight pcurve lay along them. `form::on_frame` relabels such a circle on the face's frame (the same circle, its interval shifted); its form is then on the face's grid, its pcurve u = ±s + c. `along` takes u or v as the edge's parameter either way along, offset. The volume test steps to a pcurve's ends exactly (t0(1-f) + t1 f), as an interval's end may now carry rounding. With the fit off, 38 of the 44 sew cases pass (from 29). With it on, the rig is unchanged (333/372, ladder 101/116, no case changed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BnQawySUFx57iaJZq7z1Fm
This commit is contained in:
@@ -176,3 +176,22 @@ pub fn surface(s: &Surface, [u, v]: [[f64; 2]; 2]) -> Option<(Surface, [[f64; 2]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A circle edge labelled on another frame about the same axis: the same circle, its angle
|
||||
/// measured from `frame`'s x axis (its y axis taken so the circle keeps its own direction), and
|
||||
/// its interval shifted to match, so its form lies on `frame`'s quarter grid. `None` when the
|
||||
/// circle is not about `frame`'s axis (centre on it, normal along it, within `tol`).
|
||||
pub fn on_frame(c: &Curve, [t0, t1]: [f64; 2], frame: &Frame, tol: f64) -> Option<(Curve, [f64; 2])> {
|
||||
let Curve::Circle { frame: own, radius } = c else { return None };
|
||||
let (o, x, _, z) = axes(own)?;
|
||||
let (fo, fx, _, fz) = axes(frame)?;
|
||||
let d = o - fo;
|
||||
if !((d - fz * d.dot(fz)).len() <= tol && (z.dot(fz).abs() - 1.0).abs() <= 1e-9) {
|
||||
return None;
|
||||
}
|
||||
let nx = (fx - z * fx.dot(z)).normalized()?;
|
||||
let ny = z.cross(nx);
|
||||
let shift = x.dot(ny).atan2(x.dot(nx));
|
||||
let relabel = Frame { origin: [o.x, o.y, o.z], x_axis: [nx.x, nx.y, nx.z], y_axis: [ny.x, ny.y, ny.z] };
|
||||
Some((Curve::Circle { frame: relabel, radius: *radius }, [t0 + shift, t1 + shift]))
|
||||
}
|
||||
|
||||
@@ -317,9 +317,15 @@ impl On {
|
||||
if let (Some(a), Some(b)) = (self.project(p0, None, exact), self.project(p1, None, exact)) {
|
||||
candidates.push(line(a, b));
|
||||
}
|
||||
// u or v the edge's own parameter, either way along and offset (forms on one frame's
|
||||
// quarter grid, or on the same grid turned over), the other fixed.
|
||||
if let Some([u, v]) = self.project(pm, None, exact) {
|
||||
candidates.push(line([t0, v], [t1, v]));
|
||||
candidates.push(line([u, t0], [u, t1]));
|
||||
let tm = 0.5 * (t0 + t1);
|
||||
for sign in [1.0, -1.0] {
|
||||
let (cu, cv) = (u - sign * tm, v - sign * tm);
|
||||
candidates.push(line([cu + sign * t0, v], [cu + sign * t1, v]));
|
||||
candidates.push(line([u, cv + sign * t0], [u, cv + sign * t1]));
|
||||
}
|
||||
}
|
||||
candidates.into_iter().find(|pc| self.lifts(pc, curve, [t0, t1], exact))
|
||||
}
|
||||
|
||||
@@ -1152,12 +1152,34 @@ pub(crate) fn assemble(parts: Parts, solid: bool) -> Result<Assembled> {
|
||||
}
|
||||
g
|
||||
};
|
||||
let forms: Vec<Option<(b::Curve, [f64; 2])>> = edges
|
||||
// A circle on a cylinder, cone, sphere or torus about its axis is labelled on that face's
|
||||
// frame, so its form lies on the face's quarter grid and its pcurve there is a straight line.
|
||||
let mut frame_of: Vec<Option<&b::Frame>> = vec![None; edges.len()];
|
||||
for f in &faces {
|
||||
let (b::Surface::Cylinder { frame, .. }
|
||||
| b::Surface::Cone { frame, .. }
|
||||
| b::Surface::Sphere { frame, .. }
|
||||
| b::Surface::Torus { frame, .. }) = &f.surface
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
for u in f.loops.iter().flatten() {
|
||||
if let Some(slot) = frame_of.get_mut(u.edge as usize).filter(|s| s.is_none()) {
|
||||
*slot = Some(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
let labels: Vec<(b::Curve, [f64; 2])> = edges
|
||||
.iter()
|
||||
.zip(&frame_of)
|
||||
.map(|(e, frame)| frame.and_then(|f| b::form::on_frame(&e.curve, e.interval, f, tol)).unwrap_or_else(|| (e.curve.clone(), e.interval)))
|
||||
.collect();
|
||||
let forms: Vec<Option<(b::Curve, [f64; 2])>> = labels
|
||||
.iter()
|
||||
.zip(&given_on)
|
||||
.map(|(e, given)| match e.curve {
|
||||
.map(|((curve, interval), given)| match curve {
|
||||
b::Curve::Circle { .. } | b::Curve::Ellipse { .. } if !given => {
|
||||
b::form::curve(&e.curve, e.interval).ok_or_else(|| fail("an arc of more than one turn".into())).map(Some)
|
||||
b::form::curve(curve, *interval).ok_or_else(|| fail("an arc of more than one turn".into())).map(Some)
|
||||
}
|
||||
_ => Ok(None),
|
||||
})
|
||||
@@ -1300,13 +1322,13 @@ pub(crate) fn assemble(parts: Parts, solid: bool) -> Result<Assembled> {
|
||||
}
|
||||
let record_edges = edges
|
||||
.iter()
|
||||
.zip(&forms)
|
||||
.map(|(e, form)| {
|
||||
.zip(forms.iter().zip(&labels))
|
||||
.map(|(e, (form, (label, label_interval)))| {
|
||||
let (nurbs, interval) = match form {
|
||||
Some((c, i)) => (Some(c.clone()), *i),
|
||||
None => (None, e.interval),
|
||||
None => (None, *label_interval),
|
||||
};
|
||||
b::Edge { nurbs, curve: e.curve.clone(), interval, vertices: e.vertices, tolerance: tol, uses: Vec::new() }
|
||||
b::Edge { nurbs, curve: label.clone(), interval, vertices: e.vertices, tolerance: tol, uses: Vec::new() }
|
||||
})
|
||||
.collect();
|
||||
let record_vertices = vertices.iter().map(|point| b::Vertex { point: *point, tolerance: tol }).collect();
|
||||
@@ -1424,7 +1446,8 @@ fn face_flux(s: &SurfaceEval, v0: f64, loops: &[Vec<(&Curve2, bool)>], field: &d
|
||||
let [t0, t1] = c.interval()?;
|
||||
let at = |k: f64| {
|
||||
let f = k / FLUX_PIECES as f64;
|
||||
c.eval(if *forward { t0 + (t1 - t0) * f } else { t1 - (t1 - t0) * f })
|
||||
// Exactly the ends at f = 0 and 1, whatever the interval's rounding.
|
||||
c.eval(if *forward { t0 * (1.0 - f) + t1 * f } else { t1 * (1.0 - f) + t0 * f })
|
||||
};
|
||||
for k in 0..FLUX_PIECES {
|
||||
let (a, m, z) = (at(k as f64)?, at(k as f64 + 0.5)?, at(k as f64 + 1.0)?);
|
||||
|
||||
Reference in New Issue
Block a user