feat(polyline): smooth per-segment taper for varying-width wide polylines
A wide polyline with per-vertex varying widths rendered at its widest edge (uniform-width ones were already exact). Draw the true taper instead. `WireModel.taper_widths` carries a per-point full band width. The wire instance (native slim + web fat) gains per-endpoint `world_hw_a/b`, and both wire shaders interpolate `mix(hw_a, hw_b, which_end)` so each segment's band narrows/widens linearly — depth-tested, and the linetype dash still applies. Alignment (widths must match the tessellated points) is solved by building both together: a new `TruckObject::TaperedLines(points, widths)`, filled by `common::tapered_band_points` (straight = endpoints, arc = 16 samples with the width lerped along it). to_truck routes a varying-width polyline here — LwPolyline before the PLINEGEN split, Polyline2D at its return — while a uniform-width one keeps the constant-band Contour path unchanged. `world_width` stays the widest edge as the PDF-stroke + zoom-floor fallback. Blocks fall back to the constant-widest band (`LocalWire` carries no taper_widths). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a8aece1909
commit
b3016ca892
48 changed files with 322 additions and 7 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1274,6 +1275,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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -392,6 +392,47 @@ pub(crate) fn thick_band_tube(
|
||||||
(fill_tris, lines)
|
(fill_tris, lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build a continuous WCS point list + a per-point FULL band width for a
|
||||||
|
/// tapered wide polyline, so the wire shader can interpolate each segment's two
|
||||||
|
/// endpoint widths. Each `verts` entry is `(location_xy, bulge_to_next,
|
||||||
|
/// start_width, end_width)` — the effective full widths at that vertex's segment
|
||||||
|
/// start and end (already resolved against the polyline's constant width). Arcs
|
||||||
|
/// are sampled in 16 steps with the width interpolated linearly along the arc.
|
||||||
|
/// A shared vertex is emitted once (carrying the previous segment's end width),
|
||||||
|
/// which is exact for the usual continuous taper.
|
||||||
|
pub(crate) fn tapered_band_points(
|
||||||
|
verts: &[([f64; 2], f64, f64, f64)],
|
||||||
|
is_closed: bool,
|
||||||
|
to_wcs: &dyn Fn(f64, f64) -> (f64, f64, f64),
|
||||||
|
) -> (Vec<[f64; 3]>, Vec<f32>) {
|
||||||
|
let n = verts.len();
|
||||||
|
let seg_count = if is_closed { n } else { n.saturating_sub(1) };
|
||||||
|
let mut pts: Vec<[f64; 3]> = Vec::new();
|
||||||
|
let mut widths: Vec<f32> = Vec::new();
|
||||||
|
let mut push = |x: f64, y: f64, w: f32| {
|
||||||
|
let (wx, wy, wz) = to_wcs(x, y);
|
||||||
|
pts.push([wx, wy, wz]);
|
||||||
|
widths.push(w);
|
||||||
|
};
|
||||||
|
for i in 0..seg_count {
|
||||||
|
let (p0, bulge, sw0, ew0) = verts[i];
|
||||||
|
let (p1, _, _, _) = verts[(i + 1) % n];
|
||||||
|
if i == 0 {
|
||||||
|
push(p0[0], p0[1], sw0 as f32);
|
||||||
|
}
|
||||||
|
if bulge.abs() < 1e-9 {
|
||||||
|
push(p1[0], p1[1], ew0 as f32);
|
||||||
|
} else if let Some(arc) = BulgeArc::from_bulge(p0, p1, bulge) {
|
||||||
|
for j in 1..=16usize {
|
||||||
|
let t = j as f64 / 16.0;
|
||||||
|
let s = arc.sample(t);
|
||||||
|
push(s[0], s[1], (sw0 + (ew0 - sw0) * t) as f32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(pts, widths)
|
||||||
|
}
|
||||||
|
|
||||||
/// Compute the filled boundary polygon for one polyline segment.
|
/// Compute the filled boundary polygon for one polyline segment.
|
||||||
/// For straight segments: a rectangle/trapezoid.
|
/// For straight segments: a rectangle/trapezoid.
|
||||||
/// For arc segments: an arc band (outer arc + reversed inner arc).
|
/// For arc segments: an arc band (outer arc + reversed inner arc).
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1379,6 +1380,7 @@ fn tessellate_dimension_inner(
|
||||||
}
|
}
|
||||||
if !ext2.is_empty() {
|
if !ext2.is_empty() {
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1406,6 +1408,7 @@ fn tessellate_dimension_inner(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1434,6 +1437,7 @@ fn tessellate_dimension_inner(
|
||||||
}
|
}
|
||||||
|
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1478,6 +1482,7 @@ fn tessellate_dimension_inner(
|
||||||
aci_to_rgba(&c)
|
aci_to_rgba(&c)
|
||||||
};
|
};
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -590,6 +590,7 @@ impl LeaderTess for Leader {
|
||||||
|
|
||||||
if verts.len() < 2 {
|
if verts.len() < 2 {
|
||||||
return WireModel {
|
return WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -697,6 +698,7 @@ impl LeaderTess for Leader {
|
||||||
}
|
}
|
||||||
|
|
||||||
WireModel {
|
WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -213,6 +213,51 @@ fn to_truck(pline: &LwPolyline) -> TruckEntity {
|
||||||
return thick_segments(&seg_data, &path, pline.thickness, normal, kv, tgs);
|
return thick_segments(&seg_data, &path, pline.thickness, normal, kv, tgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A wide polyline whose per-vertex widths VARY renders a smooth taper —
|
||||||
|
// handled here, before the PLINEGEN split, so both cases get it. A
|
||||||
|
// uniform-width polyline falls through to the constant-band paths below.
|
||||||
|
if let Some(band_verts) = tapered_band_verts(pline) {
|
||||||
|
let mut kv: Vec<[f64; 3]> = Vec::new();
|
||||||
|
let mut tgs: Vec<TangentGeom> = Vec::new();
|
||||||
|
for i in 0..seg_count {
|
||||||
|
let v0 = &verts[i];
|
||||||
|
let v1 = &verts[(i + 1) % count];
|
||||||
|
let p0 = to_pt(v0);
|
||||||
|
let p1 = to_pt(v1);
|
||||||
|
if i == 0 {
|
||||||
|
kv.push([p0.x, p0.y, p0.z]);
|
||||||
|
}
|
||||||
|
kv.push([p1.x, p1.y, p1.z]);
|
||||||
|
if v0.bulge.abs() < 1e-9 {
|
||||||
|
tgs.push(TangentGeom::Line {
|
||||||
|
p1: [p0.x as f32, p0.y as f32, p0.z as f32],
|
||||||
|
p2: [p1.x as f32, p1.y as f32, p1.z as f32],
|
||||||
|
});
|
||||||
|
} else if let Some(arc) = crate::entities::common::BulgeArc::from_bulge(
|
||||||
|
[v0.location.x, v0.location.y],
|
||||||
|
[v1.location.x, v1.location.y],
|
||||||
|
v0.bulge,
|
||||||
|
) {
|
||||||
|
let (wcx, wcy, wcz) = to_wcs(arc.center[0], arc.center[1]);
|
||||||
|
tgs.push(TangentGeom::Circle {
|
||||||
|
center: [wcx as f32, wcy as f32, wcz as f32],
|
||||||
|
radius: arc.radius as f32,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (pts, widths) =
|
||||||
|
crate::entities::common::tapered_band_points(&band_verts, pline.is_closed, &to_wcs);
|
||||||
|
let (fill_origin, fills) = wide_fills(pline);
|
||||||
|
return TruckEntity {
|
||||||
|
pick_tris: crate::entities::common::wide_band_tris(fill_origin, &fills),
|
||||||
|
object: TruckObject::TaperedLines(pts, widths),
|
||||||
|
snap_pts: vec![],
|
||||||
|
tangent_geoms: tgs,
|
||||||
|
key_vertices: kv,
|
||||||
|
fill_tris: vec![],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// plinegen=false: NaN-separated segments so the linetype pattern restarts per vertex.
|
// plinegen=false: NaN-separated segments so the linetype pattern restarts per vertex.
|
||||||
if !pline.plinegen {
|
if !pline.plinegen {
|
||||||
let mut pts: Vec<[f64; 3]> = Vec::new();
|
let mut pts: Vec<[f64; 3]> = Vec::new();
|
||||||
|
|
@ -321,6 +366,32 @@ fn to_truck(pline: &LwPolyline) -> TruckEntity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The per-vertex `(location, bulge, start_width, end_width)` band description
|
||||||
|
/// for a wide LwPolyline whose width VARIES — `None` when the width is uniform
|
||||||
|
/// (a constant band) so the caller keeps the cheaper constant-width path.
|
||||||
|
fn tapered_band_verts(pline: &LwPolyline) -> Option<Vec<([f64; 2], f64, f64, f64)>> {
|
||||||
|
let c = pline.constant_width;
|
||||||
|
let band: Vec<([f64; 2], f64, f64, f64)> = pline
|
||||||
|
.vertices
|
||||||
|
.iter()
|
||||||
|
.map(|v| {
|
||||||
|
let sw = if v.start_width > 1e-9 { v.start_width } else { c };
|
||||||
|
let ew = if v.end_width > 1e-9 { v.end_width } else { c };
|
||||||
|
([v.location.x, v.location.y], v.bulge, sw, ew)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let w0 = band.first().map_or(0.0, |v| v.2);
|
||||||
|
let varies = band
|
||||||
|
.iter()
|
||||||
|
.any(|&(_, _, sw, ew)| (sw - w0).abs() > 1e-9 || (ew - w0).abs() > 1e-9);
|
||||||
|
// Only a genuine, non-zero-width taper takes the per-point path.
|
||||||
|
if varies && w0.max(band.iter().map(|v| v.3).fold(0.0, f64::max)) > 1e-9 {
|
||||||
|
Some(band)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn grips(pline: &LwPolyline) -> Vec<GripDef> {
|
fn grips(pline: &LwPolyline) -> Vec<GripDef> {
|
||||||
let elev = pline.elevation;
|
let elev = pline.elevation;
|
||||||
let n = pline.vertices.len();
|
let n = pline.vertices.len();
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1694,6 +1695,7 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
xy = xy.max(y);
|
xy = xy.max(y);
|
||||||
}
|
}
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1762,6 +1764,7 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
wcs_corners[3],
|
wcs_corners[3],
|
||||||
];
|
];
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1798,6 +1801,7 @@ impl MultiLeaderTess for MultiLeader {
|
||||||
wcs_corners[0],
|
wcs_corners[0],
|
||||||
];
|
];
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -326,9 +326,22 @@ fn tessellate_polyline2d(pl: &Polyline2D) -> TruckEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (fill_origin, fills) = wide_fills(pl);
|
let (fill_origin, fills) = wide_fills(pl);
|
||||||
|
// A wide Polyline2D whose per-vertex widths VARY renders a smooth taper; a
|
||||||
|
// uniform-width one keeps the constant-band Contour.
|
||||||
|
let object = match tapered_band_verts_2d(pl) {
|
||||||
|
Some(band_verts) => {
|
||||||
|
let (pts, widths) = crate::entities::common::tapered_band_points(
|
||||||
|
&band_verts,
|
||||||
|
pl.is_closed(),
|
||||||
|
&to_wcs,
|
||||||
|
);
|
||||||
|
TruckObject::TaperedLines(pts, widths)
|
||||||
|
}
|
||||||
|
None => TruckObject::Contour(edges.into_iter().collect::<Wire>()),
|
||||||
|
};
|
||||||
TruckEntity {
|
TruckEntity {
|
||||||
pick_tris: crate::entities::common::wide_band_tris(fill_origin, &fills),
|
pick_tris: crate::entities::common::wide_band_tris(fill_origin, &fills),
|
||||||
object: TruckObject::Contour(edges.into_iter().collect::<Wire>()),
|
object,
|
||||||
snap_pts: vec![],
|
snap_pts: vec![],
|
||||||
tangent_geoms: tangents,
|
tangent_geoms: tangents,
|
||||||
key_vertices: key_verts,
|
key_vertices: key_verts,
|
||||||
|
|
@ -336,6 +349,32 @@ fn tessellate_polyline2d(pl: &Polyline2D) -> TruckEntity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Per-vertex `(location, bulge, start_width, end_width)` band description for a
|
||||||
|
/// wide Polyline2D whose width VARIES — `None` when the width is uniform.
|
||||||
|
fn tapered_band_verts_2d(
|
||||||
|
pl: &acadrust::entities::Polyline2D,
|
||||||
|
) -> Option<Vec<([f64; 2], f64, f64, f64)>> {
|
||||||
|
let d = pl.start_width.max(pl.end_width);
|
||||||
|
let band: Vec<([f64; 2], f64, f64, f64)> = pl
|
||||||
|
.vertices
|
||||||
|
.iter()
|
||||||
|
.map(|v| {
|
||||||
|
let sw = if v.start_width > 1e-9 { v.start_width } else { d };
|
||||||
|
let ew = if v.end_width > 1e-9 { v.end_width } else { d };
|
||||||
|
([v.location.x, v.location.y], v.bulge, sw, ew)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let w0 = band.first().map_or(0.0, |v| v.2);
|
||||||
|
let varies = band
|
||||||
|
.iter()
|
||||||
|
.any(|&(_, _, sw, ew)| (sw - w0).abs() > 1e-9 || (ew - w0).abs() > 1e-9);
|
||||||
|
if varies && w0.max(band.iter().map(|v| v.3).fold(0.0, f64::max)) > 1e-9 {
|
||||||
|
Some(band)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TruckConvertible for Polyline2D {
|
impl TruckConvertible for Polyline2D {
|
||||||
fn to_truck(&self, _document: &acadrust::CadDocument) -> Option<TruckEntity> {
|
fn to_truck(&self, _document: &acadrust::CadDocument) -> Option<TruckEntity> {
|
||||||
Some(tessellate_polyline2d(self))
|
Some(tessellate_polyline2d(self))
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -238,6 +239,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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ impl CadCommand for DimJogLineCommand {
|
||||||
}
|
}
|
||||||
let d = 0.3_f32;
|
let d = 0.3_f32;
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ impl CadCommand for DimTeditCommand {
|
||||||
}
|
}
|
||||||
let d = 0.2_f32;
|
let d = 0.2_f32;
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -458,6 +458,7 @@ impl CadCommand for MLeaderCollectCommand {
|
||||||
|
|
||||||
fn preview_wire(pts: &[Vec3]) -> WireModel {
|
fn preview_wire(pts: &[Vec3]) -> WireModel {
|
||||||
WireModel {
|
WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ impl CadCommand for QdimCommand {
|
||||||
}
|
}
|
||||||
let d = 0.5_f32;
|
let d = 0.5_f32;
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ impl CadCommand for ToleranceCommand {
|
||||||
}
|
}
|
||||||
let d = 0.15_f32;
|
let d = 0.15_f32;
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -180,6 +181,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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -161,6 +162,7 @@ impl CadCommand for WipeoutCommand {
|
||||||
self.poly_pts[0].z as f32,
|
self.poly_pts[0].z as f32,
|
||||||
]);
|
]);
|
||||||
Some(WireModel {
|
Some(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
|
||||||
1
src/scene/cache/block_cache.rs
vendored
1
src/scene/cache/block_cache.rs
vendored
|
|
@ -864,6 +864,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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: b.world_width,
|
world_width: b.world_width,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: b.pick_tris,
|
pick_tris: b.pick_tris,
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,11 @@ pub enum TruckObject {
|
||||||
Lines(Vec<[f64; 3]>),
|
Lines(Vec<[f64; 3]>),
|
||||||
/// Like Lines but linetype pattern restarts at each NaN-separated segment (plinegen=false).
|
/// Like Lines but linetype pattern restarts at each NaN-separated segment (plinegen=false).
|
||||||
SegmentedLines(Vec<[f64; 3]>),
|
SegmentedLines(Vec<[f64; 3]>),
|
||||||
|
/// A wide polyline whose band width VARIES (a taper): a continuous WCS point
|
||||||
|
/// list paired index-for-index with a per-point full band width. The wire
|
||||||
|
/// shader interpolates the two endpoint widths of each segment so the band
|
||||||
|
/// tapers smoothly. Points hold no NaN breaks (one continuous band).
|
||||||
|
TaperedLines(Vec<[f64; 3]>, Vec<f32>),
|
||||||
Volume(Solid),
|
Volume(Solid),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1030,6 +1031,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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1117,6 +1119,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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -370,6 +370,7 @@ pub fn tessellate(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
elem_wires.push(WireModel {
|
elem_wires.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -661,6 +662,7 @@ pub fn tessellate(
|
||||||
ftl.push(lo);
|
ftl.push(lo);
|
||||||
}
|
}
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -699,6 +701,7 @@ pub fn tessellate(
|
||||||
fpl.push(lo);
|
fpl.push(lo);
|
||||||
}
|
}
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -744,6 +747,7 @@ pub fn tessellate(
|
||||||
low.push(ll);
|
low.push(ll);
|
||||||
}
|
}
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -770,6 +774,7 @@ pub fn tessellate(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
wires.push(WireModel {
|
wires.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -826,6 +831,7 @@ pub fn tessellate(
|
||||||
(Vec::new(), Vec::new(), Vec::new())
|
(Vec::new(), Vec::new(), Vec::new())
|
||||||
};
|
};
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -864,6 +870,7 @@ pub fn tessellate(
|
||||||
(Vec::new(), Vec::new(), Vec::new())
|
(Vec::new(), Vec::new(), Vec::new())
|
||||||
};
|
};
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -899,6 +906,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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -927,6 +935,7 @@ pub fn tessellate(
|
||||||
|
|
||||||
if out.is_empty() {
|
if out.is_empty() {
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -977,6 +986,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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1026,6 +1036,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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1068,6 +1079,7 @@ pub fn tessellate(
|
||||||
// linetype dashes it); the band also backs pick.
|
// 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: polyline_band_width(entity),
|
world_width: polyline_band_width(entity),
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris,
|
pick_tris,
|
||||||
|
|
@ -1159,6 +1171,7 @@ pub fn tessellate(
|
||||||
(Vec::new(), Vec::new(), Vec::new())
|
(Vec::new(), Vec::new(), Vec::new())
|
||||||
};
|
};
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris,
|
pick_tris,
|
||||||
|
|
@ -1196,6 +1209,7 @@ pub fn tessellate(
|
||||||
(Vec::new(), Vec::new(), Vec::new())
|
(Vec::new(), Vec::new(), Vec::new())
|
||||||
};
|
};
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
pick_tris_low: Vec::new(),
|
pick_tris_low: Vec::new(),
|
||||||
|
|
@ -1224,6 +1238,7 @@ pub fn tessellate(
|
||||||
|
|
||||||
if out.is_empty() {
|
if out.is_empty() {
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1265,6 +1280,7 @@ pub fn tessellate(
|
||||||
// treatment as the Contour arm, restarting the dash per segment.
|
// 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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: polyline_band_width(entity),
|
world_width: polyline_band_width(entity),
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris,
|
pick_tris,
|
||||||
|
|
@ -1291,6 +1307,49 @@ pub fn tessellate(
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TruckObject::TaperedLines(points, widths) => {
|
||||||
|
// A wide polyline whose width varies: one continuous band wire
|
||||||
|
// carrying a per-point width; the shader interpolates each
|
||||||
|
// segment's two endpoint widths. `world_width` (the widest edge)
|
||||||
|
// stays as the constant fallback for PDF export + a hairline
|
||||||
|
// floor when zoomed out.
|
||||||
|
let (local_pts, local_pts_low) = points_to_ds(points);
|
||||||
|
let snap_pts = te.snap_pts;
|
||||||
|
let key_vertices: Vec<[f64; 3]> = te
|
||||||
|
.key_vertices
|
||||||
|
.into_iter()
|
||||||
|
.map(|[x, y, z]| [x, y, z])
|
||||||
|
.collect();
|
||||||
|
let (pick_tris, pick_tris_low) = points_to_ds(te.pick_tris);
|
||||||
|
let world_width = widths.iter().copied().fold(0.0f32, f32::max);
|
||||||
|
return vec![WireModel {
|
||||||
|
taper_widths: widths,
|
||||||
|
world_width,
|
||||||
|
fill_is_3d: false,
|
||||||
|
pick_tris,
|
||||||
|
pick_tris_low,
|
||||||
|
dash_from_start: false,
|
||||||
|
dash_align_end: None,
|
||||||
|
text_verts: Vec::new(),
|
||||||
|
name,
|
||||||
|
points: local_pts,
|
||||||
|
points_low: local_pts_low,
|
||||||
|
color,
|
||||||
|
selected,
|
||||||
|
pattern_length,
|
||||||
|
pattern,
|
||||||
|
line_weight_px,
|
||||||
|
snap_pts,
|
||||||
|
tangent_geoms: te.tangent_geoms,
|
||||||
|
aci: 0,
|
||||||
|
key_vertices,
|
||||||
|
plinegen: true,
|
||||||
|
aabb: WireModel::UNBOUNDED_AABB,
|
||||||
|
fill_tris: vec![],
|
||||||
|
fill_tris_low: Vec::new(),
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
TruckObject::Volume(_) => {
|
TruckObject::Volume(_) => {
|
||||||
// Solid3D / Region / Body → mesh tessellation lives in
|
// Solid3D / Region / Body → mesh tessellation lives in
|
||||||
// `solid3d_tess`. As a wire fallback, render the pre-computed
|
// `solid3d_tess`. As a wire fallback, render the pre-computed
|
||||||
|
|
@ -1334,6 +1393,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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,11 @@ pub struct WireModel {
|
||||||
/// units (scaling with zoom) so the band IS the wire — the linetype dash
|
/// 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.
|
/// pattern then applies to the band instead of a separate hatch fill.
|
||||||
pub world_width: f32,
|
pub world_width: f32,
|
||||||
|
/// Per-point full band width (drawing units), aligned index-for-index with
|
||||||
|
/// [`points`], for a polyline whose width VARIES (a taper). Empty = a
|
||||||
|
/// constant band of `world_width`. The wire shader reads the two endpoint
|
||||||
|
/// widths of each segment and interpolates, so the band tapers smoothly.
|
||||||
|
pub taper_widths: Vec<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).
|
||||||
|
|
@ -179,6 +184,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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -402,6 +408,7 @@ impl Default for WireModel {
|
||||||
pattern: [0.0; 8],
|
pattern: [0.0; 8],
|
||||||
line_weight_px: 1.0,
|
line_weight_px: 1.0,
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
|
taper_widths: Vec::new(),
|
||||||
aci: 0,
|
aci: 0,
|
||||||
snap_pts: Vec::new(),
|
snap_pts: Vec::new(),
|
||||||
tangent_geoms: Vec::new(),
|
tangent_geoms: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,8 @@ fn blank_instance() -> WireInstance {
|
||||||
distance_a: 0.0,
|
distance_a: 0.0,
|
||||||
distance_b: 0.0,
|
distance_b: 0.0,
|
||||||
wire_id: 0,
|
wire_id: 0,
|
||||||
|
world_hw_a: 0.0,
|
||||||
|
world_hw_b: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,11 @@ pub struct WireInstance {
|
||||||
pub distance_b: f32,
|
pub distance_b: f32,
|
||||||
/// Index into the per-wire `WireConst` storage buffer (group 1).
|
/// Index into the per-wire `WireConst` storage buffer (group 1).
|
||||||
pub wire_id: u32,
|
pub wire_id: u32,
|
||||||
|
/// World-space half-width at each endpoint for a TAPERED band. `0.0` =
|
||||||
|
/// use the per-wire constant (`WireConst.world_half_width`). The shader
|
||||||
|
/// interpolates between the two so the band tapers across the segment.
|
||||||
|
pub world_hw_a: f32,
|
||||||
|
pub world_hw_b: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
|
@ -94,6 +99,8 @@ impl WireInstance {
|
||||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, distance_a) as u64, shader_location: 4, format: wgpu::VertexFormat::Float32 },
|
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, distance_a) as u64, shader_location: 4, format: wgpu::VertexFormat::Float32 },
|
||||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, distance_b) as u64, shader_location: 5, format: wgpu::VertexFormat::Float32 },
|
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, distance_b) as u64, shader_location: 5, format: wgpu::VertexFormat::Float32 },
|
||||||
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, wire_id) as u64, shader_location: 6, format: wgpu::VertexFormat::Uint32 },
|
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, wire_id) as u64, shader_location: 6, format: wgpu::VertexFormat::Uint32 },
|
||||||
|
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, world_hw_a) as u64, shader_location: 7, format: wgpu::VertexFormat::Float32 },
|
||||||
|
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, world_hw_b) as u64, shader_location: 8, 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,
|
||||||
|
|
@ -178,6 +185,10 @@ pub struct WireInstance {
|
||||||
/// World-space half-width for a wide-polyline band (see `WireConst`). `0.0`
|
/// World-space half-width for a wide-polyline band (see `WireConst`). `0.0`
|
||||||
/// = a normal wire (uses `half_width`, screen pixels).
|
/// = a normal wire (uses `half_width`, screen pixels).
|
||||||
pub world_half_width: f32,
|
pub world_half_width: f32,
|
||||||
|
/// Per-endpoint world half-width for a tapered band (0 = use the constant
|
||||||
|
/// `world_half_width`). The shader interpolates across the segment.
|
||||||
|
pub world_hw_a: f32,
|
||||||
|
pub world_hw_b: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
|
@ -202,6 +213,8 @@ impl WireInstance {
|
||||||
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::VertexAttribute { offset: std::mem::offset_of!(WireInstance, world_half_width) as u64, shader_location: 14, format: wgpu::VertexFormat::Float32 },
|
||||||
|
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, world_hw_a) as u64, shader_location: 15, format: wgpu::VertexFormat::Float32 },
|
||||||
|
wgpu::VertexAttribute { offset: std::mem::offset_of!(WireInstance, world_hw_b) as u64, shader_location: 16, 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,
|
||||||
|
|
@ -389,6 +402,7 @@ fn emit_wire_instances(wire: &WireModel, color: [f32; 4], draw_depth: f32) -> Ve
|
||||||
}
|
}
|
||||||
let (dists, align_end, align_total) = wire_distances(wire);
|
let (dists, align_end, align_total) = wire_distances(wire);
|
||||||
let low = |i: usize| -> [f32; 3] { wire.points_low.get(i).copied().unwrap_or([0.0; 3]) };
|
let low = |i: usize| -> [f32; 3] { wire.points_low.get(i).copied().unwrap_or([0.0; 3]) };
|
||||||
|
let tw = |i: usize| -> f32 { wire.taper_widths.get(i).copied().unwrap_or(0.0) * 0.5 };
|
||||||
let mut instances: Vec<WireInstance> = Vec::with_capacity(seg_count);
|
let mut instances: Vec<WireInstance> = Vec::with_capacity(seg_count);
|
||||||
for i in 0..seg_count {
|
for i in 0..seg_count {
|
||||||
let a = wire.points[i];
|
let a = wire.points[i];
|
||||||
|
|
@ -412,6 +426,8 @@ fn emit_wire_instances(wire: &WireModel, color: [f32; 4], draw_depth: f32) -> Ve
|
||||||
align_end,
|
align_end,
|
||||||
align_total,
|
align_total,
|
||||||
world_half_width: wire.world_width * 0.5,
|
world_half_width: wire.world_width * 0.5,
|
||||||
|
world_hw_a: tw(i),
|
||||||
|
world_hw_b: tw(i + 1),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
instances
|
instances
|
||||||
|
|
@ -446,6 +462,9 @@ pub(crate) fn emit_wire_native(
|
||||||
return (Vec::new(), cst);
|
return (Vec::new(), cst);
|
||||||
}
|
}
|
||||||
let low = |i: usize| -> [f32; 3] { wire.points_low.get(i).copied().unwrap_or([0.0; 3]) };
|
let low = |i: usize| -> [f32; 3] { wire.points_low.get(i).copied().unwrap_or([0.0; 3]) };
|
||||||
|
// Per-point half-width for a tapered band (empty ⇒ 0 ⇒ shader uses the
|
||||||
|
// per-wire constant width).
|
||||||
|
let tw = |i: usize| -> f32 { wire.taper_widths.get(i).copied().unwrap_or(0.0) * 0.5 };
|
||||||
let mut instances: Vec<WireInstance> = Vec::with_capacity(seg_count);
|
let mut instances: Vec<WireInstance> = Vec::with_capacity(seg_count);
|
||||||
for i in 0..seg_count {
|
for i in 0..seg_count {
|
||||||
let a = wire.points[i];
|
let a = wire.points[i];
|
||||||
|
|
@ -461,6 +480,8 @@ pub(crate) fn emit_wire_native(
|
||||||
distance_a: dists[i],
|
distance_a: dists[i],
|
||||||
distance_b: dists[i + 1],
|
distance_b: dists[i + 1],
|
||||||
wire_id,
|
wire_id,
|
||||||
|
world_hw_a: tw(i),
|
||||||
|
world_hw_b: tw(i + 1),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
(instances, cst)
|
(instances, cst)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -345,6 +346,7 @@ pub fn apply_along(
|
||||||
xy = xy.max(y);
|
xy = xy.max(y);
|
||||||
}
|
}
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
fill_is_3d: false,
|
fill_is_3d: false,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,9 @@ struct InstanceIn {
|
||||||
@location(13) align_total: f32,
|
@location(13) align_total: f32,
|
||||||
// World-space half-width for a wide-polyline band. 0 = normal wire.
|
// World-space half-width for a wide-polyline band. 0 = normal wire.
|
||||||
@location(14) world_half_width: f32,
|
@location(14) world_half_width: f32,
|
||||||
|
// Per-endpoint world half-width for a tapered band (0 = use the constant).
|
||||||
|
@location(15) world_hw_a: f32,
|
||||||
|
@location(16) world_hw_b: 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
|
||||||
|
|
@ -135,10 +138,14 @@ struct VertexOut {
|
||||||
// `world_half_width / world_per_pixel` (pixels) so the band tracks zoom. A
|
// `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,
|
// normal wire (world_half_width == 0) uses the screen-pixel half-width,
|
||||||
// honouring the LWDISPLAY toggle (off → collapse to a 1-pixel line).
|
// honouring the LWDISPLAY toggle (off → collapse to a 1-pixel line).
|
||||||
|
// A tapered band interpolates a per-endpoint world half-width across the
|
||||||
|
// segment; a constant band uses `world_half_width`. Both clamp to a
|
||||||
|
// half-pixel so a zoomed-out band stays a hairline instead of vanishing.
|
||||||
|
let taper_hw = mix(in.world_hw_a, in.world_hw_b, which_end);
|
||||||
var hw: f32;
|
var hw: f32;
|
||||||
if in.world_half_width > 0.0 {
|
if taper_hw > 0.0 {
|
||||||
// Clamp to a half-pixel so a zoomed-out band never drops below a
|
hw = max(taper_hw / u.world_per_pixel, 0.5);
|
||||||
// hairline (its centre-line) instead of vanishing.
|
} else if in.world_half_width > 0.0 {
|
||||||
hw = max(in.world_half_width / u.world_per_pixel, 0.5);
|
hw = max(in.world_half_width / u.world_per_pixel, 0.5);
|
||||||
} else {
|
} else {
|
||||||
hw = select(0.5, in.half_width, u.lwdisplay_enable > 0.5);
|
hw = select(0.5, in.half_width, u.lwdisplay_enable > 0.5);
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,10 @@ struct InstanceIn {
|
||||||
@location(4) distance_a: f32,
|
@location(4) distance_a: f32,
|
||||||
@location(5) distance_b: f32,
|
@location(5) distance_b: f32,
|
||||||
@location(6) wire_id: u32,
|
@location(6) wire_id: u32,
|
||||||
|
// Per-endpoint world half-width for a tapered band (0 = use the per-wire
|
||||||
|
// constant `world_half_width`).
|
||||||
|
@location(7) world_hw_a: f32,
|
||||||
|
@location(8) world_hw_b: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
const DRAW_ORDER_BIAS: f32 = 0.001;
|
const DRAW_ORDER_BIAS: f32 = 0.001;
|
||||||
|
|
@ -97,10 +101,15 @@ struct VertexOut {
|
||||||
// by `world_half_width / world_per_pixel` (screen pixels) so the band grows
|
// 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
|
// and shrinks with zoom. A normal wire (world_half_width == 0) uses the
|
||||||
// screen-pixel half-width, honouring the LWDISPLAY toggle.
|
// screen-pixel half-width, honouring the LWDISPLAY toggle.
|
||||||
|
// A tapered band carries a per-endpoint world half-width on the instance:
|
||||||
|
// interpolate across the segment so the band narrows/widens smoothly. A
|
||||||
|
// constant band uses the per-wire `world_half_width`. Both clamp to a
|
||||||
|
// half-pixel so a zoomed-out band stays a hairline instead of vanishing.
|
||||||
|
let taper_hw = mix(in.world_hw_a, in.world_hw_b, which_end);
|
||||||
var hw: f32;
|
var hw: f32;
|
||||||
if c.world_half_width > 0.0 {
|
if taper_hw > 0.0 {
|
||||||
// Clamp to a half-pixel so a zoomed-out band never drops below a
|
hw = max(taper_hw / u.world_per_pixel, 0.5);
|
||||||
// hairline (its centre-line) instead of vanishing.
|
} else if c.world_half_width > 0.0 {
|
||||||
hw = max(c.world_half_width / u.world_per_pixel, 0.5);
|
hw = max(c.world_half_width / u.world_per_pixel, 0.5);
|
||||||
} else {
|
} else {
|
||||||
hw = select(0.5, c.half_width, u.lwdisplay_enable > 0.5);
|
hw = select(0.5, c.half_width, u.lwdisplay_enable > 0.5);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue