Commit Graph
22 Commits
Author SHA1 Message Date
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