Commit Graph
29 Commits
Author SHA1 Message Date
Karim Jerbi ca6239a32d chore: mark fill_tris_low unpaired sites with FIXME
dimension.rs (x2), multileader.rs (x3), table.rs (x1) emit fill_tris
without a corresponding fill_tris_low. These will trip the debug_assert_eq!
added in the preceding commit when the entity appears inside a block
definition. Each site is marked with a FIXME referencing block_cache.rs:1397
and the correct fix pattern (split_ds on each vertex).
2026-06-28 18:46:02 +01:00
Hakan SevenandClaude Opus 4.8 58da76ff70 fix(dimension): baked *D block matches live render (gaps, arrows, leaders, text)
Brings the save-time *D-block bake into parity with the live render so a
dimension no longer changes when the file is saved and reopened (the reload
draws from the block). Per a bake-vs-live audit:

- text is now the live render's own Text/MText entity (alignment, MText
  codes, text style, alignment point) — fixes every label shifting ~half a
  glyph on reload
- extension lines carry the DIMEXO start gap + DIMEXE overshoot, and slant
  by ext_line_rotation (DIMEDIT Oblique)
- terminators follow the style: oblique tick when DIMTSZ>0, else a closed
  filled SOLID arrow (was a generic open stroke)
- angular dims bake the swept arc + tangent terminators, flush extension
  lines (matching live exactly)
- radius/diameter bake the leader leg; diameter runs edge-to-edge through
  the centre; centre mark honours negative DIMCEN (centre lines)
- per-element colours/lineweights (DIMCLRD/E/T, DIMLWD/E), DIMSE/DIMSD
  suppression, DIMDLE tick overshoot
- consistent arc sweep at exactly 180 degrees

Refs #181. Remaining (custom DIMBLK arrow shapes, DIMFXLON, arrow-outside
fit, annotative viewport scale) tracked separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 20:29:35 +03:00
Hakan SevenandClaude Opus 4.8 3c5aa4453a fix(dimension): bake↔live consistency, edits, baseline/continue, grips
Batch 1-3 of the dimension audit (19 issues). The dominant class was the
baked *D block diverging from the live render: a reloaded dimension renders
from that block, so on save+reopen it silently changed.

Bake now matches the live render:
- text value/format via the shared style-resolved path (DIMDEC/DIMLFAC/
  DIMPOST/units/<>), correct height + rotation ladder, suppression
- angular dims bake their swept arc (no longer collapse to a V)
- diameter draws edge-to-edge through the centre (live + bake), centre mark
- radius/linear/aligned bake arrowheads
- group-10 (base.definition_point) synced from the edited geometry so an
  edited dim no longer shifts on reload

Editing / properties:
- aligned no longer corrupts ext_line_rotation when a coordinate is edited
- DIMTEDIT pins the moved text (text_user_positioned)
- angle properties shown/edited in degrees
- non-linear text respects the user-positioned gate

Commands / geometry:
- DIMBASELINE stacks outward (not toward the points) and its preview no
  longer tilts over non-level points
- DIMCONTINUE/DIMBASELINE inherit the base dim's text rotation (UCS chains)
- angular extension lines reach the arc (no gap)
- text grip sits on the text for auto-placed dims instead of the origin

Refs #181. (acadrust DXF round-trip + QDIM/DIMBREAK/DIMJOGLINE/DIMSPACE/
DIMEDIT to follow.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 17:47:50 +03:00
Hakan SevenandClaude Opus 4.8 29415cac20 refactor: drop residual zero-subtraction from world_offset removal
In the files where ox/oy/oz came solely from the now-zero offset
destructure (tessellate, block_cache, dimension, leader, text_support),
remove the `let [ox,oy,oz] = [0,0,0]` binding and the `- ox` arithmetic so
the coordinate math reads as the plain absolute value it now is.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:38:23 +03:00
Hakan SevenandClaude Opus 4.8 56d507fd88 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
Hakan SevenandClaude Opus 4.8 3da71698b1 wip(f64): convert command layer to DVec3 (in progress)
Migrate the command/snap/grip/cursor chain from f32 Vec3 to f64 DVec3 so
picked points, snaps and grip drags stay precise at UTM-scale coordinates
(f32 quantizes to ~0.5 m there).

Done so far:
- command/mod.rs: EntityTransform, CmdResult points, CadCommand point
  methods, TangentObject already DVec3 (radius f64).
- snap: SnapResult.world DVec3; world_to_screen/try_pt DVec3.
- GripApply -> DVec3; grip find/state (GripEdit) f64; camera
  pick_on_plane_f64 / pick_on_target_plane_f64; scene paper_to_model_f64.
- update.rs click + mouse-move + grip-drag pipelines route precise DVec3
  to on_point/on_preview_wires/on_entity_pick; ortho/polar cast at the
  UcsXform boundary only.
- cmd_result dispatch, transform helpers, dimension/arc/circle/multileader
  apply_grip, circle/arc/ellipse/shapes draw commands, array modify.

Tree does NOT compile yet (~236 errors remain across modify/annotate/
insert command files + entity apply_grip impls); committed at user request
to checkpoint progress.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 03:32:11 +03:00
Hakan SevenandClaude Opus 4.7 cca1086696 refactor(snap): widen key_vertices to f64 (world_offset removal P4)
WireModel/LocalWire/BatchEntry key_vertices are now Vec<[f64; 3]>;
tessellation keeps them in f64 (no f32 cast at offset subtraction),
producers (dimension, leader, multileader) build them from the acadrust
f64 source, the block-cache transform keeps f64, and the face3d Face3D
quad path splits them into the double-single (high, low) pair. Consumers
(snap endpoint/midpoint, xclip) read via f32 at the leaf. Still
offset-relative (inert); endpoint/midpoint snap is no longer f32-capped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-23 22:23:56 +03:00
Hakan SevenandClaude Opus 4.7 a7a1fac09a refactor(snap): widen snap_pts to f64 (world_offset removal P2)
WireModel/LocalWire/TruckEntity snap buffers and the producers/consumers
now carry snap candidates as DVec3 (offset_snap_pts, dimension/multileader
producers, block-cache transform, pick/xclip, snap module). Still
offset-relative, so behaviour is unchanged — this just lets the snap
transport survive the upcoming switch to absolute coordinates without an
extra f32 quantization. The snap module still resolves in f32 (as_vec3 at
entry), matching today's precision.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-23 18:52:42 +03:00
Hakan SevenandClaude Opus 4.7 c0f3d0de5e feat(render): double-single fills for the face3d pipeline
Table / PolyfaceMesh / greek-text / mleader-background fills go through
the face3d pipeline; their vertices were single f32 and quantized at UTM
scale. WireModel now carries a fill_tris_low residual (filled by the
tessellate Lines path), Face3DVertex gains position_low (location 3), and
face3d.wgsl reconstructs the eye-relative position from the pair. 3DFACE
quads built from key_vertices use a zero residual (no low source).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-23 18:28:52 +03:00
Hakan SevenandClaude Opus 4.7 97d6993768 refactor(wire): add WireModel.points_low parallel buffer (empty default)
Foundation for double-single positions: WireModel now carries an
index-paired f32 residual buffer (`points_low`) next to the existing
`points` high half. Empty means "all-zero residual", so every existing
constructor (interactive draw, previews) is unchanged in behaviour;
tessellation from CAD f64 will fill it in the next steps so vertex
precision survives at UTM-scale coordinates.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-23 18:28:52 +03:00
Hakan SevenandClaude Opus 4.8 2a260be31d fix(dim): restore DIMTAD=Outside placement, route all DIMTAD values
The previous DIMTAD fix made every non-below value use the text-up side,
which broke DIMTAD=2 (Outside) — that should sit on the side farthest from
the measured points. Pass the DIMTAD value through instead of a below flag:
above (1) / JIS (3) / centred (0) use the text-up side, below (4) the
opposite, and outside (2) the away-from-object side. Tests cover above-up
and outside-away. (#144)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 17:38:57 +03:00
Hakan SevenandClaude Opus 4.8 ac5d92c0b4 fix(dim): place DIMTAD "above" text by text-up side, not object side
Dimension text vertical placement chose the perpendicular side away from the
measured points, so "Above" (DIMTAD 1) flipped to below whenever the
dimension line sat on the opposite side of the geometry. Place it on the side
the text top points to instead — the perpendicular of the dimension line
normalised to read left-to-right — independent of the object. (#144)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 17:32:56 +03:00
Hakan SevenandClaude Opus 4.8 23844c137b fix(dim): keep dim line continuous when text sits above/below it
The dim-line break test used text_half + DIMGAP as its vertical
threshold, but DIMTAD 1/4 places the text exactly text_half + DIMGAP off
the line. The two terms cancel at the same DIMSCALE-scaled value, leaving
the comparison on the knife's edge — so a gap flickered into the line
depending on the DIMGAP/DIMSCALE combination. Drop DIMGAP from the
vertical threshold so above/below text stays strictly clear and the line
stays whole; centred text (DIMTAD 0) still breaks for the DIMTFILL
background. The horizontal half-width keeps DIMGAP so a real break clears
the text. (#94)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 21:09:56 +03:00
Hakan SevenandClaude Opus 4.8 a384a163f3 fix(dim): scale DIMGAP by DIMSCALE for text placement
The text-to-line gap used a raw DIMGAP while text height already scaled
by DIMSCALE, so dimension text was misplaced whenever DIMSCALE != 1.
Thread dim_scale into dimension_text_pos_f64 and text_fill_rect and scale
DIMGAP the same way as DIMTXT.

Closes #94

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 05:32:31 +03:00
Hakan SevenandClaude Opus 4.8 7fe7e0230d refactor(scene): group modules into role-based subfolders
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>
2026-06-16 11:33:46 +03:00
Hakan SevenandClaude Opus 4.8 e263cb2458 fix(dimension): break the dim line behind on-line text so DIMTFILL reads (#94)
2-D fills (the DIMTFILL background) render in a pass before all wires, so
the background could never sit above the dimension line — the line showed
through it. When the text sits on the line, gap the dimension line across
the text box (AutoCAD-style break) so the background reads cleanly over
the line. Text placed above/below the line is unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 10:09:28 +03:00
Hakan SevenandClaude Opus 4.8 6aa47c6ead fix(dimension): DIMZIN feet/inch suppression and DIMJUST 3/4 rotation (#94)
- DIMZIN now drives architectural feet/inch zero-suppression (modes 0-3)
  inside format_architectural, threaded through format_with_unit (primary
  and alternate units).
- DIMJUST 3 and 4 (over first / second extension) now rotate the text 90°
  to read along the extension line, not just shift its position.
- DIMTAD 2 (outside) and 3 (JIS) resolve to the away-from-object side,
  which equals "above" for 2-D linear dims; clarified in a comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 09:42:11 +03:00
Hakan SevenandClaude Opus 4.8 edacc895f7 fix(dimension): honour DIMTAD below and DIMTFILL background fill (#94)
Two style settings were accepted by the editor but ignored by the
renderer:
- DIMTAD=4 (Below) placed text above the dimension line like DIMTAD=1;
  the text is now offset to the opposite side.
- DIMTFILL=1 (drawing background) drew no fill; it now masks the geometry
  behind the text with the background colour (DIMTFILL=2 already used
  DIMTFILLCLR).

Verified the other enum settings render: DIMLUNIT/DIMFRAC unit formats,
DIMAUNIT/DIMADEC/DIMAZIN angular formats, DIMJUST positions,
DIMATFIT/DIMTMOVE fit, DIMTOLJ tolerance alignment, and the alternate-unit
/ zero-suppression families.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 09:37:01 +03:00
Hakan SevenandClaude Opus 4.8 b211ca07a9 feat(dimension): flip arrowheads outside when they don't fit (#94)
When the dimension line is too short for the arrowheads, they are now
placed outside the extension lines (tip on the line, body outward) with a
short stub to sit on, instead of overlapping inside. DIMSOXD suppresses
those outer stubs. Ticks are unaffected (they always fit).

DIMATFIT's exact mode ordering and DIMUPT remain for a later pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 02:13:00 +03:00
Hakan SevenandClaude Opus 4.8 40a91372fd feat(dimension): honour DIMTIX/DIMATFIT text fit; clarify remaining flags (#94)
Centred dimension text that doesn't fit between the extension lines now
slides outside (text-outside placement), unless DIMTIX forces it to stay
inside. DIMTOFL is already satisfied for linear/aligned dims (their dim
line is always drawn between the extension points).

The remaining style flags are documented in place:
- DIMATFIT arrow-outside modes and DIMUPT need an arrow autofit pass.
- DIMTXTDIRECTION (RTL) needs single-line text direction support.
- DIMARCSYM / DIMJOGANG apply only to arc-length / jogged-radius dims,
  which aren't shipped as entity variants.
- DIMUNIT is obsolete (superseded by DIMLUNIT).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 02:03:05 +03:00
Hakan SevenandClaude Opus 4.8 006dbe4631 fix(dimension): make DIMTAD/DIMTVP/DIMJUST drive text placement (#94)
Dimension text position ignored the style: the renderer returned the
saved text_middle_point whenever it was non-zero, which it always is for
app-created dims, so DIMTAD ("text over the dimension line" / ISO) and
friends had no effect — text could only be moved by grips.

Use the new acadrust text_user_positioned flag: when the text wasn't
user-placed, compute the position from the style (DIMTAD/DIMTVP/DIMJUST)
anchored on the dimension line (definition_point); when it was (grip drag
or properties edit, both now set the flag), keep the saved point.
Imported drawings keep their fidelity via the flag round-tripping through
DXF/DWG.

Also fixes linear/aligned text anchoring at the measured midpoint instead
of the actual dimension line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 01:57:58 +03:00
Hakan SevenandClaude Opus 4.8 25715d1b03 fix(grips): compute grip positions in f64 so they sit on the wire
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>
2026-06-11 00:04:37 +03:00
Hakan SevenandClaude Opus 4.8 70702d063f perf(hash): swap std HashMap/HashSet for rustc-hash FxHash
Phase 4.1. Handle / u64 keys dominate the hot maps (block_defn,
hatch / image / mesh caches, draw-depth map, viewport wire caches);
the default SipHash is wasted work on integer keys. Alias the std
types to rustc_hash::FxHashMap / FxHashSet crate-wide so every map and
set — including the cross-module draw-depth map shared between scene
and the GPU pipelines — uses the faster hasher consistently.

Mechanical: no behavioural change. Constructors moved from ::new() to
::default() (Fx variants carry no RandomState). Build + 13 tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 00:35:53 +03:00
Hakan Seven a4c65649e1 feat(grip): entity-specific popup menus for Line/Arc/Dimension
- Line: endpoint grips expose Stretch + Lengthen; midpoint exposes
  Stretch only.
- Arc: endpoint exposes Stretch + Lengthen; midpoint exposes
  Stretch + Radius + Arc Length; centre exposes Stretch.
- Dimension text grip exposes the full text-position vocabulary
  (Move with Dim Line / with Leader / Independent, Reset Text,
  Rotate Text, Above Dim Line, Center). Dim-line-position grip
  exposes Stretch + Reverse Arrows. Extension origins keep the
  default Stretch.
- Each affected entity drops `impl_entity_basics!` and writes the
  three trait impls (Grippable, PropertyEditable, Transformable)
  manually so `grip_menu` / `apply_grip_menu` can override.
- `apply_grip_menu` handles Reset Text (clear text override) and
  Center (snap text to midpoint of extension origins) inline;
  remaining actions stub to no-op pending the follow-up
  prompt / drag plumbing.
2026-05-29 09:46:05 +03:00
Hakan Seven 5755c2e130 refactor(grip): square-only marker set + oriented stretch handles
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.
2026-05-29 02:07:34 +03:00
Hakan SevenandClaude Opus 4.7 aadb64860d 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
Hakan ca07268c7c cargo fmt 2026-05-12 10:36:15 +03:00
Hakan SevenandClaude Sonnet 4.6 36d51286d4 Feat: dimension grip editing — all dimension types get stretch grips
Linear/Aligned: first point, second point, definition point, text position
Radius/Diameter: center (angle_vertex), radius point, text position
Angular2Ln/Angular3Pt: vertex, two extension points, arc def point, text
Ordinate: origin, feature location, leader endpoint, text position
Text position grip shared across all types; measurement recalculated after edit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 19:11:21 +03:00
Hakan Seven d2fdac7f1e first commit 2026-03-22 14:56:27 +03:00