fix(polyline): render fill mode boundaries

Use kernel-produced band outlines when FILLMODE is off. Preserve centerline linetype stations through inserts, clipping, projection, and PDF export.
This commit is contained in:
Hakan Seven committed 2026-08-24 22:18:44 +03:00
commit 4209c35ba3
60 files changed
+1620 -575

No files matched your search

+154
View File
@@ -1,6 +1,8 @@
use std::cell::Cell;
use std::collections::HashSet;
use crate::scene::model::object::{GripDef, GripShape, PropValue, Property};
use crate::scene::model::wire_model::PatternStationPiece;
/// Linear / angular unit format pulled from the document header so the
/// per-thread properties pipeline can format values consistently without
@@ -789,6 +791,158 @@ pub(crate) fn tapered_band_points(
(pts, widths)
}
pub(crate) struct WideBandOutline {
pub points: Vec<[f64; 3]>,
pub stations: Vec<f32>,
pub point_segments: Vec<i32>,
pub station_pieces: Vec<PatternStationPiece>,
pub source_length: f32,
}
/// Builds the boundary of a variable-width band through the kernel.
pub(crate) fn wide_band_outline(
verts: &[([f64; 2], f64, f64, f64)],
is_closed: bool,
restart_per_segment: bool,
to_wcs: &dyn Fn(f64, f64) -> (f64, f64, f64),
) -> WideBandOutline {
let source = cadkernel::geom2d::Polyline {
vertices: verts
.iter()
.map(|(position, bulge, _, _)| cadkernel::geom2d::PolylineVertex {
position: *position,
bulge: *bulge,
})
.collect(),
closed: is_closed,
};
let segment_count = if is_closed {
verts.len()
} else {
verts.len().saturating_sub(1)
};
let widths: Vec<[f64; 2]> = verts
.iter()
.take(segment_count)
.map(|(_, _, start, end)| [*start, *end])
.collect();
let boundary = cadkernel::geom2d::polyline_band_boundary(
&source,
&widths,
cadkernel::tessellation::DEFAULT_ANGLE,
);
let mut points = Vec::new();
let mut stations = Vec::new();
let mut point_segments = Vec::new();
for edge in boundary.edges {
if !points.is_empty() {
points.push([f64::NAN; 3]);
stations.push(0.0);
point_segments.push(-1);
}
points.extend(edge.points.into_iter().map(|point| {
let (x, y, z) = to_wcs(point[0], point[1]);
[x, y, z]
}));
stations.extend(
if restart_per_segment {
edge.segment_distances
} else {
edge.source_distances
}
.map(|distance| distance as f32),
);
point_segments.extend([edge.source_segment as i32; 2]);
}
let station_pieces = boundary
.station_pieces
.into_iter()
.map(|piece| {
let start = to_wcs(piece.points[0][0], piece.points[0][1]);
let end = to_wcs(piece.points[1][0], piece.points[1][1]);
PatternStationPiece {
source_segment: piece.source_segment as u32,
source_distances: piece.source_distances.map(|distance| distance as f32),
segment_distances: piece.segment_distances.map(|distance| distance as f32),
vector: [
(end.0 - start.0) as f32,
(end.1 - start.1) as f32,
(end.2 - start.2) as f32,
],
}
})
.collect();
WideBandOutline {
points,
stations,
point_segments,
station_pieces,
source_length: boundary.source_length as f32,
}
}
pub(crate) fn extrude_wide_band_outline(
outline: WideBandOutline,
extrusion: [f64; 3],
) -> WideBandOutline {
let base_points = outline.points;
let base_stations = outline.stations;
let base_segments = outline.point_segments;
let mut points = base_points.clone();
let mut stations = base_stations.clone();
let mut point_segments = base_segments.clone();
points.push([f64::NAN; 3]);
stations.push(0.0);
point_segments.push(-1);
points.extend(base_points.iter().map(|point| {
if point[0].is_finite() {
[
point[0] + extrusion[0],
point[1] + extrusion[1],
point[2] + extrusion[2],
]
} else {
*point
}
}));
stations.extend_from_slice(&base_stations);
point_segments.extend_from_slice(&base_segments);
let mut emitted = HashSet::new();
for ((&point, &station), &segment) in base_points
.iter()
.zip(&base_stations)
.zip(&base_segments)
{
if !point[0].is_finite() {
continue;
}
let key = (point.map(f64::to_bits), station.to_bits(), segment);
if !emitted.insert(key) {
continue;
}
points.push([f64::NAN; 3]);
stations.push(0.0);
point_segments.push(-1);
points.push(point);
points.push([
point[0] + extrusion[0],
point[1] + extrusion[1],
point[2] + extrusion[2],
]);
stations.push(station);
stations.push(station);
point_segments.extend([segment; 2]);
}
WideBandOutline {
points,
stations,
point_segments,
station_pieces: outline.station_pieces,
source_length: outline.source_length,
}
}
/// 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).
+5
View File
@@ -2068,6 +2068,7 @@ fn tessellate_dimension_inner(
wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(),
pattern_stations: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
@@ -2102,6 +2103,7 @@ fn tessellate_dimension_inner(
wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(),
pattern_stations: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
@@ -2136,6 +2138,7 @@ fn tessellate_dimension_inner(
wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(),
pattern_stations: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
@@ -2171,6 +2174,7 @@ fn tessellate_dimension_inner(
wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(),
pattern_stations: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
@@ -2222,6 +2226,7 @@ fn tessellate_dimension_inner(
wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(),
pattern_stations: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
+2
View File
@@ -676,6 +676,7 @@ impl LeaderTess for Leader {
return WireModel {
point_marker: None,
taper_widths: Vec::new(),
pattern_stations: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
@@ -855,6 +856,7 @@ impl LeaderTess for Leader {
WireModel {
point_marker: None,
taper_widths: Vec::new(),
pattern_stations: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
+120 -96
View File
@@ -16,6 +16,15 @@ use crate::scene::model::wire_model::TangentGeom;
const TAU: f64 = std::f64::consts::TAU;
const REVCLOUD_BULGE: f64 = 0.5;
const MAX_REVCLOUD_VERTICES: usize = 100_000;
const WIDTH_EPSILON: f64 = 1.0e-9;
fn effective_width(width: f64, constant_width: f64) -> f64 {
if width > WIDTH_EPSILON {
width
} else {
constant_width
}
}
/// Midpoint position on an arc segment defined by its bulge.
fn arc_midpoint(p0: [f64; 2], p1: [f64; 2], bulge: f64) -> [f64; 2] {
@@ -251,7 +260,7 @@ fn thick_wide_band(
}
}
fn to_render(pline: &LwPolyline) -> RenderEntity {
fn to_render(pline: &LwPolyline, fill_mode: bool) -> RenderEntity {
let verts = &pline.vertices;
if verts.is_empty() {
return RenderEntity {
@@ -268,9 +277,6 @@ fn to_render(pline: &LwPolyline) -> RenderEntity {
let normal = (pline.normal.x, pline.normal.y, pline.normal.z);
let count = verts.len();
let seg_count = if pline.is_closed { count } else { count - 1 };
let mut tangents: Vec<TangentGeom> = Vec::new();
let mut key_verts: Vec<[f64; 3]> = Vec::new();
// Convert OCS (x, y, elevation) to a WCS point.
let to_wcs = |x: f64, y: f64| -> (f64, f64, f64) {
crate::scene::view::transform::ocs_point_to_wcs((x, y, elev), normal)
@@ -280,6 +286,44 @@ fn to_render(pline: &LwPolyline) -> RenderEntity {
[wx, wy, wz]
};
let band_verts = band_verts(pline);
if !fill_mode {
let mut boundary = crate::entities::common::wide_band_outline(
&band_verts,
pline.is_closed,
!pline.plinegen,
&to_wcs,
);
if !boundary.points.is_empty() {
if pline.thickness.abs() > 1e-10 {
boundary = crate::entities::common::extrude_wide_band_outline(
boundary,
[
pline.thickness * normal.0,
pline.thickness * normal.1,
pline.thickness * normal.2,
],
);
}
let (tangent_geoms, key_vertices) = centerline_metadata(pline, &to_wcs);
return RenderEntity {
pick_tris: Vec::new(),
object: RenderObject::BoundaryLines {
points: boundary.points,
stations: boundary.stations,
point_segments: boundary.point_segments,
station_pieces: boundary.station_pieces,
source_length: boundary.source_length,
plinegen: pline.plinegen,
},
snap_pts: vec![],
tangent_geoms,
key_vertices,
fill_tris: vec![],
};
}
}
if pline.thickness.abs() > 1e-10 {
let mut path: Vec<[f64; 3]> = Vec::new();
let mut kv: Vec<[f64; 3]> = Vec::new();
@@ -341,7 +385,7 @@ fn to_render(pline: &LwPolyline) -> RenderEntity {
// 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) {
if tapered_band_verts(&band_verts).is_some() {
let mut kv: Vec<[f64; 3]> = Vec::new();
let mut tgs: Vec<TangentGeom> = Vec::new();
for i in 0..seg_count {
@@ -370,8 +414,11 @@ fn to_render(pline: &LwPolyline) -> RenderEntity {
});
}
}
let (pts, widths) =
crate::entities::common::tapered_band_points(&band_verts, pline.is_closed, &to_wcs);
let (pts, widths) = crate::entities::common::tapered_band_points(
&band_verts,
pline.is_closed,
&to_wcs,
);
let (fill_origin, fills) = wide_fills(pline);
return RenderEntity {
pick_tris: crate::entities::common::wide_band_tris(fill_origin, &fills),
@@ -444,36 +491,7 @@ fn to_render(pline: &LwPolyline) -> RenderEntity {
};
}
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);
let bulge = v0.bulge;
if bulge.abs() < 1e-9 {
tangents.push(TangentGeom::Line {
p1: [p0[0] as f32, p0[1] as f32, p0[2] as f32],
p2: [p1[0] as f32, p1[1] as f32, p1[2] 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],
bulge as f64,
) {
let (wcx, wcy, wcz) = to_wcs(arc.center[0], arc.center[1]);
tangents.push(TangentGeom::Circle {
center: [wcx as f32, wcy as f32, wcz as f32],
radius: arc.radius as f32,
});
}
if i == 0 {
key_verts.push([p0[0], p0[1], p0[2]]);
}
key_verts.push([p1[0], p1[1], p1[2]]);
}
let (tangents, key_verts) = centerline_metadata(pline, &to_wcs);
let (fill_origin, fills) = wide_fills(pline);
RenderEntity {
pick_tris: crate::entities::common::wide_band_tris(fill_origin, &fills),
@@ -491,20 +509,23 @@ fn to_render(pline: &LwPolyline) -> RenderEntity {
}
}
/// 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)>> {
/// Effective segment widths for an LwPolyline band.
fn band_verts(pline: &LwPolyline) -> Vec<([f64; 2], f64, f64, f64)> {
let c = pline.constant_width;
let band: Vec<([f64; 2], f64, f64, f64)> = pline
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 };
let sw = effective_width(v.start_width, c);
let ew = effective_width(v.end_width, c);
([v.location.x, v.location.y], v.bulge, sw, ew)
})
.collect();
.collect()
}
fn tapered_band_verts(
band: &[([f64; 2], f64, f64, f64)],
) -> Option<&[([f64; 2], f64, f64, f64)]> {
let w0 = band.first().map_or(0.0, |v| v.2);
let varies = band
.iter()
@@ -517,6 +538,47 @@ fn tapered_band_verts(pline: &LwPolyline) -> Option<Vec<([f64; 2], f64, f64, f64
}
}
fn centerline_metadata(
pline: &LwPolyline,
to_wcs: &dyn Fn(f64, f64) -> (f64, f64, f64),
) -> (Vec<TangentGeom>, Vec<[f64; 3]>) {
let count = pline.vertices.len();
let segment_count = if pline.is_closed {
count
} else {
count.saturating_sub(1)
};
let mut tangents = Vec::with_capacity(segment_count);
let mut key_vertices = Vec::with_capacity(segment_count + 1);
for index in 0..segment_count {
let start = &pline.vertices[index];
let end = &pline.vertices[(index + 1) % count];
let p0 = to_wcs(start.location.x, start.location.y);
let p1 = to_wcs(end.location.x, end.location.y);
if start.bulge.abs() < 1e-9 {
tangents.push(TangentGeom::Line {
p1: [p0.0 as f32, p0.1 as f32, p0.2 as f32],
p2: [p1.0 as f32, p1.1 as f32, p1.2 as f32],
});
} else if let Some(arc) = crate::entities::common::BulgeArc::from_bulge(
[start.location.x, start.location.y],
[end.location.x, end.location.y],
start.bulge,
) {
let center = to_wcs(arc.center[0], arc.center[1]);
tangents.push(TangentGeom::Circle {
center: [center.0 as f32, center.1 as f32, center.2 as f32],
radius: arc.radius as f32,
});
}
if index == 0 {
key_vertices.push([p0.0, p0.1, p0.2]);
}
key_vertices.push([p1.0, p1.1, p1.2]);
}
(tangents, key_vertices)
}
/// Split at vertex `idx`: a closed polyline re-opens there (one piece); an
/// open one splits into two (interior vertices only). `None` when invalid.
pub(crate) fn break_at_vertex(
@@ -852,8 +914,12 @@ fn properties(pline: &LwPolyline) -> Vec<PropSection> {
let v = pline.vertices.get(vi);
let vx = v.map_or(0.0, |v| v.location.x);
let vy = v.map_or(0.0, |v| v.location.y);
let start_w = v.map_or(0.0, |v| v.start_width);
let end_w = v.map_or(0.0, |v| v.end_width);
let start_w = v.map_or(0.0, |v| {
effective_width(v.start_width, pline.constant_width)
});
let end_w = v.map_or(0.0, |v| {
effective_width(v.end_width, pline.constant_width)
});
let mp = <LwPolyline as crate::entities::traits::MassPropsCalc>::mass_props(pline);
let cloud_arc_length = revision_cloud_arc_length(pline);
let vertex_label = if n == 0 {
@@ -964,16 +1030,7 @@ fn apply_geom_prop(pline: &mut LwPolyline, field: &str, value: &str) {
};
match field {
"elevation" => pline.elevation = v,
"global_width" if v.is_finite() && v >= 0.0 => {
pline.constant_width = v;
// Global width replaces every segment override. Keeping old
// start/end values would make this editable row appear to work
// while the renderer continued using the stale per-segment data.
for vertex in &mut pline.vertices {
vertex.start_width = 0.0;
vertex.end_width = 0.0;
}
}
"global_width" if v.is_finite() && v >= 0.0 => pline.constant_width = v,
"vertex_x" => {
if let Some(vtx) = pline.vertices.get_mut(vi) {
vtx.location.x = v;
@@ -985,27 +1042,11 @@ fn apply_geom_prop(pline: &mut LwPolyline, field: &str, value: &str) {
}
}
"start_width" if v.is_finite() && v >= 0.0 => {
if pline.constant_width > 0.0 {
let width = pline.constant_width;
for vertex in &mut pline.vertices {
vertex.start_width = width;
vertex.end_width = width;
}
pline.constant_width = 0.0;
}
if let Some(vtx) = pline.vertices.get_mut(vi) {
vtx.start_width = v;
}
}
"end_width" if v.is_finite() && v >= 0.0 => {
if pline.constant_width > 0.0 {
let width = pline.constant_width;
for vertex in &mut pline.vertices {
vertex.start_width = width;
vertex.end_width = width;
}
pline.constant_width = 0.0;
}
if let Some(vtx) = pline.vertices.get_mut(vi) {
vtx.end_width = v;
}
@@ -1104,8 +1145,8 @@ fn apply_transform(pline: &mut LwPolyline, t: &EntityTransform) {
}
impl RenderConvertible for LwPolyline {
fn to_render(&self, _document: &acadrust::CadDocument) -> Option<RenderEntity> {
Some(to_render(self))
fn to_render(&self, document: &acadrust::CadDocument) -> Option<RenderEntity> {
Some(to_render(self, document.header.fill_mode))
}
}
@@ -1226,16 +1267,8 @@ impl crate::entities::traits::Grippable for LwPolyline {
new_v.location.x = midpoint[0];
new_v.location.y = midpoint[1];
new_v.vertex_id = 0;
let effective_start = if v0.start_width > 1e-9 {
v0.start_width
} else {
self.constant_width
};
let effective_end = if v0.end_width > 1e-9 {
v0.end_width
} else {
self.constant_width
};
let effective_start = effective_width(v0.start_width, self.constant_width);
let effective_end = effective_width(v0.end_width, self.constant_width);
let middle_width = (effective_start + effective_end) * 0.5;
self.vertices[i0].end_width = middle_width;
new_v.start_width = middle_width;
@@ -1296,7 +1329,6 @@ pub(crate) fn wide_fills(pl: &acadrust::entities::LwPolyline) -> ([f64; 2], Vec<
// Feeding the stored width in whole draws every wide polyline twice as wide
// as the file asks for, which on a donut (a closed 2-vertex bulge-1 polyline)
// shows up as a disc 1.5× its real radius.
let hw_const = pl.constant_width as f32 * 0.5;
let verts = &pl.vertices;
let n = verts.len();
if n < 2 {
@@ -1308,16 +1340,8 @@ pub(crate) fn wide_fills(pl: &acadrust::entities::LwPolyline) -> ([f64; 2], Vec<
for i in 0..seg_count {
let v0 = &verts[i];
let v1 = &verts[(i + 1) % n];
let hw0 = if v0.start_width > 1e-9 {
v0.start_width as f32 * 0.5
} else {
hw_const
};
let hw1 = if v0.end_width > 1e-9 {
v0.end_width as f32 * 0.5
} else {
hw_const
};
let hw0 = effective_width(v0.start_width, pl.constant_width) as f32 * 0.5;
let hw1 = effective_width(v0.end_width, pl.constant_width) as f32 * 0.5;
if hw0 < 1e-6 && hw1 < 1e-6 {
continue;
}
+5
View File
@@ -1505,6 +1505,7 @@ impl MultiLeaderTess for MultiLeader {
wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(),
pattern_stations: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
@@ -1845,6 +1846,7 @@ impl MultiLeaderTess for MultiLeader {
wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(),
pattern_stations: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
@@ -1918,6 +1920,7 @@ impl MultiLeaderTess for MultiLeader {
wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(),
pattern_stations: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
@@ -2006,6 +2009,7 @@ impl MultiLeaderTess for MultiLeader {
wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(),
pattern_stations: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
@@ -2049,6 +2053,7 @@ impl MultiLeaderTess for MultiLeader {
wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(),
pattern_stations: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
+99 -11
View File
@@ -195,7 +195,7 @@ pub fn drawn_vertices2d(
(kept.len() >= 2).then_some(kept)
}
fn tessellate_polyline2d(pl: &Polyline2D) -> RenderEntity {
fn tessellate_polyline2d(pl: &Polyline2D, fill_mode: bool) -> RenderEntity {
let filtered = drawn_vertices2d(pl);
let verts: &[acadrust::entities::Vertex2D] = filtered.as_deref().unwrap_or(&pl.vertices);
if verts.is_empty() {
@@ -224,6 +224,47 @@ fn tessellate_polyline2d(pl: &Polyline2D) -> RenderEntity {
[wx, wy, wz]
};
if !fill_mode {
let continuous = pl.flags.bits()
& acadrust::entities::PolylineFlags::LINETYPE_CONTINUOUS.bits()
!= 0;
let mut boundary = crate::entities::common::wide_band_outline(
&band_verts_2d(pl),
pl.is_closed(),
!continuous,
&to_wcs,
);
if !boundary.points.is_empty() {
if pl.thickness.abs() > 1e-10 {
boundary = crate::entities::common::extrude_wide_band_outline(
boundary,
[
pl.thickness * normal.0,
pl.thickness * normal.1,
pl.thickness * normal.2,
],
);
}
let (tangent_geoms, key_vertices) =
centerline_metadata_2d(verts, pl.is_closed(), &to_wcs);
return RenderEntity {
pick_tris: Vec::new(),
object: RenderObject::BoundaryLines {
points: boundary.points,
stations: boundary.stations,
point_segments: boundary.point_segments,
station_pieces: boundary.station_pieces,
source_length: boundary.source_length,
plinegen: continuous,
},
snap_pts: vec![],
tangent_geoms,
key_vertices,
fill_tris: vec![],
};
}
}
if pl.thickness.abs() > 1e-10 {
let (nx, ny, nz) = normal;
let t = pl.thickness;
@@ -348,13 +389,14 @@ fn tessellate_polyline2d(pl: &Polyline2D) -> RenderEntity {
key_verts.push([p1[0], p1[1], p1[2]]);
}
let band_verts = band_verts_2d(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) {
let object = match tapered_band_verts_2d(&band_verts) {
Some(band_verts) => {
let (pts, widths) = crate::entities::common::tapered_band_points(
&band_verts,
band_verts,
pl.is_closed(),
&to_wcs,
);
@@ -376,16 +418,15 @@ fn tessellate_polyline2d(pl: &Polyline2D) -> RenderEntity {
}
}
/// 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(
/// Effective segment widths for a Polyline2D band.
fn band_verts_2d(
pl: &acadrust::entities::Polyline2D,
) -> Option<Vec<([f64; 2], f64, f64, f64)>> {
) -> Vec<([f64; 2], f64, f64, f64)> {
let default_start = pl.start_width;
let default_end = pl.end_width;
let filtered = drawn_vertices2d(pl);
let verts: &[acadrust::entities::Vertex2D] = filtered.as_deref().unwrap_or(&pl.vertices);
let band: Vec<([f64; 2], f64, f64, f64)> = verts
verts
.iter()
.map(|v| {
let sw = if v.start_width > 1e-9 {
@@ -400,7 +441,12 @@ fn tapered_band_verts_2d(
};
([v.location.x, v.location.y], v.bulge, sw, ew)
})
.collect();
.collect()
}
fn tapered_band_verts_2d(
band: &[([f64; 2], f64, f64, f64)],
) -> Option<&[([f64; 2], f64, f64, f64)]> {
let w0 = band.first().map_or(0.0, |v| v.2);
let varies = band
.iter()
@@ -412,9 +458,51 @@ fn tapered_band_verts_2d(
}
}
fn centerline_metadata_2d(
verts: &[acadrust::entities::Vertex2D],
closed: bool,
to_wcs: &dyn Fn(f64, f64) -> (f64, f64, f64),
) -> (Vec<TangentGeom>, Vec<[f64; 3]>) {
let count = verts.len();
let segment_count = if closed {
count
} else {
count.saturating_sub(1)
};
let mut tangents = Vec::with_capacity(segment_count);
let mut key_vertices = Vec::with_capacity(segment_count + 1);
for index in 0..segment_count {
let start = &verts[index];
let end = &verts[(index + 1) % count];
let p0 = to_wcs(start.location.x, start.location.y);
let p1 = to_wcs(end.location.x, end.location.y);
if start.bulge.abs() < 1e-9 {
tangents.push(TangentGeom::Line {
p1: [p0.0 as f32, p0.1 as f32, p0.2 as f32],
p2: [p1.0 as f32, p1.1 as f32, p1.2 as f32],
});
} else if let Some(arc) = crate::entities::common::BulgeArc::from_bulge(
[start.location.x, start.location.y],
[end.location.x, end.location.y],
start.bulge,
) {
let center = to_wcs(arc.center[0], arc.center[1]);
tangents.push(TangentGeom::Circle {
center: [center.0 as f32, center.1 as f32, center.2 as f32],
radius: arc.radius as f32,
});
}
if index == 0 {
key_vertices.push([p0.0, p0.1, p0.2]);
}
key_vertices.push([p1.0, p1.1, p1.2]);
}
(tangents, key_vertices)
}
impl RenderConvertible for Polyline2D {
fn to_render(&self, _document: &acadrust::CadDocument) -> Option<RenderEntity> {
Some(tessellate_polyline2d(self))
fn to_render(&self, document: &acadrust::CadDocument) -> Option<RenderEntity> {
Some(tessellate_polyline2d(self, document.header.fill_mode))
}
}
+1
View File
@@ -1669,6 +1669,7 @@ pub fn tessellate_table(
WireModel {
point_marker: None,
taper_widths: Vec::new(),
pattern_stations: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,