fix(scene): tessellate hatch-boundary polyline bulges in the wire outline
The hatch fill polygon (`hatch_model_from_dxf`) handled `BoundaryEdge:: Polyline` vertex bulges correctly — sampling arcs between v0 → v1 when v0.z (bulge) was non-zero. The wire outline path (`legacy_geometry::Hatch`) just pushed (x, y) for every vertex and ignored v.z, so any bulged boundary segment drew as a straight chord even though the fill underneath was correctly curved. Mirror the bulge-arc tessellation into the wire path, reusing the shared `arc_segments` helper from this module. Polyline edges now emit the arc start vertex + sampled arc points + the trailing endpoint, with a NaN separator added between distinct edges so connected boundaries don't draw spurious chords from one edge's last point to the next edge's first. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
cb7e6e3cc3
commit
9b4ded79be
1 changed files with 73 additions and 5 deletions
|
|
@ -1191,15 +1191,83 @@ fn legacy_geometry(entity: &EntityType, world_offset: [f64; 3]) -> Geometry {
|
||||||
for edge in &path.edges {
|
for edge in &path.edges {
|
||||||
match edge {
|
match edge {
|
||||||
acadrust::entities::BoundaryEdge::Polyline(poly) => {
|
acadrust::entities::BoundaryEdge::Polyline(poly) => {
|
||||||
|
// Hatch-boundary polyline vertices encode bulge in
|
||||||
|
// `Vector3.z`; straight segments emit just the
|
||||||
|
// start vertex, bulged segments tessellate the arc
|
||||||
|
// between v0 → v1.
|
||||||
let start_idx = pts.len();
|
let start_idx = pts.len();
|
||||||
for v in &poly.vertices {
|
let verts = &poly.vertices;
|
||||||
let p = to_wcs(v.x, v.y);
|
let count = verts.len();
|
||||||
|
let seg_count = if poly.is_closed {
|
||||||
|
count
|
||||||
|
} else {
|
||||||
|
count.saturating_sub(1)
|
||||||
|
};
|
||||||
|
if count > 0 && pts.len() != start_idx {
|
||||||
|
pts.push([f32::NAN; 3]);
|
||||||
|
}
|
||||||
|
for i in 0..seg_count {
|
||||||
|
let v0 = &verts[i];
|
||||||
|
let v1 = &verts[(i + 1) % count];
|
||||||
|
let bulge = v0.z;
|
||||||
|
if bulge.abs() < 1e-9 {
|
||||||
|
let p = to_wcs(v0.x, v0.y);
|
||||||
|
pts.push(p);
|
||||||
|
key_verts.push(p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let theta = 4.0 * bulge.atan();
|
||||||
|
let dx = v1.x - v0.x;
|
||||||
|
let dy = v1.y - v0.y;
|
||||||
|
let d = (dx * dx + dy * dy).sqrt();
|
||||||
|
if d < 1e-12 {
|
||||||
|
let p = to_wcs(v0.x, v0.y);
|
||||||
|
pts.push(p);
|
||||||
|
key_verts.push(p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let r = (d * 0.5) / (theta * 0.5).sin().abs();
|
||||||
|
let mx = (v0.x + v1.x) * 0.5;
|
||||||
|
let my = (v0.y + v1.y) * 0.5;
|
||||||
|
let px = -dy / d;
|
||||||
|
let py = dx / d;
|
||||||
|
let sign = if bulge > 0.0 { 1.0_f64 } else { -1.0_f64 };
|
||||||
|
let center_offset = r * (theta * 0.5).cos();
|
||||||
|
let cx = mx + sign * px * center_offset;
|
||||||
|
let cy = my + sign * py * center_offset;
|
||||||
|
let a0 = (v0.y - cy).atan2(v0.x - cx);
|
||||||
|
let a1 = (v1.y - cy).atan2(v1.x - cx);
|
||||||
|
let mut sweep = a1 - a0;
|
||||||
|
const TAU: f64 = std::f64::consts::TAU;
|
||||||
|
if bulge > 0.0 {
|
||||||
|
if sweep <= 0.0 { sweep += TAU; }
|
||||||
|
} else if sweep >= 0.0 { sweep -= TAU; }
|
||||||
|
if sweep.abs() < 1e-9 {
|
||||||
|
sweep = if bulge > 0.0 { TAU } else { -TAU };
|
||||||
|
}
|
||||||
|
let segs = arc_segments(sweep.abs());
|
||||||
|
for j in 0..segs {
|
||||||
|
let a = a0 + sweep * (j as f64 / segs as f64);
|
||||||
|
let p = to_wcs(cx + r * a.cos(), cy + r * a.sin());
|
||||||
|
pts.push(p);
|
||||||
|
if j == 0 {
|
||||||
|
key_verts.push(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Close the loop visually for closed polylines by
|
||||||
|
// returning to the first emitted point.
|
||||||
|
if poly.is_closed {
|
||||||
|
if let Some(first) = pts.get(start_idx).cloned() {
|
||||||
|
if first[0].is_finite() {
|
||||||
|
pts.push(first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(last) = verts.last() {
|
||||||
|
let p = to_wcs(last.x, last.y);
|
||||||
pts.push(p);
|
pts.push(p);
|
||||||
key_verts.push(p);
|
key_verts.push(p);
|
||||||
}
|
}
|
||||||
if let Some(first) = pts.get(start_idx).cloned() {
|
|
||||||
pts.push(first);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
acadrust::entities::BoundaryEdge::Line(ln) => {
|
acadrust::entities::BoundaryEdge::Line(ln) => {
|
||||||
let p0 = to_wcs(ln.start.x, ln.start.y);
|
let p0 = to_wcs(ln.start.x, ln.start.y);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue