2026-03-22 14:56:27 +03:00
|
|
|
use acadrust::{CadDocument, EntityType};
|
|
|
|
|
|
|
|
|
|
use crate::command::EntityTransform;
|
refactor: drop truck, and evaluate every curve through the kernel
The last four truck crates are gone from the manifest. What they were doing
falls into three parts, and each now has one answer instead of two.
NURBS evaluation. truck was carrying B-spline curves and surfaces for the
SPLINE entity, hatch boundaries, BLEND's endpoint frames, the spline preview
and ACIS spline-surface faces. The kernel now has both, over one de Boor
written across however many coordinates a control point holds — so a plane
curve and a space curve cannot drift apart, and a surface is the same
algorithm applied twice. The rational and the polynomial cases stop being
separate types: weights absent means polynomial.
The entity conversion. Every LINE, ARC, ELLIPSE, SPLINE and polyline was
built into truck topology purely so it could be sampled back into points.
They are sampled through `entities::curve` now, which is where each one's
geometry is already defined once and what EXTRUDE and REVOLVE read — so a
circle drawn on screen and a circle handed to the Model tab come from the
same definition. That retires four of TruckObject's variants and the module
is renamed for what it does.
SWEEP and LOFT. Both only ever produced a mesh, so both are built from point
lists: a band of quads per span, and a lid where a profile closes. Lofting
profiles of different densities resamples them by distance rather than by
index, so a circle lofted to a square no longer twists.
Two things worth noting for anyone reading the old comments. The tolerance
globals that lived in the tessellation module were never about truck at all —
they are the per-frame chord height, and they move to `curve_tol`. And the
rule that a profile handed over as `Lines` silently broke EXTRUDE and REVOLVE
no longer holds: those read the curve directly and never look at this channel.
The two `automation` test failures are unchanged from before this and are not
caused by it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 01:08:50 +03:00
|
|
|
use crate::scene::convert::acad_to_render::RenderEntity;
|
2026-06-16 11:33:46 +03:00
|
|
|
use crate::scene::model::object::{
|
feat(grip): multi-functional hover popup menu
Phase 2 of the grip overhaul: when the cursor dwells on a selected
entity's grip the overlay opens a popup menu with entity-specific
options (Stretch / Add Vertex / Remove Vertex / Convert to
Arc-Line / …). Picking an item dispatches `apply_grip_menu`; the
default `Stretch` entry is a no-op that lets the user click the
grip to start the normal drag.
- `GripMenuItem` + `GripMenuAction` (full vocabulary covering
stretch, add/remove vertex, convert to arc/line, reverse arrows,
text-position variants, hatch parameters, tangent direction).
- `Grippable` trait gains `grip_menu` + `apply_grip_menu` with
trait-default `Stretch`-only impls so every existing entity gets
the popup for free.
- `EntityTypeOps` dispatches both methods across the same entity
list as the existing grip / property machinery.
- `OpenCADStudio` carries `grip_hover` (dwell tracker) +
`grip_popup` (open menu). `update_grip_hover` runs from
`ViewportMove`: hit-tests grips, refreshes the dwell timer, and
opens the popup after 600 ms. Cursor drift past 80 px or a grip
click dismisses it.
- `view.rs` paints the popup as an absolute-positioned column of
buttons at the grip's screen anchor; each button publishes
`Message::GripMenuPick(idx)`.
- `LwPolyline` overrides `Grippable` manually (skips the standard
macro) so its vertex grip exposes Add/Remove Vertex and its
segment-midpoint grip exposes Add Vertex + Convert to Arc/Line.
`apply_grip_menu` performs the edits: vertex insertion at the
chord midpoint inheriting the previous bulge, vertex removal,
and bulge toggle for the arc/line conversion.
2026-05-29 09:30:54 +03:00
|
|
|
GripApply, GripDef, GripMenuAction, GripMenuItem, PropSection,
|
|
|
|
|
};
|
2026-06-16 11:33:46 +03:00
|
|
|
use crate::scene::convert::tess_util::FallbackGeometry;
|
2026-03-22 14:56:27 +03:00
|
|
|
|
refactor: drop truck, and evaluate every curve through the kernel
The last four truck crates are gone from the manifest. What they were doing
falls into three parts, and each now has one answer instead of two.
NURBS evaluation. truck was carrying B-spline curves and surfaces for the
SPLINE entity, hatch boundaries, BLEND's endpoint frames, the spline preview
and ACIS spline-surface faces. The kernel now has both, over one de Boor
written across however many coordinates a control point holds — so a plane
curve and a space curve cannot drift apart, and a surface is the same
algorithm applied twice. The rational and the polynomial cases stop being
separate types: weights absent means polynomial.
The entity conversion. Every LINE, ARC, ELLIPSE, SPLINE and polyline was
built into truck topology purely so it could be sampled back into points.
They are sampled through `entities::curve` now, which is where each one's
geometry is already defined once and what EXTRUDE and REVOLVE read — so a
circle drawn on screen and a circle handed to the Model tab come from the
same definition. That retires four of TruckObject's variants and the module
is renamed for what it does.
SWEEP and LOFT. Both only ever produced a mesh, so both are built from point
lists: a band of quads per span, and a lid where a profile closes. Lofting
profiles of different densities resamples them by distance rather than by
index, so a circle lofted to a square no longer twists.
Two things worth noting for anyone reading the old comments. The tolerance
globals that lived in the tessellation module were never about truck at all —
they are the per-frame chord height, and they move to `curve_tol`. And the
rule that a profile handed over as `Lines` silently broke EXTRUDE and REVOLVE
no longer holds: those read the curve directly and never look at this channel.
The two `automation` test failures are unchanged from before this and are not
caused by it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 01:08:50 +03:00
|
|
|
pub trait RenderConvertible {
|
|
|
|
|
fn to_render(&self, document: &CadDocument) -> Option<RenderEntity>;
|
2026-03-22 14:56:27 +03:00
|
|
|
}
|
|
|
|
|
|
refactor: drop truck, and evaluate every curve through the kernel
The last four truck crates are gone from the manifest. What they were doing
falls into three parts, and each now has one answer instead of two.
NURBS evaluation. truck was carrying B-spline curves and surfaces for the
SPLINE entity, hatch boundaries, BLEND's endpoint frames, the spline preview
and ACIS spline-surface faces. The kernel now has both, over one de Boor
written across however many coordinates a control point holds — so a plane
curve and a space curve cannot drift apart, and a surface is the same
algorithm applied twice. The rational and the polynomial cases stop being
separate types: weights absent means polynomial.
The entity conversion. Every LINE, ARC, ELLIPSE, SPLINE and polyline was
built into truck topology purely so it could be sampled back into points.
They are sampled through `entities::curve` now, which is where each one's
geometry is already defined once and what EXTRUDE and REVOLVE read — so a
circle drawn on screen and a circle handed to the Model tab come from the
same definition. That retires four of TruckObject's variants and the module
is renamed for what it does.
SWEEP and LOFT. Both only ever produced a mesh, so both are built from point
lists: a band of quads per span, and a lid where a profile closes. Lofting
profiles of different densities resamples them by distance rather than by
index, so a circle lofted to a square no longer twists.
Two things worth noting for anyone reading the old comments. The tolerance
globals that lived in the tessellation module were never about truck at all —
they are the per-frame chord height, and they move to `curve_tol`. And the
rule that a profile handed over as `Lines` silently broke EXTRUDE and REVOLVE
no longer holds: those read the curve directly and never look at this channel.
The two `automation` test failures are unchanged from before this and are not
caused by it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 01:08:50 +03:00
|
|
|
/// Fallback geometry for entities not routed through the render
|
refactor: remove dead world_offset plumbing (RTE migration is complete)
Now that geometry reaches the GPU/CPU as absolute coordinates via the
double-single relative-to-eye path, world_offset is always [0,0,0]. Strip
the parameter that was threaded through the whole tessellation / block-
expansion / fallback / camera-decode chain and the ExpandCtx field that
carried it — ~340 references across 20 files, all subtracting zero.
- truck_tess: to_local / to_local_low / tessellate_* drop the offset arg;
to_local is now a pure double-single split.
- tessellate: offset_to_ds → points_to_ds (pure DS split); tessellate(),
tessellate_entity(), fallback_geometry(), solid_wire_fallback(),
entity_aabb(), expand_insert(), expand_block_meshes(), the dimension
helpers (vec3_local, dimension_snap_pts, …), text_support, leader,
multileader, image_model, xclip and camera_from_view all lose the param.
- block_cache: ExpandCtx loses its world_offset field.
- Remove the now-dead offset_snap_pts and the unused set_grid_snap.
Behaviour is unchanged (the offset was zero everywhere); the compiler
verifies every call site.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:35:02 +03:00
|
|
|
/// pipeline (Viewport, Insert, Hatch outline, Ole2Frame). Returns absolute
|
|
|
|
|
/// WCS `f32` points + snap/key vertices the dispatcher wraps into a
|
|
|
|
|
/// `WireModel`.
|
2026-05-23 23:16:57 +03:00
|
|
|
pub trait FallbackTess {
|
refactor: remove dead world_offset plumbing (RTE migration is complete)
Now that geometry reaches the GPU/CPU as absolute coordinates via the
double-single relative-to-eye path, world_offset is always [0,0,0]. Strip
the parameter that was threaded through the whole tessellation / block-
expansion / fallback / camera-decode chain and the ExpandCtx field that
carried it — ~340 references across 20 files, all subtracting zero.
- truck_tess: to_local / to_local_low / tessellate_* drop the offset arg;
to_local is now a pure double-single split.
- tessellate: offset_to_ds → points_to_ds (pure DS split); tessellate(),
tessellate_entity(), fallback_geometry(), solid_wire_fallback(),
entity_aabb(), expand_insert(), expand_block_meshes(), the dimension
helpers (vec3_local, dimension_snap_pts, …), text_support, leader,
multileader, image_model, xclip and camera_from_view all lose the param.
- block_cache: ExpandCtx loses its world_offset field.
- Remove the now-dead offset_snap_pts and the unused set_grid_snap.
Behaviour is unchanged (the offset was zero everywhere); the compiler
verifies every call site.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:35:02 +03:00
|
|
|
fn fallback_geometry(&self) -> FallbackGeometry;
|
refactor(tess): move per-entity tessellation into entities/
Each entity's tessellation now lives alongside its definition in
entities/*.rs, attached to the entity type via a local trait:
LegacyTess — Viewport / Insert / Hatch / Ole2Frame
(fallback path for entities not on the truck pipeline)
LeaderTess — Leader
MultiLeaderTess — MultiLeader
DimensionTess — Dimension (carries its dim-only helpers too:
~1700 lines of dim_geometry, dim_text_*, format_*,
tolerance, suppression, etc.)
scene/tessellate.rs keeps only the cross-entity dispatcher plus the
shared dim machinery reused by leader/multileader/dimension
(ArrowKind, DimGeom, append_arrow, arrow_from_block, add_segment,
add_polyline, normalized_or, push_tri, color_or_inherit, entity_z,
offset_snap_pts). Cross-entity arc helpers + aci_to_rgba moved to
scene/tess_util.rs.
scene/tessellate.rs shrinks from ~3300 to 809 lines. Compile clean,
all 9 tests pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 23:12:27 +03:00
|
|
|
}
|
|
|
|
|
|
2026-03-22 14:56:27 +03:00
|
|
|
pub trait Grippable {
|
|
|
|
|
fn grips(&self) -> Vec<GripDef>;
|
|
|
|
|
fn apply_grip(&mut self, grip_id: usize, apply: GripApply);
|
feat(grip): multi-functional hover popup menu
Phase 2 of the grip overhaul: when the cursor dwells on a selected
entity's grip the overlay opens a popup menu with entity-specific
options (Stretch / Add Vertex / Remove Vertex / Convert to
Arc-Line / …). Picking an item dispatches `apply_grip_menu`; the
default `Stretch` entry is a no-op that lets the user click the
grip to start the normal drag.
- `GripMenuItem` + `GripMenuAction` (full vocabulary covering
stretch, add/remove vertex, convert to arc/line, reverse arrows,
text-position variants, hatch parameters, tangent direction).
- `Grippable` trait gains `grip_menu` + `apply_grip_menu` with
trait-default `Stretch`-only impls so every existing entity gets
the popup for free.
- `EntityTypeOps` dispatches both methods across the same entity
list as the existing grip / property machinery.
- `OpenCADStudio` carries `grip_hover` (dwell tracker) +
`grip_popup` (open menu). `update_grip_hover` runs from
`ViewportMove`: hit-tests grips, refreshes the dwell timer, and
opens the popup after 600 ms. Cursor drift past 80 px or a grip
click dismisses it.
- `view.rs` paints the popup as an absolute-positioned column of
buttons at the grip's screen anchor; each button publishes
`Message::GripMenuPick(idx)`.
- `LwPolyline` overrides `Grippable` manually (skips the standard
macro) so its vertex grip exposes Add/Remove Vertex and its
segment-midpoint grip exposes Add Vertex + Convert to Arc/Line.
`apply_grip_menu` performs the edits: vertex insertion at the
chord midpoint inheriting the previous bulge, vertex removal,
and bulge toggle for the arc/line conversion.
2026-05-29 09:30:54 +03:00
|
|
|
/// Items shown in the hover popup that opens when the cursor
|
|
|
|
|
/// dwells on a grip. Default is a single `Stretch` entry — i.e.
|
|
|
|
|
/// the popup just confirms the default drag behaviour. Override
|
|
|
|
|
/// per entity to add Add Vertex / Convert to Arc / Reverse Arrows /
|
|
|
|
|
/// etc.
|
|
|
|
|
fn grip_menu(&self, _grip_id: usize) -> Vec<GripMenuItem> {
|
|
|
|
|
vec![GripMenuItem { label: "Stretch", action: GripMenuAction::Stretch }]
|
|
|
|
|
}
|
|
|
|
|
/// React to a popup-menu commit. Default no-op — `Stretch` is the
|
|
|
|
|
/// "do nothing extra" path, the normal drag still happens on click.
|
|
|
|
|
/// Entities override this to mutate themselves when the user picks
|
|
|
|
|
/// Add Vertex / Remove Vertex / Convert / Reverse / …
|
|
|
|
|
fn apply_grip_menu(&mut self, _grip_id: usize, _action: GripMenuAction) {}
|
2026-05-29 14:58:35 +03:00
|
|
|
/// Same as `apply_grip_menu` but for actions that need a numeric
|
|
|
|
|
/// follow-up (Lengthen / Radius / Arc Length / Rotate Text / …).
|
|
|
|
|
/// The app prompts the user for a value on the command line and
|
|
|
|
|
/// calls this with the parsed `f64`.
|
|
|
|
|
fn apply_grip_menu_value(
|
|
|
|
|
&mut self,
|
|
|
|
|
_grip_id: usize,
|
|
|
|
|
_action: GripMenuAction,
|
|
|
|
|
_value: f64,
|
|
|
|
|
) {}
|
|
|
|
|
/// If the popup action this entity defines for `grip_id` needs a
|
|
|
|
|
/// numeric prompt, return the prompt string and the action so the
|
|
|
|
|
/// app can stash a pending-value state and route the next typed
|
|
|
|
|
/// number into `apply_grip_menu_value`. `None` for one-shot
|
|
|
|
|
/// actions handled by `apply_grip_menu`.
|
|
|
|
|
fn grip_menu_value_prompt(
|
|
|
|
|
&self,
|
|
|
|
|
_grip_id: usize,
|
|
|
|
|
_action: GripMenuAction,
|
|
|
|
|
) -> Option<&'static str> {
|
|
|
|
|
None
|
|
|
|
|
}
|
2026-08-04 16:27:03 +03:00
|
|
|
/// Convert an interactive cursor point into the numeric value consumed by
|
|
|
|
|
/// `apply_grip_menu_value`. Actions without a point-driven form return
|
|
|
|
|
/// `None`.
|
|
|
|
|
fn grip_menu_point_value(
|
|
|
|
|
&self,
|
|
|
|
|
_grip_id: usize,
|
|
|
|
|
_action: GripMenuAction,
|
|
|
|
|
_point: glam::DVec3,
|
|
|
|
|
) -> Option<f64> {
|
|
|
|
|
None
|
|
|
|
|
}
|
2026-03-22 14:56:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub trait PropertyEditable {
|
2026-07-02 22:46:19 +03:00
|
|
|
/// Type-specific property groups (Geometry, Misc, Text, …) shown after the
|
|
|
|
|
/// common General group. May be several groups; empty means none.
|
|
|
|
|
fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection>;
|
2026-03-22 14:56:27 +03:00
|
|
|
fn apply_geom_prop(&mut self, field: &str, value: &str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub trait Transformable {
|
|
|
|
|
fn apply_transform(&mut self, t: &EntityTransform);
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
/// Inquiry-time mass / area / perimeter properties for entities whose
|
|
|
|
|
/// 2D footprint has a meaningful area or perimeter (Circle, Arc, Line,
|
|
|
|
|
/// LwPolyline, Ellipse). Entities outside this set get `None` via the
|
|
|
|
|
/// dispatcher.
|
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
|
pub struct MassProps {
|
|
|
|
|
pub area: f64,
|
|
|
|
|
pub perimeter: f64,
|
|
|
|
|
pub cx: f64,
|
|
|
|
|
pub cy: f64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub trait MassPropsCalc {
|
|
|
|
|
fn mass_props(&self) -> MassProps;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Read / replace the visible string of text-like entities (Text, MText,
|
|
|
|
|
/// AttributeDefinition, AttributeEntity). `replace` rewrites every
|
|
|
|
|
/// occurrence of `search` with `rep`.
|
|
|
|
|
pub trait TextContent {
|
|
|
|
|
fn text_content(&self) -> Option<String>;
|
|
|
|
|
fn replace_text(&mut self, search: &str, rep: &str);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-29 20:30:56 +03:00
|
|
|
/// Human-readable variant name used by Quick Select / Select Similar
|
|
|
|
|
/// filtering. Stable across releases — the strings here are the canonical
|
|
|
|
|
/// type identifiers throughout the UI.
|
2026-07-26 19:30:43 +03:00
|
|
|
pub fn entity_type_name(et: &EntityType) -> &str {
|
2026-05-29 20:30:56 +03:00
|
|
|
match et {
|
|
|
|
|
EntityType::Point(_) => "Point",
|
|
|
|
|
EntityType::Line(_) => "Line",
|
|
|
|
|
EntityType::Circle(_) => "Circle",
|
|
|
|
|
EntityType::Arc(_) => "Arc",
|
|
|
|
|
EntityType::Ellipse(_) => "Ellipse",
|
|
|
|
|
EntityType::Polyline(_) => "Polyline",
|
|
|
|
|
EntityType::Polyline2D(_) => "Polyline2D",
|
|
|
|
|
EntityType::Polyline3D(_) => "Polyline3D",
|
|
|
|
|
EntityType::LwPolyline(_) => "LwPolyline",
|
|
|
|
|
EntityType::Text(_) => "Text",
|
|
|
|
|
EntityType::MText(_) => "MText",
|
|
|
|
|
EntityType::Spline(_) => "Spline",
|
2026-07-02 23:28:37 +03:00
|
|
|
EntityType::Helix(_) => "Helix",
|
2026-05-29 20:30:56 +03:00
|
|
|
EntityType::Dimension(_) => "Dimension",
|
|
|
|
|
EntityType::Hatch(_) => "Hatch",
|
|
|
|
|
EntityType::Solid(_) => "Solid",
|
|
|
|
|
EntityType::Face3D(_) => "3DFace",
|
|
|
|
|
EntityType::Insert(_) => "Insert",
|
|
|
|
|
EntityType::Block(_) => "Block",
|
|
|
|
|
EntityType::BlockEnd(_) => "BlockEnd",
|
|
|
|
|
EntityType::Ray(_) => "Ray",
|
|
|
|
|
EntityType::XLine(_) => "XLine",
|
|
|
|
|
EntityType::Viewport(_) => "Viewport",
|
|
|
|
|
EntityType::AttributeDefinition(_) => "AttributeDefinition",
|
|
|
|
|
EntityType::AttributeEntity(_) => "Attribute",
|
|
|
|
|
EntityType::Leader(_) => "Leader",
|
|
|
|
|
EntityType::MultiLeader(_) => "MultiLeader",
|
|
|
|
|
EntityType::MLine(_) => "MLine",
|
|
|
|
|
EntityType::Mesh(_) => "Mesh",
|
|
|
|
|
EntityType::RasterImage(_) => "RasterImage",
|
|
|
|
|
EntityType::Solid3D(_) => "Solid3D",
|
|
|
|
|
EntityType::Region(_) => "Region",
|
|
|
|
|
EntityType::Body(_) => "Body",
|
2026-06-14 20:11:44 +03:00
|
|
|
EntityType::Surface(_) => "Surface",
|
2026-05-29 20:30:56 +03:00
|
|
|
EntityType::Table(_) => "Table",
|
|
|
|
|
EntityType::Tolerance(_) => "Tolerance",
|
|
|
|
|
EntityType::PolyfaceMesh(_) => "PolyfaceMesh",
|
|
|
|
|
EntityType::Wipeout(_) => "Wipeout",
|
|
|
|
|
EntityType::Shape(_) => "Shape",
|
|
|
|
|
EntityType::Underlay(_) => "Underlay",
|
|
|
|
|
EntityType::Seqend(_) => "Seqend",
|
|
|
|
|
EntityType::Ole2Frame(_) => "Ole2Frame",
|
|
|
|
|
EntityType::PolygonMesh(_) => "PolygonMesh",
|
2026-07-19 22:15:40 +03:00
|
|
|
EntityType::Light(_) => "Light",
|
2026-07-21 00:19:51 +03:00
|
|
|
EntityType::SectionSymbol(_) => "SectionSymbol",
|
|
|
|
|
EntityType::ViewBorder(_) => "ViewBorder",
|
2026-07-26 19:30:43 +03:00
|
|
|
EntityType::Extended(entity) => entity.class_name(),
|
2026-05-29 20:30:56 +03:00
|
|
|
EntityType::Unknown(_) => "Unknown",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-22 14:56:27 +03:00
|
|
|
pub trait EntityTypeOps {
|
refactor: drop truck, and evaluate every curve through the kernel
The last four truck crates are gone from the manifest. What they were doing
falls into three parts, and each now has one answer instead of two.
NURBS evaluation. truck was carrying B-spline curves and surfaces for the
SPLINE entity, hatch boundaries, BLEND's endpoint frames, the spline preview
and ACIS spline-surface faces. The kernel now has both, over one de Boor
written across however many coordinates a control point holds — so a plane
curve and a space curve cannot drift apart, and a surface is the same
algorithm applied twice. The rational and the polynomial cases stop being
separate types: weights absent means polynomial.
The entity conversion. Every LINE, ARC, ELLIPSE, SPLINE and polyline was
built into truck topology purely so it could be sampled back into points.
They are sampled through `entities::curve` now, which is where each one's
geometry is already defined once and what EXTRUDE and REVOLVE read — so a
circle drawn on screen and a circle handed to the Model tab come from the
same definition. That retires four of TruckObject's variants and the module
is renamed for what it does.
SWEEP and LOFT. Both only ever produced a mesh, so both are built from point
lists: a band of quads per span, and a lid where a profile closes. Lofting
profiles of different densities resamples them by distance rather than by
index, so a circle lofted to a square no longer twists.
Two things worth noting for anyone reading the old comments. The tolerance
globals that lived in the tessellation module were never about truck at all —
they are the per-frame chord height, and they move to `curve_tol`. And the
rule that a profile handed over as `Lines` silently broke EXTRUDE and REVOLVE
no longer holds: those read the curve directly and never look at this channel.
The two `automation` test failures are unchanged from before this and are not
caused by it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 01:08:50 +03:00
|
|
|
fn to_render_entity(&self, document: &CadDocument) -> Option<RenderEntity>;
|
2026-03-22 14:56:27 +03:00
|
|
|
fn grips(&self) -> Vec<GripDef>;
|
feat(grip): multi-functional hover popup menu
Phase 2 of the grip overhaul: when the cursor dwells on a selected
entity's grip the overlay opens a popup menu with entity-specific
options (Stretch / Add Vertex / Remove Vertex / Convert to
Arc-Line / …). Picking an item dispatches `apply_grip_menu`; the
default `Stretch` entry is a no-op that lets the user click the
grip to start the normal drag.
- `GripMenuItem` + `GripMenuAction` (full vocabulary covering
stretch, add/remove vertex, convert to arc/line, reverse arrows,
text-position variants, hatch parameters, tangent direction).
- `Grippable` trait gains `grip_menu` + `apply_grip_menu` with
trait-default `Stretch`-only impls so every existing entity gets
the popup for free.
- `EntityTypeOps` dispatches both methods across the same entity
list as the existing grip / property machinery.
- `OpenCADStudio` carries `grip_hover` (dwell tracker) +
`grip_popup` (open menu). `update_grip_hover` runs from
`ViewportMove`: hit-tests grips, refreshes the dwell timer, and
opens the popup after 600 ms. Cursor drift past 80 px or a grip
click dismisses it.
- `view.rs` paints the popup as an absolute-positioned column of
buttons at the grip's screen anchor; each button publishes
`Message::GripMenuPick(idx)`.
- `LwPolyline` overrides `Grippable` manually (skips the standard
macro) so its vertex grip exposes Add/Remove Vertex and its
segment-midpoint grip exposes Add Vertex + Convert to Arc/Line.
`apply_grip_menu` performs the edits: vertex insertion at the
chord midpoint inheriting the previous bulge, vertex removal,
and bulge toggle for the arc/line conversion.
2026-05-29 09:30:54 +03:00
|
|
|
fn grip_menu(&self, grip_id: usize) -> Vec<GripMenuItem>;
|
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
|
|
|
fn apply_geom_prop(&mut self, field: &str, value: &str);
|
|
|
|
|
fn apply_grip(&mut self, grip_id: usize, apply: GripApply);
|
feat(grip): multi-functional hover popup menu
Phase 2 of the grip overhaul: when the cursor dwells on a selected
entity's grip the overlay opens a popup menu with entity-specific
options (Stretch / Add Vertex / Remove Vertex / Convert to
Arc-Line / …). Picking an item dispatches `apply_grip_menu`; the
default `Stretch` entry is a no-op that lets the user click the
grip to start the normal drag.
- `GripMenuItem` + `GripMenuAction` (full vocabulary covering
stretch, add/remove vertex, convert to arc/line, reverse arrows,
text-position variants, hatch parameters, tangent direction).
- `Grippable` trait gains `grip_menu` + `apply_grip_menu` with
trait-default `Stretch`-only impls so every existing entity gets
the popup for free.
- `EntityTypeOps` dispatches both methods across the same entity
list as the existing grip / property machinery.
- `OpenCADStudio` carries `grip_hover` (dwell tracker) +
`grip_popup` (open menu). `update_grip_hover` runs from
`ViewportMove`: hit-tests grips, refreshes the dwell timer, and
opens the popup after 600 ms. Cursor drift past 80 px or a grip
click dismisses it.
- `view.rs` paints the popup as an absolute-positioned column of
buttons at the grip's screen anchor; each button publishes
`Message::GripMenuPick(idx)`.
- `LwPolyline` overrides `Grippable` manually (skips the standard
macro) so its vertex grip exposes Add/Remove Vertex and its
segment-midpoint grip exposes Add Vertex + Convert to Arc/Line.
`apply_grip_menu` performs the edits: vertex insertion at the
chord midpoint inheriting the previous bulge, vertex removal,
and bulge toggle for the arc/line conversion.
2026-05-29 09:30:54 +03:00
|
|
|
fn apply_grip_menu(&mut self, grip_id: usize, action: GripMenuAction);
|
2026-05-29 14:58:35 +03:00
|
|
|
fn grip_menu_value_prompt(
|
|
|
|
|
&self,
|
|
|
|
|
grip_id: usize,
|
|
|
|
|
action: GripMenuAction,
|
|
|
|
|
) -> Option<&'static str>;
|
2026-08-04 16:27:03 +03:00
|
|
|
fn grip_menu_point_value(
|
|
|
|
|
&self,
|
|
|
|
|
grip_id: usize,
|
|
|
|
|
action: GripMenuAction,
|
|
|
|
|
point: glam::DVec3,
|
|
|
|
|
) -> Option<f64>;
|
2026-05-29 14:58:35 +03:00
|
|
|
fn apply_grip_menu_value(
|
|
|
|
|
&mut self,
|
|
|
|
|
grip_id: usize,
|
|
|
|
|
action: GripMenuAction,
|
|
|
|
|
value: f64,
|
|
|
|
|
);
|
2026-03-22 14:56:27 +03:00
|
|
|
fn apply_transform(&mut self, t: &EntityTransform);
|
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
|
|
|
fn mass_props(&self) -> Option<MassProps>;
|
|
|
|
|
fn text_content(&self) -> Option<String>;
|
|
|
|
|
fn replace_text(&mut self, search: &str, rep: &str);
|
2026-03-22 14:56:27 +03:00
|
|
|
}
|
|
|
|
|
|
2026-05-23 23:18:22 +03:00
|
|
|
/// Per-dispatch-function entity-variant lists. Adding a new entity that
|
|
|
|
|
/// participates in a dispatch = adding one identifier to one list here
|
|
|
|
|
/// (instead of one full match arm to each of five `match self` blocks).
|
|
|
|
|
///
|
|
|
|
|
/// `dispatch!` expands `EntityType::$Variant(e) => $body` for each name.
|
|
|
|
|
macro_rules! dispatch {
|
|
|
|
|
($self:expr, |$e:ident| $body:expr, [$($variant:ident),* $(,)?], _ => $default:expr $(,)?) => {
|
|
|
|
|
match $self {
|
|
|
|
|
$(EntityType::$variant($e) => $body,)*
|
|
|
|
|
_ => $default,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
refactor(entities): collapse trait wrapper boilerplate via macro
Every entity file ended with the same three-trait wrapper block:
impl Grippable for T {
fn grips(&self) -> Vec<GripDef> { grips(self) }
fn apply_grip(&mut self, ...) { apply_grip(self, ...) }
}
impl PropertyEditable for T { ... }
impl Transformable for T { ... }
— 23 lines of pure delegation around five same-named free functions.
Replace with `crate::impl_entity_basics!(T);` (a macro in
entities/traits.rs that expands to the three trait impls).
Applied where the file already follows the free-fn delegation
convention (Arc, Circle, Ellipse, Insert, Leader, Line, LwPolyline,
MultiLeader, Ole2Frame, Point, Spline, Viewport). Files that inlined
the trait method bodies directly (Ray, XLine, Shape, Solid, Tolerance,
MLine, Mesh, Solid3D/Region/Body, Table, RasterImage, Wipeout,
Underlay, Hatch, Polyline, Polyline2D, Polyline3D, Dimension) left
alone — they already skip the indirection. Text/MText/Attribute use
the with-text-style flavour and will move once that variant is needed
beyond the existing two manual impls.
A second macro `impl_entity_basics_with_text_styles!` is included for
the text-like flavour but no entity uses it yet.
Net: -204 lines.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 17:16:06 +03:00
|
|
|
/// Generates `Grippable`, `PropertyEditable`, `Transformable` trait impls
|
|
|
|
|
/// that delegate to identically-named free functions in the entity's own
|
|
|
|
|
/// module (`grips`, `apply_grip`, `properties`, `apply_geom_prop`,
|
|
|
|
|
/// `apply_transform`). `properties()` is called with `self` only — for
|
|
|
|
|
/// text-like entities that need the document's text-style list use
|
|
|
|
|
/// [`impl_entity_basics_with_text_styles!`] instead.
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! impl_entity_basics {
|
|
|
|
|
($T:ty) => {
|
|
|
|
|
impl $crate::entities::traits::Grippable for $T {
|
2026-06-16 11:33:46 +03:00
|
|
|
fn grips(&self) -> Vec<$crate::scene::model::object::GripDef> {
|
refactor(entities): collapse trait wrapper boilerplate via macro
Every entity file ended with the same three-trait wrapper block:
impl Grippable for T {
fn grips(&self) -> Vec<GripDef> { grips(self) }
fn apply_grip(&mut self, ...) { apply_grip(self, ...) }
}
impl PropertyEditable for T { ... }
impl Transformable for T { ... }
— 23 lines of pure delegation around five same-named free functions.
Replace with `crate::impl_entity_basics!(T);` (a macro in
entities/traits.rs that expands to the three trait impls).
Applied where the file already follows the free-fn delegation
convention (Arc, Circle, Ellipse, Insert, Leader, Line, LwPolyline,
MultiLeader, Ole2Frame, Point, Spline, Viewport). Files that inlined
the trait method bodies directly (Ray, XLine, Shape, Solid, Tolerance,
MLine, Mesh, Solid3D/Region/Body, Table, RasterImage, Wipeout,
Underlay, Hatch, Polyline, Polyline2D, Polyline3D, Dimension) left
alone — they already skip the indirection. Text/MText/Attribute use
the with-text-style flavour and will move once that variant is needed
beyond the existing two manual impls.
A second macro `impl_entity_basics_with_text_styles!` is included for
the text-like flavour but no entity uses it yet.
Net: -204 lines.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 17:16:06 +03:00
|
|
|
grips(self)
|
|
|
|
|
}
|
|
|
|
|
fn apply_grip(
|
|
|
|
|
&mut self,
|
|
|
|
|
grip_id: usize,
|
2026-06-16 11:33:46 +03:00
|
|
|
apply: $crate::scene::model::object::GripApply,
|
refactor(entities): collapse trait wrapper boilerplate via macro
Every entity file ended with the same three-trait wrapper block:
impl Grippable for T {
fn grips(&self) -> Vec<GripDef> { grips(self) }
fn apply_grip(&mut self, ...) { apply_grip(self, ...) }
}
impl PropertyEditable for T { ... }
impl Transformable for T { ... }
— 23 lines of pure delegation around five same-named free functions.
Replace with `crate::impl_entity_basics!(T);` (a macro in
entities/traits.rs that expands to the three trait impls).
Applied where the file already follows the free-fn delegation
convention (Arc, Circle, Ellipse, Insert, Leader, Line, LwPolyline,
MultiLeader, Ole2Frame, Point, Spline, Viewport). Files that inlined
the trait method bodies directly (Ray, XLine, Shape, Solid, Tolerance,
MLine, Mesh, Solid3D/Region/Body, Table, RasterImage, Wipeout,
Underlay, Hatch, Polyline, Polyline2D, Polyline3D, Dimension) left
alone — they already skip the indirection. Text/MText/Attribute use
the with-text-style flavour and will move once that variant is needed
beyond the existing two manual impls.
A second macro `impl_entity_basics_with_text_styles!` is included for
the text-like flavour but no entity uses it yet.
Net: -204 lines.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 17:16:06 +03:00
|
|
|
) {
|
|
|
|
|
apply_grip(self, grip_id, apply);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl $crate::entities::traits::PropertyEditable for $T {
|
|
|
|
|
fn geometry_properties(
|
|
|
|
|
&self,
|
|
|
|
|
_text_style_names: &[String],
|
2026-07-02 22:46:19 +03:00
|
|
|
) -> Vec<$crate::scene::model::object::PropSection> {
|
refactor(entities): collapse trait wrapper boilerplate via macro
Every entity file ended with the same three-trait wrapper block:
impl Grippable for T {
fn grips(&self) -> Vec<GripDef> { grips(self) }
fn apply_grip(&mut self, ...) { apply_grip(self, ...) }
}
impl PropertyEditable for T { ... }
impl Transformable for T { ... }
— 23 lines of pure delegation around five same-named free functions.
Replace with `crate::impl_entity_basics!(T);` (a macro in
entities/traits.rs that expands to the three trait impls).
Applied where the file already follows the free-fn delegation
convention (Arc, Circle, Ellipse, Insert, Leader, Line, LwPolyline,
MultiLeader, Ole2Frame, Point, Spline, Viewport). Files that inlined
the trait method bodies directly (Ray, XLine, Shape, Solid, Tolerance,
MLine, Mesh, Solid3D/Region/Body, Table, RasterImage, Wipeout,
Underlay, Hatch, Polyline, Polyline2D, Polyline3D, Dimension) left
alone — they already skip the indirection. Text/MText/Attribute use
the with-text-style flavour and will move once that variant is needed
beyond the existing two manual impls.
A second macro `impl_entity_basics_with_text_styles!` is included for
the text-like flavour but no entity uses it yet.
Net: -204 lines.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 17:16:06 +03:00
|
|
|
properties(self)
|
|
|
|
|
}
|
|
|
|
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
|
|
|
|
apply_geom_prop(self, field, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl $crate::entities::traits::Transformable for $T {
|
|
|
|
|
fn apply_transform(&mut self, t: &$crate::command::EntityTransform) {
|
|
|
|
|
apply_transform(self, t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Same as [`impl_entity_basics!`] but the entity's `properties(...)` free
|
|
|
|
|
/// function takes the document's text-style name list as a second
|
|
|
|
|
/// argument (Text, MText, …).
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! impl_entity_basics_with_text_styles {
|
|
|
|
|
($T:ty) => {
|
|
|
|
|
impl $crate::entities::traits::Grippable for $T {
|
2026-06-16 11:33:46 +03:00
|
|
|
fn grips(&self) -> Vec<$crate::scene::model::object::GripDef> {
|
refactor(entities): collapse trait wrapper boilerplate via macro
Every entity file ended with the same three-trait wrapper block:
impl Grippable for T {
fn grips(&self) -> Vec<GripDef> { grips(self) }
fn apply_grip(&mut self, ...) { apply_grip(self, ...) }
}
impl PropertyEditable for T { ... }
impl Transformable for T { ... }
— 23 lines of pure delegation around five same-named free functions.
Replace with `crate::impl_entity_basics!(T);` (a macro in
entities/traits.rs that expands to the three trait impls).
Applied where the file already follows the free-fn delegation
convention (Arc, Circle, Ellipse, Insert, Leader, Line, LwPolyline,
MultiLeader, Ole2Frame, Point, Spline, Viewport). Files that inlined
the trait method bodies directly (Ray, XLine, Shape, Solid, Tolerance,
MLine, Mesh, Solid3D/Region/Body, Table, RasterImage, Wipeout,
Underlay, Hatch, Polyline, Polyline2D, Polyline3D, Dimension) left
alone — they already skip the indirection. Text/MText/Attribute use
the with-text-style flavour and will move once that variant is needed
beyond the existing two manual impls.
A second macro `impl_entity_basics_with_text_styles!` is included for
the text-like flavour but no entity uses it yet.
Net: -204 lines.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 17:16:06 +03:00
|
|
|
grips(self)
|
|
|
|
|
}
|
|
|
|
|
fn apply_grip(
|
|
|
|
|
&mut self,
|
|
|
|
|
grip_id: usize,
|
2026-06-16 11:33:46 +03:00
|
|
|
apply: $crate::scene::model::object::GripApply,
|
refactor(entities): collapse trait wrapper boilerplate via macro
Every entity file ended with the same three-trait wrapper block:
impl Grippable for T {
fn grips(&self) -> Vec<GripDef> { grips(self) }
fn apply_grip(&mut self, ...) { apply_grip(self, ...) }
}
impl PropertyEditable for T { ... }
impl Transformable for T { ... }
— 23 lines of pure delegation around five same-named free functions.
Replace with `crate::impl_entity_basics!(T);` (a macro in
entities/traits.rs that expands to the three trait impls).
Applied where the file already follows the free-fn delegation
convention (Arc, Circle, Ellipse, Insert, Leader, Line, LwPolyline,
MultiLeader, Ole2Frame, Point, Spline, Viewport). Files that inlined
the trait method bodies directly (Ray, XLine, Shape, Solid, Tolerance,
MLine, Mesh, Solid3D/Region/Body, Table, RasterImage, Wipeout,
Underlay, Hatch, Polyline, Polyline2D, Polyline3D, Dimension) left
alone — they already skip the indirection. Text/MText/Attribute use
the with-text-style flavour and will move once that variant is needed
beyond the existing two manual impls.
A second macro `impl_entity_basics_with_text_styles!` is included for
the text-like flavour but no entity uses it yet.
Net: -204 lines.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 17:16:06 +03:00
|
|
|
) {
|
|
|
|
|
apply_grip(self, grip_id, apply);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl $crate::entities::traits::PropertyEditable for $T {
|
|
|
|
|
fn geometry_properties(
|
|
|
|
|
&self,
|
|
|
|
|
text_style_names: &[String],
|
2026-07-02 22:46:19 +03:00
|
|
|
) -> Vec<$crate::scene::model::object::PropSection> {
|
refactor(entities): collapse trait wrapper boilerplate via macro
Every entity file ended with the same three-trait wrapper block:
impl Grippable for T {
fn grips(&self) -> Vec<GripDef> { grips(self) }
fn apply_grip(&mut self, ...) { apply_grip(self, ...) }
}
impl PropertyEditable for T { ... }
impl Transformable for T { ... }
— 23 lines of pure delegation around five same-named free functions.
Replace with `crate::impl_entity_basics!(T);` (a macro in
entities/traits.rs that expands to the three trait impls).
Applied where the file already follows the free-fn delegation
convention (Arc, Circle, Ellipse, Insert, Leader, Line, LwPolyline,
MultiLeader, Ole2Frame, Point, Spline, Viewport). Files that inlined
the trait method bodies directly (Ray, XLine, Shape, Solid, Tolerance,
MLine, Mesh, Solid3D/Region/Body, Table, RasterImage, Wipeout,
Underlay, Hatch, Polyline, Polyline2D, Polyline3D, Dimension) left
alone — they already skip the indirection. Text/MText/Attribute use
the with-text-style flavour and will move once that variant is needed
beyond the existing two manual impls.
A second macro `impl_entity_basics_with_text_styles!` is included for
the text-like flavour but no entity uses it yet.
Net: -204 lines.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 17:16:06 +03:00
|
|
|
properties(self, text_style_names)
|
|
|
|
|
}
|
|
|
|
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
|
|
|
|
apply_geom_prop(self, field, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl $crate::entities::traits::Transformable for $T {
|
|
|
|
|
fn apply_transform(&mut self, t: &$crate::command::EntityTransform) {
|
|
|
|
|
apply_transform(self, t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-22 14:56:27 +03:00
|
|
|
impl EntityTypeOps for EntityType {
|
refactor: drop truck, and evaluate every curve through the kernel
The last four truck crates are gone from the manifest. What they were doing
falls into three parts, and each now has one answer instead of two.
NURBS evaluation. truck was carrying B-spline curves and surfaces for the
SPLINE entity, hatch boundaries, BLEND's endpoint frames, the spline preview
and ACIS spline-surface faces. The kernel now has both, over one de Boor
written across however many coordinates a control point holds — so a plane
curve and a space curve cannot drift apart, and a surface is the same
algorithm applied twice. The rational and the polynomial cases stop being
separate types: weights absent means polynomial.
The entity conversion. Every LINE, ARC, ELLIPSE, SPLINE and polyline was
built into truck topology purely so it could be sampled back into points.
They are sampled through `entities::curve` now, which is where each one's
geometry is already defined once and what EXTRUDE and REVOLVE read — so a
circle drawn on screen and a circle handed to the Model tab come from the
same definition. That retires four of TruckObject's variants and the module
is renamed for what it does.
SWEEP and LOFT. Both only ever produced a mesh, so both are built from point
lists: a band of quads per span, and a lid where a profile closes. Lofting
profiles of different densities resamples them by distance rather than by
index, so a circle lofted to a square no longer twists.
Two things worth noting for anyone reading the old comments. The tolerance
globals that lived in the tessellation module were never about truck at all —
they are the per-frame chord height, and they move to `curve_tol`. And the
rule that a profile handed over as `Lines` silently broke EXTRUDE and REVOLVE
no longer holds: those read the curve directly and never look at this channel.
The two `automation` test failures are unchanged from before this and are not
caused by it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 01:08:50 +03:00
|
|
|
fn to_render_entity(&self, document: &CadDocument) -> Option<RenderEntity> {
|
2026-05-23 23:18:22 +03:00
|
|
|
dispatch!(self,
|
refactor: drop truck, and evaluate every curve through the kernel
The last four truck crates are gone from the manifest. What they were doing
falls into three parts, and each now has one answer instead of two.
NURBS evaluation. truck was carrying B-spline curves and surfaces for the
SPLINE entity, hatch boundaries, BLEND's endpoint frames, the spline preview
and ACIS spline-surface faces. The kernel now has both, over one de Boor
written across however many coordinates a control point holds — so a plane
curve and a space curve cannot drift apart, and a surface is the same
algorithm applied twice. The rational and the polynomial cases stop being
separate types: weights absent means polynomial.
The entity conversion. Every LINE, ARC, ELLIPSE, SPLINE and polyline was
built into truck topology purely so it could be sampled back into points.
They are sampled through `entities::curve` now, which is where each one's
geometry is already defined once and what EXTRUDE and REVOLVE read — so a
circle drawn on screen and a circle handed to the Model tab come from the
same definition. That retires four of TruckObject's variants and the module
is renamed for what it does.
SWEEP and LOFT. Both only ever produced a mesh, so both are built from point
lists: a band of quads per span, and a lid where a profile closes. Lofting
profiles of different densities resamples them by distance rather than by
index, so a circle lofted to a square no longer twists.
Two things worth noting for anyone reading the old comments. The tolerance
globals that lived in the tessellation module were never about truck at all —
they are the per-frame chord height, and they move to `curve_tol`. And the
rule that a profile handed over as `Lines` silently broke EXTRUDE and REVOLVE
no longer holds: those read the curve directly and never look at this channel.
The two `automation` test failures are unchanged from before this and are not
caused by it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 01:08:50 +03:00
|
|
|
|e| RenderConvertible::to_render(e, document),
|
2026-05-23 23:18:22 +03:00
|
|
|
[
|
|
|
|
|
Point, Line, Circle, Arc, Ellipse, Spline, LwPolyline,
|
|
|
|
|
Polyline, Polyline2D, Polyline3D, Ray, XLine, RasterImage,
|
|
|
|
|
Wipeout, AttributeDefinition, AttributeEntity, MLine,
|
|
|
|
|
Tolerance, Solid, Face3D, PolygonMesh, PolyfaceMesh, Mesh,
|
|
|
|
|
Table, Text, MText, Leader, MultiLeader, Underlay, Shape,
|
2026-07-26 22:30:14 +03:00
|
|
|
Ole2Frame, Helix, Light, Extended,
|
2026-05-23 23:18:22 +03:00
|
|
|
],
|
2026-03-22 14:56:27 +03:00
|
|
|
_ => None,
|
2026-05-23 23:18:22 +03:00
|
|
|
)
|
2026-03-22 14:56:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn grips(&self) -> Vec<GripDef> {
|
2026-05-23 23:18:22 +03:00
|
|
|
dispatch!(self,
|
|
|
|
|
|e| Grippable::grips(e),
|
|
|
|
|
[
|
|
|
|
|
Line, Circle, Arc, Ellipse, LwPolyline, Polyline, Polyline2D,
|
|
|
|
|
Polyline3D, Ray, XLine, RasterImage, Wipeout,
|
|
|
|
|
AttributeDefinition, AttributeEntity, MLine, Tolerance,
|
2026-07-26 22:30:14 +03:00
|
|
|
Solid, Solid3D, Region, Body, Surface, Face3D, PolygonMesh,
|
2026-05-23 23:18:22 +03:00
|
|
|
PolyfaceMesh, Mesh, Table, Point, Spline, Text, MText,
|
|
|
|
|
Viewport, Insert, Leader, MultiLeader, Dimension, Hatch,
|
2026-07-26 22:30:14 +03:00
|
|
|
Underlay, Shape, Ole2Frame, Helix, Light, SectionSymbol,
|
|
|
|
|
ViewBorder, Extended,
|
2026-05-23 23:18:22 +03:00
|
|
|
],
|
2026-03-22 14:56:27 +03:00
|
|
|
_ => vec![],
|
2026-05-23 23:18:22 +03:00
|
|
|
)
|
2026-03-22 14:56:27 +03:00
|
|
|
}
|
|
|
|
|
|
2026-07-02 22:46:19 +03:00
|
|
|
fn geometry_properties(&self, text_style_names: &[String]) -> Vec<PropSection> {
|
2026-05-23 23:18:22 +03:00
|
|
|
dispatch!(self,
|
2026-07-02 22:46:19 +03:00
|
|
|
|e| PropertyEditable::geometry_properties(e, text_style_names),
|
2026-05-23 23:18:22 +03:00
|
|
|
[
|
|
|
|
|
Line, Circle, Arc, Ellipse, LwPolyline, Polyline, Polyline2D,
|
|
|
|
|
Polyline3D, Ray, XLine, RasterImage, Wipeout,
|
|
|
|
|
AttributeDefinition, AttributeEntity, MLine, Tolerance,
|
2026-07-26 22:30:14 +03:00
|
|
|
Solid, Solid3D, Region, Body, Surface, Face3D, PolygonMesh,
|
2026-05-23 23:18:22 +03:00
|
|
|
PolyfaceMesh, Mesh, Table, Hatch, Point, Spline, Text, MText,
|
|
|
|
|
Viewport, Insert, Dimension, Leader, MultiLeader, Underlay,
|
2026-07-26 22:30:14 +03:00
|
|
|
Shape, Ole2Frame, Helix, Light, SectionSymbol, ViewBorder,
|
|
|
|
|
Extended,
|
2026-05-23 23:18:22 +03:00
|
|
|
],
|
2026-07-02 22:46:19 +03:00
|
|
|
_ => vec![],
|
2026-05-23 23:18:22 +03:00
|
|
|
)
|
2026-03-22 14:56:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn apply_geom_prop(&mut self, field: &str, value: &str) {
|
2026-05-23 23:18:22 +03:00
|
|
|
dispatch!(self,
|
|
|
|
|
|e| PropertyEditable::apply_geom_prop(e, field, value),
|
|
|
|
|
[
|
|
|
|
|
Line, Circle, Arc, Ellipse, LwPolyline, Polyline, Polyline2D,
|
|
|
|
|
Polyline3D, Ray, XLine, RasterImage, Wipeout,
|
|
|
|
|
AttributeDefinition, AttributeEntity, MLine, Tolerance,
|
2026-07-26 22:30:14 +03:00
|
|
|
Solid, Solid3D, Region, Body, Surface, Face3D, PolygonMesh,
|
2026-05-23 23:18:22 +03:00
|
|
|
PolyfaceMesh, Mesh, Table, Hatch, Point, Spline, Text, MText,
|
|
|
|
|
Viewport, Insert, Dimension, Leader, MultiLeader, Underlay,
|
2026-07-26 22:30:14 +03:00
|
|
|
Shape, Ole2Frame, Helix, Light, SectionSymbol, ViewBorder,
|
|
|
|
|
Extended,
|
2026-05-23 23:18:22 +03:00
|
|
|
],
|
|
|
|
|
_ => {},
|
|
|
|
|
)
|
2026-03-22 14:56:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn apply_grip(&mut self, grip_id: usize, apply: GripApply) {
|
2026-05-23 23:18:22 +03:00
|
|
|
dispatch!(self,
|
|
|
|
|
|e| Grippable::apply_grip(e, grip_id, apply),
|
|
|
|
|
[
|
|
|
|
|
Line, Circle, Arc, Ellipse, LwPolyline, Polyline, Polyline2D,
|
|
|
|
|
Polyline3D, Ray, XLine, RasterImage, Wipeout,
|
|
|
|
|
AttributeDefinition, AttributeEntity, MLine, Tolerance,
|
2026-07-26 22:30:14 +03:00
|
|
|
Solid, Solid3D, Region, Body, Surface, Face3D, PolygonMesh,
|
2026-05-23 23:18:22 +03:00
|
|
|
PolyfaceMesh, Mesh, Table, Point, Spline, Text, MText,
|
|
|
|
|
Viewport, Insert, Leader, MultiLeader, Dimension, Hatch,
|
2026-07-26 22:30:14 +03:00
|
|
|
Underlay, Shape, Ole2Frame, Helix, Light, SectionSymbol,
|
|
|
|
|
ViewBorder, Extended,
|
2026-05-23 23:18:22 +03:00
|
|
|
],
|
|
|
|
|
_ => {},
|
|
|
|
|
)
|
2026-03-22 14:56:27 +03:00
|
|
|
}
|
|
|
|
|
|
feat(grip): multi-functional hover popup menu
Phase 2 of the grip overhaul: when the cursor dwells on a selected
entity's grip the overlay opens a popup menu with entity-specific
options (Stretch / Add Vertex / Remove Vertex / Convert to
Arc-Line / …). Picking an item dispatches `apply_grip_menu`; the
default `Stretch` entry is a no-op that lets the user click the
grip to start the normal drag.
- `GripMenuItem` + `GripMenuAction` (full vocabulary covering
stretch, add/remove vertex, convert to arc/line, reverse arrows,
text-position variants, hatch parameters, tangent direction).
- `Grippable` trait gains `grip_menu` + `apply_grip_menu` with
trait-default `Stretch`-only impls so every existing entity gets
the popup for free.
- `EntityTypeOps` dispatches both methods across the same entity
list as the existing grip / property machinery.
- `OpenCADStudio` carries `grip_hover` (dwell tracker) +
`grip_popup` (open menu). `update_grip_hover` runs from
`ViewportMove`: hit-tests grips, refreshes the dwell timer, and
opens the popup after 600 ms. Cursor drift past 80 px or a grip
click dismisses it.
- `view.rs` paints the popup as an absolute-positioned column of
buttons at the grip's screen anchor; each button publishes
`Message::GripMenuPick(idx)`.
- `LwPolyline` overrides `Grippable` manually (skips the standard
macro) so its vertex grip exposes Add/Remove Vertex and its
segment-midpoint grip exposes Add Vertex + Convert to Arc/Line.
`apply_grip_menu` performs the edits: vertex insertion at the
chord midpoint inheriting the previous bulge, vertex removal,
and bulge toggle for the arc/line conversion.
2026-05-29 09:30:54 +03:00
|
|
|
fn grip_menu(&self, grip_id: usize) -> Vec<GripMenuItem> {
|
|
|
|
|
dispatch!(self,
|
|
|
|
|
|e| Grippable::grip_menu(e, grip_id),
|
|
|
|
|
[
|
|
|
|
|
Line, Circle, Arc, Ellipse, LwPolyline, Polyline, Polyline2D,
|
|
|
|
|
Polyline3D, Ray, XLine, RasterImage, Wipeout,
|
|
|
|
|
AttributeDefinition, AttributeEntity, MLine, Tolerance,
|
2026-07-26 22:30:14 +03:00
|
|
|
Solid, Solid3D, Region, Body, Surface, Face3D, PolygonMesh,
|
feat(grip): multi-functional hover popup menu
Phase 2 of the grip overhaul: when the cursor dwells on a selected
entity's grip the overlay opens a popup menu with entity-specific
options (Stretch / Add Vertex / Remove Vertex / Convert to
Arc-Line / …). Picking an item dispatches `apply_grip_menu`; the
default `Stretch` entry is a no-op that lets the user click the
grip to start the normal drag.
- `GripMenuItem` + `GripMenuAction` (full vocabulary covering
stretch, add/remove vertex, convert to arc/line, reverse arrows,
text-position variants, hatch parameters, tangent direction).
- `Grippable` trait gains `grip_menu` + `apply_grip_menu` with
trait-default `Stretch`-only impls so every existing entity gets
the popup for free.
- `EntityTypeOps` dispatches both methods across the same entity
list as the existing grip / property machinery.
- `OpenCADStudio` carries `grip_hover` (dwell tracker) +
`grip_popup` (open menu). `update_grip_hover` runs from
`ViewportMove`: hit-tests grips, refreshes the dwell timer, and
opens the popup after 600 ms. Cursor drift past 80 px or a grip
click dismisses it.
- `view.rs` paints the popup as an absolute-positioned column of
buttons at the grip's screen anchor; each button publishes
`Message::GripMenuPick(idx)`.
- `LwPolyline` overrides `Grippable` manually (skips the standard
macro) so its vertex grip exposes Add/Remove Vertex and its
segment-midpoint grip exposes Add Vertex + Convert to Arc/Line.
`apply_grip_menu` performs the edits: vertex insertion at the
chord midpoint inheriting the previous bulge, vertex removal,
and bulge toggle for the arc/line conversion.
2026-05-29 09:30:54 +03:00
|
|
|
PolyfaceMesh, Mesh, Table, Point, Spline, Text, MText,
|
|
|
|
|
Viewport, Insert, Leader, MultiLeader, Dimension, Hatch,
|
2026-07-26 22:30:14 +03:00
|
|
|
Underlay, Shape, Ole2Frame, SectionSymbol, ViewBorder,
|
|
|
|
|
Extended,
|
feat(grip): multi-functional hover popup menu
Phase 2 of the grip overhaul: when the cursor dwells on a selected
entity's grip the overlay opens a popup menu with entity-specific
options (Stretch / Add Vertex / Remove Vertex / Convert to
Arc-Line / …). Picking an item dispatches `apply_grip_menu`; the
default `Stretch` entry is a no-op that lets the user click the
grip to start the normal drag.
- `GripMenuItem` + `GripMenuAction` (full vocabulary covering
stretch, add/remove vertex, convert to arc/line, reverse arrows,
text-position variants, hatch parameters, tangent direction).
- `Grippable` trait gains `grip_menu` + `apply_grip_menu` with
trait-default `Stretch`-only impls so every existing entity gets
the popup for free.
- `EntityTypeOps` dispatches both methods across the same entity
list as the existing grip / property machinery.
- `OpenCADStudio` carries `grip_hover` (dwell tracker) +
`grip_popup` (open menu). `update_grip_hover` runs from
`ViewportMove`: hit-tests grips, refreshes the dwell timer, and
opens the popup after 600 ms. Cursor drift past 80 px or a grip
click dismisses it.
- `view.rs` paints the popup as an absolute-positioned column of
buttons at the grip's screen anchor; each button publishes
`Message::GripMenuPick(idx)`.
- `LwPolyline` overrides `Grippable` manually (skips the standard
macro) so its vertex grip exposes Add/Remove Vertex and its
segment-midpoint grip exposes Add Vertex + Convert to Arc/Line.
`apply_grip_menu` performs the edits: vertex insertion at the
chord midpoint inheriting the previous bulge, vertex removal,
and bulge toggle for the arc/line conversion.
2026-05-29 09:30:54 +03:00
|
|
|
],
|
|
|
|
|
_ => vec![],
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn apply_grip_menu(&mut self, grip_id: usize, action: GripMenuAction) {
|
|
|
|
|
dispatch!(self,
|
|
|
|
|
|e| Grippable::apply_grip_menu(e, grip_id, action),
|
|
|
|
|
[
|
|
|
|
|
Line, Circle, Arc, Ellipse, LwPolyline, Polyline, Polyline2D,
|
|
|
|
|
Polyline3D, Ray, XLine, RasterImage, Wipeout,
|
|
|
|
|
AttributeDefinition, AttributeEntity, MLine, Tolerance,
|
2026-07-26 22:30:14 +03:00
|
|
|
Solid, Solid3D, Region, Body, Surface, Face3D, PolygonMesh,
|
feat(grip): multi-functional hover popup menu
Phase 2 of the grip overhaul: when the cursor dwells on a selected
entity's grip the overlay opens a popup menu with entity-specific
options (Stretch / Add Vertex / Remove Vertex / Convert to
Arc-Line / …). Picking an item dispatches `apply_grip_menu`; the
default `Stretch` entry is a no-op that lets the user click the
grip to start the normal drag.
- `GripMenuItem` + `GripMenuAction` (full vocabulary covering
stretch, add/remove vertex, convert to arc/line, reverse arrows,
text-position variants, hatch parameters, tangent direction).
- `Grippable` trait gains `grip_menu` + `apply_grip_menu` with
trait-default `Stretch`-only impls so every existing entity gets
the popup for free.
- `EntityTypeOps` dispatches both methods across the same entity
list as the existing grip / property machinery.
- `OpenCADStudio` carries `grip_hover` (dwell tracker) +
`grip_popup` (open menu). `update_grip_hover` runs from
`ViewportMove`: hit-tests grips, refreshes the dwell timer, and
opens the popup after 600 ms. Cursor drift past 80 px or a grip
click dismisses it.
- `view.rs` paints the popup as an absolute-positioned column of
buttons at the grip's screen anchor; each button publishes
`Message::GripMenuPick(idx)`.
- `LwPolyline` overrides `Grippable` manually (skips the standard
macro) so its vertex grip exposes Add/Remove Vertex and its
segment-midpoint grip exposes Add Vertex + Convert to Arc/Line.
`apply_grip_menu` performs the edits: vertex insertion at the
chord midpoint inheriting the previous bulge, vertex removal,
and bulge toggle for the arc/line conversion.
2026-05-29 09:30:54 +03:00
|
|
|
PolyfaceMesh, Mesh, Table, Point, Spline, Text, MText,
|
|
|
|
|
Viewport, Insert, Leader, MultiLeader, Dimension, Hatch,
|
2026-07-26 22:30:14 +03:00
|
|
|
Underlay, Shape, Ole2Frame, SectionSymbol, ViewBorder,
|
|
|
|
|
Extended,
|
feat(grip): multi-functional hover popup menu
Phase 2 of the grip overhaul: when the cursor dwells on a selected
entity's grip the overlay opens a popup menu with entity-specific
options (Stretch / Add Vertex / Remove Vertex / Convert to
Arc-Line / …). Picking an item dispatches `apply_grip_menu`; the
default `Stretch` entry is a no-op that lets the user click the
grip to start the normal drag.
- `GripMenuItem` + `GripMenuAction` (full vocabulary covering
stretch, add/remove vertex, convert to arc/line, reverse arrows,
text-position variants, hatch parameters, tangent direction).
- `Grippable` trait gains `grip_menu` + `apply_grip_menu` with
trait-default `Stretch`-only impls so every existing entity gets
the popup for free.
- `EntityTypeOps` dispatches both methods across the same entity
list as the existing grip / property machinery.
- `OpenCADStudio` carries `grip_hover` (dwell tracker) +
`grip_popup` (open menu). `update_grip_hover` runs from
`ViewportMove`: hit-tests grips, refreshes the dwell timer, and
opens the popup after 600 ms. Cursor drift past 80 px or a grip
click dismisses it.
- `view.rs` paints the popup as an absolute-positioned column of
buttons at the grip's screen anchor; each button publishes
`Message::GripMenuPick(idx)`.
- `LwPolyline` overrides `Grippable` manually (skips the standard
macro) so its vertex grip exposes Add/Remove Vertex and its
segment-midpoint grip exposes Add Vertex + Convert to Arc/Line.
`apply_grip_menu` performs the edits: vertex insertion at the
chord midpoint inheriting the previous bulge, vertex removal,
and bulge toggle for the arc/line conversion.
2026-05-29 09:30:54 +03:00
|
|
|
],
|
|
|
|
|
_ => {},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-29 14:58:35 +03:00
|
|
|
fn grip_menu_value_prompt(
|
|
|
|
|
&self,
|
|
|
|
|
grip_id: usize,
|
|
|
|
|
action: GripMenuAction,
|
|
|
|
|
) -> Option<&'static str> {
|
|
|
|
|
dispatch!(self,
|
|
|
|
|
|e| Grippable::grip_menu_value_prompt(e, grip_id, action),
|
|
|
|
|
[
|
|
|
|
|
Line, Circle, Arc, Ellipse, LwPolyline, Polyline, Polyline2D,
|
|
|
|
|
Polyline3D, Ray, XLine, RasterImage, Wipeout,
|
|
|
|
|
AttributeDefinition, AttributeEntity, MLine, Tolerance,
|
2026-07-26 22:30:14 +03:00
|
|
|
Solid, Solid3D, Region, Body, Surface, Face3D, PolygonMesh,
|
2026-05-29 14:58:35 +03:00
|
|
|
PolyfaceMesh, Mesh, Table, Point, Spline, Text, MText,
|
|
|
|
|
Viewport, Insert, Leader, MultiLeader, Dimension, Hatch,
|
2026-07-26 22:30:14 +03:00
|
|
|
Underlay, Shape, Ole2Frame, SectionSymbol, ViewBorder,
|
|
|
|
|
Extended,
|
2026-05-29 14:58:35 +03:00
|
|
|
],
|
|
|
|
|
_ => None,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-04 16:27:03 +03:00
|
|
|
fn grip_menu_point_value(
|
|
|
|
|
&self,
|
|
|
|
|
grip_id: usize,
|
|
|
|
|
action: GripMenuAction,
|
|
|
|
|
point: glam::DVec3,
|
|
|
|
|
) -> Option<f64> {
|
|
|
|
|
dispatch!(self,
|
|
|
|
|
|e| Grippable::grip_menu_point_value(e, grip_id, action, point),
|
|
|
|
|
[
|
|
|
|
|
Line, Circle, Arc, Ellipse, LwPolyline, Polyline, Polyline2D,
|
|
|
|
|
Polyline3D, Ray, XLine, RasterImage, Wipeout,
|
|
|
|
|
AttributeDefinition, AttributeEntity, MLine, Tolerance,
|
|
|
|
|
Solid, Solid3D, Region, Body, Surface, Face3D, PolygonMesh,
|
|
|
|
|
PolyfaceMesh, Mesh, Table, Point, Spline, Text, MText,
|
|
|
|
|
Viewport, Insert, Leader, MultiLeader, Dimension, Hatch,
|
|
|
|
|
Underlay, Shape, Ole2Frame, SectionSymbol, ViewBorder,
|
|
|
|
|
Extended,
|
|
|
|
|
],
|
|
|
|
|
_ => None,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-29 14:58:35 +03:00
|
|
|
fn apply_grip_menu_value(
|
|
|
|
|
&mut self,
|
|
|
|
|
grip_id: usize,
|
|
|
|
|
action: GripMenuAction,
|
|
|
|
|
value: f64,
|
|
|
|
|
) {
|
|
|
|
|
dispatch!(self,
|
|
|
|
|
|e| Grippable::apply_grip_menu_value(e, grip_id, action, value),
|
|
|
|
|
[
|
|
|
|
|
Line, Circle, Arc, Ellipse, LwPolyline, Polyline, Polyline2D,
|
|
|
|
|
Polyline3D, Ray, XLine, RasterImage, Wipeout,
|
|
|
|
|
AttributeDefinition, AttributeEntity, MLine, Tolerance,
|
2026-07-26 22:30:14 +03:00
|
|
|
Solid, Solid3D, Region, Body, Surface, Face3D, PolygonMesh,
|
2026-05-29 14:58:35 +03:00
|
|
|
PolyfaceMesh, Mesh, Table, Point, Spline, Text, MText,
|
|
|
|
|
Viewport, Insert, Leader, MultiLeader, Dimension, Hatch,
|
2026-07-26 22:30:14 +03:00
|
|
|
Underlay, Shape, Ole2Frame, SectionSymbol, ViewBorder,
|
2026-05-29 14:58:35 +03:00
|
|
|
],
|
|
|
|
|
_ => {},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-22 14:56:27 +03:00
|
|
|
fn apply_transform(&mut self, t: &EntityTransform) {
|
2026-05-23 23:18:22 +03:00
|
|
|
dispatch!(self,
|
|
|
|
|
|e| Transformable::apply_transform(e, t),
|
|
|
|
|
[
|
|
|
|
|
Arc, Circle, Ellipse, Hatch, Insert, Line, LwPolyline,
|
|
|
|
|
Polyline, Polyline2D, Polyline3D, Ray, XLine, RasterImage,
|
|
|
|
|
Wipeout, AttributeDefinition, AttributeEntity, MLine,
|
|
|
|
|
Tolerance, Solid, Face3D, PolygonMesh, PolyfaceMesh, Mesh,
|
|
|
|
|
Table, MText, Point, Spline, Text, Viewport, Dimension,
|
|
|
|
|
Leader, MultiLeader, Underlay, Shape, Ole2Frame,
|
2026-07-26 22:30:14 +03:00
|
|
|
Solid3D, Region, Body, Surface, Helix, Light, SectionSymbol,
|
|
|
|
|
ViewBorder, Extended,
|
2026-05-23 23:18:22 +03:00
|
|
|
],
|
|
|
|
|
_ => {},
|
|
|
|
|
)
|
2026-03-22 14:56:27 +03:00
|
|
|
}
|
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
|
|
|
|
|
|
|
|
fn mass_props(&self) -> Option<MassProps> {
|
|
|
|
|
dispatch!(self,
|
|
|
|
|
|e| Some(MassPropsCalc::mass_props(e)),
|
|
|
|
|
[Circle, Arc, Line, LwPolyline, Ellipse],
|
|
|
|
|
_ => None,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn text_content(&self) -> Option<String> {
|
|
|
|
|
dispatch!(self,
|
|
|
|
|
|e| TextContent::text_content(e),
|
|
|
|
|
[Text, MText, AttributeDefinition, AttributeEntity],
|
|
|
|
|
_ => None,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn replace_text(&mut self, search: &str, rep: &str) {
|
|
|
|
|
dispatch!(self,
|
|
|
|
|
|e| TextContent::replace_text(e, search, rep),
|
|
|
|
|
[Text, MText, AttributeDefinition, AttributeEntity],
|
|
|
|
|
_ => {},
|
|
|
|
|
)
|
|
|
|
|
}
|
2026-03-22 14:56:27 +03:00
|
|
|
}
|