fix(wipeout): keep fill boundary precise at UTM coordinates

`wipeout_boundary_2d` built the fill boundary from the wipeout's
insertion point cast straight to f32, stored in a HatchModel with
world_origin = [0, 0]. At UTM-scale coordinates (~1e6) f32 resolves to
~0.06, so the boundary collapsed and the relative-to-eye fill shader
cancelled `boundary - eye` to noise — the mask rendered as squares.

Return the boundary as small f32 offsets relative to a world_origin
(the insertion point, kept in f64) and set HatchModel.world_origin to
it at both the model-space and paper/viewport fill sites. Same fix as
the wide-polyline and on-create hatch fills.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-06-26 11:20:50 +03:00
1 parent 2672807c13
commit 4e8521010b
1 file changed
+25 -21
+25 -21
View File
@@ -4843,7 +4843,7 @@ impl Scene {
// Per-entity world_offset selection so paper-layout content
// viewports still see model-block wipeouts at the right local
// coordinates (same rationale as hatches).
let boundary = Self::wipeout_boundary_2d(wo);
let (fill_origin, boundary) = Self::wipeout_boundary_2d(wo);
if boundary.len() >= 3 {
let mut fill_color = bg_color;
if self.selected.contains(&wo.common.handle) {
@@ -4856,7 +4856,7 @@ impl Scene {
color: fill_color,
angle_offset: 0.0,
scale: 1.0,
world_origin: [0.0; 2],
world_origin: fill_origin,
vp_scissor: None,
draw_depth: 0.0,
});
@@ -4866,24 +4866,29 @@ impl Scene {
}
/// Compute the 2D (XY) boundary polygon for a Wipeout entity.
/// Wipeout fill boundary as small f32 offsets from the returned `world_origin`
/// (the insertion point, kept in f64). Building it in absolute WCS f32
/// collapsed the boundary into squares at UTM-scale coordinates; the
/// relative-to-eye hatch fill reconstructs `world_origin + offset` precisely.
fn wipeout_boundary_2d(
wo: &acadrust::entities::Wipeout,
) -> Vec<[f32; 2]> {
) -> ([f64; 2], Vec<[f32; 2]>) {
use acadrust::entities::WipeoutClipType;
let origin = [wo.insertion_point.x, wo.insertion_point.y];
let is_polygon = wo.clipping_enabled
&& wo.clip_boundary_vertices.len() >= 3
&& matches!(wo.clip_type, WipeoutClipType::Polygonal);
if is_polygon {
let ox = (wo.insertion_point.x) as f32;
let oy = (wo.insertion_point.y) as f32;
// DXF clip vertices live in image-pixel space, centred on the
// image (range −size/2 … +size/2). Image-bottom-left → insertion,
// image-y-axis points DOWN (per the DXF "v_vector points down the
// image" convention), so map:
// x_off = (clip.x + size.x/2) × u_vec
// y_off = (size.y/2 − clip.y) × v_vec ← y flipped
// Offsets are relative to `origin` (the insertion point).
let cx_of = |v: &acadrust::types::Vector2| v.x + wo.size.x * 0.5;
let cy_of = |v: &acadrust::types::Vector2| wo.size.y * 0.5 - v.y;
let mut poly: Vec<[f32; 2]> = wo
@@ -4894,7 +4899,7 @@ impl Scene {
let cy = cy_of(v);
let wx = (wo.u_vector.x * cx + wo.v_vector.x * cy) as f32;
let wy = (wo.u_vector.y * cx + wo.v_vector.y * cy) as f32;
[ox + wx, oy + wy]
[wx, wy]
})
.collect();
// Close the loop: the GPU `in_polygon` ray-cast walks
@@ -4906,28 +4911,27 @@ impl Scene {
poly.push(first);
}
}
poly
(origin, poly)
} else {
// Rectangular boundary from 4 corners.
let ox = (wo.insertion_point.x) as f32;
let oy = (wo.insertion_point.y) as f32;
let oz = wo.insertion_point.z as f32;
// Rectangular boundary from 4 corners, as offsets from `origin`.
let ux = (wo.u_vector.x * wo.size.x) as f32;
let uy = (wo.u_vector.y * wo.size.x) as f32;
let vx = (wo.v_vector.x * wo.size.y) as f32;
let vy = (wo.v_vector.y * wo.size.y) as f32;
let _ = oz;
// Close the loop (repeat corner 0): the GPU `in_polygon` ray-cast
// walks sequential vertex pairs and never wraps last→first, so an
// unclosed quad leaves the v3→v0 edge untested and the solid mask
// bleeds past the boundary — same reason the polygon branch closes.
vec![
[ox, oy],
[ox + ux, oy + uy],
[ox + ux + vx, oy + uy + vy],
[ox + vx, oy + vy],
[ox, oy],
]
(
origin,
vec![
[0.0, 0.0],
[ux, uy],
[ux + vx, uy + vy],
[vx, vy],
[0.0, 0.0],
],
)
}
}
@@ -7885,7 +7889,7 @@ impl Scene {
continue;
}
// Paper-block wipeouts live in paper coords — no `world_offset`.
let boundary = Self::wipeout_boundary_2d(wo);
let (fill_origin, boundary) = Self::wipeout_boundary_2d(wo);
if boundary.len() < 3 {
continue;
}
@@ -7900,7 +7904,7 @@ impl Scene {
color: fill_color,
angle_offset: 0.0,
scale: 1.0,
world_origin: [0.0; 2],
world_origin: fill_origin,
vp_scissor: None,
draw_depth: 0.0,
});