2026-03-22 14:56:27 +03:00
|
|
|
|
use acadrust::entities::{Text, TextHorizontalAlignment as HA, TextVerticalAlignment as VA};
|
|
|
|
|
|
|
|
|
|
|
|
use crate::command::EntityTransform;
|
feat(properties): rework Text/MText/MLeader/Leader panels to standard layout
Match the entity Properties panels to the standard CAD layout and
web-verified behaviour:
- Text: combined Justify (horizontal x vertical) dropdown; Position and
Text-alignment rows become editable/grayed by justification; drop the
Annotative-scale row.
- MText: combined Justify; editable Direction, Line space distance
(back-solves the factor) and Line space style; Defined width/height
gated on column mode.
- MLeader: regrouped to match the palette - drop the Leader Structure
group (fold landing rows into Leaders), gate Text vs Block on content
type, full 9-value attachment label set, resolve handle-backed rows
(style, text style, arrowhead, linetype) to names.
- Leader: Lines & Arrows / Text / Fit are resolved from the dimension
style (arrowhead, arrow size, dim-line lineweight/colour, text gap,
vertical text pos, overall scale); Current Vertex navigator; combined
Type; Geometry group after General; no 3D-Visualisation group.
- General: rename "LT Scale" to "Linetype scale"; show ByLayer
transparency by name. Add shared num_prop helper for conditionally
editable numeric rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 18:01:44 +03:00
|
|
|
|
use crate::entities::common::{
|
fix(props): angle rows format via AUNITS, not LUNITS (#297)
Every angle row went through the linear formatter, so switching LUNITS
to Architectural showed a block's Rotation as feet-and-inches — and the
string wouldn't parse back, wedging the field until it was cleared and
retyped. Only the decimal/fractional LUNITS survived by accident.
New edit_angle row helper formats angles with format_angle
(AUNITS/AUPREC: decimal degrees, DMS, grads or radians), applied to all
17 angle rows (rotations, obliquing, arc start/end, hatch pattern
angle, multileader text rotation). parse_angle_deg converts every
AUNITS display form back to degrees ("30°"/"30d", DMS 30°15'20",
33.33g, 0.52r) and parse_f64 falls back to it, so the displayed value
round-trips through an edit.
Closes #297
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 16:42:14 +03:00
|
|
|
|
edit_angle_prop as edit_angle, edit_prop as edit, num_prop as num_row, parse_f64, ro_prop as ro, square_grip,
|
feat(properties): rework Text/MText/MLeader/Leader panels to standard layout
Match the entity Properties panels to the standard CAD layout and
web-verified behaviour:
- Text: combined Justify (horizontal x vertical) dropdown; Position and
Text-alignment rows become editable/grayed by justification; drop the
Annotative-scale row.
- MText: combined Justify; editable Direction, Line space distance
(back-solves the factor) and Line space style; Defined width/height
gated on column mode.
- MLeader: regrouped to match the palette - drop the Leader Structure
group (fold landing rows into Leaders), gate Text vs Block on content
type, full 9-value attachment label set, resolve handle-backed rows
(style, text style, arrowhead, linetype) to names.
- Leader: Lines & Arrows / Text / Fit are resolved from the dimension
style (arrowhead, arrow size, dim-line lineweight/colour, text gap,
vertical text pos, overall scale); Current Vertex navigator; combined
Type; Geometry group after General; no 3D-Visualisation group.
- General: rename "LT Scale" to "Linetype scale"; show ByLayer
transparency by name. Add shared num_prop helper for conditionally
editable numeric rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 18:01:44 +03:00
|
|
|
|
};
|
2026-05-12 10:36:15 +03:00
|
|
|
|
use crate::entities::text_support::{
|
|
|
|
|
|
resolve_dxf_special_chars, resolve_text_style, text_local_bounds,
|
|
|
|
|
|
};
|
2026-03-22 14:56:27 +03:00
|
|
|
|
use crate::entities::traits::{Grippable, PropertyEditable, Transformable, TruckConvertible};
|
2026-07-07 00:53:19 +03:00
|
|
|
|
use crate::scene::convert::acad_to_truck::{GlyphRun, TextStroke, TruckEntity, TruckObject};
|
2026-06-16 11:33:46 +03:00
|
|
|
|
use crate::scene::text::lff;
|
|
|
|
|
|
use crate::scene::model::object::{GripApply, GripDef, PropSection, PropValue, Property};
|
|
|
|
|
|
use crate::scene::model::wire_model::SnapHint;
|
2026-03-22 14:56:27 +03:00
|
|
|
|
|
feat(properties): rework Text/MText/MLeader/Leader panels to standard layout
Match the entity Properties panels to the standard CAD layout and
web-verified behaviour:
- Text: combined Justify (horizontal x vertical) dropdown; Position and
Text-alignment rows become editable/grayed by justification; drop the
Annotative-scale row.
- MText: combined Justify; editable Direction, Line space distance
(back-solves the factor) and Line space style; Defined width/height
gated on column mode.
- MLeader: regrouped to match the palette - drop the Leader Structure
group (fold landing rows into Leaders), gate Text vs Block on content
type, full 9-value attachment label set, resolve handle-backed rows
(style, text style, arrowhead, linetype) to names.
- Leader: Lines & Arrows / Text / Fit are resolved from the dimension
style (arrowhead, arrow size, dim-line lineweight/colour, text gap,
vertical text pos, overall scale); Current Vertex navigator; combined
Type; Geometry group after General; no 3D-Visualisation group.
- General: rename "LT Scale" to "Linetype scale"; show ByLayer
transparency by name. Add shared num_prop helper for conditionally
editable numeric rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 18:01:44 +03:00
|
|
|
|
/// Combined single-line-text justification (horizontal × vertical) shown as one
|
|
|
|
|
|
/// dropdown value. Horizontal-only modes (Aligned/Middle/Fit) ignore the
|
|
|
|
|
|
/// vertical component the way the underlying alignment does.
|
|
|
|
|
|
fn text_justify_str(h: &HA, v: &VA) -> &'static str {
|
|
|
|
|
|
match (h, v) {
|
|
|
|
|
|
(HA::Aligned, _) => "Aligned",
|
|
|
|
|
|
(HA::Fit, _) => "Fit",
|
|
|
|
|
|
(HA::Middle, _) => "Middle",
|
|
|
|
|
|
(HA::Left, VA::Baseline) => "Left",
|
|
|
|
|
|
(HA::Center, VA::Baseline) => "Center",
|
|
|
|
|
|
(HA::Right, VA::Baseline) => "Right",
|
|
|
|
|
|
(HA::Left, VA::Top) => "Top left",
|
|
|
|
|
|
(HA::Center, VA::Top) => "Top center",
|
|
|
|
|
|
(HA::Right, VA::Top) => "Top right",
|
|
|
|
|
|
(HA::Left, VA::Middle) => "Middle left",
|
|
|
|
|
|
(HA::Center, VA::Middle) => "Middle center",
|
|
|
|
|
|
(HA::Right, VA::Middle) => "Middle right",
|
|
|
|
|
|
(HA::Left, VA::Bottom) => "Bottom left",
|
|
|
|
|
|
(HA::Center, VA::Bottom) => "Bottom center",
|
|
|
|
|
|
(HA::Right, VA::Bottom) => "Bottom right",
|
2026-03-22 14:56:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-03 09:51:34 +03:00
|
|
|
|
pub(crate) fn sync_text_alignment_point(t: &mut Text) {
|
2026-03-22 14:56:27 +03:00
|
|
|
|
let needs_alignment_point = !matches!(
|
|
|
|
|
|
(t.horizontal_alignment, t.vertical_alignment),
|
|
|
|
|
|
(HA::Left, VA::Baseline)
|
|
|
|
|
|
);
|
|
|
|
|
|
if needs_alignment_point {
|
|
|
|
|
|
if t.alignment_point.is_none() {
|
|
|
|
|
|
t.alignment_point = Some(t.insertion_point);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
t.alignment_point = None;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat(text): render TEXT as SDF quads behind OCS_TEXT_SDF flag
Wires the SDF glyph atlas into the render loop end-to-end for top-level
TEXT entities, gated by the OCS_TEXT_SDF env var so the default path is
unchanged. Verified rendering on a real drawing: glyph quads land exactly
over the existing stroke text (position, size, rotation), confirming the
atlas → shader → pipeline → screen path and the RTE / UV math.
- text::text_run_placement — extracted from to_truck (behaviour-preserved)
so the stroke and SDF-quad paths compute an identical run placement.
- sdf_atlas::text_atlas — process-wide GlyphAtlas (Mutex/OnceLock), shared
by the collector (bakes glyphs) and the GPU upload (reads texels).
- Scene::sdf_text_vertices — collects per-glyph TextVertex for the frame's
TEXT entities via layout_glyph_quads + push_glyph_vertices. Empty unless
OCS_TEXT_SDF is set.
- ViewportData.text_verts + Pipeline text_pipeline/atlas/vbuf, upload_text
(rebuilds the R8 atlas texture when it grows), and a text render pass
drawn over the wires.
Still additive over the stroke text; only top-level TEXT is wired (MTEXT,
attributes, and block-internal text are next), and the collector re-runs
per frame (vertex caching by geometry epoch is a follow-up).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 23:02:34 +03:00
|
|
|
|
/// Resolved placement of a TEXT run: the baseline-anchored run origin (WCS xy)
|
|
|
|
|
|
/// plus every parameter needed to lay the glyphs out. Shared by `to_truck` (the
|
|
|
|
|
|
/// stroke path) and the SDF-quad text collector so both place text identically.
|
|
|
|
|
|
pub struct TextPlacement {
|
|
|
|
|
|
/// Run-local origin (glyph space `[0,0]` maps here), WCS xy, kept f64.
|
|
|
|
|
|
pub origin: [f64; 2],
|
|
|
|
|
|
pub height: f32,
|
|
|
|
|
|
pub rotation: f32,
|
|
|
|
|
|
pub width_factor: f32,
|
|
|
|
|
|
pub oblique_angle: f32,
|
|
|
|
|
|
pub font: String,
|
|
|
|
|
|
/// Raw entity value (as passed to the tessellator).
|
|
|
|
|
|
pub value: String,
|
|
|
|
|
|
/// Full WCS insertion point, for the Insertion snap.
|
|
|
|
|
|
pub wcs_insertion: [f64; 3],
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 00:53:19 +03:00
|
|
|
|
/// Parse a TEXT value's `%%` control codes through acadrust's `parse_plain_text`
|
|
|
|
|
|
/// (the same parser MTEXT uses), then re-encode into the stroke tessellator's
|
|
|
|
|
|
/// inline grammar: specials arrive resolved to Unicode, and `%%u`/`%%o`
|
|
|
|
|
|
/// underline/overline become `\L…\l` / `\O…\o` decoration markers. This keeps
|
|
|
|
|
|
/// TEXT parsing in acadrust rather than OCS's own tokenizer.
|
2026-07-07 01:15:36 +03:00
|
|
|
|
pub(crate) fn acad_text_encode(value: &str) -> String {
|
2026-07-07 00:53:19 +03:00
|
|
|
|
use acadrust::entities::mtext_format::parse_plain_text;
|
|
|
|
|
|
let doc = parse_plain_text(value);
|
|
|
|
|
|
let mut out = String::new();
|
|
|
|
|
|
for para in &doc.paragraphs {
|
|
|
|
|
|
for span in ¶.spans {
|
|
|
|
|
|
let (u, o) = (span.properties.underline(), span.properties.overline());
|
|
|
|
|
|
if u {
|
|
|
|
|
|
out.push_str("\\L");
|
|
|
|
|
|
}
|
|
|
|
|
|
if o {
|
|
|
|
|
|
out.push_str("\\O");
|
|
|
|
|
|
}
|
|
|
|
|
|
out.push_str(&span.text);
|
|
|
|
|
|
if o {
|
|
|
|
|
|
out.push_str("\\o");
|
|
|
|
|
|
}
|
|
|
|
|
|
if u {
|
|
|
|
|
|
out.push_str("\\l");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
out
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-22 14:56:27 +03:00
|
|
|
|
fn to_truck(t: &Text, document: &acadrust::CadDocument) -> TruckEntity {
|
feat(text): render TEXT as SDF quads behind OCS_TEXT_SDF flag
Wires the SDF glyph atlas into the render loop end-to-end for top-level
TEXT entities, gated by the OCS_TEXT_SDF env var so the default path is
unchanged. Verified rendering on a real drawing: glyph quads land exactly
over the existing stroke text (position, size, rotation), confirming the
atlas → shader → pipeline → screen path and the RTE / UV math.
- text::text_run_placement — extracted from to_truck (behaviour-preserved)
so the stroke and SDF-quad paths compute an identical run placement.
- sdf_atlas::text_atlas — process-wide GlyphAtlas (Mutex/OnceLock), shared
by the collector (bakes glyphs) and the GPU upload (reads texels).
- Scene::sdf_text_vertices — collects per-glyph TextVertex for the frame's
TEXT entities via layout_glyph_quads + push_glyph_vertices. Empty unless
OCS_TEXT_SDF is set.
- ViewportData.text_verts + Pipeline text_pipeline/atlas/vbuf, upload_text
(rebuilds the R8 atlas texture when it grows), and a text render pass
drawn over the wires.
Still additive over the stroke text; only top-level TEXT is wired (MTEXT,
attributes, and block-internal text are next), and the collector re-runs
per frame (vertex caching by geometry epoch is a follow-up).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 23:02:34 +03:00
|
|
|
|
let p = text_run_placement(t, document);
|
|
|
|
|
|
let snap_pt = glam::DVec3::new(p.wcs_insertion[0], p.wcs_insertion[1], p.wcs_insertion[2]);
|
2026-07-07 00:53:19 +03:00
|
|
|
|
// Parse `%%` codes via acadrust, re-encoded for the stroke tessellator.
|
|
|
|
|
|
let value = acad_text_encode(&p.value);
|
feat(text): render TEXT as SDF quads behind OCS_TEXT_SDF flag
Wires the SDF glyph atlas into the render loop end-to-end for top-level
TEXT entities, gated by the OCS_TEXT_SDF env var so the default path is
unchanged. Verified rendering on a real drawing: glyph quads land exactly
over the existing stroke text (position, size, rotation), confirming the
atlas → shader → pipeline → screen path and the RTE / UV math.
- text::text_run_placement — extracted from to_truck (behaviour-preserved)
so the stroke and SDF-quad paths compute an identical run placement.
- sdf_atlas::text_atlas — process-wide GlyphAtlas (Mutex/OnceLock), shared
by the collector (bakes glyphs) and the GPU upload (reads texels).
- Scene::sdf_text_vertices — collects per-glyph TextVertex for the frame's
TEXT entities via layout_glyph_quads + push_glyph_vertices. Empty unless
OCS_TEXT_SDF is set.
- ViewportData.text_verts + Pipeline text_pipeline/atlas/vbuf, upload_text
(rebuilds the R8 atlas texture when it grows), and a text render pass
drawn over the wires.
Still additive over the stroke text; only top-level TEXT is wired (MTEXT,
attributes, and block-internal text are next), and the collector re-runs
per frame (vertex caching by geometry epoch is a follow-up).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 23:02:34 +03:00
|
|
|
|
// Strokes are in glyph-local space (origin = [0,0]).
|
|
|
|
|
|
let (strokes, fill_tris) = lff::tessellate_text_ex(
|
|
|
|
|
|
[0.0, 0.0],
|
|
|
|
|
|
p.height,
|
|
|
|
|
|
p.rotation,
|
|
|
|
|
|
p.width_factor,
|
|
|
|
|
|
p.oblique_angle,
|
|
|
|
|
|
&p.font,
|
2026-07-07 00:53:19 +03:00
|
|
|
|
&value,
|
feat(text): render TEXT as SDF quads behind OCS_TEXT_SDF flag
Wires the SDF glyph atlas into the render loop end-to-end for top-level
TEXT entities, gated by the OCS_TEXT_SDF env var so the default path is
unchanged. Verified rendering on a real drawing: glyph quads land exactly
over the existing stroke text (position, size, rotation), confirming the
atlas → shader → pipeline → screen path and the RTE / UV math.
- text::text_run_placement — extracted from to_truck (behaviour-preserved)
so the stroke and SDF-quad paths compute an identical run placement.
- sdf_atlas::text_atlas — process-wide GlyphAtlas (Mutex/OnceLock), shared
by the collector (bakes glyphs) and the GPU upload (reads texels).
- Scene::sdf_text_vertices — collects per-glyph TextVertex for the frame's
TEXT entities via layout_glyph_quads + push_glyph_vertices. Empty unless
OCS_TEXT_SDF is set.
- ViewportData.text_verts + Pipeline text_pipeline/atlas/vbuf, upload_text
(rebuilds the R8 atlas texture when it grows), and a text render pass
drawn over the wires.
Still additive over the stroke text; only top-level TEXT is wired (MTEXT,
attributes, and block-internal text are next), and the collector re-runs
per frame (vertex caching by geometry epoch is a follow-up).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 23:02:34 +03:00
|
|
|
|
);
|
|
|
|
|
|
TruckEntity {
|
fix(pick): hit-test lineweight width and thickness walls
Selection tested a flat 8 px radius around each wire's centreline and
nothing else, so two things the user can see were not pickable.
A wire renders as a band `line_weight_px` wide, so pick now widens to that
rendered half-width via `pick_tolerance_px`, mirroring the wire shader's
`select(0.5, half_width, lwdisplay_enable)`. Behaviour is unchanged for
every standard weight (the widest, 2.11 mm, renders 7.97 px half-width —
just under the 8 px threshold); this keeps the two sides from drifting
apart if the display boost in `lineweight_to_px` ever changes.
Entities extruded by a DXF thickness draw their wall as four edges with
nothing in between, so the interior was a hole the cursor fell through.
WireModel gains `pick_tris`: triangles hit-testing treats as solid and the
renderer never uploads. Kept apart from `fill_tris` because that channel
reaches the GPU, and a wall that rendered shaded would change how every
thickness drawing looks. Ranked below `fill_tris` — those are drawn, these
are not, so where they overlap the drawn face is what the user means.
Walls are dropped when a wire is projected into a floating viewport:
`points` are reprojected and clipped into paper coords there, so cloned
model-space triangles would hit-test at a model-scale offset. Extruded
entities stay selectable by their edges, which do get projected.
The wall loop runs on every hover that misses everything else, so it
rejects on the wire's AABB first — an extruded circle is ~128 triangles
and a drawing full of thickness would otherwise project every wall on
each mouse move.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 14:58:55 +03:00
|
|
|
|
pick_tris: Vec::new(),
|
feat(text): render TEXT as SDF quads behind OCS_TEXT_SDF flag
Wires the SDF glyph atlas into the render loop end-to-end for top-level
TEXT entities, gated by the OCS_TEXT_SDF env var so the default path is
unchanged. Verified rendering on a real drawing: glyph quads land exactly
over the existing stroke text (position, size, rotation), confirming the
atlas → shader → pipeline → screen path and the RTE / UV math.
- text::text_run_placement — extracted from to_truck (behaviour-preserved)
so the stroke and SDF-quad paths compute an identical run placement.
- sdf_atlas::text_atlas — process-wide GlyphAtlas (Mutex/OnceLock), shared
by the collector (bakes glyphs) and the GPU upload (reads texels).
- Scene::sdf_text_vertices — collects per-glyph TextVertex for the frame's
TEXT entities via layout_glyph_quads + push_glyph_vertices. Empty unless
OCS_TEXT_SDF is set.
- ViewportData.text_verts + Pipeline text_pipeline/atlas/vbuf, upload_text
(rebuilds the R8 atlas texture when it grows), and a text render pass
drawn over the wires.
Still additive over the stroke text; only top-level TEXT is wired (MTEXT,
attributes, and block-internal text are next), and the collector re-runs
per frame (vertex caching by geometry epoch is a follow-up).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 23:02:34 +03:00
|
|
|
|
object: TruckObject::Text(vec![TextStroke {
|
|
|
|
|
|
strokes,
|
|
|
|
|
|
origin: p.origin,
|
|
|
|
|
|
color: None,
|
|
|
|
|
|
fill_tris,
|
2026-07-07 00:53:19 +03:00
|
|
|
|
run: Some(GlyphRun {
|
|
|
|
|
|
text: value,
|
|
|
|
|
|
font: p.font.clone(),
|
|
|
|
|
|
height: p.height,
|
|
|
|
|
|
rotation: p.rotation,
|
|
|
|
|
|
width_factor: p.width_factor,
|
|
|
|
|
|
oblique: p.oblique_angle,
|
|
|
|
|
|
tracking: 1.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 00:53:19 +03:00
|
|
|
|
}),
|
feat(text): render TEXT as SDF quads behind OCS_TEXT_SDF flag
Wires the SDF glyph atlas into the render loop end-to-end for top-level
TEXT entities, gated by the OCS_TEXT_SDF env var so the default path is
unchanged. Verified rendering on a real drawing: glyph quads land exactly
over the existing stroke text (position, size, rotation), confirming the
atlas → shader → pipeline → screen path and the RTE / UV math.
- text::text_run_placement — extracted from to_truck (behaviour-preserved)
so the stroke and SDF-quad paths compute an identical run placement.
- sdf_atlas::text_atlas — process-wide GlyphAtlas (Mutex/OnceLock), shared
by the collector (bakes glyphs) and the GPU upload (reads texels).
- Scene::sdf_text_vertices — collects per-glyph TextVertex for the frame's
TEXT entities via layout_glyph_quads + push_glyph_vertices. Empty unless
OCS_TEXT_SDF is set.
- ViewportData.text_verts + Pipeline text_pipeline/atlas/vbuf, upload_text
(rebuilds the R8 atlas texture when it grows), and a text render pass
drawn over the wires.
Still additive over the stroke text; only top-level TEXT is wired (MTEXT,
attributes, and block-internal text are next), and the collector re-runs
per frame (vertex caching by geometry epoch is a follow-up).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 23:02:34 +03:00
|
|
|
|
}]),
|
|
|
|
|
|
snap_pts: vec![(snap_pt, SnapHint::Insertion)],
|
|
|
|
|
|
tangent_geoms: vec![],
|
|
|
|
|
|
key_vertices: vec![],
|
|
|
|
|
|
fill_tris: vec![],
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Compute a TEXT entity's run placement (origin + layout params). Extracted
|
|
|
|
|
|
/// from `to_truck` verbatim so the stroke and SDF-quad paths agree exactly.
|
|
|
|
|
|
pub fn text_run_placement(t: &Text, document: &acadrust::CadDocument) -> TextPlacement {
|
2026-05-02 20:06:22 +03:00
|
|
|
|
let normal = (t.normal.x, t.normal.y, t.normal.z);
|
2026-06-16 11:33:46 +03:00
|
|
|
|
let (wsx, wsy, wsz) = crate::scene::view::transform::ocs_point_to_wcs(
|
2026-05-12 10:36:15 +03:00
|
|
|
|
(
|
|
|
|
|
|
t.insertion_point.x,
|
|
|
|
|
|
t.insertion_point.y,
|
|
|
|
|
|
t.insertion_point.z,
|
|
|
|
|
|
),
|
2026-05-02 20:06:22 +03:00
|
|
|
|
normal,
|
2026-03-22 14:56:27 +03:00
|
|
|
|
);
|
|
|
|
|
|
let resolved_style = resolve_text_style(&t.style, document);
|
|
|
|
|
|
let font_name = resolved_style.font_name;
|
2026-05-21 12:38:03 +03:00
|
|
|
|
// AutoCAD text geometry rule: the entity stores the FINAL width factor /
|
|
|
|
|
|
// oblique angle, copied from the style at creation and persisting through
|
|
|
|
|
|
// style edits. Use it as-is. Only fall back to the style when the entity
|
|
|
|
|
|
// value is missing (the parser reports 0.0 for default-omitted fields).
|
|
|
|
|
|
let base_wf = if t.width_factor.abs() > 1e-9 {
|
|
|
|
|
|
(t.width_factor as f32).clamp(0.01, 100.0)
|
2026-05-12 10:36:15 +03:00
|
|
|
|
} else {
|
2026-05-21 12:38:03 +03:00
|
|
|
|
resolved_style.width_factor.max(0.01)
|
|
|
|
|
|
};
|
2026-07-03 12:42:55 +03:00
|
|
|
|
// Backward mirrors text left-right (negative width factor); upside-down
|
|
|
|
|
|
// rotates 180° about the anchor. The effective state is the TextStyle flag
|
|
|
|
|
|
// XOR the entity's own generation flags (DXF group 71: bit 2 = backward,
|
|
|
|
|
|
// bit 4 = upside-down). A MIRROR toggles the entity bit for a true glyph
|
|
|
|
|
|
// mirror, and XOR keeps a double mirror an involution.
|
|
|
|
|
|
let eff_backward = resolved_style.is_backward ^ (t.generation_flags & 0x2 != 0);
|
|
|
|
|
|
let eff_upside = resolved_style.is_upside_down ^ (t.generation_flags & 0x4 != 0);
|
|
|
|
|
|
let width_factor = if eff_backward { -base_wf } else { base_wf };
|
|
|
|
|
|
let rotation = if eff_upside {
|
2026-04-26 22:10:32 +03:00
|
|
|
|
t.rotation as f32 + std::f32::consts::PI
|
|
|
|
|
|
} else {
|
|
|
|
|
|
t.rotation as f32
|
|
|
|
|
|
};
|
2026-05-21 12:38:03 +03:00
|
|
|
|
let oblique_angle = if t.oblique_angle.abs() > 1e-9 {
|
|
|
|
|
|
t.oblique_angle as f32
|
|
|
|
|
|
} else {
|
|
|
|
|
|
resolved_style.oblique_angle
|
|
|
|
|
|
};
|
2026-06-22 18:47:45 +03:00
|
|
|
|
// Anchor stays f64: large coordinates (UTM etc.) lose ~0.5 units of
|
|
|
|
|
|
// precision when cast to f32, which snaps text baselines onto a coarse
|
|
|
|
|
|
// grid and makes adjacent rows collide. Only the small local offsets
|
|
|
|
|
|
// below are computed in f32.
|
|
|
|
|
|
let anchor: [f64; 2] = match (
|
2026-03-22 14:56:27 +03:00
|
|
|
|
&t.horizontal_alignment,
|
|
|
|
|
|
&t.vertical_alignment,
|
|
|
|
|
|
&t.alignment_point,
|
|
|
|
|
|
) {
|
2026-06-22 18:47:45 +03:00
|
|
|
|
(HA::Aligned | HA::Middle | HA::Fit, _, Some(a)) => [a.x, a.y],
|
|
|
|
|
|
(HA::Center | HA::Right, _, Some(a)) => [a.x, a.y],
|
|
|
|
|
|
(_, VA::Bottom | VA::Middle | VA::Top, Some(a)) => [a.x, a.y],
|
|
|
|
|
|
_ => [t.insertion_point.x, t.insertion_point.y],
|
2026-03-22 14:56:27 +03:00
|
|
|
|
};
|
2026-04-26 21:57:51 +03:00
|
|
|
|
// Strip %%u/%%o for bounds (they add no width); resolve %%d/%%c/%%p for correct advance.
|
|
|
|
|
|
let value_for_bounds = resolve_dxf_special_chars(&t.value);
|
2026-03-22 14:56:27 +03:00
|
|
|
|
let bounds = text_local_bounds(
|
|
|
|
|
|
&font_name,
|
2026-04-26 21:57:51 +03:00
|
|
|
|
&value_for_bounds,
|
2026-03-22 14:56:27 +03:00
|
|
|
|
t.height as f32,
|
|
|
|
|
|
width_factor,
|
|
|
|
|
|
oblique_angle,
|
|
|
|
|
|
);
|
2026-06-22 18:47:45 +03:00
|
|
|
|
let (anchor_local_x, anchor_local_y) = if let Some(b) = bounds {
|
|
|
|
|
|
// Horizontal anchor uses the pen advance box [0, advance] so leading /
|
2026-07-03 12:42:55 +03:00
|
|
|
|
// trailing spaces keep their width instead of snapping to the inked
|
|
|
|
|
|
// glyphs. Signed by the width factor: backward text grows in −x, so its
|
|
|
|
|
|
// Center/Right offset flips sign too, otherwise the anchor pushes the
|
|
|
|
|
|
// box one way while the strokes run the other (the bounds advance is
|
|
|
|
|
|
// always positive — it uses |width_factor|). Left keeps its 0 reference.
|
|
|
|
|
|
let sign = width_factor.signum();
|
2026-03-22 14:56:27 +03:00
|
|
|
|
let ax = match t.horizontal_alignment {
|
2026-06-22 18:47:45 +03:00
|
|
|
|
HA::Left => 0.0,
|
2026-07-03 12:42:55 +03:00
|
|
|
|
HA::Center | HA::Middle => b.advance * 0.5 * sign,
|
|
|
|
|
|
HA::Right | HA::Aligned | HA::Fit => b.advance * sign,
|
2026-03-22 14:56:27 +03:00
|
|
|
|
};
|
2026-06-22 18:47:45 +03:00
|
|
|
|
// Vertical anchor uses the inked extent (cap / baseline geometry).
|
2026-03-22 14:56:27 +03:00
|
|
|
|
let ay = match t.vertical_alignment {
|
|
|
|
|
|
VA::Baseline => 0.0,
|
2026-06-22 18:47:45 +03:00
|
|
|
|
VA::Bottom => b.ink_min[1],
|
|
|
|
|
|
VA::Middle => (b.ink_min[1] + b.ink_max[1]) * 0.5,
|
|
|
|
|
|
VA::Top => b.ink_max[1],
|
2026-03-22 14:56:27 +03:00
|
|
|
|
};
|
|
|
|
|
|
(ax, ay)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
(0.0, 0.0)
|
|
|
|
|
|
};
|
2026-04-29 20:58:44 +03:00
|
|
|
|
let (cos_r, sin_r) = (rotation.cos() as f64, rotation.sin() as f64);
|
|
|
|
|
|
// Keep origin as f64 — large coordinates (UTM etc.) must not be cast to
|
|
|
|
|
|
// f32 here; world_offset subtraction happens later in tessellate.rs.
|
2026-06-22 18:47:45 +03:00
|
|
|
|
let anchor_f64 = anchor;
|
2026-04-29 20:58:44 +03:00
|
|
|
|
let origin: [f64; 2] = [
|
|
|
|
|
|
anchor_f64[0] - (anchor_local_x as f64 * cos_r - anchor_local_y as f64 * sin_r),
|
|
|
|
|
|
anchor_f64[1] - (anchor_local_x as f64 * sin_r + anchor_local_y as f64 * cos_r),
|
2026-03-22 14:56:27 +03:00
|
|
|
|
];
|
feat(text): render TEXT as SDF quads behind OCS_TEXT_SDF flag
Wires the SDF glyph atlas into the render loop end-to-end for top-level
TEXT entities, gated by the OCS_TEXT_SDF env var so the default path is
unchanged. Verified rendering on a real drawing: glyph quads land exactly
over the existing stroke text (position, size, rotation), confirming the
atlas → shader → pipeline → screen path and the RTE / UV math.
- text::text_run_placement — extracted from to_truck (behaviour-preserved)
so the stroke and SDF-quad paths compute an identical run placement.
- sdf_atlas::text_atlas — process-wide GlyphAtlas (Mutex/OnceLock), shared
by the collector (bakes glyphs) and the GPU upload (reads texels).
- Scene::sdf_text_vertices — collects per-glyph TextVertex for the frame's
TEXT entities via layout_glyph_quads + push_glyph_vertices. Empty unless
OCS_TEXT_SDF is set.
- ViewportData.text_verts + Pipeline text_pipeline/atlas/vbuf, upload_text
(rebuilds the R8 atlas texture when it grows), and a text render pass
drawn over the wires.
Still additive over the stroke text; only top-level TEXT is wired (MTEXT,
attributes, and block-internal text are next), and the collector re-runs
per frame (vertex caching by geometry epoch is a follow-up).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 23:02:34 +03:00
|
|
|
|
TextPlacement {
|
|
|
|
|
|
origin,
|
|
|
|
|
|
height: t.height as f32,
|
2026-04-26 22:10:32 +03:00
|
|
|
|
rotation,
|
2026-03-22 14:56:27 +03:00
|
|
|
|
width_factor,
|
|
|
|
|
|
oblique_angle,
|
feat(text): render TEXT as SDF quads behind OCS_TEXT_SDF flag
Wires the SDF glyph atlas into the render loop end-to-end for top-level
TEXT entities, gated by the OCS_TEXT_SDF env var so the default path is
unchanged. Verified rendering on a real drawing: glyph quads land exactly
over the existing stroke text (position, size, rotation), confirming the
atlas → shader → pipeline → screen path and the RTE / UV math.
- text::text_run_placement — extracted from to_truck (behaviour-preserved)
so the stroke and SDF-quad paths compute an identical run placement.
- sdf_atlas::text_atlas — process-wide GlyphAtlas (Mutex/OnceLock), shared
by the collector (bakes glyphs) and the GPU upload (reads texels).
- Scene::sdf_text_vertices — collects per-glyph TextVertex for the frame's
TEXT entities via layout_glyph_quads + push_glyph_vertices. Empty unless
OCS_TEXT_SDF is set.
- ViewportData.text_verts + Pipeline text_pipeline/atlas/vbuf, upload_text
(rebuilds the R8 atlas texture when it grows), and a text render pass
drawn over the wires.
Still additive over the stroke text; only top-level TEXT is wired (MTEXT,
attributes, and block-internal text are next), and the collector re-runs
per frame (vertex caching by geometry epoch is a follow-up).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 23:02:34 +03:00
|
|
|
|
font: font_name,
|
|
|
|
|
|
value: t.value.clone(),
|
|
|
|
|
|
wcs_insertion: [wsx, wsy, wsz],
|
2026-03-22 14:56:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn grips(t: &Text) -> Vec<GripDef> {
|
2026-06-11 00:04:37 +03:00
|
|
|
|
let p = glam::DVec3::new(
|
|
|
|
|
|
t.insertion_point.x,
|
|
|
|
|
|
t.insertion_point.y,
|
|
|
|
|
|
t.insertion_point.z,
|
2026-03-22 14:56:27 +03:00
|
|
|
|
);
|
|
|
|
|
|
vec![square_grip(0, p)]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-02 22:46:19 +03:00
|
|
|
|
fn properties(t: &Text, text_style_names: &[String]) -> Vec<PropSection> {
|
feat(properties): rework Text/MText/MLeader/Leader panels to standard layout
Match the entity Properties panels to the standard CAD layout and
web-verified behaviour:
- Text: combined Justify (horizontal x vertical) dropdown; Position and
Text-alignment rows become editable/grayed by justification; drop the
Annotative-scale row.
- MText: combined Justify; editable Direction, Line space distance
(back-solves the factor) and Line space style; Defined width/height
gated on column mode.
- MLeader: regrouped to match the palette - drop the Leader Structure
group (fold landing rows into Leaders), gate Text vs Block on content
type, full 9-value attachment label set, resolve handle-backed rows
(style, text style, arrowhead, linetype) to names.
- Leader: Lines & Arrows / Text / Fit are resolved from the dimension
style (arrowhead, arrow size, dim-line lineweight/colour, text gap,
vertical text pos, overall scale); Current Vertex navigator; combined
Type; Geometry group after General; no 3D-Visualisation group.
- General: rename "LT Scale" to "Linetype scale"; show ByLayer
transparency by name. Add shared num_prop helper for conditionally
editable numeric rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 18:01:44 +03:00
|
|
|
|
// Which geometry rows are live depends on justification. Plain Left text is
|
|
|
|
|
|
// anchored by the insertion point and has no second alignment point; every
|
|
|
|
|
|
// other justification anchors on the alignment point (and recomputes the
|
|
|
|
|
|
// insertion point), except Aligned/Fit which are true two-point spans where
|
|
|
|
|
|
// both points are live.
|
|
|
|
|
|
let is_plain_left = matches!(t.horizontal_alignment, HA::Left)
|
|
|
|
|
|
&& matches!(t.vertical_alignment, VA::Baseline);
|
|
|
|
|
|
let pos_editable = is_plain_left || matches!(t.horizontal_alignment, HA::Aligned | HA::Fit);
|
|
|
|
|
|
let align_editable = !is_plain_left;
|
|
|
|
|
|
// The alignment point is meaningless (reset to the origin) for plain-Left text.
|
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
|
|
|
|
let ap = t.alignment_point.unwrap_or(t.insertion_point);
|
feat(properties): rework Text/MText/MLeader/Leader panels to standard layout
Match the entity Properties panels to the standard CAD layout and
web-verified behaviour:
- Text: combined Justify (horizontal x vertical) dropdown; Position and
Text-alignment rows become editable/grayed by justification; drop the
Annotative-scale row.
- MText: combined Justify; editable Direction, Line space distance
(back-solves the factor) and Line space style; Defined width/height
gated on column mode.
- MLeader: regrouped to match the palette - drop the Leader Structure
group (fold landing rows into Leaders), gate Text vs Block on content
type, full 9-value attachment label set, resolve handle-backed rows
(style, text style, arrowhead, linetype) to names.
- Leader: Lines & Arrows / Text / Fit are resolved from the dimension
style (arrowhead, arrow size, dim-line lineweight/colour, text gap,
vertical text pos, overall scale); Current Vertex navigator; combined
Type; Geometry group after General; no 3D-Visualisation group.
- General: rename "LT Scale" to "Linetype scale"; show ByLayer
transparency by name. Add shared num_prop helper for conditionally
editable numeric rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 18:01:44 +03:00
|
|
|
|
let (ax, ay, az) = if align_editable {
|
|
|
|
|
|
(ap.x, ap.y, ap.z)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
(0.0, 0.0, 0.0)
|
|
|
|
|
|
};
|
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![
|
|
|
|
|
|
Property {
|
|
|
|
|
|
label: "Contents".into(),
|
|
|
|
|
|
field: "content",
|
|
|
|
|
|
value: PropValue::EditText(t.value.clone()),
|
2026-03-22 14:56:27 +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
|
|
|
|
Property {
|
|
|
|
|
|
label: "Style".into(),
|
|
|
|
|
|
field: "style",
|
|
|
|
|
|
value: PropValue::Choice {
|
|
|
|
|
|
selected: if t.style.trim().is_empty() {
|
|
|
|
|
|
"Standard".into()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
t.style.clone()
|
|
|
|
|
|
},
|
|
|
|
|
|
options: text_style_names.to_vec(),
|
|
|
|
|
|
},
|
2026-03-22 14:56:27 +03:00
|
|
|
|
},
|
feat(properties): rework Text/MText/MLeader/Leader panels to standard layout
Match the entity Properties panels to the standard CAD layout and
web-verified behaviour:
- Text: combined Justify (horizontal x vertical) dropdown; Position and
Text-alignment rows become editable/grayed by justification; drop the
Annotative-scale row.
- MText: combined Justify; editable Direction, Line space distance
(back-solves the factor) and Line space style; Defined width/height
gated on column mode.
- MLeader: regrouped to match the palette - drop the Leader Structure
group (fold landing rows into Leaders), gate Text vs Block on content
type, full 9-value attachment label set, resolve handle-backed rows
(style, text style, arrowhead, linetype) to names.
- Leader: Lines & Arrows / Text / Fit are resolved from the dimension
style (arrowhead, arrow size, dim-line lineweight/colour, text gap,
vertical text pos, overall scale); Current Vertex navigator; combined
Type; Geometry group after General; no 3D-Visualisation group.
- General: rename "LT Scale" to "Linetype scale"; show ByLayer
transparency by name. Add shared num_prop helper for conditionally
editable numeric rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 18:01:44 +03:00
|
|
|
|
ro("Annotative", "annotative", "No"),
|
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
|
|
|
|
Property {
|
|
|
|
|
|
label: "Justify".into(),
|
feat(properties): rework Text/MText/MLeader/Leader panels to standard layout
Match the entity Properties panels to the standard CAD layout and
web-verified behaviour:
- Text: combined Justify (horizontal x vertical) dropdown; Position and
Text-alignment rows become editable/grayed by justification; drop the
Annotative-scale row.
- MText: combined Justify; editable Direction, Line space distance
(back-solves the factor) and Line space style; Defined width/height
gated on column mode.
- MLeader: regrouped to match the palette - drop the Leader Structure
group (fold landing rows into Leaders), gate Text vs Block on content
type, full 9-value attachment label set, resolve handle-backed rows
(style, text style, arrowhead, linetype) to names.
- Leader: Lines & Arrows / Text / Fit are resolved from the dimension
style (arrowhead, arrow size, dim-line lineweight/colour, text gap,
vertical text pos, overall scale); Current Vertex navigator; combined
Type; Geometry group after General; no 3D-Visualisation group.
- General: rename "LT Scale" to "Linetype scale"; show ByLayer
transparency by name. Add shared num_prop helper for conditionally
editable numeric rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 18:01:44 +03:00
|
|
|
|
field: "justify",
|
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
|
|
|
|
value: PropValue::Choice {
|
feat(properties): rework Text/MText/MLeader/Leader panels to standard layout
Match the entity Properties panels to the standard CAD layout and
web-verified behaviour:
- Text: combined Justify (horizontal x vertical) dropdown; Position and
Text-alignment rows become editable/grayed by justification; drop the
Annotative-scale row.
- MText: combined Justify; editable Direction, Line space distance
(back-solves the factor) and Line space style; Defined width/height
gated on column mode.
- MLeader: regrouped to match the palette - drop the Leader Structure
group (fold landing rows into Leaders), gate Text vs Block on content
type, full 9-value attachment label set, resolve handle-backed rows
(style, text style, arrowhead, linetype) to names.
- Leader: Lines & Arrows / Text / Fit are resolved from the dimension
style (arrowhead, arrow size, dim-line lineweight/colour, text gap,
vertical text pos, overall scale); Current Vertex navigator; combined
Type; Geometry group after General; no 3D-Visualisation group.
- General: rename "LT Scale" to "Linetype scale"; show ByLayer
transparency by name. Add shared num_prop helper for conditionally
editable numeric rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 18:01:44 +03:00
|
|
|
|
selected: text_justify_str(
|
|
|
|
|
|
&t.horizontal_alignment,
|
|
|
|
|
|
&t.vertical_alignment,
|
|
|
|
|
|
)
|
|
|
|
|
|
.to_string(),
|
|
|
|
|
|
options: [
|
|
|
|
|
|
"Left",
|
|
|
|
|
|
"Center",
|
|
|
|
|
|
"Right",
|
|
|
|
|
|
"Aligned",
|
|
|
|
|
|
"Middle",
|
|
|
|
|
|
"Fit",
|
|
|
|
|
|
"Top left",
|
|
|
|
|
|
"Top center",
|
|
|
|
|
|
"Top right",
|
|
|
|
|
|
"Middle left",
|
|
|
|
|
|
"Middle center",
|
|
|
|
|
|
"Middle right",
|
|
|
|
|
|
"Bottom left",
|
|
|
|
|
|
"Bottom center",
|
|
|
|
|
|
"Bottom right",
|
|
|
|
|
|
]
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(str::to_string)
|
|
|
|
|
|
.collect(),
|
2026-03-22 14:56:27 +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("Height", "height", t.height),
|
fix(props): angle rows format via AUNITS, not LUNITS (#297)
Every angle row went through the linear formatter, so switching LUNITS
to Architectural showed a block's Rotation as feet-and-inches — and the
string wouldn't parse back, wedging the field until it was cleared and
retyped. Only the decimal/fractional LUNITS survived by accident.
New edit_angle row helper formats angles with format_angle
(AUNITS/AUPREC: decimal degrees, DMS, grads or radians), applied to all
17 angle rows (rotations, obliquing, arc start/end, hatch pattern
angle, multileader text rotation). parse_angle_deg converts every
AUNITS display form back to degrees ("30°"/"30d", DMS 30°15'20",
33.33g, 0.52r) and parse_f64 falls back to it, so the displayed value
round-trips through an edit.
Closes #297
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 16:42:14 +03:00
|
|
|
|
edit_angle("Rotation", "rotation", t.rotation.to_degrees()),
|
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("Width factor", "width_factor", t.width_factor),
|
fix(props): angle rows format via AUNITS, not LUNITS (#297)
Every angle row went through the linear formatter, so switching LUNITS
to Architectural showed a block's Rotation as feet-and-inches — and the
string wouldn't parse back, wedging the field until it was cleared and
retyped. Only the decimal/fractional LUNITS survived by accident.
New edit_angle row helper formats angles with format_angle
(AUNITS/AUPREC: decimal degrees, DMS, grads or radians), applied to all
17 angle rows (rotations, obliquing, arc start/end, hatch pattern
angle, multileader text rotation). parse_angle_deg converts every
AUNITS display form back to degrees ("30°"/"30d", DMS 30°15'20",
33.33g, 0.52r) and parse_f64 falls back to it, so the displayed value
round-trips through an edit.
Closes #297
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 16:42:14 +03:00
|
|
|
|
edit_angle("Obliquing", "oblique_angle", t.oblique_angle.to_degrees()),
|
feat(properties): rework Text/MText/MLeader/Leader panels to standard layout
Match the entity Properties panels to the standard CAD layout and
web-verified behaviour:
- Text: combined Justify (horizontal x vertical) dropdown; Position and
Text-alignment rows become editable/grayed by justification; drop the
Annotative-scale row.
- MText: combined Justify; editable Direction, Line space distance
(back-solves the factor) and Line space style; Defined width/height
gated on column mode.
- MLeader: regrouped to match the palette - drop the Leader Structure
group (fold landing rows into Leaders), gate Text vs Block on content
type, full 9-value attachment label set, resolve handle-backed rows
(style, text style, arrowhead, linetype) to names.
- Leader: Lines & Arrows / Text / Fit are resolved from the dimension
style (arrowhead, arrow size, dim-line lineweight/colour, text gap,
vertical text pos, overall scale); Current Vertex navigator; combined
Type; Geometry group after General; no 3D-Visualisation group.
- General: rename "LT Scale" to "Linetype scale"; show ByLayer
transparency by name. Add shared num_prop helper for conditionally
editable numeric rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 18:01:44 +03:00
|
|
|
|
num_row("Text alignment X", "align_x", ax, align_editable),
|
|
|
|
|
|
num_row("Text alignment Y", "align_y", ay, align_editable),
|
|
|
|
|
|
num_row("Text alignment Z", "align_z", az, align_editable),
|
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
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
PropSection {
|
|
|
|
|
|
title: "Geometry".into(),
|
|
|
|
|
|
props: vec![
|
feat(properties): rework Text/MText/MLeader/Leader panels to standard layout
Match the entity Properties panels to the standard CAD layout and
web-verified behaviour:
- Text: combined Justify (horizontal x vertical) dropdown; Position and
Text-alignment rows become editable/grayed by justification; drop the
Annotative-scale row.
- MText: combined Justify; editable Direction, Line space distance
(back-solves the factor) and Line space style; Defined width/height
gated on column mode.
- MLeader: regrouped to match the palette - drop the Leader Structure
group (fold landing rows into Leaders), gate Text vs Block on content
type, full 9-value attachment label set, resolve handle-backed rows
(style, text style, arrowhead, linetype) to names.
- Leader: Lines & Arrows / Text / Fit are resolved from the dimension
style (arrowhead, arrow size, dim-line lineweight/colour, text gap,
vertical text pos, overall scale); Current Vertex navigator; combined
Type; Geometry group after General; no 3D-Visualisation group.
- General: rename "LT Scale" to "Linetype scale"; show ByLayer
transparency by name. Add shared num_prop helper for conditionally
editable numeric rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 18:01:44 +03:00
|
|
|
|
num_row("Position X", "ins_x", t.insertion_point.x, pos_editable),
|
|
|
|
|
|
num_row("Position Y", "ins_y", t.insertion_point.y, pos_editable),
|
|
|
|
|
|
num_row("Position Z", "ins_z", t.insertion_point.z, pos_editable),
|
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
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
PropSection {
|
|
|
|
|
|
title: "Misc".into(),
|
|
|
|
|
|
props: vec![
|
2026-07-03 08:06:00 +03:00
|
|
|
|
Property {
|
|
|
|
|
|
label: "Upside down".into(),
|
|
|
|
|
|
field: "upside_down",
|
|
|
|
|
|
value: PropValue::BoolToggle {
|
|
|
|
|
|
field: "upside_down",
|
|
|
|
|
|
value: t.generation_flags & 0x4 != 0,
|
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
|
|
|
|
},
|
2026-07-03 08:06:00 +03:00
|
|
|
|
},
|
|
|
|
|
|
Property {
|
|
|
|
|
|
label: "Backward".into(),
|
|
|
|
|
|
field: "backward",
|
|
|
|
|
|
value: PropValue::BoolToggle {
|
|
|
|
|
|
field: "backward",
|
|
|
|
|
|
value: t.generation_flags & 0x2 != 0,
|
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
|
|
|
|
},
|
2026-07-03 08:06:00 +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
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
2026-03-22 14:56:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn apply_geom_prop(t: &mut Text, field: &str, value: &str) {
|
|
|
|
|
|
match field {
|
|
|
|
|
|
"content" => {
|
|
|
|
|
|
t.value = value.to_string();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
"style" => {
|
|
|
|
|
|
t.style = value.to_string();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
feat(properties): rework Text/MText/MLeader/Leader panels to standard layout
Match the entity Properties panels to the standard CAD layout and
web-verified behaviour:
- Text: combined Justify (horizontal x vertical) dropdown; Position and
Text-alignment rows become editable/grayed by justification; drop the
Annotative-scale row.
- MText: combined Justify; editable Direction, Line space distance
(back-solves the factor) and Line space style; Defined width/height
gated on column mode.
- MLeader: regrouped to match the palette - drop the Leader Structure
group (fold landing rows into Leaders), gate Text vs Block on content
type, full 9-value attachment label set, resolve handle-backed rows
(style, text style, arrowhead, linetype) to names.
- Leader: Lines & Arrows / Text / Fit are resolved from the dimension
style (arrowhead, arrow size, dim-line lineweight/colour, text gap,
vertical text pos, overall scale); Current Vertex navigator; combined
Type; Geometry group after General; no 3D-Visualisation group.
- General: rename "LT Scale" to "Linetype scale"; show ByLayer
transparency by name. Add shared num_prop helper for conditionally
editable numeric rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 18:01:44 +03:00
|
|
|
|
"justify" => {
|
|
|
|
|
|
let (h, v) = match value {
|
|
|
|
|
|
"Left" => (HA::Left, VA::Baseline),
|
|
|
|
|
|
"Center" => (HA::Center, VA::Baseline),
|
|
|
|
|
|
"Right" => (HA::Right, VA::Baseline),
|
|
|
|
|
|
"Aligned" => (HA::Aligned, VA::Baseline),
|
|
|
|
|
|
"Middle" => (HA::Middle, VA::Baseline),
|
|
|
|
|
|
"Fit" => (HA::Fit, VA::Baseline),
|
|
|
|
|
|
"Top left" => (HA::Left, VA::Top),
|
|
|
|
|
|
"Top center" => (HA::Center, VA::Top),
|
|
|
|
|
|
"Top right" => (HA::Right, VA::Top),
|
|
|
|
|
|
"Middle left" => (HA::Left, VA::Middle),
|
|
|
|
|
|
"Middle center" => (HA::Center, VA::Middle),
|
|
|
|
|
|
"Middle right" => (HA::Right, VA::Middle),
|
|
|
|
|
|
"Bottom left" => (HA::Left, VA::Bottom),
|
|
|
|
|
|
"Bottom center" => (HA::Center, VA::Bottom),
|
|
|
|
|
|
"Bottom right" => (HA::Right, VA::Bottom),
|
2026-03-22 14:56:27 +03:00
|
|
|
|
_ => return,
|
|
|
|
|
|
};
|
feat(properties): rework Text/MText/MLeader/Leader panels to standard layout
Match the entity Properties panels to the standard CAD layout and
web-verified behaviour:
- Text: combined Justify (horizontal x vertical) dropdown; Position and
Text-alignment rows become editable/grayed by justification; drop the
Annotative-scale row.
- MText: combined Justify; editable Direction, Line space distance
(back-solves the factor) and Line space style; Defined width/height
gated on column mode.
- MLeader: regrouped to match the palette - drop the Leader Structure
group (fold landing rows into Leaders), gate Text vs Block on content
type, full 9-value attachment label set, resolve handle-backed rows
(style, text style, arrowhead, linetype) to names.
- Leader: Lines & Arrows / Text / Fit are resolved from the dimension
style (arrowhead, arrow size, dim-line lineweight/colour, text gap,
vertical text pos, overall scale); Current Vertex navigator; combined
Type; Geometry group after General; no 3D-Visualisation group.
- General: rename "LT Scale" to "Linetype scale"; show ByLayer
transparency by name. Add shared num_prop helper for conditionally
editable numeric rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 18:01:44 +03:00
|
|
|
|
t.horizontal_alignment = h;
|
|
|
|
|
|
t.vertical_alignment = v;
|
2026-03-22 14:56:27 +03:00
|
|
|
|
sync_text_alignment_point(t);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-07-03 08:06:00 +03:00
|
|
|
|
"upside_down" => {
|
|
|
|
|
|
let set = if value == "toggle" {
|
|
|
|
|
|
t.generation_flags & 0x4 == 0
|
|
|
|
|
|
} else {
|
|
|
|
|
|
value == "true"
|
|
|
|
|
|
};
|
|
|
|
|
|
if set {
|
|
|
|
|
|
t.generation_flags |= 0x4;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
t.generation_flags &= !0x4;
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
"backward" => {
|
|
|
|
|
|
let set = if value == "toggle" {
|
|
|
|
|
|
t.generation_flags & 0x2 == 0
|
|
|
|
|
|
} else {
|
|
|
|
|
|
value == "true"
|
|
|
|
|
|
};
|
|
|
|
|
|
if set {
|
|
|
|
|
|
t.generation_flags |= 0x2;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
t.generation_flags &= !0x2;
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-22 14:56:27 +03:00
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
let Some(v) = parse_f64(value) else {
|
|
|
|
|
|
return;
|
|
|
|
|
|
};
|
|
|
|
|
|
match field {
|
|
|
|
|
|
"ins_x" => t.insertion_point.x = v,
|
|
|
|
|
|
"ins_y" => t.insertion_point.y = v,
|
|
|
|
|
|
"ins_z" => t.insertion_point.z = v,
|
2026-07-03 08:06:00 +03:00
|
|
|
|
"align_x" | "align_y" | "align_z" => {
|
|
|
|
|
|
let ins = t.insertion_point;
|
|
|
|
|
|
let ap = t.alignment_point.get_or_insert(ins);
|
|
|
|
|
|
match field {
|
|
|
|
|
|
"align_x" => ap.x = v,
|
|
|
|
|
|
"align_y" => ap.y = v,
|
|
|
|
|
|
_ => ap.z = v,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-22 14:56:27 +03:00
|
|
|
|
"height" if v > 0.0 => t.height = v,
|
|
|
|
|
|
"rotation" => t.rotation = v.to_radians(),
|
|
|
|
|
|
"width_factor" if v > 0.0 => t.width_factor = v,
|
|
|
|
|
|
"oblique_angle" => t.oblique_angle = v.to_radians(),
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn apply_grip(t: &mut Text, _grip_id: usize, apply: GripApply) {
|
|
|
|
|
|
match apply {
|
|
|
|
|
|
GripApply::Absolute(p) => {
|
|
|
|
|
|
t.insertion_point.x = p.x as f64;
|
|
|
|
|
|
t.insertion_point.y = p.y as f64;
|
|
|
|
|
|
t.insertion_point.z = p.z as f64;
|
|
|
|
|
|
}
|
|
|
|
|
|
GripApply::Translate(d) => {
|
|
|
|
|
|
t.insertion_point.x += d.x as f64;
|
|
|
|
|
|
t.insertion_point.y += d.y as f64;
|
|
|
|
|
|
t.insertion_point.z += d.z as f64;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn apply_transform(t: &mut Text, tr: &EntityTransform) {
|
2026-06-16 11:33:46 +03:00
|
|
|
|
crate::scene::view::transform::apply_standard_entity_transform(t, tr, |entity, p1, p2| {
|
|
|
|
|
|
crate::scene::view::transform::reflect_xy_point(
|
2026-03-22 14:56:27 +03:00
|
|
|
|
&mut entity.insertion_point.x,
|
|
|
|
|
|
&mut entity.insertion_point.y,
|
|
|
|
|
|
p1,
|
|
|
|
|
|
p2,
|
|
|
|
|
|
);
|
|
|
|
|
|
if let Some(ref mut a) = entity.alignment_point {
|
2026-06-16 11:33:46 +03:00
|
|
|
|
crate::scene::view::transform::reflect_xy_point(&mut a.x, &mut a.y, p1, p2);
|
2026-03-22 14:56:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
let dx = (p2.x - p1.x) as f64;
|
|
|
|
|
|
let dy = (p2.y - p1.y) as f64;
|
|
|
|
|
|
let line_angle = dy.atan2(dx);
|
|
|
|
|
|
entity.rotation = 2.0 * line_angle - entity.rotation;
|
|
|
|
|
|
entity.oblique_angle = -entity.oblique_angle;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl TruckConvertible for Text {
|
|
|
|
|
|
fn to_truck(&self, document: &acadrust::CadDocument) -> Option<TruckEntity> {
|
|
|
|
|
|
Some(to_truck(self, document))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Grippable for Text {
|
|
|
|
|
|
fn grips(&self) -> Vec<GripDef> {
|
|
|
|
|
|
grips(self)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
|
|
|
|
|
apply_grip(self, grip_id, apply);
|
|
|
|
|
|
}
|
feat(grip): popup menus for polylines, spline, mline, hatch, leaders, text
Vocabulary added: AddFitPoint, RemoveFitPoint, Refit, RefineVertices,
TangentDirection, MoveWithText, StackText, UnstackText.
Polyline/Polyline2D/Polyline3D: vertex Add/Remove. MLine: same.
Spline: CV Add/Remove plus Refine Vertices (chord-midpoint insertion,
weights kept in sync, knots cleared so to_truck rebuilds uniform vec).
Hatch: centroid Origin/Angle/Scale (Angle and Scale prompt for value).
Leader: vertex Add/Remove (arrow + centroid stay Stretch-only).
MultiLeader: vertex Add/Remove Leader; MText location Move-with-Leader /
Move-Independent (menu live, structural edits stubbed). Text/MText:
Rotate prompts for degrees and sets rotation.
Spline, Leader, MultiLeader switched from impl_entity_basics! to manual
trait impls so grip_menu can be overridden.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 15:14:43 +03:00
|
|
|
|
|
2026-06-16 11:33:46 +03:00
|
|
|
|
fn grip_menu(&self, _grip_id: usize) -> Vec<crate::scene::model::object::GripMenuItem> {
|
|
|
|
|
|
use crate::scene::model::object::{GripMenuAction, GripMenuItem};
|
feat(grip): popup menus for polylines, spline, mline, hatch, leaders, text
Vocabulary added: AddFitPoint, RemoveFitPoint, Refit, RefineVertices,
TangentDirection, MoveWithText, StackText, UnstackText.
Polyline/Polyline2D/Polyline3D: vertex Add/Remove. MLine: same.
Spline: CV Add/Remove plus Refine Vertices (chord-midpoint insertion,
weights kept in sync, knots cleared so to_truck rebuilds uniform vec).
Hatch: centroid Origin/Angle/Scale (Angle and Scale prompt for value).
Leader: vertex Add/Remove (arrow + centroid stay Stretch-only).
MultiLeader: vertex Add/Remove Leader; MText location Move-with-Leader /
Move-Independent (menu live, structural edits stubbed). Text/MText:
Rotate prompts for degrees and sets rotation.
Spline, Leader, MultiLeader switched from impl_entity_basics! to manual
trait impls so grip_menu can be overridden.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 15:14:43 +03:00
|
|
|
|
vec![
|
2026-06-11 00:04:37 +03:00
|
|
|
|
GripMenuItem {
|
|
|
|
|
|
label: "Stretch",
|
|
|
|
|
|
action: GripMenuAction::Stretch,
|
|
|
|
|
|
},
|
|
|
|
|
|
GripMenuItem {
|
|
|
|
|
|
label: "Move with Text",
|
|
|
|
|
|
action: GripMenuAction::MoveWithText,
|
|
|
|
|
|
},
|
|
|
|
|
|
GripMenuItem {
|
|
|
|
|
|
label: "Rotate",
|
|
|
|
|
|
action: GripMenuAction::RotateText,
|
|
|
|
|
|
},
|
feat(grip): popup menus for polylines, spline, mline, hatch, leaders, text
Vocabulary added: AddFitPoint, RemoveFitPoint, Refit, RefineVertices,
TangentDirection, MoveWithText, StackText, UnstackText.
Polyline/Polyline2D/Polyline3D: vertex Add/Remove. MLine: same.
Spline: CV Add/Remove plus Refine Vertices (chord-midpoint insertion,
weights kept in sync, knots cleared so to_truck rebuilds uniform vec).
Hatch: centroid Origin/Angle/Scale (Angle and Scale prompt for value).
Leader: vertex Add/Remove (arrow + centroid stay Stretch-only).
MultiLeader: vertex Add/Remove Leader; MText location Move-with-Leader /
Move-Independent (menu live, structural edits stubbed). Text/MText:
Rotate prompts for degrees and sets rotation.
Spline, Leader, MultiLeader switched from impl_entity_basics! to manual
trait impls so grip_menu can be overridden.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 15:14:43 +03:00
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-16 11:33:46 +03:00
|
|
|
|
fn apply_grip_menu(&mut self, _grip_id: usize, _action: crate::scene::model::object::GripMenuAction) {
|
feat(grip): popup menus for polylines, spline, mline, hatch, leaders, text
Vocabulary added: AddFitPoint, RemoveFitPoint, Refit, RefineVertices,
TangentDirection, MoveWithText, StackText, UnstackText.
Polyline/Polyline2D/Polyline3D: vertex Add/Remove. MLine: same.
Spline: CV Add/Remove plus Refine Vertices (chord-midpoint insertion,
weights kept in sync, knots cleared so to_truck rebuilds uniform vec).
Hatch: centroid Origin/Angle/Scale (Angle and Scale prompt for value).
Leader: vertex Add/Remove (arrow + centroid stay Stretch-only).
MultiLeader: vertex Add/Remove Leader; MText location Move-with-Leader /
Move-Independent (menu live, structural edits stubbed). Text/MText:
Rotate prompts for degrees and sets rotation.
Spline, Leader, MultiLeader switched from impl_entity_basics! to manual
trait impls so grip_menu can be overridden.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 15:14:43 +03:00
|
|
|
|
// Move-with-Text falls through to Stretch (single grip moves
|
|
|
|
|
|
// the whole text); Rotate needs a follow-up angle handled by
|
|
|
|
|
|
// `apply_grip_menu_value`.
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn grip_menu_value_prompt(
|
|
|
|
|
|
&self,
|
|
|
|
|
|
_grip_id: usize,
|
2026-06-16 11:33:46 +03:00
|
|
|
|
action: crate::scene::model::object::GripMenuAction,
|
feat(grip): popup menus for polylines, spline, mline, hatch, leaders, text
Vocabulary added: AddFitPoint, RemoveFitPoint, Refit, RefineVertices,
TangentDirection, MoveWithText, StackText, UnstackText.
Polyline/Polyline2D/Polyline3D: vertex Add/Remove. MLine: same.
Spline: CV Add/Remove plus Refine Vertices (chord-midpoint insertion,
weights kept in sync, knots cleared so to_truck rebuilds uniform vec).
Hatch: centroid Origin/Angle/Scale (Angle and Scale prompt for value).
Leader: vertex Add/Remove (arrow + centroid stay Stretch-only).
MultiLeader: vertex Add/Remove Leader; MText location Move-with-Leader /
Move-Independent (menu live, structural edits stubbed). Text/MText:
Rotate prompts for degrees and sets rotation.
Spline, Leader, MultiLeader switched from impl_entity_basics! to manual
trait impls so grip_menu can be overridden.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 15:14:43 +03:00
|
|
|
|
) -> Option<&'static str> {
|
2026-06-16 11:33:46 +03:00
|
|
|
|
use crate::scene::model::object::GripMenuAction as A;
|
feat(grip): popup menus for polylines, spline, mline, hatch, leaders, text
Vocabulary added: AddFitPoint, RemoveFitPoint, Refit, RefineVertices,
TangentDirection, MoveWithText, StackText, UnstackText.
Polyline/Polyline2D/Polyline3D: vertex Add/Remove. MLine: same.
Spline: CV Add/Remove plus Refine Vertices (chord-midpoint insertion,
weights kept in sync, knots cleared so to_truck rebuilds uniform vec).
Hatch: centroid Origin/Angle/Scale (Angle and Scale prompt for value).
Leader: vertex Add/Remove (arrow + centroid stay Stretch-only).
MultiLeader: vertex Add/Remove Leader; MText location Move-with-Leader /
Move-Independent (menu live, structural edits stubbed). Text/MText:
Rotate prompts for degrees and sets rotation.
Spline, Leader, MultiLeader switched from impl_entity_basics! to manual
trait impls so grip_menu can be overridden.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 15:14:43 +03:00
|
|
|
|
match action {
|
|
|
|
|
|
A::RotateText => Some("Rotation (deg)"),
|
|
|
|
|
|
_ => None,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn apply_grip_menu_value(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
_grip_id: usize,
|
2026-06-16 11:33:46 +03:00
|
|
|
|
action: crate::scene::model::object::GripMenuAction,
|
feat(grip): popup menus for polylines, spline, mline, hatch, leaders, text
Vocabulary added: AddFitPoint, RemoveFitPoint, Refit, RefineVertices,
TangentDirection, MoveWithText, StackText, UnstackText.
Polyline/Polyline2D/Polyline3D: vertex Add/Remove. MLine: same.
Spline: CV Add/Remove plus Refine Vertices (chord-midpoint insertion,
weights kept in sync, knots cleared so to_truck rebuilds uniform vec).
Hatch: centroid Origin/Angle/Scale (Angle and Scale prompt for value).
Leader: vertex Add/Remove (arrow + centroid stay Stretch-only).
MultiLeader: vertex Add/Remove Leader; MText location Move-with-Leader /
Move-Independent (menu live, structural edits stubbed). Text/MText:
Rotate prompts for degrees and sets rotation.
Spline, Leader, MultiLeader switched from impl_entity_basics! to manual
trait impls so grip_menu can be overridden.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 15:14:43 +03:00
|
|
|
|
value: f64,
|
|
|
|
|
|
) {
|
2026-06-16 11:33:46 +03:00
|
|
|
|
use crate::scene::model::object::GripMenuAction as A;
|
feat(grip): popup menus for polylines, spline, mline, hatch, leaders, text
Vocabulary added: AddFitPoint, RemoveFitPoint, Refit, RefineVertices,
TangentDirection, MoveWithText, StackText, UnstackText.
Polyline/Polyline2D/Polyline3D: vertex Add/Remove. MLine: same.
Spline: CV Add/Remove plus Refine Vertices (chord-midpoint insertion,
weights kept in sync, knots cleared so to_truck rebuilds uniform vec).
Hatch: centroid Origin/Angle/Scale (Angle and Scale prompt for value).
Leader: vertex Add/Remove (arrow + centroid stay Stretch-only).
MultiLeader: vertex Add/Remove Leader; MText location Move-with-Leader /
Move-Independent (menu live, structural edits stubbed). Text/MText:
Rotate prompts for degrees and sets rotation.
Spline, Leader, MultiLeader switched from impl_entity_basics! to manual
trait impls so grip_menu can be overridden.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 15:14:43 +03:00
|
|
|
|
if matches!(action, A::RotateText) {
|
|
|
|
|
|
self.rotation = value.to_radians();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-22 14:56:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl PropertyEditable for Text {
|
2026-07-02 22:46:19 +03:00
|
|
|
|
fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection> {
|
2026-03-22 14:56:27 +03:00
|
|
|
|
properties(self, text_style_names)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
|
|
|
|
|
apply_geom_prop(self, field, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Transformable for Text {
|
|
|
|
|
|
fn apply_transform(&mut self, t: &EntityTransform) {
|
|
|
|
|
|
apply_transform(self, t);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
refactor(commands): per-entity MassProps + TextContent traits
Two free helpers in app/commands.rs lived as big match-on-EntityType
blocks: massprop_entity (Circle/Arc/Line/LwPolyline/Ellipse area +
perimeter + centroid, ~140 lines) and entity_text_content /
replace_entity_text (Text/MText/AttributeDefinition/AttributeEntity).
Move each to its own trait in entities/traits.rs and implement
per-entity:
MassPropsCalc — Circle, Arc, Line, LwPolyline, Ellipse
TextContent — Text, MText, AttributeDefinition, AttributeEntity
EntityTypeOps gets dispatch shims that route to the trait via the
existing macro, so callsites just call entity.mass_props() /
entity.text_content() / entity.replace_text(search, rep).
The remaining free helpers in commands.rs (entity_list_details,
entity_extra_info, entity_type_name, build_data_extraction_csv) are
single-command formatting helpers tightly coupled to LIST / EXTRACT /
text-format output. Distributing them as per-entity methods would
inflate every entity file with command-side formatting concerns
without making the command-side any cleaner — left in place.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 23:30:34 +03:00
|
|
|
|
|
|
|
|
|
|
impl crate::entities::traits::TextContent for acadrust::entities::Text {
|
|
|
|
|
|
fn text_content(&self) -> Option<String> {
|
|
|
|
|
|
Some(self.value.clone())
|
|
|
|
|
|
}
|
|
|
|
|
|
fn replace_text(&mut self, search: &str, rep: &str) {
|
|
|
|
|
|
let search_lc = search.to_lowercase();
|
|
|
|
|
|
if self.value.to_lowercase().contains(&search_lc) {
|
|
|
|
|
|
self.value = self.value.replace(search, rep);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|