2026-04-02 19:06:20 +03:00
|
|
|
|
use acadrust::entities::Tolerance;
|
|
|
|
|
|
|
|
|
|
|
|
use crate::command::EntityTransform;
|
|
|
|
|
|
use crate::entities::common::{edit_prop as edit, ro_prop as ro, square_grip};
|
|
|
|
|
|
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
2026-07-07 18:22:08 +03:00
|
|
|
|
use crate::scene::convert::acad_to_truck::{GlyphRun, TextStroke, TruckEntity, TruckObject};
|
2026-06-16 11:33:46 +03:00
|
|
|
|
use crate::scene::model::object::{GripApply, GripDef, PropSection};
|
|
|
|
|
|
use crate::scene::model::wire_model::SnapHint;
|
|
|
|
|
|
use crate::scene::text::lff;
|
|
|
|
|
|
use crate::scene::view::transform;
|
2026-04-02 19:06:20 +03:00
|
|
|
|
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
// ── GDT text parser ───────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
/// Parse a DXF tolerance text string into rows of cell strings.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// DXF format: `{\Fgdt;p}%%v0.5%%vA%%vB%%v%%v^J{\Fgdt;j}%%v0.1%%vA%%v%%v%%v`
|
|
|
|
|
|
/// - `^J` → row separator
|
|
|
|
|
|
/// - `%%v` → cell separator within a row
|
|
|
|
|
|
/// - `{\Fgdt;X}` → GDT symbol X (mapped to a text label)
|
|
|
|
|
|
fn parse_gdt_rows(raw: &str) -> Vec<Vec<String>> {
|
|
|
|
|
|
raw.split("^J")
|
|
|
|
|
|
.filter(|row| !row.trim().is_empty())
|
|
|
|
|
|
.map(|row| {
|
|
|
|
|
|
row.split("%%v")
|
|
|
|
|
|
.map(|cell| substitute_gdt_codes(cell.trim()))
|
|
|
|
|
|
.collect()
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Replace `{\Fgdt;X}` sequences with a short ASCII label and strip other
|
|
|
|
|
|
/// MTEXT-style format codes `{\...}`.
|
|
|
|
|
|
fn substitute_gdt_codes(s: &str) -> String {
|
|
|
|
|
|
let mut out = String::new();
|
|
|
|
|
|
let mut chars = s.chars().peekable();
|
|
|
|
|
|
while let Some(ch) = chars.next() {
|
|
|
|
|
|
if ch == '{' {
|
|
|
|
|
|
// Collect to closing '}'
|
|
|
|
|
|
let mut inner = String::new();
|
|
|
|
|
|
let mut depth = 1usize;
|
|
|
|
|
|
for c in chars.by_ref() {
|
|
|
|
|
|
match c {
|
2026-05-12 10:36:15 +03:00
|
|
|
|
'{' => {
|
|
|
|
|
|
depth += 1;
|
|
|
|
|
|
inner.push(c);
|
|
|
|
|
|
}
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
'}' => {
|
|
|
|
|
|
depth -= 1;
|
2026-05-12 10:36:15 +03:00
|
|
|
|
if depth == 0 {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
inner.push(c);
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
inner.push(c);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Is it a GDT font switch?
|
|
|
|
|
|
if let Some(rest) = inner.strip_prefix("\\Fgdt;") {
|
|
|
|
|
|
// rest is the symbol letter(s)
|
|
|
|
|
|
if let Some(sym_char) = rest.chars().next() {
|
|
|
|
|
|
out.push_str(gdt_char_to_ascii(sym_char));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// other format codes: skip
|
|
|
|
|
|
} else {
|
|
|
|
|
|
out.push(ch);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
out
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Map a GDT font character to a short ASCII approximation.
|
|
|
|
|
|
fn gdt_char_to_ascii(c: char) -> &'static str {
|
|
|
|
|
|
match c {
|
2026-05-12 10:36:15 +03:00
|
|
|
|
'a' => "SRT", // Straightness
|
|
|
|
|
|
'b' => "FLT", // Flatness
|
|
|
|
|
|
'c' => "FLT", // Flatness
|
|
|
|
|
|
'd' => "PSF", // Profile of Surface
|
|
|
|
|
|
'e' => "CYL", // Cylindricity
|
|
|
|
|
|
'f' => "PRL", // Profile of Line
|
|
|
|
|
|
'g' => "CIR", // Circularity
|
|
|
|
|
|
'h' => "PAR", // Parallelism
|
|
|
|
|
|
'i' => "SYM", // Symmetry
|
|
|
|
|
|
'j' => "PRP", // Perpendicularity
|
|
|
|
|
|
'k' => "PLN", // Profile of Line
|
|
|
|
|
|
'l' => "(L)", // LMC
|
|
|
|
|
|
'm' => "(M)", // MMC / Diameter
|
|
|
|
|
|
'n' => "ANG", // Angularity
|
|
|
|
|
|
'o' => "(o)", // at maximum material boundary
|
|
|
|
|
|
'p' => "POS", // Position
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
'q' => "(q)",
|
2026-05-12 10:36:15 +03:00
|
|
|
|
'r' => "RUN", // Circular Runout
|
|
|
|
|
|
's' => "(S)", // RFS / Regardless of Feature Size
|
|
|
|
|
|
't' => "TRN", // Total Runout
|
|
|
|
|
|
'u' => "CON", // Concentricity
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
'v' => "(v)",
|
|
|
|
|
|
'w' => "(w)",
|
|
|
|
|
|
_ => "?",
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Feature-control frame builder ─────────────────────────────────────────────
|
|
|
|
|
|
|
2026-07-07 18:22:08 +03:00
|
|
|
|
/// One text cell of a feature-control frame, ready to become a `TextStroke`
|
|
|
|
|
|
/// with a `GlyphRun` so the cell text can render as SDF glyph quads (or, when
|
|
|
|
|
|
/// SDF is off, from `strokes`). `origin` is relative to the tolerance insertion
|
|
|
|
|
|
/// point (already rotated); `strokes` are the glyph polylines rotated about the
|
|
|
|
|
|
/// origin (no origin translation — the wire-builder adds the origin).
|
|
|
|
|
|
struct TolCell {
|
|
|
|
|
|
text: String,
|
|
|
|
|
|
origin: [f32; 2],
|
|
|
|
|
|
strokes: Vec<Vec<[f32; 2]>>,
|
|
|
|
|
|
height: f32,
|
|
|
|
|
|
rotation: f32,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Tessellate a Tolerance entity's feature-control frame.
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
///
|
2026-07-07 18:22:08 +03:00
|
|
|
|
/// Returns (`box_strokes`, `cells`):
|
|
|
|
|
|
/// - `box_strokes` — the outer border, row separators and column dividers as
|
|
|
|
|
|
/// 2-D polylines (rotated; run-less, so they always render as strokes)
|
|
|
|
|
|
/// - `cells` — one [`TolCell`] per non-empty cell, carrying its text + a
|
|
|
|
|
|
/// `GlyphRun` so the cell renders as SDF text (frame stays geometry).
|
|
|
|
|
|
fn tessellate_tolerance(tol: &Tolerance) -> (Vec<Vec<[f32; 2]>>, Vec<TolCell>) {
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
if tol.text.is_empty() {
|
2026-07-07 18:22:08 +03:00
|
|
|
|
return (vec![], vec![]);
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let rows = parse_gdt_rows(&tol.text);
|
|
|
|
|
|
if rows.is_empty() {
|
2026-07-07 18:22:08 +03:00
|
|
|
|
return (vec![], vec![]);
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Metrics ──────────────────────────────────────────────────────────
|
2026-05-12 10:36:15 +03:00
|
|
|
|
let h = if tol.text_height > 1e-6 {
|
|
|
|
|
|
tol.text_height as f32
|
|
|
|
|
|
} else {
|
|
|
|
|
|
2.5_f32
|
|
|
|
|
|
};
|
2026-05-21 12:38:03 +03:00
|
|
|
|
// DIMGAP — stored on the entity (resolved from the dim style at creation).
|
|
|
|
|
|
// Fall back to AutoCAD's 0.35 × height convention only when missing.
|
|
|
|
|
|
let gap = if tol.dimension_gap > 1e-6 {
|
|
|
|
|
|
tol.dimension_gap as f32
|
|
|
|
|
|
} else {
|
|
|
|
|
|
(h * 0.35).max(0.1)
|
|
|
|
|
|
};
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
let cell_h = h + 2.0 * gap;
|
|
|
|
|
|
let char_w = h * 0.65;
|
|
|
|
|
|
let min_cell_w = h * 1.4;
|
|
|
|
|
|
|
|
|
|
|
|
// Column widths: max across all rows
|
|
|
|
|
|
let max_cols = rows.iter().map(|r| r.len()).max().unwrap_or(0);
|
|
|
|
|
|
let mut col_widths: Vec<f32> = vec![0.0_f32; max_cols];
|
|
|
|
|
|
for row in &rows {
|
|
|
|
|
|
for (ci, cell) in row.iter().enumerate() {
|
|
|
|
|
|
let w = (cell.len() as f32 * char_w).max(min_cell_w);
|
|
|
|
|
|
if ci < col_widths.len() {
|
|
|
|
|
|
col_widths[ci] = col_widths[ci].max(w);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
let total_w: f32 = col_widths.iter().sum();
|
|
|
|
|
|
let total_h = cell_h * rows.len() as f32;
|
|
|
|
|
|
|
2026-04-29 20:58:44 +03:00
|
|
|
|
// ── Transform helpers (local space — translation applied in tessellate.rs) ──
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
let angle = (tol.direction.y as f32).atan2(tol.direction.x as f32);
|
|
|
|
|
|
let (sa, ca) = angle.sin_cos();
|
|
|
|
|
|
|
2026-04-29 20:58:44 +03:00
|
|
|
|
// Rotate only; origin is kept as f64 and applied later with full precision.
|
2026-05-12 10:36:15 +03:00
|
|
|
|
let rot = |x: f32, y: f32| -> [f32; 2] { [x * ca - y * sa, x * sa + y * ca] };
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
|
2026-07-07 18:22:08 +03:00
|
|
|
|
let mut box_out: Vec<Vec<[f32; 2]>> = Vec::new();
|
|
|
|
|
|
let mut cells: Vec<TolCell> = Vec::new();
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
|
|
|
|
|
|
// ── Outer border ──────────────────────────────────────────────────────
|
2026-07-07 18:22:08 +03:00
|
|
|
|
box_out.push(vec![
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
rot(0.0, 0.0),
|
|
|
|
|
|
rot(total_w, 0.0),
|
|
|
|
|
|
rot(total_w, total_h),
|
|
|
|
|
|
rot(0.0, total_h),
|
|
|
|
|
|
rot(0.0, 0.0),
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
// ── Row separators ─────────────────────────────────────────────────────
|
|
|
|
|
|
for ri in 1..rows.len() {
|
|
|
|
|
|
let y = cell_h * ri as f32;
|
2026-07-07 18:22:08 +03:00
|
|
|
|
box_out.push(vec![rot(0.0, y), rot(total_w, y)]);
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Column dividers ────────────────────────────────────────────────────
|
|
|
|
|
|
let mut x_cursor = 0.0_f32;
|
|
|
|
|
|
for ci in 0..col_widths.len().saturating_sub(1) {
|
|
|
|
|
|
x_cursor += col_widths[ci];
|
|
|
|
|
|
for ri in 0..rows.len() {
|
|
|
|
|
|
if ci + 1 < rows[ri].len() {
|
|
|
|
|
|
let y0 = cell_h * ri as f32;
|
|
|
|
|
|
let y1 = y0 + cell_h;
|
2026-07-07 18:22:08 +03:00
|
|
|
|
box_out.push(vec![rot(x_cursor, y0), rot(x_cursor, y1)]);
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Text content per cell ─────────────────────────────────────────────
|
2026-07-07 18:22:08 +03:00
|
|
|
|
// Each cell becomes a TolCell: its origin is the (rotated) cell position
|
|
|
|
|
|
// relative to the insertion point; its `strokes` are the glyph polylines
|
|
|
|
|
|
// rotated about that origin (used only when SDF is off). The GlyphRun the
|
|
|
|
|
|
// caller attaches lets the cell render as SDF text.
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
for (ri, row) in rows.iter().enumerate() {
|
|
|
|
|
|
let row_y = cell_h * ri as f32 + gap;
|
|
|
|
|
|
let mut cell_x = 0.0_f32;
|
|
|
|
|
|
for (ci, cell) in row.iter().enumerate() {
|
2026-05-12 10:36:15 +03:00
|
|
|
|
let cw = if ci < col_widths.len() {
|
|
|
|
|
|
col_widths[ci]
|
|
|
|
|
|
} else {
|
|
|
|
|
|
0.0
|
|
|
|
|
|
};
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
if !cell.is_empty() {
|
|
|
|
|
|
let text_w = cell.len() as f32 * char_w;
|
|
|
|
|
|
let tx = cell_x + (cw - text_w) * 0.5;
|
2026-06-25 18:17:01 +01:00
|
|
|
|
let (local_strokes, _) =
|
feat(fonts): switch the text engine from CXF to LibreCAD LFF fonts
Replace the small QCAD CXF stroke-font set with the full LibreCAD LFF font
library (46 fonts) and a new LFF parser/engine in src/scene/lff.rs. The
parser reads the `[hex] char` glyph blocks, tessellates `,A<bulge>` arcs, and
resolves `C<hex>` glyph references. Per LibreCAD's RS_Polyline convention a
vertex's bulge curves the segment ending at that vertex — getting this wrong
turned round letters like O into sideways capsules.
Font name resolution mirrors LibreCAD: match by file stem, map common
AutoCAD/DXF SHX names (txt, isocp, complex, italic, gothic*, gost, …) to the
nearest LFF family, and fall back to `standard`. A missing glyph in the chosen
family falls back to the broad `unicode` font so accented/Cyrillic/Greek text
still renders. Cap height stays 9 units, so the text scale is unchanged.
All text consumers move from cxf:: to lff::; complex linetypes still render
embedded text but no longer draw embedded shapes (LibreCAD ships no shape
file). The CXF module and every *.cxf asset are removed, and the MText / text
style font pickers list the LFF families.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 03:31:20 +03:00
|
|
|
|
lff::tessellate_text_ex([0.0, 0.0], h, 0.0, 1.0, 0.0, "txt", cell);
|
2026-07-07 18:22:08 +03:00
|
|
|
|
// Glyph polylines rotated about the cell origin (no origin
|
|
|
|
|
|
// translation — the wire-builder adds `origin`).
|
|
|
|
|
|
let strokes: Vec<Vec<[f32; 2]>> = local_strokes
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|pl| pl.into_iter().map(|[px, py]| rot(px, py)).collect())
|
|
|
|
|
|
.filter(|pl: &Vec<[f32; 2]>| !pl.is_empty())
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
cells.push(TolCell {
|
|
|
|
|
|
text: cell.clone(),
|
|
|
|
|
|
origin: rot(tx, row_y),
|
|
|
|
|
|
strokes,
|
|
|
|
|
|
height: h,
|
|
|
|
|
|
rotation: angle,
|
|
|
|
|
|
});
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
}
|
|
|
|
|
|
cell_x += cw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 18:22:08 +03:00
|
|
|
|
(box_out, cells)
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── TruckConvertible ──────────────────────────────────────────────────────────
|
|
|
|
|
|
|
2026-04-02 19:06:20 +03:00
|
|
|
|
impl TruckConvertible for Tolerance {
|
|
|
|
|
|
fn to_truck(&self, _document: &acadrust::CadDocument) -> Option<TruckEntity> {
|
|
|
|
|
|
if self.text.is_empty() {
|
|
|
|
|
|
return None;
|
|
|
|
|
|
}
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
|
refactor(snap): producers emit f64 snap points (world_offset removal P3)
TruckEntity.snap_pts is now DVec3 and every entity producer builds snap
candidates straight from the acadrust f64 coordinates instead of casting
to f32 first (text, mtext, arc, circle, ellipse, point, shape, solid,
mline, mtext, attribute, tolerance, table, underlay, ole2frame, mesh,
multileader, dimension, block-cache). offset_snap_pts stays f64
throughout. Still offset-relative (inert) — but snap precision is no
longer capped at ~0.5 m by an early f32 cast, which fixes a latent
UTM-scale snap error and sets up the absolute-coordinate switch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-23 22:10:29 +03:00
|
|
|
|
let snap_pt = glam::DVec3::new(
|
|
|
|
|
|
self.insertion_point.x,
|
|
|
|
|
|
self.insertion_point.y,
|
|
|
|
|
|
self.insertion_point.z,
|
2026-04-02 19:06:20 +03:00
|
|
|
|
);
|
|
|
|
|
|
|
2026-04-29 20:58:44 +03:00
|
|
|
|
// Build the feature-control frame in local space; origin stored as f64.
|
2026-07-07 18:22:08 +03:00
|
|
|
|
let (box_strokes, cells) = tessellate_tolerance(self);
|
|
|
|
|
|
let ins = [self.insertion_point.x, self.insertion_point.y];
|
2026-04-02 19:06:20 +03:00
|
|
|
|
|
2026-07-07 18:22:08 +03:00
|
|
|
|
// Frame geometry first (run-less → always strokes; also the anchor
|
|
|
|
|
|
// group so its origin = the insertion point), then one run-group per
|
|
|
|
|
|
// text cell so the cell text renders as SDF glyphs (or strokes when
|
|
|
|
|
|
// SDF is off).
|
|
|
|
|
|
let mut groups: Vec<TextStroke> = Vec::with_capacity(1 + cells.len());
|
|
|
|
|
|
if !box_strokes.is_empty() {
|
|
|
|
|
|
groups.push(TextStroke {
|
|
|
|
|
|
strokes: box_strokes,
|
|
|
|
|
|
origin: ins,
|
2026-06-11 00:04:37 +03:00
|
|
|
|
color: None,
|
2026-06-25 18:17:01 +01:00
|
|
|
|
fill_tris: vec![],
|
2026-07-07 00:53:19 +03:00
|
|
|
|
run: None,
|
2026-07-07 18:22:08 +03:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
for cell in cells {
|
|
|
|
|
|
groups.push(TextStroke {
|
|
|
|
|
|
strokes: cell.strokes,
|
|
|
|
|
|
origin: [ins[0] + cell.origin[0] as f64, ins[1] + cell.origin[1] as f64],
|
|
|
|
|
|
color: None,
|
|
|
|
|
|
fill_tris: vec![],
|
|
|
|
|
|
run: Some(GlyphRun {
|
|
|
|
|
|
text: cell.text,
|
|
|
|
|
|
font: "txt".to_string(),
|
|
|
|
|
|
height: cell.height,
|
|
|
|
|
|
rotation: cell.rotation,
|
|
|
|
|
|
width_factor: 1.0,
|
|
|
|
|
|
oblique: 0.0,
|
|
|
|
|
|
tracking: 0.0,
|
feat(mtext): structured editor with selection, copy, and visible bold
Rewrite the in-app MTEXT editor to run on acadrust's structured
MTextDocument model end-to-end: load via parse_mtext, edit a flat Cell
model, serialize via to_mtext_string. Retires the hand-rolled format-code
string handling, the parallel visible_spans parser, the composed_value
prefix, and the Edit-raw-code tab.
Selection and copy:
- double-click selects the word, triple-click selects all (click-counting)
- Ctrl+A selects all text
- Ctrl+C copies the selected text to the clipboard
Formatting:
- per-selection Bold/Italic/Underline/Overline/Strike/Case/Alignment
- Bold renders as a visibly thicker SDF pen, keyed (family, char, bold)
through the whole text pipeline; the resolved style font name is stamped
so the \f...|b1; code survives serialization
- per-selection font and color changes
- editor color dropdown reuses the Properties panel color selector
Global toolbar defaults (font/color/oblique/width/tracking) fold onto the
document as real span properties instead of a leading code prefix; a
trailing empty paragraph is preserved so Enter at the end keeps its line.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 20:18:50 +03:00
|
|
|
|
bold: false,
|
2026-07-07 18:22:08 +03:00
|
|
|
|
}),
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
if groups.is_empty() {
|
|
|
|
|
|
return None;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Some(TruckEntity {
|
|
|
|
|
|
object: TruckObject::Text(groups),
|
2026-04-02 19:06:20 +03:00
|
|
|
|
snap_pts: vec![(snap_pt, SnapHint::Insertion)],
|
|
|
|
|
|
tangent_geoms: vec![],
|
|
|
|
|
|
key_vertices: vec![],
|
2026-05-06 23:24:26 +03:00
|
|
|
|
fill_tris: vec![],
|
2026-04-02 19:06:20 +03:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
// ── Grippable ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
2026-04-02 19:06:20 +03:00
|
|
|
|
impl Grippable for Tolerance {
|
|
|
|
|
|
fn grips(&self) -> Vec<GripDef> {
|
|
|
|
|
|
vec![square_grip(
|
|
|
|
|
|
0,
|
2026-06-11 00:04:37 +03:00
|
|
|
|
glam::DVec3::new(
|
|
|
|
|
|
self.insertion_point.x,
|
|
|
|
|
|
self.insertion_point.y,
|
|
|
|
|
|
self.insertion_point.z,
|
2026-04-02 19:06:20 +03:00
|
|
|
|
),
|
|
|
|
|
|
)]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
|
|
|
|
|
if grip_id == 0 {
|
|
|
|
|
|
match apply {
|
|
|
|
|
|
GripApply::Translate(d) => {
|
|
|
|
|
|
self.insertion_point.x += d.x as f64;
|
|
|
|
|
|
self.insertion_point.y += d.y as f64;
|
|
|
|
|
|
self.insertion_point.z += d.z as f64;
|
|
|
|
|
|
}
|
|
|
|
|
|
GripApply::Absolute(p) => {
|
|
|
|
|
|
self.insertion_point.x = p.x as f64;
|
|
|
|
|
|
self.insertion_point.y = p.y as f64;
|
|
|
|
|
|
self.insertion_point.z = p.z as f64;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
// ── PropertyEditable ──────────────────────────────────────────────────────────
|
|
|
|
|
|
|
2026-04-02 19:06:20 +03:00
|
|
|
|
impl PropertyEditable for Tolerance {
|
2026-07-02 22:46:19 +03:00
|
|
|
|
fn geometry_properties(&self, _text_style_names: &[String]) -> Vec<PropSection> {
|
feat(properties): conform entity property groups to PROPERTIES.md
Rewrite each entity's property builder to the per-entity group/row spec:
the correct sections (Geometry, Misc, Text, Pattern, Lines & Arrows, Leaders,
Block, …) with the spec's rows and real computed values — Diameter,
Circumference, Area, Delta X/Y/Z, Angle, Total angle, Arc length, Radius ratio,
etc. — plus apply routing for the editable rows.
Covers ~26 entities (Line, Circle, Arc, Point, Ellipse, Spline, polylines,
Ray/XLine, Insert, Text, MText, Hatch, Leader, MultiLeader, Tolerance, Solid,
Solid3D/Region/Body, meshes, MLine, Raster/Wipeout, Underlay, Table, Viewport,
Attribute, OLE). The 3D Visualization/Material group and the full Dimension
spec are deferred (they need document access for material/dimstyle name
resolution). Rows whose data is not in the pinned acadrust revision are shown
as empty placeholders, to be filled when the dependency is bumped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 23:03:18 +03:00
|
|
|
|
vec![
|
|
|
|
|
|
PropSection {
|
|
|
|
|
|
title: "Text".into(),
|
|
|
|
|
|
props: vec![
|
|
|
|
|
|
ro("Text style", "tol_text_style", String::new()),
|
|
|
|
|
|
edit("Text height", "tol_text_height", self.text_height),
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
PropSection {
|
|
|
|
|
|
title: "Geometry".into(),
|
|
|
|
|
|
props: vec![
|
|
|
|
|
|
edit("Position X", "tol_ix", self.insertion_point.x),
|
|
|
|
|
|
edit("Position Y", "tol_iy", self.insertion_point.y),
|
|
|
|
|
|
edit("Position Z", "tol_iz", self.insertion_point.z),
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
PropSection {
|
|
|
|
|
|
title: "Misc".into(),
|
|
|
|
|
|
props: vec![
|
|
|
|
|
|
ro(
|
|
|
|
|
|
"Dimension style",
|
|
|
|
|
|
"tol_dim_style",
|
|
|
|
|
|
if self.dimension_style_name.is_empty() {
|
|
|
|
|
|
"(default)".to_string()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
self.dimension_style_name.clone()
|
|
|
|
|
|
},
|
feat(entities): surface remaining 2D + Region fields in properties
Closes out the 2D-entity audit (and the Region partner that ships next
to Solid3D / Body). Round-trip-only metadata fields plus a few editable
ones gain Properties-panel rows so the values the parser populates are
visible and don't get silently dropped on save:
- Hatch: seed_points count, pixel_size, normal
- Polyline2D: smooth_surface, default_start_width, default_end_width,
thickness, normal (with editors for the widths/thickness)
- Region: uid, silhouettes (shared file with Solid3D / Body)
- Underlay: clip_inverted
- RasterImage: class_version, definition_handle, definition_reactor_handle
- Wipeout: class_version, clip_mode, definition_handle,
definition_reactor_handle
- Leader: annotation_handle, override_color
- Tolerance: dimension_style_name, dimension_style_handle, dimension_gap,
text_height (editable), direction, normal
- MLine: style_handle, style_element_count, justification picker
- Ole2Frame: version, source_application, binary_data size, dwg_mode,
dwg_trailing_byte, is_paper_space
- Viewport: snap_base/spacing/angle, twist_angle, front/back clip Z,
circle_sides, grid_flags, grid_major, base_ucs_handle, ucs_ortho_type,
background_handle, shade_plot_handle, visual_style_handle,
default_lighting, default_lighting_type, ambient_color (with editors
for the snap / clip / circle_sides values)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 21:28:16 +03:00
|
|
|
|
),
|
feat(properties): conform entity property groups to PROPERTIES.md
Rewrite each entity's property builder to the per-entity group/row spec:
the correct sections (Geometry, Misc, Text, Pattern, Lines & Arrows, Leaders,
Block, …) with the spec's rows and real computed values — Diameter,
Circumference, Area, Delta X/Y/Z, Angle, Total angle, Arc length, Radius ratio,
etc. — plus apply routing for the editable rows.
Covers ~26 entities (Line, Circle, Arc, Point, Ellipse, Spline, polylines,
Ray/XLine, Insert, Text, MText, Hatch, Leader, MultiLeader, Tolerance, Solid,
Solid3D/Region/Body, meshes, MLine, Raster/Wipeout, Underlay, Table, Viewport,
Attribute, OLE). The 3D Visualization/Material group and the full Dimension
spec are deferred (they need document access for material/dimstyle name
resolution). Rows whose data is not in the pinned acadrust revision are shown
as empty placeholders, to be filled when the dependency is bumped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 23:03:18 +03:00
|
|
|
|
edit("Direction X", "tol_dir_x", self.direction.x),
|
|
|
|
|
|
edit("Direction Y", "tol_dir_y", self.direction.y),
|
|
|
|
|
|
edit("Direction Z", "tol_dir_z", self.direction.z),
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
2026-04-02 19:06:20 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
2026-05-12 10:36:15 +03:00
|
|
|
|
let Ok(v) = value.trim().parse::<f64>() else {
|
|
|
|
|
|
return;
|
|
|
|
|
|
};
|
2026-04-02 19:06:20 +03:00
|
|
|
|
match field {
|
|
|
|
|
|
"tol_ix" => self.insertion_point.x = v,
|
|
|
|
|
|
"tol_iy" => self.insertion_point.y = v,
|
|
|
|
|
|
"tol_iz" => self.insertion_point.z = v,
|
feat(entities): surface remaining 2D + Region fields in properties
Closes out the 2D-entity audit (and the Region partner that ships next
to Solid3D / Body). Round-trip-only metadata fields plus a few editable
ones gain Properties-panel rows so the values the parser populates are
visible and don't get silently dropped on save:
- Hatch: seed_points count, pixel_size, normal
- Polyline2D: smooth_surface, default_start_width, default_end_width,
thickness, normal (with editors for the widths/thickness)
- Region: uid, silhouettes (shared file with Solid3D / Body)
- Underlay: clip_inverted
- RasterImage: class_version, definition_handle, definition_reactor_handle
- Wipeout: class_version, clip_mode, definition_handle,
definition_reactor_handle
- Leader: annotation_handle, override_color
- Tolerance: dimension_style_name, dimension_style_handle, dimension_gap,
text_height (editable), direction, normal
- MLine: style_handle, style_element_count, justification picker
- Ole2Frame: version, source_application, binary_data size, dwg_mode,
dwg_trailing_byte, is_paper_space
- Viewport: snap_base/spacing/angle, twist_angle, front/back clip Z,
circle_sides, grid_flags, grid_major, base_ucs_handle, ucs_ortho_type,
background_handle, shade_plot_handle, visual_style_handle,
default_lighting, default_lighting_type, ambient_color (with editors
for the snap / clip / circle_sides values)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 21:28:16 +03:00
|
|
|
|
"tol_text_height" if v > 0.0 => self.text_height = v,
|
feat(properties): conform entity property groups to PROPERTIES.md
Rewrite each entity's property builder to the per-entity group/row spec:
the correct sections (Geometry, Misc, Text, Pattern, Lines & Arrows, Leaders,
Block, …) with the spec's rows and real computed values — Diameter,
Circumference, Area, Delta X/Y/Z, Angle, Total angle, Arc length, Radius ratio,
etc. — plus apply routing for the editable rows.
Covers ~26 entities (Line, Circle, Arc, Point, Ellipse, Spline, polylines,
Ray/XLine, Insert, Text, MText, Hatch, Leader, MultiLeader, Tolerance, Solid,
Solid3D/Region/Body, meshes, MLine, Raster/Wipeout, Underlay, Table, Viewport,
Attribute, OLE). The 3D Visualization/Material group and the full Dimension
spec are deferred (they need document access for material/dimstyle name
resolution). Rows whose data is not in the pinned acadrust revision are shown
as empty placeholders, to be filled when the dependency is bumped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 23:03:18 +03:00
|
|
|
|
"tol_dir_x" => self.direction.x = v,
|
|
|
|
|
|
"tol_dir_y" => self.direction.y = v,
|
|
|
|
|
|
"tol_dir_z" => self.direction.z = v,
|
2026-04-02 19:06:20 +03:00
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat: Tolerance GD&T feature control frame render
Replace raw text dump with proper boxed feature-control frame:
- parse_gdt_rows(): splits %%v cells and ^J row separators
- substitute_gdt_codes(): strips {\Fgdt;X} patterns, maps symbol chars
to 3-letter ASCII labels (POS, PRP, PAR, SYM, CIR, CYL, …)
- tessellate_tolerance(): outer rectangle, row separators, column
dividers, and CXF-tessellated cell text — all as Vec<Vec<[f32;2]>>
(TruckObject::Text polyline path)
- Column widths computed per cell content; text centred in each cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:23:06 +03:00
|
|
|
|
// ── Transformable ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
2026-04-02 19:06:20 +03:00
|
|
|
|
impl Transformable for Tolerance {
|
|
|
|
|
|
fn apply_transform(&mut self, t: &EntityTransform) {
|
|
|
|
|
|
transform::apply_standard_entity_transform(self, t, |entity, p1, p2| {
|
|
|
|
|
|
transform::reflect_xy_point(
|
|
|
|
|
|
&mut entity.insertion_point.x,
|
|
|
|
|
|
&mut entity.insertion_point.y,
|
|
|
|
|
|
p1,
|
|
|
|
|
|
p2,
|
|
|
|
|
|
);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|