feat(lwpolyline): render the wide-polyline band via the wire shader
A wide polyline's flat band was drawn by the hatch pipeline: a solid fill with only draw-order depth. It ignored the entity linetype (the width "crushed" the dash pattern) and showed through 3-D geometry in a tilted view. Draw the band from the polyline's own centre-line wire instead. A new `WireModel.world_width` (drawing units) makes the wire vertex shader expand the quad by `world_half_width / world_per_pixel` (clamped to a hairline), so the band is sized in world units and tracks zoom. Because it is now a wire: - the GPU dash shader dashes the band by arc-length — the linetype applies; - it is depth-tested at the polyline elevation — no draw-order see-through; - no CPU band triangles or per-dash geometry are generated. `world_half_width` rides in `WireConst` (native, replacing a pad) and the web `WireInstance` (loc 14); both wire shaders branch on it. The tessellator sets `world_width` from the LwPolyline width on the Contour / SegmentedLines arms, and the model-space fill loop no longer emits a hatch band for any LwPolyline. Pick still uses the band polygon (`pick_tris`). Blocks keep the hatch band: `LocalWire` carries no `world_width` (finalize zeroes it), so a scaled insert still scales its band correctly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
3d37e1f8d8
commit
bd1a5affd7
44 changed files with 158 additions and 17 deletions
|
|
@ -1235,6 +1235,7 @@ pub(super) fn on_tick(&mut self, t: Instant) -> Task<Message> {
|
||||||
let far_pos = base + dir * far;
|
let far_pos = base + dir * far;
|
||||||
let far_neg = base - dir * far;
|
let far_neg = base - dir * far;
|
||||||
let guide = crate::scene::WireModel {
|
let guide = crate::scene::WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -1273,6 +1274,7 @@ pub(super) fn on_tick(&mut self, t: Instant) -> Task<Message> {
|
||||||
let far_pos = h.base + h.dir * far;
|
let far_pos = h.base + h.dir * far;
|
||||||
let far_neg = h.base - h.dir * far;
|
let far_neg = h.base - h.dir * far;
|
||||||
previews.push(crate::scene::WireModel {
|
previews.push(crate::scene::WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -1352,6 +1352,7 @@ fn tessellate_dimension_inner(
|
||||||
let (ext1, ext2) = split_ext_lines(&geom.ext_lines);
|
let (ext1, ext2) = split_ext_lines(&geom.ext_lines);
|
||||||
if !ext1.is_empty() {
|
if !ext1.is_empty() {
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -1378,6 +1379,7 @@ fn tessellate_dimension_inner(
|
||||||
}
|
}
|
||||||
if !ext2.is_empty() {
|
if !ext2.is_empty() {
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -1404,6 +1406,7 @@ fn tessellate_dimension_inner(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -1431,6 +1434,7 @@ fn tessellate_dimension_inner(
|
||||||
}
|
}
|
||||||
|
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -1474,6 +1478,7 @@ fn tessellate_dimension_inner(
|
||||||
aci_to_rgba(&c)
|
aci_to_rgba(&c)
|
||||||
};
|
};
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -590,6 +590,7 @@ impl LeaderTess for Leader {
|
||||||
|
|
||||||
if verts.len() < 2 {
|
if verts.len() < 2 {
|
||||||
return WireModel {
|
return WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -696,6 +697,7 @@ impl LeaderTess for Leader {
|
||||||
}
|
}
|
||||||
|
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -1404,6 +1404,7 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
// WireModels so the renderer respects per-piece coloring.
|
// WireModels so the renderer respects per-piece coloring.
|
||||||
let mut wires: Vec<WireModel> = Vec::new();
|
let mut wires: Vec<WireModel> = Vec::new();
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -1693,6 +1694,7 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
xy = xy.max(y);
|
xy = xy.max(y);
|
||||||
}
|
}
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -1760,6 +1762,7 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
wcs_corners[3],
|
wcs_corners[3],
|
||||||
];
|
];
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -1795,6 +1798,7 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
wcs_corners[0],
|
wcs_corners[0],
|
||||||
];
|
];
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -753,6 +753,7 @@ pub fn tessellate_table(
|
||||||
let mk =
|
let mk =
|
||||||
|color: [f32; 4], points: Vec<[f32; 3]>, fill_tris: Vec<[f32; 3]>, lw: f32| -> WireModel {
|
|color: [f32; 4], points: Vec<[f32; 3]>, fill_tris: Vec<[f32; 3]>, lw: f32| -> WireModel {
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,7 @@ impl CadCommand for AlignedDimensionCommand {
|
||||||
let p1 = p1.as_vec3();
|
let p1 = p1.as_vec3();
|
||||||
let p2 = p2.as_vec3();
|
let p2 = p2.as_vec3();
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: 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 d1 = d1.as_vec3();
|
||||||
let d2 = d2.as_vec3();
|
let d2 = d2.as_vec3();
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,7 @@ fn v3(pt: DVec3) -> Vector3 {
|
||||||
|
|
||||||
fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,7 @@ fn v3(p: DVec3) -> Vector3 {
|
||||||
|
|
||||||
fn preview_line(a: Vec3, b: Vec3) -> WireModel {
|
fn preview_line(a: Vec3, b: Vec3) -> WireModel {
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,7 @@ impl CadCommand for DimBaselineCommand {
|
||||||
let dim_line_pt2 = dim_line_pt2.as_vec3();
|
let dim_line_pt2 = dim_line_pt2.as_vec3();
|
||||||
let pt = pt.as_vec3();
|
let pt = pt.as_vec3();
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,7 @@ impl CadCommand for DimContinueCommand {
|
||||||
let dim_line_pt = p1 + perp * (dim_line_perp - p1.dot(perp));
|
let dim_line_pt = p1 + perp * (dim_line_perp - p1.dot(perp));
|
||||||
let dim_line_pt2 = pt + perp * (dim_line_perp - pt.dot(perp));
|
let dim_line_pt2 = pt + perp * (dim_line_perp - pt.dot(perp));
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ impl CadCommand for DimJogLineCommand {
|
||||||
}
|
}
|
||||||
let d = 0.3_f32;
|
let d = 0.3_f32;
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ impl CadCommand for DimTeditCommand {
|
||||||
}
|
}
|
||||||
let d = 0.2_f32;
|
let d = 0.2_f32;
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel {
|
||||||
points.push([w2.x, w2.y, w2.z]);
|
points.push([w2.x, w2.y, w2.z]);
|
||||||
}
|
}
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,7 @@ fn v3(pt: DVec3) -> Vector3 {
|
||||||
|
|
||||||
fn preview_wire(points: Vec<DVec3>) -> WireModel {
|
fn preview_wire(points: Vec<DVec3>) -> WireModel {
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,7 @@ fn preview_wire(pts: &[Vec3]) -> WireModel {
|
||||||
points.push([w2.x, w2.y, w2.z]);
|
points.push([w2.x, w2.y, w2.z]);
|
||||||
}
|
}
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -458,6 +458,7 @@ impl CadCommand for MLeaderCollectCommand {
|
||||||
|
|
||||||
fn preview_wire(pts: &[Vec3]) -> WireModel {
|
fn preview_wire(pts: &[Vec3]) -> WireModel {
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ fn ordinate_elbow(feature: DVec3, leader: DVec3, is_x: bool) -> DVec3 {
|
||||||
|
|
||||||
fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ impl CadCommand for QdimCommand {
|
||||||
}
|
}
|
||||||
let d = 0.5_f32;
|
let d = 0.5_f32;
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,7 @@ fn v3(pt: DVec3) -> Vector3 {
|
||||||
|
|
||||||
fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,7 @@ impl CadCommand for TableCommand {
|
||||||
let y = pt.y;
|
let y = pt.y;
|
||||||
let z = pt.z;
|
let z = pt.z;
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ impl CadCommand for ToleranceCommand {
|
||||||
}
|
}
|
||||||
let d = 0.15_f32;
|
let d = 0.15_f32;
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,7 @@ impl CadCommand for AttdefCommand {
|
||||||
// Show a small cross at the insertion point.
|
// Show a small cross at the insertion point.
|
||||||
let d = 0.15_f32;
|
let d = 0.15_f32;
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,7 @@ impl CadCommand for MlineCommand {
|
||||||
.collect();
|
.collect();
|
||||||
pts.push([pt.x, pt.y, pt.z]);
|
pts.push([pt.x, pt.y, pt.z]);
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ impl CadCommand for ImageCommand {
|
||||||
let p3 = [origin.x, origin.y + world_height, origin.z];
|
let p3 = [origin.x, origin.y + world_height, origin.z];
|
||||||
|
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ impl CadCommand for RayCommand {
|
||||||
let dir = (pt - base).normalize_or_zero();
|
let dir = (pt - base).normalize_or_zero();
|
||||||
let far = base + dir * DISPLAY_EXTENT;
|
let far = base + dir * DISPLAY_EXTENT;
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -179,6 +180,7 @@ impl CadCommand for XLineCommand {
|
||||||
let far_pos = base + dir * DISPLAY_EXTENT;
|
let far_pos = base + dir * DISPLAY_EXTENT;
|
||||||
let far_neg = base - dir * DISPLAY_EXTENT;
|
let far_neg = base - dir * DISPLAY_EXTENT;
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ impl CadCommand for RevCloudCommand {
|
||||||
self.points[0].z as f32,
|
self.points[0].z as f32,
|
||||||
]);
|
]);
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ impl CadCommand for WipeoutCommand {
|
||||||
let min = p1.min(pt);
|
let min = p1.min(pt);
|
||||||
let max = p1.max(pt);
|
let max = p1.max(pt);
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -160,6 +161,7 @@ impl CadCommand for WipeoutCommand {
|
||||||
self.poly_pts[0].z as f32,
|
self.poly_pts[0].z as f32,
|
||||||
]);
|
]);
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ impl CadCommand for AreaCommand {
|
||||||
pts.push(f(pt));
|
pts.push(f(pt));
|
||||||
pts.push(f(self.points[0]));
|
pts.push(f(self.points[0]));
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ impl CadCommand for DistCommand {
|
||||||
let p1 = self.first?;
|
let p1 = self.first?;
|
||||||
// The preview wire is purely visual, so f32 vertices are fine here.
|
// The preview wire is purely visual, so f32 vertices are fine here.
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ impl MeasureGeomCommand {
|
||||||
/// Build a cyan preview wire connecting the picked points and the cursor.
|
/// Build a cyan preview wire connecting the picked points and the cursor.
|
||||||
fn preview_wire(name: &str, pts: Vec<[f32; 3]>) -> WireModel {
|
fn preview_wire(name: &str, pts: Vec<[f32; 3]>) -> WireModel {
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -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.
|
/// Build a preview wire from a NaN-break point list.
|
||||||
fn preview_wire(points: Vec<[f32; 3]>, color: [f32; 4], name: &str) -> WireModel {
|
fn preview_wire(points: Vec<[f32; 3]>, color: [f32; 4], name: &str) -> WireModel {
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ impl CadCommand for MviewCommand {
|
||||||
let pt = pt.as_vec3();
|
let pt = pt.as_vec3();
|
||||||
let c1 = self.corner1?;
|
let c1 = self.corner1?;
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -305,6 +305,7 @@ inventory::submit!(crate::command::CommandRegistration {
|
||||||
|
|
||||||
fn wire(name: &str, points: Vec<[f32; 3]>) -> WireModel {
|
fn wire(name: &str, points: Vec<[f32; 3]>) -> WireModel {
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ impl CadCommand for PlotWindowCommand {
|
||||||
let p1 = self.p1?.as_vec3();
|
let p1 = self.p1?.as_vec3();
|
||||||
// Draw the selection rectangle.
|
// Draw the selection rectangle.
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ impl CadCommand for ZoomWindowCommand {
|
||||||
let max = p1.max(pt);
|
let max = p1.max(pt);
|
||||||
// Draw a rectangle preview
|
// Draw a rectangle preview
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
7
src/scene/cache/block_cache.rs
vendored
7
src/scene/cache/block_cache.rs
vendored
|
|
@ -325,6 +325,12 @@ fn build_defn(
|
||||||
subs.push(LocalSub::Nested(build_nested_ref(nested_ins, doc, bg_color)));
|
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) {
|
for lw in tessellate_sub_local(doc, entity, anno_scale, bg_color) {
|
||||||
subs.push(LocalSub::Wire(lw));
|
subs.push(LocalSub::Wire(lw));
|
||||||
}
|
}
|
||||||
|
|
@ -848,6 +854,7 @@ impl Batches {
|
||||||
// the cached defn.
|
// the cached defn.
|
||||||
let color = crate::scene::view::render::adapt_to_bg(b.color, bg_color);
|
let color = crate::scene::view::render::adapt_to_bg(b.color, bg_color);
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: b.pick_tris,
|
pick_tris: b.pick_tris,
|
||||||
pick_tris_low: b.pick_tris_low,
|
pick_tris_low: b.pick_tris_low,
|
||||||
|
|
|
||||||
|
|
@ -650,6 +650,7 @@ pub(crate) fn tessellate_entity(
|
||||||
(ins.insert_point.z) as f32,
|
(ins.insert_point.z) as f32,
|
||||||
);
|
);
|
||||||
let marker = WireModel {
|
let marker = WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -1029,6 +1030,7 @@ fn lod_stub_wire(
|
||||||
// LOD boundary. #19.
|
// LOD boundary. #19.
|
||||||
let stored_color = if selected { WireModel::SELECTED } else { color };
|
let stored_color = if selected { WireModel::SELECTED } else { color };
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: 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 };
|
let stored_color = if selected { WireModel::SELECTED } else { color };
|
||||||
WireModel {
|
WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -370,6 +370,7 @@ pub fn tessellate(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
elem_wires.push(WireModel {
|
elem_wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -660,6 +661,7 @@ pub fn tessellate(
|
||||||
ftl.push(lo);
|
ftl.push(lo);
|
||||||
}
|
}
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -697,6 +699,7 @@ pub fn tessellate(
|
||||||
fpl.push(lo);
|
fpl.push(lo);
|
||||||
}
|
}
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -741,6 +744,7 @@ pub fn tessellate(
|
||||||
low.push(ll);
|
low.push(ll);
|
||||||
}
|
}
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -766,6 +770,7 @@ pub fn tessellate(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -821,6 +826,7 @@ pub fn tessellate(
|
||||||
(Vec::new(), Vec::new(), Vec::new())
|
(Vec::new(), Vec::new(), Vec::new())
|
||||||
};
|
};
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -858,6 +864,7 @@ pub fn tessellate(
|
||||||
(Vec::new(), Vec::new(), Vec::new())
|
(Vec::new(), Vec::new(), Vec::new())
|
||||||
};
|
};
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -892,6 +899,7 @@ pub fn tessellate(
|
||||||
// are empty → the early-return path above).
|
// are empty → the early-return path above).
|
||||||
if !sdf_verts.is_empty() {
|
if !sdf_verts.is_empty() {
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -919,6 +927,7 @@ pub fn tessellate(
|
||||||
|
|
||||||
if out.is_empty() {
|
if out.is_empty() {
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -968,6 +977,7 @@ pub fn tessellate(
|
||||||
.map(|[kx, ky, kz]| [kx, ky, kz])
|
.map(|[kx, ky, kz]| [kx, ky, kz])
|
||||||
.collect();
|
.collect();
|
||||||
return vec![WireModel {
|
return vec![WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -1016,6 +1026,7 @@ pub fn tessellate(
|
||||||
.map(|[x, y, z]| [x, y, z])
|
.map(|[x, y, z]| [x, y, z])
|
||||||
.collect();
|
.collect();
|
||||||
return vec![WireModel {
|
return vec![WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -1052,9 +1063,12 @@ pub fn tessellate(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|[x, y, z]| [x, y, z])
|
.map(|[x, y, z]| [x, y, z])
|
||||||
.collect();
|
.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);
|
let (pick_tris, pick_tris_low) = points_to_ds(te.pick_tris);
|
||||||
return vec![WireModel {
|
return vec![WireModel {
|
||||||
|
world_width: lwpolyline_band_width(entity),
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris,
|
pick_tris,
|
||||||
pick_tris_low,
|
pick_tris_low,
|
||||||
|
|
@ -1140,6 +1154,7 @@ pub fn tessellate(
|
||||||
(Vec::new(), Vec::new(), Vec::new())
|
(Vec::new(), Vec::new(), Vec::new())
|
||||||
};
|
};
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris,
|
pick_tris,
|
||||||
pick_tris_low,
|
pick_tris_low,
|
||||||
|
|
@ -1176,6 +1191,7 @@ pub fn tessellate(
|
||||||
(Vec::new(), Vec::new(), Vec::new())
|
(Vec::new(), Vec::new(), Vec::new())
|
||||||
};
|
};
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
dash_from_start: false,
|
dash_from_start: false,
|
||||||
|
|
@ -1203,6 +1219,7 @@ pub fn tessellate(
|
||||||
|
|
||||||
if out.is_empty() {
|
if out.is_empty() {
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -1239,9 +1256,11 @@ pub fn tessellate(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|[x, y, z]| [x, y, z])
|
.map(|[x, y, z]| [x, y, z])
|
||||||
.collect();
|
.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);
|
let (pick_tris, pick_tris_low) = points_to_ds(te.pick_tris);
|
||||||
return vec![WireModel {
|
return vec![WireModel {
|
||||||
|
world_width: lwpolyline_band_width(entity),
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris,
|
pick_tris,
|
||||||
pick_tris_low,
|
pick_tris_low,
|
||||||
|
|
@ -1310,6 +1329,7 @@ pub fn tessellate(
|
||||||
let snap_pts: Vec<(glam::DVec3, SnapHint)> =
|
let snap_pts: Vec<(glam::DVec3, SnapHint)> =
|
||||||
snap_pts.into_iter().map(|(p, h)| (p.as_dvec3(), h)).collect();
|
snap_pts.into_iter().map(|(p, h)| (p.as_dvec3(), h)).collect();
|
||||||
vec![WireModel {
|
vec![WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: 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) ───────
|
// ── Fallback geometry (Viewport, Insert, Hatch outline, Ole2Frame) ───────
|
||||||
//
|
//
|
||||||
// Per-entity blocks have moved to their respective `entities/*.rs` files
|
// Per-entity blocks have moved to their respective `entities/*.rs` files
|
||||||
|
|
|
||||||
|
|
@ -691,15 +691,14 @@ 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, fill_origin, fills) = match entity {
|
let (common, fill_origin, fills) = match entity {
|
||||||
// A thickened wide LwPolyline becomes a 3-D tube: its bottom cap
|
// A wide LwPolyline no longer draws its band here: the shader
|
||||||
// is drawn as a real depth-tested surface by the tessellator, not
|
// expands the centre-line wire to `world_width` (a real
|
||||||
// as this flat 2-D band (which, having only draw-order depth,
|
// depth-tested band that also carries the linetype dash), so a
|
||||||
// would show through the tube walls in a 3-D view).
|
// second flat 2-D band with only draw-order depth would double
|
||||||
EntityType::LwPolyline(pl) if pl.thickness.abs() > 1e-10 => continue,
|
// it and show through 3-D geometry. A thickened one is likewise
|
||||||
EntityType::LwPolyline(pl) => {
|
// a 3-D tube built by the tessellator. Its band still backs pick
|
||||||
let (o, f) = crate::entities::lwpolyline::wide_fills(pl);
|
// (`pick_tris`), just not this fill.
|
||||||
(&pl.common, o, f)
|
EntityType::LwPolyline(_) => continue,
|
||||||
}
|
|
||||||
EntityType::Polyline2D(pl) => {
|
EntityType::Polyline2D(pl) => {
|
||||||
let (o, f) = crate::entities::polyline::wide_fills(pl);
|
let (o, f) = crate::entities::polyline::wide_fills(pl);
|
||||||
(&pl.common, o, f)
|
(&pl.common, o, f)
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,12 @@ pub struct WireModel {
|
||||||
pub pattern: [f32; 8],
|
pub pattern: [f32; 8],
|
||||||
/// Rendered line width in screen pixels (half-width = line_weight_px / 2).
|
/// Rendered line width in screen pixels (half-width = line_weight_px / 2).
|
||||||
pub line_weight_px: f32,
|
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).
|
/// ACI color index (1-255). 0 means true-color or unknown (no CTB lookup).
|
||||||
pub aci: u8,
|
pub aci: u8,
|
||||||
/// Pre-baked snap candidates (Center, Node, Quadrant, Insertion).
|
/// Pre-baked snap candidates (Center, Node, Quadrant, Insertion).
|
||||||
|
|
@ -173,6 +179,7 @@ impl WireModel {
|
||||||
/// Create a solid wire (no dash pattern, 1px weight).
|
/// Create a solid wire (no dash pattern, 1px weight).
|
||||||
pub fn solid(name: String, points: Vec<[f32; 3]>, color: [f32; 4], selected: bool) -> Self {
|
pub fn solid(name: String, points: Vec<[f32; 3]>, color: [f32; 4], selected: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -394,6 +401,7 @@ impl Default for WireModel {
|
||||||
pattern_length: 0.0,
|
pattern_length: 0.0,
|
||||||
pattern: [0.0; 8],
|
pattern: [0.0; 8],
|
||||||
line_weight_px: 1.0,
|
line_weight_px: 1.0,
|
||||||
|
world_width: 0.0,
|
||||||
aci: 0,
|
aci: 0,
|
||||||
snap_pts: Vec::new(),
|
snap_pts: Vec::new(),
|
||||||
tangent_geoms: Vec::new(),
|
tangent_geoms: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,11 @@ pub struct WireConst {
|
||||||
pub draw_depth: f32,
|
pub draw_depth: f32,
|
||||||
pub align_end: f32,
|
pub align_end: f32,
|
||||||
pub align_total: 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 _pad1: f32,
|
||||||
pub _pad2: f32,
|
pub _pad2: f32,
|
||||||
}
|
}
|
||||||
|
|
@ -171,6 +175,9 @@ pub struct WireInstance {
|
||||||
/// and the total wire length. `align_total == 0.0` = not aligned.
|
/// and the total wire length. `align_total == 0.0` = not aligned.
|
||||||
pub align_end: f32,
|
pub align_end: f32,
|
||||||
pub align_total: 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")]
|
#[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, 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_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, 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 {
|
wgpu::VertexBufferLayout {
|
||||||
array_stride: std::mem::size_of::<WireInstance>() as u64,
|
array_stride: std::mem::size_of::<WireInstance>() as u64,
|
||||||
|
|
@ -403,6 +411,7 @@ fn emit_wire_instances(wire: &WireModel, color: [f32; 4], draw_depth: f32) -> Ve
|
||||||
draw_depth,
|
draw_depth,
|
||||||
align_end,
|
align_end,
|
||||||
align_total,
|
align_total,
|
||||||
|
world_half_width: wire.world_width * 0.5,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
instances
|
instances
|
||||||
|
|
@ -427,7 +436,7 @@ pub(crate) fn emit_wire_native(
|
||||||
draw_depth,
|
draw_depth,
|
||||||
align_end,
|
align_end,
|
||||||
align_total,
|
align_total,
|
||||||
_pad0: 0.0,
|
world_half_width: wire.world_width * 0.5,
|
||||||
_pad1: 0.0,
|
_pad1: 0.0,
|
||||||
_pad2: 0.0,
|
_pad2: 0.0,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -305,6 +305,7 @@ pub fn apply_along(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|s| s.len() >= 2)
|
.filter(|s| s.len() >= 2)
|
||||||
.map(|pts| WireModel {
|
.map(|pts| WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -344,6 +345,7 @@ pub fn apply_along(
|
||||||
xy = xy.max(y);
|
xy = xy.max(y);
|
||||||
}
|
}
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ struct InstanceIn {
|
||||||
// "A"-type endpoint alignment: end-dash length + total wire length.
|
// "A"-type endpoint alignment: end-dash length + total wire length.
|
||||||
@location(12) align_end: f32,
|
@location(12) align_end: f32,
|
||||||
@location(13) align_total: 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
|
// 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.
|
// Select the clip-space position for this vertex's endpoint.
|
||||||
let clip_pos = mix(clip_a, clip_b, which_end);
|
let clip_pos = mix(clip_a, clip_b, which_end);
|
||||||
|
|
||||||
// LWDISPLAY off → collapse to a 1-pixel-wide line (half_width = 0.5).
|
// A wide polyline carries its band width in world units: expand the quad by
|
||||||
let hw = select(0.5, in.half_width, u.lwdisplay_enable > 0.5);
|
// `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).
|
// Offset in clip space (multiply by w to un-apply perspective division).
|
||||||
let ndc_offset = perp_ndc * hw * side;
|
let ndc_offset = perp_ndc * hw * side;
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ struct WireConst {
|
||||||
draw_depth: f32,
|
draw_depth: f32,
|
||||||
align_end: f32,
|
align_end: f32,
|
||||||
align_total: f32,
|
align_total: f32,
|
||||||
_pad0: f32,
|
world_half_width: f32,
|
||||||
_pad1: f32,
|
_pad1: f32,
|
||||||
_pad2: f32,
|
_pad2: f32,
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +93,18 @@ struct VertexOut {
|
||||||
|
|
||||||
let clip_pos = mix(clip_a, clip_b, which_end);
|
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 ndc_offset = perp_ndc * hw * side;
|
||||||
let final_clip = clip_pos + vec4<f32>(ndc_offset * clip_pos.w, 0.0, 0.0);
|
let final_clip = clip_pos + vec4<f32>(ndc_offset * clip_pos.w, 0.0, 0.0);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue