feat: complex linetype text elements and ellipse quadrant snaps
Complex linetypes now render embedded text elements (e.g. GAS_LINE's "GAS" labels): the LtSegment::Text variant is parsed from LIN files, scaled along with shape/dash segments, and tessellated via cxf::tessellate_text_ex aligned to the linetype direction. Full ellipses gain four Quadrant snap hints at ±major/±minor axis endpoints in addition to the existing Center snap, matching AutoCAD snap behaviour. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7bae75f5d6
commit
392f4503d8
4 changed files with 131 additions and 14 deletions
|
|
@ -9,6 +9,7 @@
|
|||
use crate::linetypes::{ComplexLt, LtSegment};
|
||||
use crate::scene::cxf;
|
||||
use crate::scene::wire_model::WireModel;
|
||||
use crate::entities::text_support::resolve_dxf_special_chars;
|
||||
|
||||
// ── Public entry point ────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -39,7 +40,7 @@ pub fn apply_along(
|
|||
.iter()
|
||||
.map(|s| match s {
|
||||
LtSeg::Dash(l) | LtSeg::Space(l) => *l,
|
||||
LtSeg::Dot | LtSeg::Shape { .. } => 0.0,
|
||||
LtSeg::Dot | LtSeg::Shape { .. } | LtSeg::Text { .. } => 0.0,
|
||||
})
|
||||
.sum();
|
||||
if pattern_len < 1e-10 {
|
||||
|
|
@ -134,6 +135,45 @@ pub fn apply_along(
|
|||
let shape_strokes = emit_shape(sh_name, insert, fwd, perp, *sh_scale, *rot_deg);
|
||||
strokes.extend(shape_strokes);
|
||||
|
||||
elem_idx += 1;
|
||||
elem_consumed = 0.0;
|
||||
if pos < 1e-6 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
LtSeg::Text {
|
||||
text,
|
||||
style,
|
||||
x,
|
||||
y,
|
||||
scale: tx_scale,
|
||||
rot_deg,
|
||||
} => {
|
||||
flush(&mut cur_stroke, &mut strokes);
|
||||
|
||||
let insert = lerp(ps, pe, pos / seg_len);
|
||||
let insert = offset_pt(insert, fwd, perp, *x, *y);
|
||||
let fwd_angle = fwd[1].atan2(fwd[0]) + rot_deg.to_radians();
|
||||
let resolved = resolve_dxf_special_chars(text);
|
||||
let text_strokes = cxf::tessellate_text_ex(
|
||||
[insert[0], insert[1]],
|
||||
*tx_scale,
|
||||
fwd_angle,
|
||||
1.0,
|
||||
0.0,
|
||||
style,
|
||||
&resolved,
|
||||
);
|
||||
for stroke in &text_strokes {
|
||||
if stroke.len() >= 2 {
|
||||
let pts: Vec<[f32; 3]> = stroke
|
||||
.iter()
|
||||
.map(|&[sx, sy]| [sx, sy, insert[2]])
|
||||
.collect();
|
||||
strokes.push(pts);
|
||||
}
|
||||
}
|
||||
|
||||
elem_idx += 1;
|
||||
elem_consumed = 0.0;
|
||||
if pos < 1e-6 {
|
||||
|
|
@ -180,6 +220,14 @@ enum LtSeg {
|
|||
scale: f32,
|
||||
rot_deg: f32,
|
||||
},
|
||||
Text {
|
||||
text: String,
|
||||
style: String,
|
||||
x: f32,
|
||||
y: f32,
|
||||
scale: f32,
|
||||
rot_deg: f32,
|
||||
},
|
||||
}
|
||||
|
||||
fn scale_segments(segs: &[LtSegment], scale: f32) -> Vec<LtSeg> {
|
||||
|
|
@ -188,19 +236,21 @@ fn scale_segments(segs: &[LtSegment], scale: f32) -> Vec<LtSeg> {
|
|||
LtSegment::Dash(l) => LtSeg::Dash(l * scale),
|
||||
LtSegment::Space(l) => LtSeg::Space(l * scale),
|
||||
LtSegment::Dot => LtSeg::Dot,
|
||||
LtSegment::Shape {
|
||||
name,
|
||||
x,
|
||||
y,
|
||||
scale: sh_scale,
|
||||
rot_deg,
|
||||
} => LtSeg::Shape {
|
||||
LtSegment::Shape { name, x, y, scale: sh_scale, rot_deg } => LtSeg::Shape {
|
||||
name: name.clone(),
|
||||
x: x * scale,
|
||||
y: y * scale,
|
||||
scale: *sh_scale * scale,
|
||||
rot_deg: *rot_deg,
|
||||
},
|
||||
LtSegment::Text { text, style, x, y, scale: tx_scale, rot_deg } => LtSeg::Text {
|
||||
text: text.clone(),
|
||||
style: style.clone(),
|
||||
x: x * scale,
|
||||
y: y * scale,
|
||||
scale: *tx_scale * scale,
|
||||
rot_deg: *rot_deg,
|
||||
},
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue