feat(solid3d): draw spline (intcurve) edges of ACIS solids

A curved solid's fillet/blend boundaries are `intcurve-curve` spline edges.
The edge collector handled only straight and elliptic curves, so a spline
edge fell back to a straight chord between its two vertices — or, for a
closed-loop edge whose endpoints coincide (a sphere/torus face rim), to
nothing at all. Those boundaries simply weren't drawn.

Both the feature-edge list and the face-boundary sampler now sample the
spline over the edge's own parameter span via the new `SatIntCurve`, so the
curved edges draw as real curves. On budweiser2018.dwg handle 2700 the
solid's edge-segment count rises from 110 to 392 — the previously missing
sphere/torus rim curves.

The face-boundary sampler feeding a real loop (instead of a degenerate
chord) is also the prerequisite for trimming those curved faces to their
extent, a follow-up.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-18 00:18:10 +03:00
1 parent d80d037e2f
commit 9b7fe2c84d
1 file changed
+31 -2
+31 -2
View File
@@ -15,8 +15,9 @@ use std::f64::consts::TAU;
use acadrust::entities::acis::types::Sense;
use acadrust::entities::acis::{
SabReader, SatCoedge, SatConeSurface, SatDocument, SatEdge, SatEllipseCurve, SatFace, SatLoop,
SatPlaneSurface, SatPoint, SatPointer, SatSphereSurface, SatTorusSurface, SatVertex,
SabReader, SatCoedge, SatConeSurface, SatDocument, SatEdge, SatEllipseCurve, SatFace,
SatIntCurve, SatLoop, SatPlaneSurface, SatPoint, SatPointer, SatSphereSurface, SatTorusSurface,
SatVertex,
};
use acadrust::entities::{Body, Region, Solid3D};
@@ -608,6 +609,15 @@ fn collect_feature_edges(sat: &SatDocument) -> Vec<[f64; 3]> {
if let Some(p) = vertex_point(sat, edge.end_vertex()) {
pts.push(p);
}
} else if let Some(ic) = SatIntCurve::from_record(cr) {
// Spline edge — sample the actual curve instead of the straight
// chord the fallback below would draw (or nothing, for a closed
// loop whose endpoints coincide).
pts = ic
.sample_range(edge.start_param(), edge.end_param(), EDGE_SEGS)
.into_iter()
.map(|(x, y, z)| [x, y, z])
.collect();
}
}
if pts.len() < 2 {
@@ -947,6 +957,25 @@ pub(crate) fn append_coedge_points(
return;
}
}
// Spline (intcurve) edge: sample its own arc so a curved face boundary
// (fillet/blend) is a real loop rather than a straight chord — without
// this the face's parametric extent collapses and it can't be trimmed.
if let Some(ic) = SatIntCurve::from_record(curve_rec) {
let mut sampled: Vec<[f64; 3]> = ic
.sample_range(edge.start_param(), edge.end_param(), circ_segs)
.into_iter()
.map(|(x, y, z)| [x, y, z])
.collect();
// Drop the shared end point so adjacent coedges don't double up.
sampled.pop();
if sampled.len() >= 2 {
if !fwd {
sampled.reverse();
}
pts.extend(sampled);
return;
}
}
}
// Straight / unsupported curve: keep the single start vertex.