From 7e6de57c8df15368636490ad6bf35ac990b23529 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Mon, 6 Apr 2026 14:46:47 +0300 Subject: [PATCH] feat: Wipeout solid fill masking Wipeout entities now render a background-colored solid fill polygon (via the hatch GPU pipeline) that masks other hatch fills beneath them. Fills use model space background (0.11 grey) or paper space background (0.22/0.24/0.28). Polygon and rectangular clip boundaries both supported. Selected wipeouts use a translucent blue highlight instead. Co-Authored-By: Claude Sonnet 4.6 --- src/scene/mod.rs | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/scene/mod.rs b/src/scene/mod.rs index a4ce82b3b..47bd58c95 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -1178,9 +1178,82 @@ impl Scene { } } + // ── Wipeout solid fills ─────────────────────────────────────────────── + // Wipeouts are rendered as background-colored filled polygons so that + // they mask hatch fills drawn earlier in the same pass. + // Wipeout fills are appended last so they render on top of other hatches. + let bg_color: [f32; 4] = if self.current_layout == "Model" { + [0.11, 0.11, 0.11, 1.0] + } else { + [0.22, 0.24, 0.28, 1.0] + }; + for entity in self.document.entities() { + let EntityType::Wipeout(wo) = entity else { continue }; + if entity.common().invisible { + continue; + } + if self.document.layers + .get(&entity.common().layer) + .map(|l| l.flags.off || l.flags.frozen) + .unwrap_or(false) + { + continue; + } + let boundary = Self::wipeout_boundary_2d(wo); + if boundary.len() >= 3 { + let mut fill_color = bg_color; + if self.selected.contains(&wo.common.handle) { + fill_color = [0.15, 0.55, 1.00, 0.35]; + } + models.push(HatchModel { + boundary, + pattern: hatch_model::HatchPattern::Solid, + name: "WIPEOUT_FILL".into(), + color: fill_color, + angle_offset: 0.0, + scale: 1.0, + }); + } + } + models } + /// Compute the 2D (XY) boundary polygon for a Wipeout entity. + fn wipeout_boundary_2d(wo: &acadrust::entities::Wipeout) -> Vec<[f32; 2]> { + use acadrust::entities::WipeoutClipType; + + 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; + wo.clip_boundary_vertices.iter().map(|v| { + let wx = (wo.u_vector.x * v.x * wo.size.x + wo.v_vector.x * v.y * wo.size.y) as f32; + let wy = (wo.u_vector.y * v.x * wo.size.x + wo.v_vector.y * v.y * wo.size.y) as f32; + [ox + wx, oy + wy] + }).collect() + } 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; + 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; + vec![ + [ox, oy], + [ox + ux, oy + uy], + [ox + ux + vx, oy + uy + vy], + [ox + vx, oy + vy], + ] + } + } + fn hatch_model_from_dxf(dxf: &DxfHatch, color: [f32; 4]) -> Option { let path = dxf .paths