diff --git a/src/app/update/viewport.rs b/src/app/update/viewport.rs index 59b8fe867..58cfc940c 100644 --- a/src/app/update/viewport.rs +++ b/src/app/update/viewport.rs @@ -1235,6 +1235,7 @@ pub(super) fn on_tick(&mut self, t: Instant) -> Task { let far_pos = base + dir * far; let far_neg = base - dir * far; let guide = crate::scene::WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1273,6 +1274,7 @@ pub(super) fn on_tick(&mut self, t: Instant) -> Task { let far_pos = h.base + h.dir * far; let far_neg = h.base - h.dir * far; previews.push(crate::scene::WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/entities/dimension.rs b/src/entities/dimension.rs index f8c643f2a..21c49483a 100644 --- a/src/entities/dimension.rs +++ b/src/entities/dimension.rs @@ -1352,6 +1352,7 @@ fn tessellate_dimension_inner( let (ext1, ext2) = split_ext_lines(&geom.ext_lines); if !ext1.is_empty() { wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1378,6 +1379,7 @@ fn tessellate_dimension_inner( } if !ext2.is_empty() { wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1404,6 +1406,7 @@ fn tessellate_dimension_inner( } } else { wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1431,6 +1434,7 @@ fn tessellate_dimension_inner( } wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1474,6 +1478,7 @@ fn tessellate_dimension_inner( aci_to_rgba(&c) }; wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/entities/leader.rs b/src/entities/leader.rs index 721a20e67..ab8981800 100644 --- a/src/entities/leader.rs +++ b/src/entities/leader.rs @@ -590,6 +590,7 @@ impl LeaderTess for Leader { if verts.len() < 2 { return WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -696,6 +697,7 @@ impl LeaderTess for Leader { } WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/entities/multileader.rs b/src/entities/multileader.rs index bed9dfd04..7759e9c34 100644 --- a/src/entities/multileader.rs +++ b/src/entities/multileader.rs @@ -1404,6 +1404,7 @@ impl MultiLeaderTess for MultiLeader { // WireModels so the renderer respects per-piece coloring. let mut wires: Vec = Vec::new(); wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1693,6 +1694,7 @@ impl MultiLeaderTess for MultiLeader { xy = xy.max(y); } wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1760,6 +1762,7 @@ impl MultiLeaderTess for MultiLeader { wcs_corners[3], ]; wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1795,6 +1798,7 @@ impl MultiLeaderTess for MultiLeader { wcs_corners[0], ]; wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/entities/table.rs b/src/entities/table.rs index 08fe7d080..fef5197ff 100644 --- a/src/entities/table.rs +++ b/src/entities/table.rs @@ -753,6 +753,7 @@ pub fn tessellate_table( let mk = |color: [f32; 4], points: Vec<[f32; 3]>, fill_tris: Vec<[f32; 3]>, lw: f32| -> WireModel { WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/aligned_dim.rs b/src/modules/annotate/aligned_dim.rs index 16fd3eadb..f21f0a9c5 100644 --- a/src/modules/annotate/aligned_dim.rs +++ b/src/modules/annotate/aligned_dim.rs @@ -186,6 +186,7 @@ impl CadCommand for AlignedDimensionCommand { let p1 = p1.as_vec3(); let p2 = p2.as_vec3(); Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -237,6 +238,7 @@ fn preview_aligned(p1: DVec3, p2: DVec3, dim_pt: DVec3) -> WireModel { let d1 = d1.as_vec3(); let d2 = d2.as_vec3(); WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/angular_dim.rs b/src/modules/annotate/angular_dim.rs index 070609008..ab8a07732 100644 --- a/src/modules/annotate/angular_dim.rs +++ b/src/modules/annotate/angular_dim.rs @@ -130,6 +130,7 @@ fn v3(pt: DVec3) -> Vector3 { fn preview_wire(points: Vec) -> WireModel { WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/diameter_dim.rs b/src/modules/annotate/diameter_dim.rs index 8b1616bc7..19cbcd985 100644 --- a/src/modules/annotate/diameter_dim.rs +++ b/src/modules/annotate/diameter_dim.rs @@ -180,6 +180,7 @@ fn v3(p: DVec3) -> Vector3 { fn preview_line(a: Vec3, b: Vec3) -> WireModel { WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/dim_baseline.rs b/src/modules/annotate/dim_baseline.rs index d9d5b0af3..a27fa3280 100644 --- a/src/modules/annotate/dim_baseline.rs +++ b/src/modules/annotate/dim_baseline.rs @@ -179,6 +179,7 @@ impl CadCommand for DimBaselineCommand { let dim_line_pt2 = dim_line_pt2.as_vec3(); let pt = pt.as_vec3(); Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/dim_continue.rs b/src/modules/annotate/dim_continue.rs index ea7717e82..1a5547f0a 100644 --- a/src/modules/annotate/dim_continue.rs +++ b/src/modules/annotate/dim_continue.rs @@ -159,6 +159,7 @@ impl CadCommand for DimContinueCommand { let dim_line_pt = p1 + perp * (dim_line_perp - p1.dot(perp)); let dim_line_pt2 = pt + perp * (dim_line_perp - pt.dot(perp)); Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/dimjogline.rs b/src/modules/annotate/dimjogline.rs index fc3ec32fc..0189c8d7e 100644 --- a/src/modules/annotate/dimjogline.rs +++ b/src/modules/annotate/dimjogline.rs @@ -85,6 +85,7 @@ impl CadCommand for DimJogLineCommand { } let d = 0.3_f32; Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/dimtedit.rs b/src/modules/annotate/dimtedit.rs index 7704ed870..a20e59322 100644 --- a/src/modules/annotate/dimtedit.rs +++ b/src/modules/annotate/dimtedit.rs @@ -100,6 +100,7 @@ impl CadCommand for DimTeditCommand { } let d = 0.2_f32; Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/leader_cmd.rs b/src/modules/annotate/leader_cmd.rs index 98c210317..8bc8d29e9 100644 --- a/src/modules/annotate/leader_cmd.rs +++ b/src/modules/annotate/leader_cmd.rs @@ -163,6 +163,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel { points.push([w2.x, w2.y, w2.z]); } WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/linear_dim.rs b/src/modules/annotate/linear_dim.rs index cb2ab82b1..29e39faf6 100644 --- a/src/modules/annotate/linear_dim.rs +++ b/src/modules/annotate/linear_dim.rs @@ -218,6 +218,7 @@ fn v3(pt: DVec3) -> Vector3 { fn preview_wire(points: Vec) -> WireModel { WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/mleader_cmd.rs b/src/modules/annotate/mleader_cmd.rs index 4b2f0a7d1..d4bc07f4f 100644 --- a/src/modules/annotate/mleader_cmd.rs +++ b/src/modules/annotate/mleader_cmd.rs @@ -150,6 +150,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel { points.push([w2.x, w2.y, w2.z]); } WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/mleader_edit.rs b/src/modules/annotate/mleader_edit.rs index eaa77728a..0e8368996 100644 --- a/src/modules/annotate/mleader_edit.rs +++ b/src/modules/annotate/mleader_edit.rs @@ -458,6 +458,7 @@ impl CadCommand for MLeaderCollectCommand { fn preview_wire(pts: &[Vec3]) -> WireModel { WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/ordinate_dim.rs b/src/modules/annotate/ordinate_dim.rs index 7f76d6052..8c374cf44 100644 --- a/src/modules/annotate/ordinate_dim.rs +++ b/src/modules/annotate/ordinate_dim.rs @@ -121,6 +121,7 @@ fn ordinate_elbow(feature: DVec3, leader: DVec3, is_x: bool) -> DVec3 { fn preview_wire(points: Vec) -> WireModel { WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/qdim.rs b/src/modules/annotate/qdim.rs index d38b5fb7a..95685f7d4 100644 --- a/src/modules/annotate/qdim.rs +++ b/src/modules/annotate/qdim.rs @@ -90,6 +90,7 @@ impl CadCommand for QdimCommand { } let d = 0.5_f32; Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/radius_dim.rs b/src/modules/annotate/radius_dim.rs index aa6e99b70..368ad1650 100644 --- a/src/modules/annotate/radius_dim.rs +++ b/src/modules/annotate/radius_dim.rs @@ -185,6 +185,7 @@ fn v3(pt: DVec3) -> Vector3 { fn preview_wire(points: Vec) -> WireModel { WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/table_cmd.rs b/src/modules/annotate/table_cmd.rs index 901b55b93..b1629c548 100644 --- a/src/modules/annotate/table_cmd.rs +++ b/src/modules/annotate/table_cmd.rs @@ -139,6 +139,7 @@ impl CadCommand for TableCommand { let y = pt.y; let z = pt.z; Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/annotate/tolerance_cmd.rs b/src/modules/annotate/tolerance_cmd.rs index ee9df6698..fa03e52ea 100644 --- a/src/modules/annotate/tolerance_cmd.rs +++ b/src/modules/annotate/tolerance_cmd.rs @@ -84,6 +84,7 @@ impl CadCommand for ToleranceCommand { } let d = 0.15_f32; Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/draw/draw/attdef.rs b/src/modules/draw/draw/attdef.rs index e5e958602..701ba552d 100644 --- a/src/modules/draw/draw/attdef.rs +++ b/src/modules/draw/draw/attdef.rs @@ -160,6 +160,7 @@ impl CadCommand for AttdefCommand { // Show a small cross at the insertion point. let d = 0.15_f32; Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/draw/draw/mline.rs b/src/modules/draw/draw/mline.rs index 0b75b137b..d35a8013d 100644 --- a/src/modules/draw/draw/mline.rs +++ b/src/modules/draw/draw/mline.rs @@ -136,6 +136,7 @@ impl CadCommand for MlineCommand { .collect(); pts.push([pt.x, pt.y, pt.z]); Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/draw/draw/raster_image.rs b/src/modules/draw/draw/raster_image.rs index 2c919db18..84535321b 100644 --- a/src/modules/draw/draw/raster_image.rs +++ b/src/modules/draw/draw/raster_image.rs @@ -108,6 +108,7 @@ impl CadCommand for ImageCommand { let p3 = [origin.x, origin.y + world_height, origin.z]; Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/draw/draw/ray.rs b/src/modules/draw/draw/ray.rs index d4077a978..d394827a3 100644 --- a/src/modules/draw/draw/ray.rs +++ b/src/modules/draw/draw/ray.rs @@ -84,6 +84,7 @@ impl CadCommand for RayCommand { let dir = (pt - base).normalize_or_zero(); let far = base + dir * DISPLAY_EXTENT; Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -179,6 +180,7 @@ impl CadCommand for XLineCommand { let far_pos = base + dir * DISPLAY_EXTENT; let far_neg = base - dir * DISPLAY_EXTENT; Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/draw/draw/revcloud.rs b/src/modules/draw/draw/revcloud.rs index d4c160399..1d1573303 100644 --- a/src/modules/draw/draw/revcloud.rs +++ b/src/modules/draw/draw/revcloud.rs @@ -89,6 +89,7 @@ impl CadCommand for RevCloudCommand { self.points[0].z as f32, ]); Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/draw/draw/wipeout.rs b/src/modules/draw/draw/wipeout.rs index 1629f411b..1b748d0f5 100644 --- a/src/modules/draw/draw/wipeout.rs +++ b/src/modules/draw/draw/wipeout.rs @@ -114,6 +114,7 @@ impl CadCommand for WipeoutCommand { let min = p1.min(pt); let max = p1.max(pt); Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -160,6 +161,7 @@ impl CadCommand for WipeoutCommand { self.poly_pts[0].z as f32, ]); Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/draw/inquiry/area.rs b/src/modules/draw/inquiry/area.rs index 3dab672c6..885b302cb 100644 --- a/src/modules/draw/inquiry/area.rs +++ b/src/modules/draw/inquiry/area.rs @@ -69,6 +69,7 @@ impl CadCommand for AreaCommand { pts.push(f(pt)); pts.push(f(self.points[0])); Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/draw/inquiry/dist.rs b/src/modules/draw/inquiry/dist.rs index a595d97e9..83463324c 100644 --- a/src/modules/draw/inquiry/dist.rs +++ b/src/modules/draw/inquiry/dist.rs @@ -63,6 +63,7 @@ impl CadCommand for DistCommand { let p1 = self.first?; // The preview wire is purely visual, so f32 vertices are fine here. Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/draw/inquiry/measuregeom.rs b/src/modules/draw/inquiry/measuregeom.rs index b9ef5fa52..64e889dab 100644 --- a/src/modules/draw/inquiry/measuregeom.rs +++ b/src/modules/draw/inquiry/measuregeom.rs @@ -67,6 +67,7 @@ impl MeasureGeomCommand { /// Build a cyan preview wire connecting the picked points and the cursor. fn preview_wire(name: &str, pts: Vec<[f32; 3]>) -> WireModel { WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/draw/modify/trim.rs b/src/modules/draw/modify/trim.rs index c469e0908..1d0562ae4 100644 --- a/src/modules/draw/modify/trim.rs +++ b/src/modules/draw/modify/trim.rs @@ -3059,6 +3059,7 @@ fn collect_runs(pts: &[[f64; 2]], take: &dyn Fn([f64; 2]) -> bool, out: &mut Vec /// Build a preview wire from a NaN-break point list. fn preview_wire(points: Vec<[f32; 3]>, color: [f32; 4], name: &str) -> WireModel { WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/layout/mview.rs b/src/modules/layout/mview.rs index 89522771f..21ac1b263 100644 --- a/src/modules/layout/mview.rs +++ b/src/modules/layout/mview.rs @@ -81,6 +81,7 @@ impl CadCommand for MviewCommand { let pt = pt.as_vec3(); let c1 = self.corner1?; Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/model/primitive_cmd.rs b/src/modules/model/primitive_cmd.rs index b5c29744e..78566374f 100644 --- a/src/modules/model/primitive_cmd.rs +++ b/src/modules/model/primitive_cmd.rs @@ -305,6 +305,7 @@ inventory::submit!(crate::command::CommandRegistration { fn wire(name: &str, points: Vec<[f32; 3]>) -> WireModel { WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/view/plot_window.rs b/src/modules/view/plot_window.rs index f156523fc..8e1c6a4e7 100644 --- a/src/modules/view/plot_window.rs +++ b/src/modules/view/plot_window.rs @@ -48,6 +48,7 @@ impl CadCommand for PlotWindowCommand { let p1 = self.p1?.as_vec3(); // Draw the selection rectangle. Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/modules/view/zoom_window.rs b/src/modules/view/zoom_window.rs index 957574ae3..79271d5e3 100644 --- a/src/modules/view/zoom_window.rs +++ b/src/modules/view/zoom_window.rs @@ -65,6 +65,7 @@ impl CadCommand for ZoomWindowCommand { let max = p1.max(pt); // Draw a rectangle preview Some(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/scene/cache/block_cache.rs b/src/scene/cache/block_cache.rs index b305311c9..2a644e5dc 100644 --- a/src/scene/cache/block_cache.rs +++ b/src/scene/cache/block_cache.rs @@ -325,6 +325,12 @@ fn build_defn( subs.push(LocalSub::Nested(build_nested_ref(nested_ins, doc, bg_color))); } _ => { + // A wide polyline inside a block keeps its thin centre-line + // here (`LocalWire` carries no `world_width`, and `finalize` + // rebuilds every block wire with `world_width: 0.0`); its band + // is drawn by the hatch pipeline (`push_block_wide_fills`), + // which transforms with the block instance so the band scales + // correctly. The shader band is a top-level-entity path only. for lw in tessellate_sub_local(doc, entity, anno_scale, bg_color) { subs.push(LocalSub::Wire(lw)); } @@ -848,6 +854,7 @@ impl Batches { // the cached defn. let color = crate::scene::view::render::adapt_to_bg(b.color, bg_color); WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: b.pick_tris, pick_tris_low: b.pick_tris_low, diff --git a/src/scene/convert/tess.rs b/src/scene/convert/tess.rs index 1981e37f0..db375308a 100644 --- a/src/scene/convert/tess.rs +++ b/src/scene/convert/tess.rs @@ -650,6 +650,7 @@ pub(crate) fn tessellate_entity( (ins.insert_point.z) as f32, ); let marker = WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1029,6 +1030,7 @@ fn lod_stub_wire( // LOD boundary. #19. let stored_color = if selected { WireModel::SELECTED } else { color }; WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1115,6 +1117,7 @@ fn lod_stub_wire_3d( } let stored_color = if selected { WireModel::SELECTED } else { color }; WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/scene/convert/tessellate.rs b/src/scene/convert/tessellate.rs index d72c71efb..b8a888c40 100644 --- a/src/scene/convert/tessellate.rs +++ b/src/scene/convert/tessellate.rs @@ -370,6 +370,7 @@ pub fn tessellate( None }; elem_wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -660,6 +661,7 @@ pub fn tessellate( ftl.push(lo); } wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -697,6 +699,7 @@ pub fn tessellate( fpl.push(lo); } wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -741,6 +744,7 @@ pub fn tessellate( low.push(ll); } wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -766,6 +770,7 @@ pub fn tessellate( }); } wires.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -821,6 +826,7 @@ pub fn tessellate( (Vec::new(), Vec::new(), Vec::new()) }; out.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -858,6 +864,7 @@ pub fn tessellate( (Vec::new(), Vec::new(), Vec::new()) }; out.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -892,6 +899,7 @@ pub fn tessellate( // are empty → the early-return path above). if !sdf_verts.is_empty() { out.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -919,6 +927,7 @@ pub fn tessellate( if out.is_empty() { out.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -968,6 +977,7 @@ pub fn tessellate( .map(|[kx, ky, kz]| [kx, ky, kz]) .collect(); return vec![WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1016,6 +1026,7 @@ pub fn tessellate( .map(|[x, y, z]| [x, y, z]) .collect(); return vec![WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1052,9 +1063,12 @@ pub fn tessellate( .into_iter() .map(|[x, y, z]| [x, y, z]) .collect(); - // A wide polyline arrives here: its band is pick geometry. + // A wide polyline arrives here: the shader expands this + // centre-line to `world_width` so the band IS the wire (the + // linetype dashes it); the band also backs pick. let (pick_tris, pick_tris_low) = points_to_ds(te.pick_tris); return vec![WireModel { + world_width: lwpolyline_band_width(entity), fill_is_3d: false, pick_tris, pick_tris_low, @@ -1140,6 +1154,7 @@ pub fn tessellate( (Vec::new(), Vec::new(), Vec::new()) }; out.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris, pick_tris_low, @@ -1176,6 +1191,7 @@ pub fn tessellate( (Vec::new(), Vec::new(), Vec::new()) }; out.push(WireModel { + world_width: 0.0, pick_tris: Vec::new(), pick_tris_low: Vec::new(), dash_from_start: false, @@ -1203,6 +1219,7 @@ pub fn tessellate( if out.is_empty() { out.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1239,9 +1256,11 @@ pub fn tessellate( .into_iter() .map(|[x, y, z]| [x, y, z]) .collect(); - // A wide polyline with PLINEGEN=0 arrives here. + // A wide polyline with PLINEGEN=0 arrives here: same shader-band + // treatment as the Contour arm, restarting the dash per segment. let (pick_tris, pick_tris_low) = points_to_ds(te.pick_tris); return vec![WireModel { + world_width: lwpolyline_band_width(entity), fill_is_3d: false, pick_tris, pick_tris_low, @@ -1310,6 +1329,7 @@ pub fn tessellate( let snap_pts: Vec<(glam::DVec3, SnapHint)> = snap_pts.into_iter().map(|(p, h)| (p.as_dvec3(), h)).collect(); vec![WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -1489,6 +1509,29 @@ pub(crate) fn entity_z(entity: &EntityType) -> f32 { } } +/// Band width (drawing units) of a wide LwPolyline whose flat band is drawn by +/// expanding its centre-line wire in the shader. `0.0` = a zero-width polyline +/// or a non-LwPolyline (a normal screen-pixel wire). A thickness-extruded +/// polyline is excluded: its band becomes a 3-D tube (`thick_wide_band`), not a +/// flat shader band. A tapering polyline (per-vertex start/end widths) collapses +/// to its widest edge — the shader carries one width per wire. +fn lwpolyline_band_width(entity: &EntityType) -> f32 { + match entity { + EntityType::LwPolyline(p) if p.thickness.abs() <= 1e-10 => { + let mut w = p.constant_width; + for v in &p.vertices { + w = w.max(v.start_width).max(v.end_width); + } + if w > 1e-9 { + w as f32 + } else { + 0.0 + } + } + _ => 0.0, + } +} + // ── Fallback geometry (Viewport, Insert, Hatch outline, Ole2Frame) ─────── // // Per-entity blocks have moved to their respective `entities/*.rs` files diff --git a/src/scene/entity.rs b/src/scene/entity.rs index a467ca6bc..c006565fe 100644 --- a/src/scene/entity.rs +++ b/src/scene/entity.rs @@ -691,15 +691,14 @@ impl Scene { // Wide LWPolyline and Polyline2D fills for entity in self.document.entities() { let (common, fill_origin, fills) = match entity { - // A thickened wide LwPolyline becomes a 3-D tube: its bottom cap - // is drawn as a real depth-tested surface by the tessellator, not - // as this flat 2-D band (which, having only draw-order depth, - // would show through the tube walls in a 3-D view). - EntityType::LwPolyline(pl) if pl.thickness.abs() > 1e-10 => continue, - EntityType::LwPolyline(pl) => { - let (o, f) = crate::entities::lwpolyline::wide_fills(pl); - (&pl.common, o, f) - } + // A wide LwPolyline no longer draws its band here: the shader + // expands the centre-line wire to `world_width` (a real + // depth-tested band that also carries the linetype dash), so a + // second flat 2-D band with only draw-order depth would double + // it and show through 3-D geometry. A thickened one is likewise + // a 3-D tube built by the tessellator. Its band still backs pick + // (`pick_tris`), just not this fill. + EntityType::LwPolyline(_) => continue, EntityType::Polyline2D(pl) => { let (o, f) = crate::entities::polyline::wide_fills(pl); (&pl.common, o, f) diff --git a/src/scene/model/wire_model.rs b/src/scene/model/wire_model.rs index d59fdd617..2057a32e0 100644 --- a/src/scene/model/wire_model.rs +++ b/src/scene/model/wire_model.rs @@ -54,6 +54,12 @@ pub struct WireModel { pub pattern: [f32; 8], /// Rendered line width in screen pixels (half-width = line_weight_px / 2). pub line_weight_px: f32, + /// World-space band width (drawing units). `0.0` = a normal wire whose + /// width comes from `line_weight_px` (screen pixels). Non-zero = a wide + /// polyline: the shader expands this centre-line to `world_width` world + /// units (scaling with zoom) so the band IS the wire — the linetype dash + /// pattern then applies to the band instead of a separate hatch fill. + pub world_width: f32, /// ACI color index (1-255). 0 means true-color or unknown (no CTB lookup). pub aci: u8, /// Pre-baked snap candidates (Center, Node, Quadrant, Insertion). @@ -173,6 +179,7 @@ impl WireModel { /// Create a solid wire (no dash pattern, 1px weight). pub fn solid(name: String, points: Vec<[f32; 3]>, color: [f32; 4], selected: bool) -> Self { Self { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -394,6 +401,7 @@ impl Default for WireModel { pattern_length: 0.0, pattern: [0.0; 8], line_weight_px: 1.0, + world_width: 0.0, aci: 0, snap_pts: Vec::new(), tangent_geoms: Vec::new(), diff --git a/src/scene/pipeline/wire_gpu.rs b/src/scene/pipeline/wire_gpu.rs index 6bdbc0d0c..cffdbcb23 100644 --- a/src/scene/pipeline/wire_gpu.rs +++ b/src/scene/pipeline/wire_gpu.rs @@ -119,7 +119,11 @@ pub struct WireConst { pub draw_depth: f32, pub align_end: f32, pub align_total: f32, - pub _pad0: f32, + /// World-space half-width for a wide-polyline band. `0.0` = a normal wire + /// (uses `half_width`, screen pixels). Non-zero = the vertex shader expands + /// the quad by `world_half_width / world_per_pixel` so the band tracks zoom + /// in drawing units. + pub world_half_width: f32, pub _pad1: f32, pub _pad2: f32, } @@ -171,6 +175,9 @@ pub struct WireInstance { /// and the total wire length. `align_total == 0.0` = not aligned. pub align_end: f32, pub align_total: f32, + /// World-space half-width for a wide-polyline band (see `WireConst`). `0.0` + /// = a normal wire (uses `half_width`, screen pixels). + pub world_half_width: f32, } #[cfg(target_arch = "wasm32")] @@ -194,6 +201,7 @@ impl WireInstance { wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, pos_b_low) as u64, shader_location: 11, format: wgpu::VertexFormat::Float32x3 }, wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, align_end) as u64, shader_location: 12, format: wgpu::VertexFormat::Float32 }, wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, align_total) as u64, shader_location: 13, format: wgpu::VertexFormat::Float32 }, + wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, world_half_width) as u64, shader_location: 14, format: wgpu::VertexFormat::Float32 }, ]; wgpu::VertexBufferLayout { array_stride: std::mem::size_of::() as u64, @@ -403,6 +411,7 @@ fn emit_wire_instances(wire: &WireModel, color: [f32; 4], draw_depth: f32) -> Ve draw_depth, align_end, align_total, + world_half_width: wire.world_width * 0.5, }); } instances @@ -427,7 +436,7 @@ pub(crate) fn emit_wire_native( draw_depth, align_end, align_total, - _pad0: 0.0, + world_half_width: wire.world_width * 0.5, _pad1: 0.0, _pad2: 0.0, }; diff --git a/src/scene/text/complex_lt.rs b/src/scene/text/complex_lt.rs index 1e80264f2..814963fb4 100644 --- a/src/scene/text/complex_lt.rs +++ b/src/scene/text/complex_lt.rs @@ -305,6 +305,7 @@ pub fn apply_along( .into_iter() .filter(|s| s.len() >= 2) .map(|pts| WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), @@ -344,6 +345,7 @@ pub fn apply_along( xy = xy.max(y); } out.push(WireModel { + world_width: 0.0, fill_is_3d: false, pick_tris: Vec::new(), pick_tris_low: Vec::new(), diff --git a/src/shaders/wire.wgsl b/src/shaders/wire.wgsl index c9efdbf23..0de11db05 100644 --- a/src/shaders/wire.wgsl +++ b/src/shaders/wire.wgsl @@ -59,6 +59,8 @@ struct InstanceIn { // "A"-type endpoint alignment: end-dash length + total wire length. @location(12) align_end: f32, @location(13) align_total: f32, + // World-space half-width for a wide-polyline band. 0 = normal wire. + @location(14) world_half_width: f32, } // Draw-order depth bias: shifts clip-space z so 2D entities of different @@ -129,8 +131,18 @@ struct VertexOut { // Select the clip-space position for this vertex's endpoint. let clip_pos = mix(clip_a, clip_b, which_end); - // LWDISPLAY off → collapse to a 1-pixel-wide line (half_width = 0.5). - let hw = select(0.5, in.half_width, u.lwdisplay_enable > 0.5); + // A wide polyline carries its band width in world units: expand the quad by + // `world_half_width / world_per_pixel` (pixels) so the band tracks zoom. A + // normal wire (world_half_width == 0) uses the screen-pixel half-width, + // honouring the LWDISPLAY toggle (off → collapse to a 1-pixel line). + var hw: f32; + if in.world_half_width > 0.0 { + // Clamp to a half-pixel so a zoomed-out band never drops below a + // hairline (its centre-line) instead of vanishing. + hw = max(in.world_half_width / u.world_per_pixel, 0.5); + } else { + hw = select(0.5, in.half_width, u.lwdisplay_enable > 0.5); + } // Offset in clip space (multiply by w to un-apply perspective division). let ndc_offset = perp_ndc * hw * side; diff --git a/src/shaders/wire_indexed.wgsl b/src/shaders/wire_indexed.wgsl index e08a230d8..4f44344c3 100644 --- a/src/shaders/wire_indexed.wgsl +++ b/src/shaders/wire_indexed.wgsl @@ -30,7 +30,7 @@ struct WireConst { draw_depth: f32, align_end: f32, align_total: f32, - _pad0: f32, + world_half_width: f32, _pad1: f32, _pad2: f32, } @@ -93,7 +93,18 @@ struct VertexOut { let clip_pos = mix(clip_a, clip_b, which_end); - let hw = select(0.5, c.half_width, u.lwdisplay_enable > 0.5); + // A wide polyline carries its band width in world units: expand the quad + // by `world_half_width / world_per_pixel` (screen pixels) so the band grows + // and shrinks with zoom. A normal wire (world_half_width == 0) uses the + // screen-pixel half-width, honouring the LWDISPLAY toggle. + var hw: f32; + if c.world_half_width > 0.0 { + // Clamp to a half-pixel so a zoomed-out band never drops below a + // hairline (its centre-line) instead of vanishing. + hw = max(c.world_half_width / u.world_per_pixel, 0.5); + } else { + hw = select(0.5, c.half_width, u.lwdisplay_enable > 0.5); + } let ndc_offset = perp_ndc * hw * side; let final_clip = clip_pos + vec4(ndc_offset * clip_pos.w, 0.0, 0.0);