forked from 1b-agent/opencadstudio-upstream
feat(mtext): integrate every inline format code in MTEXT renderer
Replace the line-based MTEXT pipeline with a run-aware parser + renderer.
Every DXF MTEXT inline code now reaches the stroke output: \H \W \Q \T \A
\C \c \f \F \M \X, the \p block (q/i/l/r/t/s), brace-scoped state, and
the \L\l \O\o \K\k decoration toggles.
Parser ([text_support]): emits Vec<MTextLine{ runs, align, indent_*,
tab_stops }> with per-run RunState; braces scope character state, paragraph
props persist across them.
Renderer ([mtext]): atomise → wrap → render. Per-run height/width/oblique/
tracking/font/colour/decorations/valign; paragraph indents and tab stops
honoured; default 4-em tab fallback.
Plumbing: tessellate() now returns Vec<WireModel>; TextStroke gains an
optional inline-colour override so per-run \C/\c emits one wire per colour.
cxf::tessellate_text_run adds a tracking multiplier. block_cache folds
multi-wire results into a single LocalWire (colour split preserved at
top-level, flattened inside cached defns).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
1 parent
ff31a07213
commit
f12e864aa6
10 files changed
+1223
-148
No files matched your search
@@ -10,9 +10,15 @@ use crate::scene::wire_model::{SnapHint, TangentGeom};
|
||||
/// One group of glyph strokes with its world-space origin stored in f64.
|
||||
/// Strokes are in glyph-local space (origin = [0,0]) so that the large
|
||||
/// world offset can be subtracted with f64 precision in tessellate.rs.
|
||||
///
|
||||
/// `color`, when set, overrides the entity colour for just this group — used
|
||||
/// by MTEXT inline `\C` / `\c` per-run colour. Strokes sharing the same
|
||||
/// (color, None) override are merged into one WireModel downstream; runs with
|
||||
/// distinct colours emit their own WireModel.
|
||||
pub struct TextStroke {
|
||||
pub strokes: Vec<Vec<[f32; 2]>>,
|
||||
pub origin: [f64; 2],
|
||||
pub color: Option<[f32; 3]>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
||||
@@ -401,9 +401,32 @@ fn tessellate_sub_local(
|
||||
// Pass `local_offset` as the f64 world-offset so tessellate subtracts it
|
||||
// before casting to f32 — same precision-preservation trick used for
|
||||
// top-level entities, applied per-defn.
|
||||
let wire = tessellate::tessellate(
|
||||
//
|
||||
// tessellate() may emit multiple WireModels for a single sub-entity (e.g.
|
||||
// MTEXT with inline `\C` / `\c` colour switches splits one entity into
|
||||
// one wire per colour group). The block-defn cache stores a single
|
||||
// LocalWire per sub-entity, so we fold the wires into one — points get
|
||||
// concatenated with NaN separators, the primary colour comes from the
|
||||
// first wire. Per-segment colour is lost inside cached block defns; for
|
||||
// top-level MTEXT outside blocks the colour split is preserved via the
|
||||
// hot path in `tessellate_entity`.
|
||||
let mut wires_out = tessellate::tessellate(
|
||||
doc, h, sub, false, sub_color, pat_len, pat, lw_px, local_offset, anno_scale,
|
||||
);
|
||||
if wires_out.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let mut wire = wires_out.remove(0);
|
||||
for extra in wires_out {
|
||||
if !wire.points.is_empty() && !extra.points.is_empty() {
|
||||
wire.points.push([f32::NAN, f32::NAN, f32::NAN]);
|
||||
}
|
||||
wire.points.extend(extra.points);
|
||||
wire.key_vertices.extend(extra.key_vertices);
|
||||
wire.snap_pts.extend(extra.snap_pts);
|
||||
wire.tangent_geoms.extend(extra.tangent_geoms);
|
||||
wire.fill_tris.extend(extra.fill_tris);
|
||||
}
|
||||
|
||||
if wire.points.len() > 100_000 {
|
||||
return None;
|
||||
|
||||
+30
-3
@@ -226,6 +226,33 @@ pub fn tessellate_text_ex(
|
||||
oblique_angle: f32,
|
||||
font_name: &str,
|
||||
text: &str,
|
||||
) -> Vec<Vec<[f32; 2]>> {
|
||||
tessellate_text_run(
|
||||
origin,
|
||||
height,
|
||||
rotation,
|
||||
width_factor,
|
||||
oblique_angle,
|
||||
1.0,
|
||||
font_name,
|
||||
text,
|
||||
)
|
||||
}
|
||||
|
||||
/// Like [`tessellate_text_ex`] but takes a `tracking` multiplier applied to
|
||||
/// the per-glyph `letter_spacing` advance. `tracking = 1.0` keeps the legacy
|
||||
/// behaviour; MTEXT's `\T<v>;` inline code routes here so per-run character
|
||||
/// spacing scales independently of the width factor.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn tessellate_text_run(
|
||||
origin: [f32; 2],
|
||||
height: f32,
|
||||
rotation: f32,
|
||||
width_factor: f32,
|
||||
oblique_angle: f32,
|
||||
tracking: f32,
|
||||
font_name: &str,
|
||||
text: &str,
|
||||
) -> Vec<Vec<[f32; 2]>> {
|
||||
if text.is_empty() || height <= 0.0 {
|
||||
return vec![];
|
||||
@@ -388,7 +415,7 @@ pub fn tessellate_text_ex(
|
||||
}
|
||||
} else {
|
||||
// Partial digit sequence — advance as unknown glyph and move on
|
||||
cursor_x += (6.0 + font.letter_spacing) * wf;
|
||||
cursor_x += (6.0 + font.letter_spacing * tracking) * wf;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -417,11 +444,11 @@ pub fn tessellate_text_ex(
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
cursor_x += (glyph.advance + font.letter_spacing) * wf;
|
||||
cursor_x += (glyph.advance + font.letter_spacing * tracking) * wf;
|
||||
}
|
||||
None => {
|
||||
warn_missing_glyph(font_name, render_ch);
|
||||
cursor_x += (6.0 + font.letter_spacing) * wf;
|
||||
cursor_x += (6.0 + font.letter_spacing * tracking) * wf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+46
-30
@@ -4777,7 +4777,7 @@ fn tessellate_entity(
|
||||
} else {
|
||||
(0.0_f32, [0.0f32; 8])
|
||||
};
|
||||
let mut wire = tessellate::tessellate(
|
||||
let mut wires = tessellate::tessellate(
|
||||
document,
|
||||
h,
|
||||
e,
|
||||
@@ -4789,8 +4789,11 @@ fn tessellate_entity(
|
||||
world_offset,
|
||||
1.0,
|
||||
);
|
||||
wire.aabb = entity_aabb(e, world_offset);
|
||||
return vec![wire];
|
||||
let ab = entity_aabb(e, world_offset);
|
||||
for w in &mut wires {
|
||||
w.aabb = ab;
|
||||
}
|
||||
return wires;
|
||||
}
|
||||
|
||||
let (entity_color, pattern_length, pattern, line_weight_px, aci) =
|
||||
@@ -5066,7 +5069,7 @@ fn tessellate_entity(
|
||||
let sub_aabb = entity_aabb(&sub, world_offset);
|
||||
let sub_pattern_length = sub_pattern_length * pslt_factor;
|
||||
let sub_pattern = sub_pattern.map(|v| v * pslt_factor);
|
||||
let mut wire = tessellate::tessellate(
|
||||
let mut wires = tessellate::tessellate(
|
||||
document,
|
||||
h,
|
||||
&sub,
|
||||
@@ -5078,10 +5081,12 @@ fn tessellate_entity(
|
||||
world_offset,
|
||||
anno_scale,
|
||||
);
|
||||
wire.name = h.value().to_string();
|
||||
wire.aci = sub_aci;
|
||||
wire.aabb = sub_aabb;
|
||||
vec![wire]
|
||||
for w in &mut wires {
|
||||
w.name = h.value().to_string();
|
||||
w.aci = sub_aci;
|
||||
w.aabb = sub_aabb;
|
||||
}
|
||||
wires
|
||||
})
|
||||
.collect();
|
||||
append_insert_attribute_wires(
|
||||
@@ -5219,7 +5224,7 @@ fn tessellate_entity(
|
||||
}
|
||||
}
|
||||
|
||||
let mut base = tessellate::tessellate(
|
||||
let mut bases = tessellate::tessellate(
|
||||
document,
|
||||
h,
|
||||
e,
|
||||
@@ -5231,28 +5236,37 @@ fn tessellate_entity(
|
||||
world_offset,
|
||||
anno_scale,
|
||||
);
|
||||
base.aci = aci;
|
||||
base.aabb = aabb;
|
||||
for b in &mut bases {
|
||||
b.aci = aci;
|
||||
b.aabb = aabb;
|
||||
}
|
||||
|
||||
// Complex linetypes (with embedded shapes / text) expand the *base*
|
||||
// polyline along its tangent. Text-type entities never have a complex
|
||||
// linetype assigned, so we only consult the first wire here — multi-wire
|
||||
// returns come exclusively from MTEXT colour splits which can't trigger
|
||||
// this path.
|
||||
if let Some(clt) = crate::linetypes::complex_lt(lt_name) {
|
||||
let mut wires = complex_lt::apply_along(
|
||||
&base.name,
|
||||
&base.points,
|
||||
clt,
|
||||
(lt_scale * pslt_factor).max(1e-4),
|
||||
entity_color,
|
||||
sel,
|
||||
base.line_weight_px,
|
||||
);
|
||||
if !wires.is_empty() {
|
||||
for w in &mut wires {
|
||||
w.aabb = aabb;
|
||||
if let Some(base) = bases.first() {
|
||||
let mut wires = complex_lt::apply_along(
|
||||
&base.name,
|
||||
&base.points,
|
||||
clt,
|
||||
(lt_scale * pslt_factor).max(1e-4),
|
||||
entity_color,
|
||||
sel,
|
||||
base.line_weight_px,
|
||||
);
|
||||
if !wires.is_empty() {
|
||||
for w in &mut wires {
|
||||
w.aabb = aabb;
|
||||
}
|
||||
return wires;
|
||||
}
|
||||
return wires;
|
||||
}
|
||||
}
|
||||
|
||||
vec![base]
|
||||
bases
|
||||
}
|
||||
|
||||
/// Build the 4 OBB corners (CCW: bl, br, tr, tl) of a Text / MText entity
|
||||
@@ -5565,7 +5579,7 @@ fn append_insert_attribute_wires(
|
||||
sub_color
|
||||
};
|
||||
let sub_aabb = entity_aabb(&attr_entity, world_offset);
|
||||
let mut wire = tessellate::tessellate(
|
||||
let mut attr_wires = tessellate::tessellate(
|
||||
document,
|
||||
insert_handle,
|
||||
&attr_entity,
|
||||
@@ -5579,10 +5593,12 @@ fn append_insert_attribute_wires(
|
||||
);
|
||||
// Use the INSERT's handle so selection / picking groups attribute
|
||||
// text with the parent insert instead of treating it as a stray text.
|
||||
wire.name = insert_handle.value().to_string();
|
||||
wire.aci = sub_aci;
|
||||
wire.aabb = sub_aabb;
|
||||
wires.push(wire);
|
||||
for w in &mut attr_wires {
|
||||
w.name = insert_handle.value().to_string();
|
||||
w.aci = sub_aci;
|
||||
w.aabb = sub_aabb;
|
||||
}
|
||||
wires.extend(attr_wires);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+118
-49
@@ -112,7 +112,7 @@ pub fn tessellate(
|
||||
line_weight_px: f32,
|
||||
world_offset: [f64; 3],
|
||||
anno_scale: f32,
|
||||
) -> WireModel {
|
||||
) -> Vec<WireModel> {
|
||||
let color = if selected {
|
||||
WireModel::SELECTED
|
||||
} else {
|
||||
@@ -145,7 +145,7 @@ pub fn tessellate(
|
||||
// MultiLeader is handled by scene/mod.rs since it emits multiple WireModels
|
||||
// (leader, text, frame, fill) with distinct colors.
|
||||
if let EntityType::Leader(leader) = entity {
|
||||
return tessellate_leader_single(
|
||||
return vec![tessellate_leader_single(
|
||||
document,
|
||||
handle,
|
||||
leader,
|
||||
@@ -154,74 +154,141 @@ pub fn tessellate(
|
||||
line_weight_px,
|
||||
world_offset,
|
||||
anno_scale,
|
||||
);
|
||||
)];
|
||||
}
|
||||
|
||||
// ── Try the truck path first ───────────────────────────────────────────
|
||||
if let Some(te) = convert(entity, document) {
|
||||
match te.object {
|
||||
// ── Text / MText: pre-tessellated glyph strokes ───────────────
|
||||
//
|
||||
// Strokes are pre-grouped by world origin (one TextStroke per
|
||||
// line / per run / per fragment), each carrying an optional
|
||||
// colour override produced by MTEXT inline `\C` / `\c`. We bin
|
||||
// groups by override colour and emit one WireModel per bin so a
|
||||
// single MTEXT can hand back N colour-distinct wires when the
|
||||
// value mixes inline colours.
|
||||
TruckObject::Text(stroke_groups) => {
|
||||
// Each TextStroke keeps its strokes in glyph-local space and
|
||||
// its world origin as f64. Subtract world_offset in f64 before
|
||||
// casting to f32 so large UTM coordinates don't crush precision.
|
||||
let [ox, oy, oz] = world_offset;
|
||||
let elev = entity_z(entity) - oz as f32;
|
||||
|
||||
let mut points: Vec<[f32; 3]> = Vec::new();
|
||||
let mut first = true;
|
||||
// Annotation scale: scale glyph strokes relative to the text
|
||||
// insertion point (first group's origin) so multi-line MText
|
||||
// lines spread apart correctly as well as growing in size.
|
||||
// anno_scale anchors at the first group's origin so multi-line
|
||||
// MText lines spread apart correctly as they grow.
|
||||
let ref_origin = stroke_groups
|
||||
.first()
|
||||
.map(|g| g.origin)
|
||||
.unwrap_or([0.0, 0.0]);
|
||||
let ref_lx = (ref_origin[0] - ox) as f32;
|
||||
let ref_ly = (ref_origin[1] - oy) as f32;
|
||||
|
||||
// Selection forces a single uniform colour — never split.
|
||||
let split_by_color = !selected;
|
||||
|
||||
// Bins: key = Some(rgb) or None (= inherit entity colour).
|
||||
// Preserve insertion order so wire-emit ordering tracks the
|
||||
// original glyph stream (useful for stable hit-test indexing).
|
||||
let mut bins: Vec<(Option<[f32; 3]>, Vec<[f32; 3]>)> = Vec::new();
|
||||
let mut bin_first: Vec<bool> = Vec::new();
|
||||
let find_or_make =
|
||||
|key: Option<[f32; 3]>, bins: &mut Vec<(Option<[f32; 3]>, Vec<[f32; 3]>)>, firsts: &mut Vec<bool>| -> usize {
|
||||
if let Some(i) = bins.iter().position(|(k, _)| *k == key) {
|
||||
i
|
||||
} else {
|
||||
bins.push((key, Vec::new()));
|
||||
firsts.push(true);
|
||||
bins.len() - 1
|
||||
}
|
||||
};
|
||||
|
||||
for group in &stroke_groups {
|
||||
let lx = (group.origin[0] - ox) as f32;
|
||||
let ly = (group.origin[1] - oy) as f32;
|
||||
let slx = (lx - ref_lx) * anno_scale + ref_lx;
|
||||
let sly = (ly - ref_ly) * anno_scale + ref_ly;
|
||||
let bin_key = if split_by_color { group.color } else { None };
|
||||
let bi = find_or_make(bin_key, &mut bins, &mut bin_first);
|
||||
let pts = &mut bins[bi].1;
|
||||
for stroke in &group.strokes {
|
||||
if stroke.len() < 2 {
|
||||
continue;
|
||||
}
|
||||
if !first && !points.is_empty() {
|
||||
points.push([f32::NAN, f32::NAN, f32::NAN]);
|
||||
if !bin_first[bi] && !pts.is_empty() {
|
||||
pts.push([f32::NAN, f32::NAN, f32::NAN]);
|
||||
}
|
||||
first = false;
|
||||
bin_first[bi] = false;
|
||||
for &[x, y] in stroke {
|
||||
points.push([x * anno_scale + slx, y * anno_scale + sly, elev]);
|
||||
pts.push([x * anno_scale + slx, y * anno_scale + sly, elev]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let snap_pts = offset_snap_pts(te.snap_pts, world_offset);
|
||||
let [ox, oy, oz] = world_offset;
|
||||
let key_vertices: Vec<[f32; 3]> = te
|
||||
.key_vertices
|
||||
.into_iter()
|
||||
.map(|[x, y, z]| [(x - ox) as f32, (y - oy) as f32, (z - oz) as f32])
|
||||
.collect();
|
||||
return WireModel {
|
||||
name,
|
||||
points,
|
||||
color,
|
||||
selected,
|
||||
pattern_length: 0.0,
|
||||
pattern: [0.0; 8],
|
||||
line_weight_px,
|
||||
snap_pts,
|
||||
tangent_geoms: te.tangent_geoms,
|
||||
aci: 0,
|
||||
key_vertices,
|
||||
aabb: WireModel::UNBOUNDED_AABB,
|
||||
plinegen: true,
|
||||
vp_scissor: None,
|
||||
fill_tris: vec![],
|
||||
};
|
||||
|
||||
// Empty input (no glyphs) → emit a single empty wire so the
|
||||
// entity still has a hit-test target via snap_pts.
|
||||
if bins.is_empty() {
|
||||
return vec![WireModel {
|
||||
name,
|
||||
points: Vec::new(),
|
||||
color,
|
||||
selected,
|
||||
pattern_length: 0.0,
|
||||
pattern: [0.0; 8],
|
||||
line_weight_px,
|
||||
snap_pts,
|
||||
tangent_geoms: te.tangent_geoms,
|
||||
aci: 0,
|
||||
key_vertices,
|
||||
aabb: WireModel::UNBOUNDED_AABB,
|
||||
plinegen: true,
|
||||
vp_scissor: None,
|
||||
fill_tris: vec![],
|
||||
}];
|
||||
}
|
||||
|
||||
let bin_count = bins.len();
|
||||
let mut out: Vec<WireModel> = Vec::with_capacity(bin_count);
|
||||
for (idx, (override_rgb, pts)) in bins.into_iter().enumerate() {
|
||||
let wire_color = match override_rgb {
|
||||
Some([r, g, b]) => [r, g, b, color[3]],
|
||||
None => color,
|
||||
};
|
||||
// Snap points and key vertices belong to the entity as a
|
||||
// whole — attach them only to the first emitted wire so
|
||||
// pickers / hover don't double-count.
|
||||
let (snap, keys, tangents) = if idx == 0 {
|
||||
(
|
||||
snap_pts.clone(),
|
||||
key_vertices.clone(),
|
||||
te.tangent_geoms.clone(),
|
||||
)
|
||||
} else {
|
||||
(Vec::new(), Vec::new(), Vec::new())
|
||||
};
|
||||
out.push(WireModel {
|
||||
name: name.clone(),
|
||||
points: pts,
|
||||
color: wire_color,
|
||||
selected,
|
||||
pattern_length: 0.0,
|
||||
pattern: [0.0; 8],
|
||||
line_weight_px,
|
||||
snap_pts: snap,
|
||||
tangent_geoms: tangents,
|
||||
aci: 0,
|
||||
key_vertices: keys,
|
||||
aabb: WireModel::UNBOUNDED_AABB,
|
||||
plinegen: true,
|
||||
vp_scissor: None,
|
||||
fill_tris: vec![],
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// ── Standard topology objects ─────────────────────────────────
|
||||
@@ -239,7 +306,7 @@ pub fn tessellate(
|
||||
[(kx - ox) as f32, (ky - oy) as f32, (kz - oz) as f32]
|
||||
})
|
||||
.collect();
|
||||
return WireModel {
|
||||
return vec![WireModel {
|
||||
name,
|
||||
points: vec![
|
||||
[x - s, y, z],
|
||||
@@ -260,7 +327,7 @@ pub fn tessellate(
|
||||
plinegen: true,
|
||||
vp_scissor: None,
|
||||
fill_tris: vec![],
|
||||
};
|
||||
}];
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -275,7 +342,7 @@ pub fn tessellate(
|
||||
.into_iter()
|
||||
.map(|[x, y, z]| [(x - ox) as f32, (y - oy) as f32, (z - oz) as f32])
|
||||
.collect();
|
||||
return WireModel {
|
||||
return vec![WireModel {
|
||||
name,
|
||||
points,
|
||||
color,
|
||||
@@ -291,7 +358,7 @@ pub fn tessellate(
|
||||
plinegen: true,
|
||||
vp_scissor: None,
|
||||
fill_tris: vec![],
|
||||
};
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,7 +371,7 @@ pub fn tessellate(
|
||||
.into_iter()
|
||||
.map(|[x, y, z]| [(x - ox) as f32, (y - oy) as f32, (z - oz) as f32])
|
||||
.collect();
|
||||
return WireModel {
|
||||
return vec![WireModel {
|
||||
name,
|
||||
points,
|
||||
color,
|
||||
@@ -320,7 +387,7 @@ pub fn tessellate(
|
||||
plinegen: true,
|
||||
vp_scissor: None,
|
||||
fill_tris: vec![],
|
||||
};
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,7 +418,7 @@ pub fn tessellate(
|
||||
.into_iter()
|
||||
.map(|[x, y, z]| [(x - ox) as f32, (y - oy) as f32, (z - oz) as f32])
|
||||
.collect();
|
||||
return WireModel {
|
||||
return vec![WireModel {
|
||||
name,
|
||||
points: local_pts,
|
||||
color,
|
||||
@@ -367,7 +434,7 @@ pub fn tessellate(
|
||||
plinegen: true,
|
||||
vp_scissor: None,
|
||||
fill_tris,
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
TruckObject::SegmentedLines(points) => {
|
||||
@@ -388,7 +455,7 @@ pub fn tessellate(
|
||||
.into_iter()
|
||||
.map(|[x, y, z]| [(x - ox) as f32, (y - oy) as f32, (z - oz) as f32])
|
||||
.collect();
|
||||
return WireModel {
|
||||
return vec![WireModel {
|
||||
name,
|
||||
points: local_pts,
|
||||
color,
|
||||
@@ -404,7 +471,7 @@ pub fn tessellate(
|
||||
vp_scissor: None,
|
||||
aabb: WireModel::UNBOUNDED_AABB,
|
||||
fill_tris: vec![],
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
TruckObject::Volume(_) => {
|
||||
@@ -427,14 +494,14 @@ pub fn tessellate(
|
||||
let sp = Vec3::new((p.x - ox) as f32, (p.y - oy) as f32, (p.z - oz) as f32);
|
||||
wm.snap_pts.push((sp, SnapHint::Insertion));
|
||||
}
|
||||
return wm;
|
||||
return vec![wm];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Legacy fallback for Viewport and other unhandled types ────────────
|
||||
let (points, snap_pts, tangent_geoms, key_vertices) = legacy_geometry(entity, world_offset);
|
||||
WireModel {
|
||||
vec![WireModel {
|
||||
name,
|
||||
points,
|
||||
color,
|
||||
@@ -450,7 +517,7 @@ pub fn tessellate(
|
||||
plinegen: true,
|
||||
vp_scissor: None,
|
||||
fill_tris: vec![],
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
pub fn tessellate_dimension(
|
||||
@@ -1539,7 +1606,7 @@ pub fn tessellate_multileader(
|
||||
for sub in synth_ins.explode_from_document(document) {
|
||||
let normalized =
|
||||
crate::modules::home::modify::explode::normalize_insert_entity(sub);
|
||||
let mut wire = tessellate(
|
||||
let mut sub_wires = tessellate(
|
||||
document,
|
||||
handle,
|
||||
&normalized,
|
||||
@@ -1551,8 +1618,10 @@ pub fn tessellate_multileader(
|
||||
world_offset,
|
||||
1.0,
|
||||
);
|
||||
wire.name = name.clone();
|
||||
wires.push(wire);
|
||||
for w in &mut sub_wires {
|
||||
w.name = name.clone();
|
||||
}
|
||||
wires.extend(sub_wires);
|
||||
}
|
||||
// Block attributes attached to the multileader — render each as
|
||||
// its own attribute entity at WCS location like INSERT does.
|
||||
|
||||
Reference in new issue
Block a user