From 9b7fe2c84dabfac323792abb9a3d0642514e9555 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Sat, 18 Jul 2026 00:18:10 +0300 Subject: [PATCH] feat(solid3d): draw spline (intcurve) edges of ACIS solids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/scene/convert/solid3d_tess.rs | 33 +++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/scene/convert/solid3d_tess.rs b/src/scene/convert/solid3d_tess.rs index acd3c3a74..32e116b63 100644 --- a/src/scene/convert/solid3d_tess.rs +++ b/src/scene/convert/solid3d_tess.rs @@ -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.