feat(mline): GPU-dash multiline elements with shared A-type alignment
MLINE dashed elements were fully CPU-expanded (apply_along) — one wire per dash — to keep the parallel lines in phase. Move pure-dash elements onto the GPU dash shader and end-align every element to the multiline's centre-line so they share one interior phase. - wire_distances now accumulates arc-length from the double-single (high+low) points, so the dash phase stays precise at UTM coordinates. f32-high-only deltas quantised ~0.1 there and drifted the parallel lines apart — the original reason MLINE dashes were CPU-side. - New WireModel.dash_align_end carries a shared begin/end dash length. The shader uses it as align_end for every parallel element (the interior phase depends on align_end, not the wire's own length) while align_total stays the element's own length, so each still ends on a dash at its own endpoint. - MLINE tessellation derives that length once from the centre-line, routes pure dash/space/dot elements to the GPU pattern and text-bearing elements to apply_along. - apply_along gained an "A"-type mode (shared reference length): a solid begin/end dash and a phased interior, so its dashes — and embedded text — line up with the GPU-dashed sibling elements instead of tiling from start. Pure-dash MLINEs now cost one WireModel + pattern per element instead of N segments; single (non-MLINE) entities keep the from-start tiling unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
743dc1026d
commit
5bd683cf4e
41 changed files with 263 additions and 14 deletions
|
|
@ -258,22 +258,30 @@ 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.
|
||||
dists[i] = if !wire.plinegen && !p[0].is_finite() && q[0].is_finite() {
|
||||
0.0
|
||||
} else {
|
||||
dists[i - 1]
|
||||
};
|
||||
if !wire.plinegen && !p[0].is_finite() && q[0].is_finite() {
|
||||
acc = 0.0;
|
||||
}
|
||||
dists[i] = acc as f32;
|
||||
} else {
|
||||
let dx = q[0] - p[0];
|
||||
let dy = q[1] - p[1];
|
||||
let dz = q[2] - p[2];
|
||||
dists[i] = dists[i - 1] + (dx * dx + dy * dy + dz * dz).sqrt();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -295,6 +303,20 @@ fn wire_distances(wire: &WireModel) -> (Vec<f32>, f32, f32) {
|
|||
return (dists, 0.0, 0.0);
|
||||
}
|
||||
|
||||
// Shared "A"-type for MLINE elements: the caller fixes the begin/end
|
||||
// solid-dash length (`dash_align_end`, derived once from the multiline's
|
||||
// centre-line length) so every parallel element runs the SAME interior phase
|
||||
// — the shader's interior walk depends on `align_end`, not on the wire's own
|
||||
// length — while `align_total` stays this wire's own length, so each element
|
||||
// still ends on a dash at its own endpoint.
|
||||
if let Some(ae) = wire.dash_align_end {
|
||||
if total <= pat_len {
|
||||
// Shorter than one full period → solid (matches the per-wire path).
|
||||
return (dists, total, total);
|
||||
}
|
||||
return (dists, ae.clamp(1e-4, total * 0.5), total);
|
||||
}
|
||||
|
||||
// Align only a proper alternating pattern that BEGINS with a dash followed
|
||||
// by a gap — every standard linetype does (DASHED/DASHDOT/CENTER/HIDDEN/…).
|
||||
// Gap-first, dot-first, single-element, or consecutive-dash patterns keep
|
||||
|
|
|
|||
Loading…
Reference in a new issue