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>
Groundwork for making the Properties panel conform to the per-entity group/row
spec (PROPERTIES.md). The panel already renders a Vec of sections, but each
entity could only express a single geometry section.
Change PropertyEditable::geometry_properties (and the EntityTypeOps enum
dispatch) to return Vec<PropSection>, update the two impl macros, and flatten
in properties_sectioned so the panel shows [General] + the entity's groups.
Every entity builder is wrapped to return its existing section as a one-element
Vec — pure mechanical change, no rows or titles altered, panel output identical.
Enables Phase 2: rewriting each entity's groups/rows to match the spec.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Two UTM-scale rendering defects.
- UCS icon jittered: it projects only axis DIRECTIONS but went through the
full view_proj, whose ~5.7e6 translation cancels catastrophically in f32.
Feed it the rotation-only view_proj_rte instead (translation dropped).
- Fallback outline geometry (hatch boundary, viewport/insert/ole2frame
frames, Solid3D edge wires) built absolute f32 points with an empty
points_low, so at UTM the outline sat ~0.5 m off its own fill (the hatch
fill renders precisely via the world_origin-relative HatchModel pipeline).
FallbackGeometry now carries absolute f64 points + key vertices; the
tessellate dispatcher splits them into the double-single high/low pair the
relative-to-eye wire pass needs, so outlines land exactly on their fills.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Whole command/snap/grip/cursor chain now builds on f64 DVec3 end to end,
so picked points, snaps and grip drags stay precise at UTM-scale
coordinates instead of quantizing to ~0.5 m through f32.
- All ~80 command files migrated: draw (line/circle/arc/ellipse/shapes
in full f64; others via point methods), modify, annotate, insert, view,
inquiry, layers, groups, clipboard, layout.
- Entity apply_grip impls updated for GripApply::{Absolute,Translate}(DVec3).
- Construction sites (EntityTransform/CmdResult/DynAnchor) emit DVec3;
wire-preview boundaries cast to f32 only at the GPU edge.
- Modify/annotate commands keep internal Vec3 geometry where precision is
non-critical, casting at the DVec3 boundary; key draw commands store f64.
Builds clean (no warnings). Visual verification of crosshair/snap/grip at
extreme UTM zoom pending.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the 30 flat modules under src/scene/ into six folders by role:
convert (entity→geometry), text (fonts/shaping), model (render models),
pick (hit-test/selection/grips/xclip), view (camera/transform/render),
cache (block + property caches). pipeline/ is unchanged.
Full path migration — all `scene::X` references become
`scene::<group>::X`, no compatibility re-exports. render.rs items that
were `pub(super)` (parent was scene) become `pub(in crate::scene)` to
keep their original scene-wide visibility.
No behavior change; build and tests are green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two follow-ups from the boundary-arc convention audit:
- ELLIPSE is a WCS entity in DXF (codes 10/11 are world coordinates,
unlike ARC/CIRCLE). The renderer ran center and major_axis through the
arbitrary-axis OCS, which misplaced any ellipse whose normal isn't Z-up
— e.g. the (0,0,-1) result of a mirrored-block explode. Read them as
WCS; pairs with acadrust 569bd4d, which stores WCS on its explode
paths. Z-up ellipses are unaffected (the conversion was an identity).
- The MIRROR command's hand-rolled hatch closure swapped boundary-arc
angles itself — only valid for ccw arcs and ignorant of the stored
mirrored-angle / wrap-encoded-sweep conventions. It now builds a
reflection Transform (new reflection_about_xy_line helper) and
delegates to acadrust's transform_hatch, the single source of truth
for those conventions. Covered by a Line→Arc→Line continuity + sweep
regression test.
Note: full effect of the ellipse change lands once the acadrust pin is
bumped to 569bd4d; until then only the (previously broken) non-Z-up
corner differs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Grip markers drew visibly offset from the entity on large-coordinate
(UTM-scale) drawings: every Grippable impl cast the f64 entity coordinates
to f32 BEFORE the world-offset subtraction, and at 1e7 magnitude an f32
holds ~0.9 drawing units of rounding error. The wires subtract the offset
in f64 first (precision-preserving), so the grips landed up to ~1 unit
away from the geometry they belong to.
GripDef.world is now DVec3: producers fill it straight from the f64
entity data, refresh_selected_grips subtracts the world offset in f64,
and only the small offset-local result is cast to f32 for screen-space
projection (grips_to_screen / find_hit_grip) and drag state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1 of bringing the grip vocabulary in line with the standard
CAD convention: drop the per-position-type shape encoding (Diamond
for translate, Circle for parameter) and use a plain square for
every endpoint / vertex / centre grip. The behavioural midpoint vs
vertex distinction stays — it lives on `GripDef::is_midpoint` and
drives `Translate` vs `Absolute` in the grip-edit dispatcher — only
the marker shape collapses.
- `GripShape` keeps `Square`, `Rectangle`, `Triangle`. Diamond /
Circle are gone.
- `GripDef` gains `dir: Option<[f32; 2]>` — a world-XY direction
vector consumed by `Rectangle` to orient the box along its
segment.
- `entities::common` exposes `square_grip`, `center_grip` (same
square marker, flagged as translate), `rectangle_grip(id, world,
dir)` for oriented mid-segment handles, and a kept-for-Phase-2
`triangle_grip`.
- `overlay::GripMarker` / `grips_to_screen[_paper]` thread `dir`
through to the canvas painter; the Rectangle path rotates the
box around the grip centre using the world-XY direction (flipping
the sin for screen-Y).
Every existing `diamond_grip(id, w)` call becomes `center_grip(id,
w)` — the visual change is square-replacing-diamond at line
midpoints / arc midpoints / circle centres / ellipse centres /
viewport / hatch loop centroids / dimension grips / image corners /
multileader / ray / underlay / ole2frame / solid3d.
`LwPolyline` grows a stretch handle on every segment (straight or
arc), drawn as the new oriented rectangle. The grip-edit path
translates both segment endpoints for straight segments and
adjusts the arc bulge from the new midpoint for arc segments.
The polyline arm of the hatch wire outline guarded its NaN separator
with `if count > 0 && pts.len() != start_idx`. `start_idx` was assigned
to `pts.len()` two lines earlier with nothing in between to grow `pts`,
so the inequality was structurally always false — the separator was
never emitted.
When a hatch carried more than one boundary path (multi-region hatch),
the renderer drew a straight segment from the last vertex of path A to
the first vertex of path B because the two polylines lived in `pts`
back-to-back with no NaN break. The result was a visible stray line
between hatch regions.
Switch the polyline arm to the same `if !pts.is_empty()` guard the
Line / CircularArc / EllipticArc arms use, and capture `start_idx`
*after* the separator so the closed-loop close-back logic still points
at this polyline's first real vertex.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Hatch boundary-mirror transform called
`crate::scene::transform::reflect_xy_point(&mut x.x, &mut x.y, p1, p2)`
nine times with rustfmt expanding each long-path call into a six-line
block. Total: ~55 lines for nine reflects.
Add a local `use crate::scene::transform::reflect_xy_point as reflect;`
at the top of the closure so each reflect collapses to a single line.
-19 lines, no behavior change.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The same DXF/DWG bulge → (center, radius, sweep) formula was inlined
in six places (hatch outline, polyline tess + truck path, lwpolyline
×4, scene hatch fill, explode bulge_to_arc) — each with subtly
different sweep-direction handling for the bulge sign.
Add `entities::common::BulgeArc { center, radius, start_angle,
end_angle, sweep }` with one `from_bulge(p0, p1, bulge)` constructor
and a `sample(t)` helper. Callsites collapse to a few lines.
Net: -121 lines, one canonical sweep-direction convention shared by
all consumers.
array.rs (the path-along-curve f32 helper) keeps its inline math —
its sign convention is inverted relative to the others and changing
it without exercising the array-path command risks regressing the
output.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The trait isn't deprecating anything — it carries the path entities
take when they're not covered by the truck topology pipeline
(Viewport, Insert, Hatch outline, Ole2Frame). "Fallback" names the
purpose; "Legacy" implied retirement that isn't actually on the
roadmap.
Same rename for LegacyGeometry → FallbackGeometry,
legacy_geometry() → fallback_geometry().
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
Polyline, Line, CircularArc, EllipticArc, and Spline boundary edges
all get diamond grips at their control vertices. Dragging a grip updates
the underlying DXF boundary vertex and immediately rebuilds the GPU
hatch model via Scene::apply_grip.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>