forked from 1b-agent/opencadstudio-upstream
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:
commit
4209c35ba3
60 files changed
+1620
-575
No files matched your search
Vendored
+203
-9
@@ -23,7 +23,10 @@ use acadrust::{CadDocument, EntityType, Handle};
|
||||
use cadkernel::space::{Plane as KernelPlane, Vec3 as KernelVec3};
|
||||
|
||||
use crate::scene::convert::tessellate;
|
||||
use crate::scene::model::wire_model::{PointMarker, SnapHint, TangentGeom, WireModel};
|
||||
use crate::scene::model::wire_model::{
|
||||
decode_pattern_station_map, encode_pattern_stations, pattern_station_values, PatternStationMap,
|
||||
PatternStationPiece, PointMarker, SnapHint, TangentGeom, WireModel,
|
||||
};
|
||||
|
||||
const MAX_NESTING_DEPTH: usize = 32;
|
||||
/// Skip wires whose world-AABB projects to fewer than this many pixels in
|
||||
@@ -70,6 +73,7 @@ pub struct LocalWire {
|
||||
pub pattern_length: f32,
|
||||
pub pattern: [f32; 8],
|
||||
pub line_weight_px: f32,
|
||||
pub pattern_stations: Vec<f32>,
|
||||
/// World-space band width for a wide polyline (see `WireModel.world_width`).
|
||||
/// Block-local; the expand-time transform scales it by the insert so the
|
||||
/// shader band grows with a scaled insert. `0.0` = a normal wire.
|
||||
@@ -865,6 +869,7 @@ fn tessellate_sub_local(
|
||||
pattern_length: pat_len,
|
||||
pattern: pat,
|
||||
line_weight_px: lw_px,
|
||||
pattern_stations: wire.pattern_stations,
|
||||
world_width: wire.world_width,
|
||||
plinegen: wire.plinegen,
|
||||
plot_visible: frame_mode.is_none_or(|mode| mode == 1)
|
||||
@@ -1433,6 +1438,7 @@ struct StyleKey {
|
||||
pattern_length: u32,
|
||||
pattern: [u32; 8],
|
||||
line_weight_px: u32,
|
||||
source_length: u32,
|
||||
/// Wide-polyline band width (bit-cast). Keeps bands of different widths — and
|
||||
/// bands vs thin wires of the same colour/style — in separate batches so the
|
||||
/// finalized WireModel carries one correct `world_width`.
|
||||
@@ -1475,6 +1481,10 @@ struct BatchEntry {
|
||||
pattern_length: f32,
|
||||
pattern: [f32; 8],
|
||||
line_weight_px: f32,
|
||||
pattern_stations: Vec<f32>,
|
||||
pattern_station_segments: Vec<i32>,
|
||||
pattern_station_pieces: Vec<PatternStationPiece>,
|
||||
source_length: f32,
|
||||
world_width: f32,
|
||||
point_marker: Option<PointMarker>,
|
||||
aci: u8,
|
||||
@@ -1555,6 +1565,7 @@ impl BatchEntry {
|
||||
pat_len: f32,
|
||||
pat: [f32; 8],
|
||||
lw_px: f32,
|
||||
source_length: f32,
|
||||
world_width: f32,
|
||||
point_marker: Option<PointMarker>,
|
||||
aci: u8,
|
||||
@@ -1578,6 +1589,7 @@ impl BatchEntry {
|
||||
pattern_length: pat_len,
|
||||
pattern: pat,
|
||||
line_weight_px: lw_px,
|
||||
source_length,
|
||||
world_width,
|
||||
point_marker,
|
||||
aci,
|
||||
@@ -1606,6 +1618,9 @@ impl Batches {
|
||||
if b.hide_unselected && !selected {
|
||||
b.points.clear();
|
||||
b.points_low.clear();
|
||||
b.pattern_stations.clear();
|
||||
b.pattern_station_segments.clear();
|
||||
b.pattern_station_pieces.clear();
|
||||
}
|
||||
let aabb = if b.min_x.is_infinite() {
|
||||
WireModel::UNBOUNDED_AABB
|
||||
@@ -1628,9 +1643,18 @@ impl Batches {
|
||||
);
|
||||
}
|
||||
}
|
||||
if !b.pattern_stations.is_empty() {
|
||||
b.pattern_stations = encode_pattern_stations(
|
||||
std::mem::take(&mut b.pattern_stations),
|
||||
b.source_length,
|
||||
&b.pattern_station_segments,
|
||||
&b.pattern_station_pieces,
|
||||
);
|
||||
}
|
||||
WireModel {
|
||||
point_marker: b.point_marker,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: b.pattern_stations,
|
||||
world_width: b.world_width,
|
||||
depth_override: b.local_depth,
|
||||
display_visible: !b.hide_unselected || selected,
|
||||
@@ -1681,6 +1705,7 @@ fn style_key(
|
||||
pat_len: f32,
|
||||
pat: [f32; 8],
|
||||
lw_px: f32,
|
||||
source_length: f32,
|
||||
world_width: f32,
|
||||
point_marker: Option<PointMarker>,
|
||||
aci: u8,
|
||||
@@ -1714,6 +1739,7 @@ fn style_key(
|
||||
pat[7].to_bits(),
|
||||
],
|
||||
line_weight_px: lw_px.to_bits(),
|
||||
source_length: source_length.to_bits(),
|
||||
world_width: world_width.to_bits(),
|
||||
point_marker: point_marker.map(|marker| PointMarkerKey {
|
||||
origin: marker.origin.to_array().map(f64::to_bits),
|
||||
@@ -2060,6 +2086,116 @@ fn transformed_wire_length_scale(lw: &LocalWire, xform: &Transform) -> f32 {
|
||||
}
|
||||
}
|
||||
|
||||
struct TransformedStationData {
|
||||
values: Vec<f32>,
|
||||
source_length: f32,
|
||||
point_segments: Vec<i32>,
|
||||
pieces: Vec<PatternStationPiece>,
|
||||
}
|
||||
|
||||
fn transformed_station_data(
|
||||
values: &[f32],
|
||||
map: &PatternStationMap,
|
||||
plinegen: bool,
|
||||
xform: &Transform,
|
||||
) -> Option<TransformedStationData> {
|
||||
if values.len() != map.point_segments.len() || map.pieces.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut pieces = Vec::with_capacity(map.pieces.len());
|
||||
let mut global = 0.0_f32;
|
||||
let mut local = 0.0_f32;
|
||||
let mut current_segment = None;
|
||||
for piece in &map.pieces {
|
||||
if current_segment != Some(piece.source_segment) {
|
||||
current_segment = Some(piece.source_segment);
|
||||
local = 0.0;
|
||||
}
|
||||
let vector = Vector3::new(
|
||||
piece.vector[0] as f64,
|
||||
piece.vector[1] as f64,
|
||||
piece.vector[2] as f64,
|
||||
);
|
||||
let mapped = xform.matrix.transform_direction(vector);
|
||||
let chord = vector.length();
|
||||
let mapped_chord = mapped.length();
|
||||
let original_length = (piece.source_distances[1] - piece.source_distances[0]).abs();
|
||||
let length = if chord > 1e-12 && mapped_chord.is_finite() {
|
||||
original_length * (mapped_chord / chord) as f32
|
||||
} else {
|
||||
original_length
|
||||
};
|
||||
pieces.push(PatternStationPiece {
|
||||
source_segment: piece.source_segment,
|
||||
source_distances: [global, global + length],
|
||||
segment_distances: [local, local + length],
|
||||
vector: [mapped.x as f32, mapped.y as f32, mapped.z as f32],
|
||||
});
|
||||
global += length;
|
||||
local += length;
|
||||
}
|
||||
|
||||
let fallback_scale = map
|
||||
.pieces
|
||||
.last()
|
||||
.map(|piece| piece.source_distances[1])
|
||||
.filter(|length| length.abs() > 1e-12)
|
||||
.map_or(1.0, |length| global / length);
|
||||
let mapped_values = values
|
||||
.iter()
|
||||
.zip(&map.point_segments)
|
||||
.map(|(&value, &segment)| {
|
||||
if segment < 0 {
|
||||
return 0.0;
|
||||
}
|
||||
let segment = segment as u32;
|
||||
let range = |piece: &PatternStationPiece| {
|
||||
if plinegen {
|
||||
piece.source_distances
|
||||
} else {
|
||||
piece.segment_distances
|
||||
}
|
||||
};
|
||||
let distance_to = |piece: &PatternStationPiece| {
|
||||
let [start, end] = range(piece);
|
||||
if value < start {
|
||||
start - value
|
||||
} else if value > end {
|
||||
value - end
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
};
|
||||
let Some((old_piece, new_piece)) = map
|
||||
.pieces
|
||||
.iter()
|
||||
.zip(&pieces)
|
||||
.filter(|(piece, _)| piece.source_segment == segment)
|
||||
.min_by(|(a, _), (b, _)| distance_to(a).total_cmp(&distance_to(b)))
|
||||
else {
|
||||
return value * fallback_scale;
|
||||
};
|
||||
let old = range(old_piece);
|
||||
let new = range(new_piece);
|
||||
let span = old[1] - old[0];
|
||||
let t = if span.abs() > 1e-12 {
|
||||
((value - old[0]) / span).clamp(0.0, 1.0)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
new[0] + (new[1] - new[0]) * t
|
||||
})
|
||||
.collect();
|
||||
|
||||
Some(TransformedStationData {
|
||||
values: mapped_values,
|
||||
source_length: global,
|
||||
point_segments: map.point_segments.clone(),
|
||||
pieces,
|
||||
})
|
||||
}
|
||||
|
||||
fn transformed_point_marker(marker: PointMarker, xform: &Transform) -> (PointMarker, f64) {
|
||||
let origin = xform.apply(Vector3::new(marker.origin.x, marker.origin.y, marker.origin.z));
|
||||
let map_direction = |direction: glam::DVec3| {
|
||||
@@ -2163,16 +2299,38 @@ fn emit_wire(
|
||||
lw.line_weight_px
|
||||
};
|
||||
|
||||
// Pattern distances are stored in block-local units. Scale them by the
|
||||
// wire's actual path-length ratio so uniform and non-uniform INSERTs both
|
||||
// stay dimensionally consistent with their transformed geometry.
|
||||
let pattern_scale = if final_pat_len > 0.0 {
|
||||
transformed_wire_length_scale(lw, accum_xform)
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let station_values = pattern_station_values(&lw.pattern_stations, lw.points.len());
|
||||
let station_map = decode_pattern_station_map(&lw.pattern_stations, lw.points.len());
|
||||
let transformed_stations = station_values.and_then(|(values, _)| {
|
||||
station_map.as_ref().and_then(|map| {
|
||||
transformed_station_data(values, map, lw.plinegen, accum_xform)
|
||||
})
|
||||
});
|
||||
let has_stations = station_values.is_some();
|
||||
let pattern_scale = transformed_stations.as_ref().map_or_else(
|
||||
|| {
|
||||
if final_pat_len > 0.0 || has_stations {
|
||||
transformed_wire_length_scale(lw, accum_xform)
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
},
|
||||
|data| {
|
||||
station_values.map_or(1.0, |(_, source_length)| {
|
||||
if source_length.abs() > 1e-12 {
|
||||
data.source_length / source_length
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
})
|
||||
},
|
||||
);
|
||||
let final_pat_len = final_pat_len * ctx.pslt_factor * pattern_scale;
|
||||
let final_pat = final_pat.map(|v| v * ctx.pslt_factor * pattern_scale);
|
||||
let source_length = transformed_stations.as_ref().map_or_else(
|
||||
|| station_values.map_or(0.0, |(_, length)| length * pattern_scale),
|
||||
|data| data.source_length,
|
||||
);
|
||||
|
||||
// A wide polyline's band width is baked in block-local units; scale it by
|
||||
// the insert transform so the shader band matches the scaled geometry.
|
||||
@@ -2211,6 +2369,7 @@ fn emit_wire(
|
||||
final_pat_len,
|
||||
final_pat,
|
||||
final_lw_px,
|
||||
source_length,
|
||||
final_world_width,
|
||||
point_marker,
|
||||
final_aci,
|
||||
@@ -2223,6 +2382,12 @@ fn emit_wire(
|
||||
local_depth,
|
||||
);
|
||||
|
||||
if transformed_stations.is_some() {
|
||||
if let Some(closed) = out.by_style.remove(&key) {
|
||||
out.closed.push(closed);
|
||||
}
|
||||
}
|
||||
|
||||
// If the open batch for this style would exceed wgpu's per-buffer limit
|
||||
// after appending this wire, finalize it now and start a fresh batch.
|
||||
if let Some(existing) = out.by_style.get(&key) {
|
||||
@@ -2241,6 +2406,7 @@ fn emit_wire(
|
||||
final_pat_len,
|
||||
final_pat,
|
||||
final_lw_px,
|
||||
source_length,
|
||||
final_world_width,
|
||||
point_marker,
|
||||
final_aci,
|
||||
@@ -2253,6 +2419,9 @@ fn emit_wire(
|
||||
)
|
||||
});
|
||||
entry.local_depth = local_depth;
|
||||
if let Some(data) = &transformed_stations {
|
||||
entry.pattern_station_pieces.clone_from(&data.pieces);
|
||||
}
|
||||
|
||||
// NaN separator between previously-appended geometry and this wire so the
|
||||
// GPU shader treats them as disconnected polylines within one buffer.
|
||||
@@ -2263,14 +2432,33 @@ fn emit_wire(
|
||||
if needs_sep {
|
||||
entry.points.push([f32::NAN; 3]);
|
||||
entry.points_low.push([0.0; 3]);
|
||||
if has_stations {
|
||||
entry.pattern_stations.push(0.0);
|
||||
}
|
||||
if transformed_stations.is_some() {
|
||||
entry.pattern_station_segments.push(-1);
|
||||
}
|
||||
}
|
||||
// Iterate paired with the matching low residual so the GPU keeps
|
||||
// sub-f32 precision once the INSERT transform lands the wire in
|
||||
// world space at UTM-scale coordinates.
|
||||
for (idx, p) in lw.points.iter().enumerate() {
|
||||
let station = transformed_stations
|
||||
.as_ref()
|
||||
.map(|data| data.values[idx])
|
||||
.or_else(|| has_stations.then(|| lw.pattern_stations[idx] * pattern_scale));
|
||||
let station_segment = transformed_stations
|
||||
.as_ref()
|
||||
.map(|data| data.point_segments[idx]);
|
||||
if p[0].is_nan() {
|
||||
entry.points.push([f32::NAN; 3]);
|
||||
entry.points_low.push([0.0; 3]);
|
||||
if let Some(station) = station {
|
||||
entry.pattern_stations.push(station);
|
||||
}
|
||||
if let Some(segment) = station_segment {
|
||||
entry.pattern_station_segments.push(segment);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let pl = lw.points_low.get(idx).copied().unwrap_or([0.0; 3]);
|
||||
@@ -2311,6 +2499,12 @@ fn emit_wire(
|
||||
}
|
||||
entry.points.push(q);
|
||||
entry.points_low.push([qx_l, qy_l, qz_l]);
|
||||
if let Some(station) = station {
|
||||
entry.pattern_stations.push(station);
|
||||
}
|
||||
if let Some(segment) = station_segment {
|
||||
entry.pattern_station_segments.push(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,15 @@ pub enum RenderObject {
|
||||
Lines(Vec<[f64; 3]>),
|
||||
/// Like Lines but linetype pattern restarts at each NaN-separated segment (plinegen=false).
|
||||
SegmentedLines(Vec<[f64; 3]>),
|
||||
/// Kernel band boundary with centreline-based linetype stations.
|
||||
BoundaryLines {
|
||||
points: Vec<[f64; 3]>,
|
||||
stations: Vec<f32>,
|
||||
point_segments: Vec<i32>,
|
||||
station_pieces: Vec<crate::scene::model::wire_model::PatternStationPiece>,
|
||||
source_length: f32,
|
||||
plinegen: bool,
|
||||
},
|
||||
/// 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
|
||||
|
||||
@@ -750,6 +750,7 @@ fn tessellate_entity_inner(
|
||||
return vec![WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -1110,6 +1111,7 @@ fn tessellate_entity_inner(
|
||||
let marker = WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -1508,6 +1510,7 @@ fn lod_stub_wire(
|
||||
WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -1602,6 +1605,7 @@ fn lod_stub_wire_3d(
|
||||
WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
|
||||
@@ -147,6 +147,7 @@ fn point_cloud_wires(
|
||||
let mut wires = vec![WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -400,6 +401,7 @@ pub fn tessellate(
|
||||
out.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -562,6 +564,7 @@ pub fn tessellate(
|
||||
elem_wires.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -870,6 +873,7 @@ pub fn tessellate(
|
||||
wires.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -920,6 +924,7 @@ pub fn tessellate(
|
||||
wires.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -972,6 +977,7 @@ pub fn tessellate(
|
||||
wires.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -1005,6 +1011,7 @@ pub fn tessellate(
|
||||
wires.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -1068,6 +1075,7 @@ pub fn tessellate(
|
||||
out.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -1113,6 +1121,7 @@ pub fn tessellate(
|
||||
out.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -1155,6 +1164,7 @@ pub fn tessellate(
|
||||
out.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -1190,6 +1200,7 @@ pub fn tessellate(
|
||||
out.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -1259,6 +1270,7 @@ pub fn tessellate(
|
||||
return vec![WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -1355,8 +1367,7 @@ pub fn tessellate(
|
||||
} else {
|
||||
color
|
||||
};
|
||||
// Basic curve entities use the Lines path for large-coordinate precision,
|
||||
// but they must still preserve the entity's resolved linetype pattern.
|
||||
// Basic curves keep their resolved linetype.
|
||||
let (edge_pattern_length, edge_pattern) =
|
||||
if matches!(
|
||||
entity,
|
||||
@@ -1385,7 +1396,8 @@ pub fn tessellate(
|
||||
out.push(WireModel {
|
||||
point_marker,
|
||||
taper_widths: Vec::new(),
|
||||
world_width: polyline_band_width(entity),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: polyline_band_width(entity, document.header.fill_mode),
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
plot_visible: true,
|
||||
@@ -1429,6 +1441,7 @@ pub fn tessellate(
|
||||
out.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
pick_tris: Vec::new(),
|
||||
pick_tris_low: Vec::new(),
|
||||
@@ -1464,6 +1477,7 @@ pub fn tessellate(
|
||||
out.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -1498,6 +1512,61 @@ pub fn tessellate(
|
||||
return out;
|
||||
}
|
||||
|
||||
RenderObject::BoundaryLines {
|
||||
points,
|
||||
stations,
|
||||
point_segments,
|
||||
station_pieces,
|
||||
source_length,
|
||||
plinegen,
|
||||
} => {
|
||||
let (local_pts, local_pts_low) = points_to_ds(points);
|
||||
let key_vertices = te
|
||||
.key_vertices
|
||||
.into_iter()
|
||||
.map(|[x, y, z]| [x, y, z])
|
||||
.collect();
|
||||
let station_data = crate::scene::model::wire_model::encode_pattern_stations(
|
||||
stations,
|
||||
source_length,
|
||||
&point_segments,
|
||||
&station_pieces,
|
||||
);
|
||||
return vec![WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: station_data,
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
plot_visible: true,
|
||||
fill_is_3d: false,
|
||||
fill_is_2d_solid: false,
|
||||
render_instance: None,
|
||||
pick_tris: Vec::new(),
|
||||
pick_tris_low: Vec::new(),
|
||||
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: te.snap_pts,
|
||||
tangent_geoms: te.tangent_geoms,
|
||||
aci: 0,
|
||||
key_vertices,
|
||||
plinegen,
|
||||
aabb: WireModel::UNBOUNDED_AABB,
|
||||
fill_tris: Vec::new(),
|
||||
fill_tris_low: Vec::new(),
|
||||
}];
|
||||
}
|
||||
|
||||
RenderObject::SegmentedLines(points) => {
|
||||
let (local_pts, local_pts_low) = points_to_ds(points);
|
||||
let snap_pts = te.snap_pts;
|
||||
@@ -1512,7 +1581,8 @@ pub fn tessellate(
|
||||
return vec![WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
world_width: polyline_band_width(entity),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: polyline_band_width(entity, document.header.fill_mode),
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
plot_visible: true,
|
||||
@@ -1561,6 +1631,7 @@ pub fn tessellate(
|
||||
return vec![WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: widths,
|
||||
pattern_stations: Vec::new(),
|
||||
world_width,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -1683,6 +1754,7 @@ pub fn tessellate(
|
||||
vec![WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -2140,14 +2212,11 @@ pub(crate) fn entity_z(entity: &EntityType) -> f32 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Band width (drawing units) of a wide polyline whose flat band is drawn by
|
||||
/// expanding its centre-line wire in the shader. `0.0` = a zero-width polyline,
|
||||
/// a non-polyline (a normal screen-pixel wire), or a thickness-extruded one — an
|
||||
/// LwPolyline thickens into a 3-D tube (`thick_wide_band`) and a Polyline2D
|
||||
/// keeps its hatch band (only its centre-line extrudes; no tube yet), so neither
|
||||
/// takes the flat shader band. A tapering polyline (per-vertex start/end widths)
|
||||
/// collapses to its widest edge — the shader carries one width per wire.
|
||||
fn polyline_band_width(entity: &EntityType) -> f32 {
|
||||
/// Shader band width, or zero while FILLMODE draws the kernel boundary.
|
||||
fn polyline_band_width(entity: &EntityType, fill_mode: bool) -> f32 {
|
||||
if !fill_mode {
|
||||
return 0.0;
|
||||
}
|
||||
let w = match entity {
|
||||
EntityType::LwPolyline(p) if p.thickness.abs() <= 1e-10 => {
|
||||
let mut w = p.constant_width;
|
||||
|
||||
@@ -59,6 +59,72 @@ pub struct PointMarker {
|
||||
pub viewport_percent: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PatternStationPiece {
|
||||
pub source_segment: u32,
|
||||
pub source_distances: [f32; 2],
|
||||
pub segment_distances: [f32; 2],
|
||||
pub vector: [f32; 3],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct PatternStationMap {
|
||||
pub point_segments: Vec<i32>,
|
||||
pub pieces: Vec<PatternStationPiece>,
|
||||
}
|
||||
|
||||
pub(crate) fn pattern_station_values(data: &[f32], point_count: usize) -> Option<(&[f32], f32)> {
|
||||
(data.len() >= point_count + 1).then(|| (&data[..point_count], data[point_count]))
|
||||
}
|
||||
|
||||
pub(crate) fn encode_pattern_stations(
|
||||
mut stations: Vec<f32>,
|
||||
source_length: f32,
|
||||
point_segments: &[i32],
|
||||
pieces: &[PatternStationPiece],
|
||||
) -> Vec<f32> {
|
||||
let point_count = stations.len();
|
||||
stations.push(source_length);
|
||||
if point_segments.len() != point_count || pieces.is_empty() {
|
||||
return stations;
|
||||
}
|
||||
stations.extend(point_segments.iter().map(|segment| *segment as f32));
|
||||
for piece in pieces {
|
||||
stations.push(piece.source_segment as f32);
|
||||
stations.extend(piece.source_distances);
|
||||
stations.extend(piece.segment_distances);
|
||||
stations.extend(piece.vector);
|
||||
}
|
||||
stations
|
||||
}
|
||||
|
||||
pub(crate) fn decode_pattern_station_map(
|
||||
data: &[f32],
|
||||
point_count: usize,
|
||||
) -> Option<PatternStationMap> {
|
||||
let metadata_start = point_count.checked_mul(2)?.checked_add(1)?;
|
||||
if data.len() < metadata_start || (data.len() - metadata_start) % 8 != 0 {
|
||||
return None;
|
||||
}
|
||||
let point_segments = data[point_count + 1..metadata_start]
|
||||
.iter()
|
||||
.map(|value| *value as i32)
|
||||
.collect();
|
||||
let pieces = data[metadata_start..]
|
||||
.chunks_exact(8)
|
||||
.map(|values| PatternStationPiece {
|
||||
source_segment: values[0] as u32,
|
||||
source_distances: [values[1], values[2]],
|
||||
segment_distances: [values[3], values[4]],
|
||||
vector: [values[5], values[6], values[7]],
|
||||
})
|
||||
.collect();
|
||||
Some(PatternStationMap {
|
||||
point_segments,
|
||||
pieces,
|
||||
})
|
||||
}
|
||||
|
||||
impl PointMarker {
|
||||
pub fn resolve(self, point: glam::DVec3, view_height: f64) -> glam::DVec3 {
|
||||
let normal = self.normal.normalize_or(glam::DVec3::Z);
|
||||
@@ -111,6 +177,8 @@ pub struct WireModel {
|
||||
/// 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>,
|
||||
/// Centreline stations and optional source-piece transform metadata.
|
||||
pub pattern_stations: Vec<f32>,
|
||||
/// ACI color index (1-255). 0 means true-color or unknown (no CTB lookup).
|
||||
pub aci: u8,
|
||||
/// Pre-baked snap candidates (Center, Node, Quadrant, Insertion).
|
||||
@@ -249,6 +317,7 @@ impl WireModel {
|
||||
Self {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -625,6 +694,7 @@ impl Default for WireModel {
|
||||
line_weight_px: 1.0,
|
||||
world_width: 0.0,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
aci: 0,
|
||||
snap_pts: Vec::new(),
|
||||
tangent_geoms: Vec::new(),
|
||||
|
||||
+89
-7
@@ -16,7 +16,9 @@ use acadrust::types::{Handle, Transform, Vector3};
|
||||
use acadrust::CadDocument;
|
||||
use cadkernel::geom2d::{contains, Curve, Line, Tolerance};
|
||||
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use crate::scene::model::wire_model::{
|
||||
decode_pattern_station_map, encode_pattern_stations, pattern_station_values, WireModel,
|
||||
};
|
||||
|
||||
const NAN3: [f32; 3] = [f32::NAN, f32::NAN, f32::NAN];
|
||||
|
||||
@@ -144,6 +146,7 @@ pub fn clip_wires(wires: &mut Vec<WireModel>, poly: &[[f64; 2]]) {
|
||||
if keep_marker == Some(false) {
|
||||
w.points.clear();
|
||||
w.points_low.clear();
|
||||
w.pattern_stations.clear();
|
||||
w.point_marker = None;
|
||||
} else if keep_marker.is_none() && !w.points.is_empty() {
|
||||
// Absolute → local f32 (NaN separators preserved).
|
||||
@@ -158,7 +161,15 @@ pub fn clip_wires(wires: &mut Vec<WireModel>, poly: &[[f64; 2]]) {
|
||||
[(a[0] - rx) as f32, (a[1] - ry) as f32, a[2] as f32]
|
||||
})
|
||||
.collect();
|
||||
let clipped = clip_polyline(&local, &lpoly);
|
||||
let station_data = pattern_station_values(&w.pattern_stations, w.points.len());
|
||||
let source_length = station_data.map(|(_, source_length)| source_length);
|
||||
let station_map = decode_pattern_station_map(&w.pattern_stations, w.points.len());
|
||||
let (clipped, clipped_stations, clipped_segments) = clip_polyline_stationed(
|
||||
&local,
|
||||
&lpoly,
|
||||
station_data.map(|(stations, _)| stations),
|
||||
station_map.as_ref().map(|map| map.point_segments.as_slice()),
|
||||
);
|
||||
// Local → absolute, re-split into double-single high/low.
|
||||
let mut hi = Vec::with_capacity(clipped.len());
|
||||
let mut lo = Vec::with_capacity(clipped.len());
|
||||
@@ -176,6 +187,16 @@ pub fn clip_wires(wires: &mut Vec<WireModel>, poly: &[[f64; 2]]) {
|
||||
}
|
||||
w.points = hi;
|
||||
w.points_low = lo;
|
||||
if let Some(source_length) = source_length {
|
||||
w.pattern_stations = encode_pattern_stations(
|
||||
clipped_stations,
|
||||
source_length,
|
||||
&clipped_segments,
|
||||
station_map.as_ref().map_or(&[], |map| map.pieces.as_slice()),
|
||||
);
|
||||
} else {
|
||||
w.pattern_stations.clear();
|
||||
}
|
||||
}
|
||||
if !w.fill_tris.is_empty() {
|
||||
let local: Vec<[f32; 3]> = (0..w.fill_tris.len())
|
||||
@@ -277,6 +298,7 @@ pub fn frame_wire(
|
||||
WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -438,8 +460,20 @@ fn point_in_poly(x: f32, y: f32, poly: &[[f32; 2]]) -> bool {
|
||||
|
||||
/// Clip a NaN-separated polyline to `poly`, returning a NaN-separated polyline
|
||||
/// of only the inside portions.
|
||||
#[cfg(test)]
|
||||
fn clip_polyline(pts: &[[f32; 3]], poly: &[[f32; 2]]) -> Vec<[f32; 3]> {
|
||||
clip_polyline_stationed(pts, poly, None, None).0
|
||||
}
|
||||
|
||||
fn clip_polyline_stationed(
|
||||
pts: &[[f32; 3]],
|
||||
poly: &[[f32; 2]],
|
||||
stations: Option<&[f32]>,
|
||||
point_segments: Option<&[i32]>,
|
||||
) -> (Vec<[f32; 3]>, Vec<f32>, Vec<i32>) {
|
||||
let mut out: Vec<[f32; 3]> = Vec::new();
|
||||
let mut out_stations = Vec::new();
|
||||
let mut out_segments = Vec::new();
|
||||
let mut i = 0;
|
||||
while i < pts.len() {
|
||||
if !pts[i][0].is_finite() || !pts[i][1].is_finite() {
|
||||
@@ -453,32 +487,80 @@ fn clip_polyline(pts: &[[f32; 3]], poly: &[[f32; 2]]) -> Vec<[f32; 3]> {
|
||||
let seg = &pts[start..i];
|
||||
let mut last: Option<[f32; 3]> = None;
|
||||
for j in 0..seg.len().saturating_sub(1) {
|
||||
for (a, b) in clip_segment(seg[j], seg[j + 1], poly) {
|
||||
for (ta, tb) in clip_segment_params(seg[j], seg[j + 1], poly) {
|
||||
let lerp_point = |t: f32| {
|
||||
[
|
||||
seg[j][0] + (seg[j + 1][0] - seg[j][0]) * t,
|
||||
seg[j][1] + (seg[j + 1][1] - seg[j][1]) * t,
|
||||
seg[j][2] + (seg[j + 1][2] - seg[j][2]) * t,
|
||||
]
|
||||
};
|
||||
let a = lerp_point(ta);
|
||||
let b = lerp_point(tb);
|
||||
let station = |t: f32| {
|
||||
stations.map_or(0.0, |values| {
|
||||
let a = values[start + j];
|
||||
a + (values[start + j + 1] - a) * t
|
||||
})
|
||||
};
|
||||
let contiguous = last.is_some_and(|l| {
|
||||
(l[0] - a[0]).abs() <= 1e-4 && (l[1] - a[1]).abs() <= 1e-4
|
||||
});
|
||||
if !contiguous {
|
||||
if !out.is_empty() {
|
||||
out.push(NAN3);
|
||||
if stations.is_some() {
|
||||
out_stations.push(0.0);
|
||||
}
|
||||
if point_segments.is_some() {
|
||||
out_segments.push(-1);
|
||||
}
|
||||
}
|
||||
out.push(a);
|
||||
if stations.is_some() {
|
||||
out_stations.push(station(ta));
|
||||
}
|
||||
if let Some(segments) = point_segments {
|
||||
out_segments.push(segments[start + j]);
|
||||
}
|
||||
}
|
||||
out.push(b);
|
||||
if stations.is_some() {
|
||||
out_stations.push(station(tb));
|
||||
}
|
||||
if let Some(segments) = point_segments {
|
||||
out_segments.push(segments[start + j]);
|
||||
}
|
||||
last = Some(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
(out, out_stations, out_segments)
|
||||
}
|
||||
|
||||
/// Return the inside-the-polygon sub-segments of `p0`→`p1` as endpoint pairs.
|
||||
/// Handles convex and concave boundaries by testing the midpoint of every
|
||||
/// interval between consecutive boundary crossings.
|
||||
#[cfg(test)]
|
||||
fn clip_segment(
|
||||
p0: [f32; 3],
|
||||
p1: [f32; 3],
|
||||
poly: &[[f32; 2]],
|
||||
) -> Vec<([f32; 3], [f32; 3])> {
|
||||
let lerp = |t: f32| {
|
||||
[
|
||||
p0[0] + (p1[0] - p0[0]) * t,
|
||||
p0[1] + (p1[1] - p0[1]) * t,
|
||||
p0[2] + (p1[2] - p0[2]) * t,
|
||||
]
|
||||
};
|
||||
clip_segment_params(p0, p1, poly)
|
||||
.into_iter()
|
||||
.map(|(a, b)| (lerp(a), lerp(b)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn clip_segment_params(p0: [f32; 3], p1: [f32; 3], poly: &[[f32; 2]]) -> Vec<(f32, f32)> {
|
||||
let mut ts: Vec<f32> = vec![0.0, 1.0];
|
||||
let n = poly.len();
|
||||
let mut j = n - 1;
|
||||
@@ -493,7 +575,7 @@ fn clip_segment(
|
||||
ts.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
|
||||
ts.dedup_by(|a, b| (*a - *b).abs() < 1e-7);
|
||||
|
||||
let lerp = |t: f32| {
|
||||
let point = |t: f32| {
|
||||
[
|
||||
p0[0] + (p1[0] - p0[0]) * t,
|
||||
p0[1] + (p1[1] - p0[1]) * t,
|
||||
@@ -502,9 +584,9 @@ fn clip_segment(
|
||||
};
|
||||
let mut out = Vec::new();
|
||||
for w in ts.windows(2) {
|
||||
let mid = lerp(0.5 * (w[0] + w[1]));
|
||||
let mid = point(0.5 * (w[0] + w[1]));
|
||||
if point_in_poly(mid[0], mid[1], poly) {
|
||||
out.push((lerp(w[0]), lerp(w[1])));
|
||||
out.push((w[0], w[1]));
|
||||
}
|
||||
}
|
||||
out
|
||||
|
||||
@@ -25,8 +25,7 @@ pub struct Uniforms {
|
||||
/// zooming inside MSPACE changes one scalar instead of re-tessellating and
|
||||
/// re-uploading every dashed wire.
|
||||
pub linetype_scale: f32,
|
||||
/// FILLMODE toggle. Wire shaders use this slot to draw wide polylines as
|
||||
/// boundary outlines when fills are disabled.
|
||||
/// Pads the struct to the uniform alignment required by wgpu.
|
||||
pub _pad: f32,
|
||||
|
||||
// ── Relative-to-eye (double-single) additions ───────────────────────────
|
||||
|
||||
@@ -406,40 +406,45 @@ fn pack_color(color: [f32; 4]) -> [u8; 4] {
|
||||
/// non-dash-first patterns keep the legacy centred phase.
|
||||
fn wire_distances(wire: &WireModel) -> (Vec<f32>, f32, f32) {
|
||||
let n = wire.points.len();
|
||||
let mut dists = vec![0.0_f32; n];
|
||||
let mut has_break = false;
|
||||
// Accumulate arc-length in f64 from double-single deltas (high + low). An
|
||||
// f32-high-only delta `q[0] - p[0]` quantises ~0.1 at UTM coordinates
|
||||
// (−1.2M), which shifts the dash phase and drifts parallel lines out of sync
|
||||
// — the reason MLINE elements were CPU-dashed. `points_low` may be empty for
|
||||
// non-RTE wires → treat the low half as zero (same as the old behaviour).
|
||||
let mut acc = 0.0_f64;
|
||||
for i in 1..n {
|
||||
let p = wire.points[i - 1];
|
||||
let q = wire.points[i];
|
||||
if !p[0].is_finite() || !q[0].is_finite() {
|
||||
has_break = true;
|
||||
// plinegen=false: reset to 0 at the first real point after a NaN separator.
|
||||
if !wire.plinegen && !p[0].is_finite() && q[0].is_finite() {
|
||||
acc = 0.0;
|
||||
let explicit = wire.pattern_stations.len() >= n + 1;
|
||||
let (mut dists, has_break, total) = if explicit {
|
||||
(
|
||||
wire.pattern_stations[..n].to_vec(),
|
||||
!wire.plinegen,
|
||||
wire.pattern_stations[n],
|
||||
)
|
||||
} else {
|
||||
let mut dists = vec![0.0_f32; n];
|
||||
let mut has_break = false;
|
||||
// Accumulate arc-length in f64 from double-single deltas (high + low).
|
||||
let mut acc = 0.0_f64;
|
||||
for i in 1..n {
|
||||
let p = wire.points[i - 1];
|
||||
let q = wire.points[i];
|
||||
if !p[0].is_finite() || !q[0].is_finite() {
|
||||
has_break = true;
|
||||
if !wire.plinegen && !p[0].is_finite() && q[0].is_finite() {
|
||||
acc = 0.0;
|
||||
}
|
||||
dists[i] = acc as f32;
|
||||
} else {
|
||||
let pl = wire.points_low.get(i - 1).copied().unwrap_or([0.0; 3]);
|
||||
let ql = wire.points_low.get(i).copied().unwrap_or([0.0; 3]);
|
||||
let dx = (q[0] as f64 - p[0] as f64) + (ql[0] as f64 - pl[0] as f64);
|
||||
let dy = (q[1] as f64 - p[1] as f64) + (ql[1] as f64 - pl[1] as f64);
|
||||
let dz = (q[2] as f64 - p[2] as f64) + (ql[2] as f64 - pl[2] as f64);
|
||||
acc += (dx * dx + dy * dy + dz * dz).sqrt();
|
||||
dists[i] = acc as f32;
|
||||
}
|
||||
dists[i] = acc as f32;
|
||||
} else {
|
||||
let pl = wire.points_low.get(i - 1).copied().unwrap_or([0.0; 3]);
|
||||
let ql = wire.points_low.get(i).copied().unwrap_or([0.0; 3]);
|
||||
let dx = (q[0] as f64 - p[0] as f64) + (ql[0] as f64 - pl[0] as f64);
|
||||
let dy = (q[1] as f64 - p[1] as f64) + (ql[1] as f64 - pl[1] as f64);
|
||||
let dz = (q[2] as f64 - p[2] as f64) + (ql[2] as f64 - pl[2] as f64);
|
||||
acc += (dx * dx + dy * dy + dz * dz).sqrt();
|
||||
dists[i] = acc as f32;
|
||||
}
|
||||
}
|
||||
let total = dists.last().copied().unwrap_or(0.0);
|
||||
(dists, has_break, total)
|
||||
};
|
||||
|
||||
let pat_len = wire.pattern_length;
|
||||
if pat_len <= 1e-6 || has_break || n < 2 {
|
||||
return (dists, 0.0, 0.0);
|
||||
}
|
||||
let total = dists[n - 1];
|
||||
if total <= 1e-6 {
|
||||
return (dists, 0.0, 0.0);
|
||||
}
|
||||
|
||||
+58
-4
@@ -246,8 +246,17 @@ impl Scene {
|
||||
continue;
|
||||
}
|
||||
|
||||
let clipped =
|
||||
clip_polyline_to_rect(&projected_pts, vp_x0, vp_y0, vp_x1, vp_y1, pcz);
|
||||
let stationed = wire.pattern_stations.len() > wire.points.len();
|
||||
let source_length = stationed.then(|| wire.pattern_stations[wire.points.len()]);
|
||||
let (clipped, mut clipped_stations) = clip_polyline_to_rect(
|
||||
&projected_pts,
|
||||
stationed.then_some(&wire.pattern_stations[..wire.points.len()]),
|
||||
vp_x0,
|
||||
vp_y0,
|
||||
vp_x1,
|
||||
vp_y1,
|
||||
pcz,
|
||||
);
|
||||
if clipped.is_empty() && projected_text.is_empty() {
|
||||
continue;
|
||||
}
|
||||
@@ -312,6 +321,15 @@ impl Scene {
|
||||
}
|
||||
let mut out = wire.clone();
|
||||
out.points = clipped;
|
||||
if let Some(source_length) = source_length {
|
||||
for station in &mut clipped_stations {
|
||||
*station *= scale;
|
||||
}
|
||||
clipped_stations.push(source_length * scale);
|
||||
out.pattern_stations = clipped_stations;
|
||||
} else {
|
||||
out.pattern_stations.clear();
|
||||
}
|
||||
out.text_verts = projected_text;
|
||||
// Paper coordinates are small sheet units — no relative-to-eye
|
||||
// residual is needed, and keeping the model wire's points_low
|
||||
@@ -980,14 +998,16 @@ fn cs_clip(
|
||||
/// Returns a new points vec with proper NaN separators at clip boundaries.
|
||||
fn clip_polyline_to_rect(
|
||||
pts: &[[f32; 3]],
|
||||
stations: Option<&[f32]>,
|
||||
xmin: f32,
|
||||
ymin: f32,
|
||||
xmax: f32,
|
||||
ymax: f32,
|
||||
z: f32,
|
||||
) -> Vec<[f32; 3]> {
|
||||
) -> (Vec<[f32; 3]>, Vec<f32>) {
|
||||
const NAN3: [f32; 3] = [f32::NAN, f32::NAN, f32::NAN];
|
||||
let mut result: Vec<[f32; 3]> = Vec::new();
|
||||
let mut result_stations = Vec::new();
|
||||
let mut i = 0;
|
||||
|
||||
while i < pts.len() {
|
||||
@@ -1016,19 +1036,50 @@ fn clip_polyline_to_rect(
|
||||
pen_down = false;
|
||||
}
|
||||
Some((cx0, cy0, cx1, cy1)) => {
|
||||
let parameter = |x: f32, y: f32| {
|
||||
let dx = x1 - x0;
|
||||
let dy = y1 - y0;
|
||||
if dx.abs() >= dy.abs() && dx.abs() > 1e-12 {
|
||||
(x - x0) / dx
|
||||
} else if dy.abs() > 1e-12 {
|
||||
(y - y0) / dy
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
};
|
||||
let station = |t: f32| {
|
||||
stations.map_or(0.0, |values| {
|
||||
let start_station = values[start + j];
|
||||
start_station
|
||||
+ (values[start + j + 1] - start_station) * t
|
||||
})
|
||||
};
|
||||
if !pen_down {
|
||||
if !result.is_empty() {
|
||||
result.push(NAN3);
|
||||
if stations.is_some() {
|
||||
result_stations.push(0.0);
|
||||
}
|
||||
}
|
||||
result.push([cx0, cy0, z]);
|
||||
if stations.is_some() {
|
||||
result_stations.push(station(parameter(cx0, cy0)));
|
||||
}
|
||||
pen_down = true;
|
||||
} else if let Some(&[lx, ly, _]) = result.last() {
|
||||
if (lx - cx0).abs() > 1e-4 || (ly - cy0).abs() > 1e-4 {
|
||||
result.push(NAN3);
|
||||
result.push([cx0, cy0, z]);
|
||||
if stations.is_some() {
|
||||
result_stations.push(0.0);
|
||||
result_stations.push(station(parameter(cx0, cy0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push([cx1, cy1, z]);
|
||||
if stations.is_some() {
|
||||
result_stations.push(station(parameter(cx1, cy1)));
|
||||
}
|
||||
// If the exit point was clipped, lift pen.
|
||||
if (cx1 - x1).abs() > 1e-4 || (cy1 - y1).abs() > 1e-4 {
|
||||
pen_down = false;
|
||||
@@ -1044,6 +1095,9 @@ fn clip_polyline_to_rect(
|
||||
.unwrap_or(false)
|
||||
{
|
||||
result.pop();
|
||||
if stations.is_some() {
|
||||
result_stations.pop();
|
||||
}
|
||||
}
|
||||
result
|
||||
(result, result_stations)
|
||||
}
|
||||
@@ -311,6 +311,7 @@ pub fn apply_along(
|
||||
.map(|pts| WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
@@ -358,6 +359,7 @@ pub fn apply_along(
|
||||
out.push(WireModel {
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
world_width: 0.0,
|
||||
depth_override: None,
|
||||
display_visible: true,
|
||||
|
||||
@@ -3527,7 +3527,6 @@ impl Scene {
|
||||
};
|
||||
let mut uniforms =
|
||||
Uniforms::new(&inst.camera, full_bounds, self.document.header.lineweight_display);
|
||||
uniforms._pad = if self.document.header.fill_mode { 1.0 } else { 0.0 };
|
||||
if self.document.header.paper_space_linetype_scaling
|
||||
&& !inst.paper_sheet
|
||||
&& inst.tile_idx.is_none()
|
||||
|
||||
Reference in new issue
Block a user