feat(polyline): Polyline2D width band + tube, and block-instance shader bands
Extends the wide-polyline shader band to the two cases the first pass left on the hatch pipeline. Polyline2D: - A flat wide Polyline2D now draws its band from the centre-line wire (`world_width`), same as LwPolyline (`polyline_band_width` handles both). - A thickened wide Polyline2D extrudes a solid 3-D tube. `thick_wide_band`'s body moves to a shared `common::thick_band_tube(origin, fills, thickness, normal, to_wcs)`; LwPolyline delegates to it and Polyline2D calls it from its thickness path. Tube edges read black for both kinds. - The model-space wide-band hatch loop is deleted (both kinds now wire/tube). Blocks: - Block-internal wide polylines draw the shader band too. `LocalWire` carries `world_width`; `emit_wire` scales it by the insert transform (average of the X/Y axis image lengths) so the band tracks a scaled insert, and the scaled width joins the batch `style_key`. `push_block_wide_fills` (the block hatch band) is removed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
298bfd4803
commit
a8aece1909
6 changed files with 161 additions and 219 deletions
|
|
@ -334,6 +334,64 @@ pub(crate) fn wide_band_tris(origin: [f64; 2], fills: &[Vec<[f32; 2]>]) -> Vec<[
|
|||
out
|
||||
}
|
||||
|
||||
/// Extrude a wide-polyline band (from `wide_fills`) into a solid tube for a DXF
|
||||
/// thickness (code 39): a vertical wall between every band-boundary point and
|
||||
/// its `thickness`-along-`normal` copy, plus triangulated bottom and top caps.
|
||||
/// Returns `(fill_tris, edge_lines)` as flat WCS f64 lists — the caller wraps
|
||||
/// them in a `TruckEntity` (object = `Lines(edge_lines)`, `fill_tris`, and
|
||||
/// `pick_tris = fill_tris`). Shared by LwPolyline and Polyline2D so both wide
|
||||
/// polyline kinds extrude the same solid instead of just their centre-line.
|
||||
///
|
||||
/// `polyline_segment_fill` emits each band loop as the outer boundary forward
|
||||
/// then the inner boundary back, so its two transition edges (`half-1 → half`
|
||||
/// and `n-1 → 0`) are radial cap ends inside the band — no wall is drawn there.
|
||||
pub(crate) fn thick_band_tube(
|
||||
origin: [f64; 2],
|
||||
fills: &[Vec<[f32; 2]>],
|
||||
thickness: f64,
|
||||
normal: (f64, f64, f64),
|
||||
to_wcs: &dyn Fn(f64, f64) -> (f64, f64, f64),
|
||||
) -> (Vec<[f64; 3]>, Vec<[f64; 3]>) {
|
||||
let (nx, ny, nz) = normal;
|
||||
let t = thickness;
|
||||
let off = |p: [f64; 3]| -> [f64; 3] { [p[0] + t * nx, p[1] + t * ny, p[2] + t * nz] };
|
||||
let push_seg = |lines: &mut Vec<[f64; 3]>, a: [f64; 3], b: [f64; 3]| {
|
||||
lines.push(a);
|
||||
lines.push(b);
|
||||
lines.push([f64::NAN; 3]);
|
||||
};
|
||||
let mut lines: Vec<[f64; 3]> = Vec::new();
|
||||
let mut fill_tris: Vec<[f64; 3]> = Vec::new();
|
||||
for poly in fills {
|
||||
let n = poly.len();
|
||||
if n < 4 {
|
||||
continue;
|
||||
}
|
||||
let half = n / 2;
|
||||
let bot: Vec<[f64; 3]> = poly
|
||||
.iter()
|
||||
.map(|&[x, y]| {
|
||||
let (wx, wy, wz) = to_wcs(origin[0] + x as f64, origin[1] + y as f64);
|
||||
[wx, wy, wz]
|
||||
})
|
||||
.collect();
|
||||
let top: Vec<[f64; 3]> = bot.iter().map(|&p| off(p)).collect();
|
||||
for k in 0..n {
|
||||
push_seg(&mut lines, bot[k], top[k]);
|
||||
if k == half - 1 || k == n - 1 {
|
||||
continue;
|
||||
}
|
||||
let kn = (k + 1) % n;
|
||||
push_seg(&mut lines, bot[k], bot[kn]);
|
||||
push_seg(&mut lines, top[k], top[kn]);
|
||||
fill_tris.extend_from_slice(&[bot[k], bot[kn], top[kn], bot[k], top[kn], top[k]]);
|
||||
}
|
||||
fill_tris.extend(crate::entities::mesh::triangulate_planar(&bot));
|
||||
fill_tris.extend(crate::entities::mesh::triangulate_planar(&top));
|
||||
}
|
||||
(fill_tris, lines)
|
||||
}
|
||||
|
||||
/// Compute the filled boundary polygon for one polyline segment.
|
||||
/// For straight segments: a rectangle/trapezoid.
|
||||
/// For arc segments: an arc band (outer arc + reversed inner arc).
|
||||
|
|
|
|||
|
|
@ -115,57 +115,8 @@ fn thick_wide_band(
|
|||
tangents: Vec<TangentGeom>,
|
||||
) -> TruckEntity {
|
||||
let (origin, fills) = wide_fills(pl);
|
||||
let (nx, ny, nz) = normal;
|
||||
let t = thickness;
|
||||
let off = |p: [f64; 3]| -> [f64; 3] { [p[0] + t * nx, p[1] + t * ny, p[2] + t * nz] };
|
||||
let push_seg = |lines: &mut Vec<[f64; 3]>, a: [f64; 3], b: [f64; 3]| {
|
||||
lines.push(a);
|
||||
lines.push(b);
|
||||
lines.push([f64::NAN; 3]);
|
||||
};
|
||||
|
||||
let mut lines: Vec<[f64; 3]> = Vec::new();
|
||||
// Shaded surfaces: the vertical walls plus the top cap. The flat bottom cap
|
||||
// is already drawn by the wide-fill hatch path, so it is not repeated here.
|
||||
let mut fill_tris: Vec<[f64; 3]> = Vec::new();
|
||||
for poly in &fills {
|
||||
let n = poly.len();
|
||||
if n < 4 {
|
||||
continue;
|
||||
}
|
||||
// `polyline_segment_fill` returns the band loop as the outer boundary
|
||||
// forward then the inner boundary back, so the two transition edges
|
||||
// (half-1 → half and n-1 → 0) are the radial segment ends.
|
||||
let half = n / 2;
|
||||
let bot: Vec<[f64; 3]> = poly
|
||||
.iter()
|
||||
.map(|&[x, y]| {
|
||||
let (wx, wy, wz) = to_wcs(origin[0] + x as f64, origin[1] + y as f64);
|
||||
[wx, wy, wz]
|
||||
})
|
||||
.collect();
|
||||
let top: Vec<[f64; 3]> = bot.iter().map(|&p| off(p)).collect();
|
||||
for k in 0..n {
|
||||
// Vertical at every boundary point.
|
||||
push_seg(&mut lines, bot[k], top[k]);
|
||||
// Skip the two radial cap edges — they stay inside the band.
|
||||
if k == half - 1 || k == n - 1 {
|
||||
continue;
|
||||
}
|
||||
let kn = (k + 1) % n;
|
||||
push_seg(&mut lines, bot[k], bot[kn]); // bottom boundary
|
||||
push_seg(&mut lines, top[k], top[kn]); // top boundary
|
||||
// Wall quad (two triangles).
|
||||
fill_tris.extend_from_slice(&[
|
||||
bot[k], bot[kn], top[kn], bot[k], top[kn], top[k],
|
||||
]);
|
||||
}
|
||||
// Bottom + top caps — the band ring triangulated at each plane. The
|
||||
// bottom cap is a real 3-D surface (with depth) so the walls occlude it
|
||||
// correctly; the flat 2-D band fill is skipped for thickened polylines.
|
||||
fill_tris.extend(crate::entities::mesh::triangulate_planar(&bot));
|
||||
fill_tris.extend(crate::entities::mesh::triangulate_planar(&top));
|
||||
}
|
||||
let (fill_tris, lines) =
|
||||
crate::entities::common::thick_band_tube(origin, &fills, thickness, normal, to_wcs);
|
||||
|
||||
TruckEntity {
|
||||
pick_tris: fill_tris.clone(),
|
||||
|
|
|
|||
|
|
@ -238,6 +238,28 @@ fn tessellate_polyline2d(pl: &Polyline2D) -> TruckEntity {
|
|||
let (wbx, wby, wbz) = to_wcs(ox1, oy1);
|
||||
kv.push([wbx, wby, wbz]);
|
||||
}
|
||||
// A wide Polyline2D extrudes its whole band into a solid tube (walls +
|
||||
// caps), same as a thickened LwPolyline; a zero-width one falls through
|
||||
// to the centre-line extrusion below.
|
||||
let is_wide = pl.start_width > 1e-9
|
||||
|| pl.end_width > 1e-9
|
||||
|| pl
|
||||
.vertices
|
||||
.iter()
|
||||
.any(|v| v.start_width > 1e-9 || v.end_width > 1e-9);
|
||||
if is_wide {
|
||||
let (origin, fills) = wide_fills(pl);
|
||||
let (fill_tris, lines) =
|
||||
crate::entities::common::thick_band_tube(origin, &fills, t, normal, &to_wcs);
|
||||
return TruckEntity {
|
||||
pick_tris: fill_tris.clone(),
|
||||
object: TruckObject::Lines(lines),
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: tgs,
|
||||
key_vertices: kv,
|
||||
fill_tris,
|
||||
};
|
||||
}
|
||||
let mut pts: Vec<[f64; 3]> = Vec::with_capacity(path.len() * 2 + kv.len() * 3 + 4);
|
||||
pts.extend_from_slice(&path);
|
||||
pts.push([f64::NAN; 3]);
|
||||
|
|
|
|||
Loading…
Reference in a new issue