fix(polyline): keep wide-polyline fills precise at UTM coordinates
The solid fill band of a wide LwPolyline / Polyline2D was built from absolute WCS vertex coordinates cast straight to f32, then handed to a HatchModel with world_origin = [0, 0]. At UTM-scale coordinates (~1e6) f32 resolves to ~0.06, so the half-width offset collapsed and the relative-to-eye fill shader cancelled `boundary - eye` to noise — the band rendered as a string of squares. (Exploding the polyline drops the width, hence no band and a clean line, which is why that "fixed" it.) Build the band relative to a world_origin (the first vertex) in f64 and store the small f32 offsets, mirroring how baked hatch fills keep precision. Same class of bug as the on-create hatch-fill fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
0317dad14e
commit
2672807c13
3 changed files with 42 additions and 14 deletions
|
|
@ -601,13 +601,19 @@ impl crate::entities::traits::Transformable for LwPolyline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn wide_fills(pl: &acadrust::entities::LwPolyline) -> Vec<Vec<[f32; 2]>> {
|
/// Solid-fill bands for a wide LwPolyline, plus the `world_origin` they are
|
||||||
|
/// relative to. The band vertices are small f32 offsets from `origin` (the
|
||||||
|
/// first vertex), so the relative-to-eye hatch fill keeps sub-unit precision at
|
||||||
|
/// UTM-scale coordinates — building them in absolute f32 collapsed the band into
|
||||||
|
/// a string of squares far from the origin.
|
||||||
|
pub(crate) fn wide_fills(pl: &acadrust::entities::LwPolyline) -> ([f64; 2], Vec<Vec<[f32; 2]>>) {
|
||||||
let hw_const = (pl.constant_width / 2.0) as f32;
|
let hw_const = (pl.constant_width / 2.0) as f32;
|
||||||
let verts = &pl.vertices;
|
let verts = &pl.vertices;
|
||||||
let n = verts.len();
|
let n = verts.len();
|
||||||
if n < 2 {
|
if n < 2 {
|
||||||
return vec![];
|
return ([0.0; 2], vec![]);
|
||||||
}
|
}
|
||||||
|
let origin = [verts[0].location.x, verts[0].location.y];
|
||||||
let seg_count = if pl.is_closed { n } else { n - 1 };
|
let seg_count = if pl.is_closed { n } else { n - 1 };
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
for i in 0..seg_count {
|
for i in 0..seg_count {
|
||||||
|
|
@ -626,15 +632,21 @@ pub(crate) fn wide_fills(pl: &acadrust::entities::LwPolyline) -> Vec<Vec<[f32; 2
|
||||||
if hw0 < 1e-6 && hw1 < 1e-6 {
|
if hw0 < 1e-6 && hw1 < 1e-6 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let p0 = [v0.location.x as f32, v0.location.y as f32];
|
let p0 = [
|
||||||
let p1 = [v1.location.x as f32, v1.location.y as f32];
|
(v0.location.x - origin[0]) as f32,
|
||||||
|
(v0.location.y - origin[1]) as f32,
|
||||||
|
];
|
||||||
|
let p1 = [
|
||||||
|
(v1.location.x - origin[0]) as f32,
|
||||||
|
(v1.location.y - origin[1]) as f32,
|
||||||
|
];
|
||||||
if let Some(poly) =
|
if let Some(poly) =
|
||||||
crate::entities::common::polyline_segment_fill(p0, p1, hw0, hw1, v0.bulge as f32)
|
crate::entities::common::polyline_segment_fill(p0, p1, hw0, hw1, v0.bulge as f32)
|
||||||
{
|
{
|
||||||
out.push(poly);
|
out.push(poly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out
|
(origin, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::entities::traits::MassPropsCalc for acadrust::entities::LwPolyline {
|
impl crate::entities::traits::MassPropsCalc for acadrust::entities::LwPolyline {
|
||||||
|
|
|
||||||
|
|
@ -682,13 +682,17 @@ impl Transformable for Polyline3D {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Generate solid-fill boundary polygons for each wide segment of a Polyline2D.
|
/// Generate solid-fill boundary polygons for each wide segment of a Polyline2D.
|
||||||
pub(crate) fn wide_fills(pl: &acadrust::entities::Polyline2D) -> Vec<Vec<[f32; 2]>> {
|
/// Solid-fill bands for a wide Polyline2D, plus the `world_origin` they are
|
||||||
|
/// relative to (the first vertex). See `lwpolyline::wide_fills` — offsets are
|
||||||
|
/// f32 from `origin` so the band stays precise at UTM-scale coordinates.
|
||||||
|
pub(crate) fn wide_fills(pl: &acadrust::entities::Polyline2D) -> ([f64; 2], Vec<Vec<[f32; 2]>>) {
|
||||||
let hw_default = (pl.start_width.max(pl.end_width) / 2.0) as f32;
|
let hw_default = (pl.start_width.max(pl.end_width) / 2.0) as f32;
|
||||||
let verts = &pl.vertices;
|
let verts = &pl.vertices;
|
||||||
let n = verts.len();
|
let n = verts.len();
|
||||||
if n < 2 {
|
if n < 2 {
|
||||||
return vec![];
|
return ([0.0; 2], vec![]);
|
||||||
}
|
}
|
||||||
|
let origin = [verts[0].location.x, verts[0].location.y];
|
||||||
let seg_count = if pl.is_closed() { n } else { n - 1 };
|
let seg_count = if pl.is_closed() { n } else { n - 1 };
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
for i in 0..seg_count {
|
for i in 0..seg_count {
|
||||||
|
|
@ -707,13 +711,19 @@ pub(crate) fn wide_fills(pl: &acadrust::entities::Polyline2D) -> Vec<Vec<[f32; 2
|
||||||
if hw0 < 1e-6 && hw1 < 1e-6 {
|
if hw0 < 1e-6 && hw1 < 1e-6 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let p0 = [v0.location.x as f32, v0.location.y as f32];
|
let p0 = [
|
||||||
let p1 = [v1.location.x as f32, v1.location.y as f32];
|
(v0.location.x - origin[0]) as f32,
|
||||||
|
(v0.location.y - origin[1]) as f32,
|
||||||
|
];
|
||||||
|
let p1 = [
|
||||||
|
(v1.location.x - origin[0]) as f32,
|
||||||
|
(v1.location.y - origin[1]) as f32,
|
||||||
|
];
|
||||||
if let Some(poly) =
|
if let Some(poly) =
|
||||||
crate::entities::common::polyline_segment_fill(p0, p1, hw0, hw1, v0.bulge as f32)
|
crate::entities::common::polyline_segment_fill(p0, p1, hw0, hw1, v0.bulge as f32)
|
||||||
{
|
{
|
||||||
out.push(poly);
|
out.push(poly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out
|
(origin, out)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4763,9 +4763,15 @@ impl Scene {
|
||||||
|
|
||||||
// Wide LWPolyline and Polyline2D fills
|
// Wide LWPolyline and Polyline2D fills
|
||||||
for entity in self.document.entities() {
|
for entity in self.document.entities() {
|
||||||
let (common, fills) = match entity {
|
let (common, fill_origin, fills) = match entity {
|
||||||
EntityType::LwPolyline(pl) => (&pl.common, crate::entities::lwpolyline::wide_fills(pl)),
|
EntityType::LwPolyline(pl) => {
|
||||||
EntityType::Polyline2D(pl) => (&pl.common, crate::entities::polyline::wide_fills(pl)),
|
let (o, f) = crate::entities::lwpolyline::wide_fills(pl);
|
||||||
|
(&pl.common, o, f)
|
||||||
|
}
|
||||||
|
EntityType::Polyline2D(pl) => {
|
||||||
|
let (o, f) = crate::entities::polyline::wide_fills(pl);
|
||||||
|
(&pl.common, o, f)
|
||||||
|
}
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
if fills.is_empty() {
|
if fills.is_empty() {
|
||||||
|
|
@ -4792,7 +4798,7 @@ impl Scene {
|
||||||
color,
|
color,
|
||||||
angle_offset: 0.0,
|
angle_offset: 0.0,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
world_origin: [0.0; 2],
|
world_origin: fill_origin,
|
||||||
vp_scissor: None,
|
vp_scissor: None,
|
||||||
draw_depth: depth_map.get(&common.handle.value()).copied().unwrap_or(0.0),
|
draw_depth: depth_map.get(&common.handle.value()).copied().unwrap_or(0.0),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue