Commit Graph
20 Commits
Author SHA1 Message Date
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 e31dbcb5c4 feat(dynamic-input): per-step specs for rectangle, polygon and ellipse
Extend the DynSpec coverage and add the guides those commands need:

- Rectangle: two-corner enters width/height as unsigned magnitudes (the
  sign follows the cursor side, like the angle entry), drawn with the
  rectangle as the guide; rotated rectangle's height is a single distance
  measured square to the base edge (dimension line offset off the edge
  with extension lines); centre rectangle shows half-width/half-height.
- Polygon: inscribed/circumscribed vertex step shows radius + rotation.
- Ellipse: minor-axis step (all three modes) measures the half-length
  perpendicular to the major axis and draws that semi-axis from the
  centre; angle steps anchor their arc at the centre.

Adds DynGuide::Perp / PerpDim (perpendicular measure to a reference line)
and DynSpec::ref_point, plus Width/Height role handling (unsigned display,
cursor-signed commit) and diameter value scaling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:46:34 +03:00
Hakan SevenandClaude Opus 4.8 b0ee1c39bd feat(dynamic-input): per-step DynSpec framework + guide overhaul
Introduce a per-command, per-step dynamic-input description so each step
shows the value boxes and guide geometry that make sense for it, instead
of a one-size-fits-all distance/angle pair.

- command: add DynSpec{anchor, fields(role+label), guide}, DynRole
  (X/Y/Z/Distance/Angle/Radius/Diameter/Width/Height/Factor/Count with
  label + value scaling), DynGuide(None/Polar/AxisDelta/Radius/RectSides)
  and DynAnchor. New dyn_spec() trait method defaults to None so commands
  on the legacy dyn_field() path are unchanged.
- overlay: redraw the dynamic-input layer by guide — polar reference line
  + angle arc, single radius line, dotted axis legs, or a cursor row —
  and place each box by its role. Angle reads as the unsigned magnitude
  of the short arc; a typed polar angle takes the sign of the cursor side.
- app: route fields/guide/anchor through the tab; resolve points from the
  step anchor and de-scale typed values by role (typed diameter -> radius).
- circle: CR shows a radius value on one line to the cursor; CD shows the
  diameter (twice the cursor radius). No angle arc on radius steps.
- pline/mline: keep polar dynamic input on vertex steps that also accept
  keyword letters via point_step_accepts_keywords().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 17:12:08 +03:00
Hakan SevenandClaude Opus 4.8 9b1bd011ca feat(blocks): switch dynamic-block visibility states from a lookup grip
A block reference with a visibility parameter now shows a triangular
lookup grip at the parameter point. Clicking it opens a dropdown of the
named states (current one marked); picking a state toggles which of the
anonymous block's members are visible and rebuilds, so the reference
shows the chosen profile instead of being frozen on the saved state.

The state list is data-driven (owned strings from the file) and applying
it mutates other entities (the anonymous block's members), so it stays
separate from the static GripMenuAction grip-menu: a dedicated
VisibilityPopup and an app-level apply path. The anonymous block is a
parallel-ordered clone of its definition, so a member visible in the
definition's state maps to the same index in the inserted block; the
chosen visibility flags round-trip through the DWG writer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 20:11:19 +03:00
Hakan Seven 6a70e99dd5 Merge pull request #80 from mf4633/feature/plugin-host
Add plugin host (Phase 1)
2026-06-10 00:37:37 +03:00
Michael Flynn e46701456f Add plugin host architecture and Storm Sewer add-on package
Generic plugin runtime (src/plugin/, HostSession, per-tab plugin_state, command router). QGIS-style add-on layout with plugin.toml and single PluginRegistration. Storm Sewer refactored as reference consumer with dispatch.rs, catchments, LandXML, headless tests (43 passing). Core update.rs decoupled from storm_sewer via CadCommand object-pick hooks. Docs: plugin-architecture.md, plugin-template, PR and issue-78 reply drafts.
2026-06-09 13:41:21 -04:00
Hakan SevenandClaude Opus 4.8 4b878b88c4 feat(input): scalar entry via dynamic input
Text-input steps (OFFSET distance, POLYGON sides, radii, counts) were
swallowed by the dynamic-input overlay: typed digits landed in the field
buffer and Enter committed a point pick, so the value never reached
on_text_input (issue #61). Now each text step shows one scalar box whose
value routes to on_text_input on commit; non-numeric prompts (names,
keywords) keep going to the command line so mixed text like "Bolt12"
isn't split.

OFFSET reworked: object first, then through-mode by default — the offset
follows the cursor at the perpendicular distance to the object. Typing a
number locks the magnitude (cursor then only picks the side); a value
typed and clicked without Enter is applied before the point pick.

The dynamic-input overlay and the command line now show the active
step's prompt and hold it until the step completes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 18:32:03 +03:00
Hakan SevenandClaude Opus 4.7 f952fc9bb7 fix(dyn-input): support cartesian coords via "," (#35)
Typing \`,\` while DYN was on appended the character to the focused
field's buffer (treated as a European decimal point), so users
couldn't switch to cartesian coordinates mid-command. The dynamic
input was stuck in polar \`[Distance, Angle]\` whenever a base point
existed.

\`,\` now acts as the coordinate separator (matching the AutoCAD
convention): pressing it locks the current field's buffer, then
either advances within the existing cartesian set or reshapes a
polar configuration into cartesian:

- \`[Distance, Angle]\` + \`,\` on the first field → \`[X(buf), Y]\`
- \`[X, Y]\` + \`,\` on Y → \`[X, Y, Z]\`
- otherwise advances to the next field (Tab-equivalent)

\`sync_dyn_fields\` accepts both polar and cartesian shapes as valid
\`Point\` configurations so a mouse-move after the reshape doesn't
revert to polar. \`dyn_resolve_point\` and the overlay's live-value
formatter treat cartesian inputs as RELATIVE to the base point when
one exists — matching the DYN-on relative-coords convention from
#26. \`DynComponent::Z\` plus its label / value paths fill out the
3-D variant.

Closes #35

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 00:46:18 +03:00
Hakan SevenandClaude Opus 4.7 4abde6001a feat(dyn-input): TAB-editable distance/angle/coordinate fields
Dinamik giriş artık cursor yanında düzenlenebilir kutular gösteriyor.
TAB ile kutular arası geçilir, bir kutuya yazınca o kutu kilitlenir
(değer sabitlenir), diğerleri cursor'ı takip etmeye devam eder, Enter
ile çözülen nokta komuta point-pick olarak iletilir.

- DocumentTab.dyn_fields / dyn_active state; DynComponent + DynFieldEntry
- sync_dyn_fields komutun dyn_field() + base noktasına göre kutu kümesini
  türetir: nokta+base → [d, açı], nokta+base yok → [X, Y], radius → [d],
  açı → [açı]
- klavye yönlendirme: rakam/./-/+ aktif kutuya, TAB sonraki kutuya,
  Enter try_dyn_commit, Backspace kilidi açar
- dyn_capturing iken command-line text alanı on_input'suz bırakılıyor
  ki focus'u ve tuşları çalmasın; girişler global key aboneliğinden
  geliyor (numpad dahil text alanı üzerinden yakalanıyor)
- çoklu-kutu overlay render: aktif kutu vurgulu, kilitli kutu farklı
  kenar rengi

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 14:08:57 +03:00
Hakan SevenandClaude Opus 4.7 2145ab8a66 feat(render): phase 0 — render-mode pick_list + 3-flag pipeline plumbing
Replaces the binary Wireframe/Solid toggle with a 7-mode dropdown
that maps onto acadrust's `ViewportRenderMode` enum: Wireframe 2D /
Wireframe 3D / Hidden Line / Flat Shaded / Gouraud Shaded / Flat
Shaded + Edges / Gouraud Shaded + Edges. Per-tab state lives on
`DocumentTab::render_mode`; the old `SetWireframe` message forwards
to `SetRenderMode` so the ribbon + WIREFRAME / SOLID command line
keep working.

`scene::render::render_mode_flags()` translates the mode into three
pipeline-facing booleans (`face3d_fill`, `mesh_fill`,
`show_3d_edges`); `mesh_fill` / `show_3d_edges` are stubs that the
upcoming phases will consume. `Primitive` carries the derived
`view_wireframe` plus the two new fields through to `prepare()`.

`Pipeline::upload_face3d` takes a `wireframe_only` flag that drops
3DFACE quad fills (built from `key_vertices`) while keeping the
`all_wires.fill_tris` channel — that channel carries text-LOD greek
rectangles and PolyfaceMesh face triangles which must stay visible
in wireframe mode. 3DFACE edges stay on regardless of mode.

Solid3D mesh wireframe rendering, Hidden Line z-prepass, and Flat /
Gouraud shader uniforms come in phases 1–4.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 00:08:37 +03:00
HakanandClaude Opus 4.7 35acdb2dc8 feat(app): add fixed Start tab with welcome page and recent documents
A pinned "Start" tab now lives at index 0. It cannot be closed, opens
on launch, and replaces the model-space viewport with a welcome page —
similar to Thunderbird's first-run screen.

- Boot opens with only the Start tab; Drawing1 is no longer auto-spawned.
- The Properties left panel becomes a Recent Documents rail on the
  Start tab. Recents persist to disk
  (%APPDATA%/H7CAD/recent.txt on Windows, $XDG_CONFIG_HOME/H7CAD/recent.txt
  on Linux, ~/Library/Application Support/H7CAD/recent.txt on macOS), up
  to 20 entries, saved on every open and rehydrated at boot.
  Each row opens or removes the file in place.
- The Support ribbon group (Donate / Report / About / Changelog) is
  retired; the same actions surface on the welcome page instead. The
  Donate button is the primary call-to-action — solid #B03020 brand
  fill with a soft brand-tinted ambient glow halo behind the action row
  (Thunderbird-style coloured glow against the neutral page).
- Drawing-only overlays (mouse-area, viewcube, navbar, dynamic input,
  selection markers) are suppressed on the Start tab so its clicks
  don't trigger CAD commands. OpenFile's "current_is_empty" check
  refuses to replace the Start tab, and TabClose ignores requests on
  it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 16:19:31 +03:00
Hakan ca07268c7c cargo fmt 2026-05-12 10:36:15 +03:00
Hakan SevenandClaude Sonnet 4.6 8f90a68930 feat: restore and persist saved camera position per layout
- On file open and layout tab switch, restore the camera from the
  document's saved view (*Active VPort for model space, sheet viewport
  for paper space) instead of fitting to wire bounds.
- After any zoom/pan/rotate, write the new camera position back to the
  document and mark the file dirty so it is saved with the file.
- Uses last_synced_camera_gen to avoid marking the file dirty on the
  initial camera restore.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 07:07:19 +03:00
Hakan SevenandClaude Sonnet 4.6 578dd51af4 fix: set A4 landscape limits on initial Layout1 at document creation
acadrust initialises every Layout with 12×9 imperial defaults; override
all non-Model layouts to 297×210mm in new_drawing() so the first paper
tab opens at A4 size, consistent with layouts added later.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 14:23:38 +03:00
Hakan SevenandClaude Sonnet 4.6 1fceec92ea feat: MLEADERSTYLE command — create and manage MultiLeader styles
Stores MultiLeaderStyle objects in document.objects. Supports:
  MLEADERSTYLE LIST — list all styles
  MLEADERSTYLE NEW <name> — create a new style
  MLEADERSTYLE SET <name> <prop> <val> — edit text_height,
    arrowhead_size, landing_distance, landing_gap
  MLEADERSTYLE CURRENT [<name>] — get/set active style

Active style name tracked per-tab in active_mleader_style.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 18:13:21 +03:00
Hakan SevenandClaude Sonnet 4.6 a773a1a1f1 feat: REFEDIT / REFCLOSE — in-place block reference editing
REFEDIT picks an INSERT, extracts the block entities into model space
with the INSERT transform applied (translate + rotate + uniform scale),
and enters an editing session.  Any normal modify command works on the
temporary entities.

REFCLOSE SAVE applies the inverse transform, replaces the block
definition, and rebuilds all derived caches (hatch/image/mesh), so
every INSERT of that block reflects the edits immediately.
REFCLOSE DISCARD removes the temp entities without changing the block.

Non-uniform-scale inserts are rejected with an error message.
RefEditSession is stored on DocumentTab; undo snapshots bracket the
begin and close operations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 12:40:19 +03:00
Hakan SevenandClaude Sonnet 4.6 51e6c4b359 feat: Dynamic Input overlay (DYNMODE / F12)
- overlay.rs: dynamic_input_overlay() canvas widget draws a semi-transparent
  coordinate tooltip near the cursor; shows X/Y in absolute mode or
  dist/angle relative to last_point when a command base point exists
- statusbar: DYN toggle pill with F12 tooltip (calls ToggleDynInput)
- app/mod.rs: dyn_input: bool field (default true), ToggleDynInput message
- view.rs: builds DYN label and pushes overlay into viewport_stack when
  dyn_input=true and a command is active
- document.rs: last_cursor_screen: iced::Point — updated on every ViewportMove
- ROADMAP.md: Dynamic Input overlay marked ✅

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 00:12:09 +03:00
Hakan SevenandClaude Sonnet 4.6 41a5d98b33 feat: per-viewport background color (BACKGROUND command)
- DocumentTab: bg_color / paper_bg_color Optional<[f32;4]> fields
- view.rs: use custom color when set, fall back to defaults
- BACKGROUND <r> <g> <b>: set model or paper-space background (0–255 RGB)
- BACKGROUND RESET: restore default dark grey / paper grey

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 23:26:26 +03:00
Hakan SevenandClaude Sonnet 4.6 20629d587a Feat: implement WCS↔UCS coordinate transform pipeline
- Add `active_ucs: Option<Ucs>` per-tab state to DocumentTab
- Add `ucs_to_wcs`, `wcs_to_ucs`, `ucs_z_axis`, `ucs_rotated_z` helpers
- Add `Camera::pick_on_plane` for ray–plane intersection against any plane
- Mouse picks project onto the active UCS XY plane instead of world XY
- Typed coordinates are converted from UCS space to WCS before dispatch
- UCS command now really activates/deactivates a UCS:
  - `UCS W` resets to WCS (clears active_ucs)
  - `UCS <name>` activates a named UCS
  - `UCS SAVE <name>` saves the current active UCS
  - `UCS ORIGIN x,y,z` shifts origin (in current UCS space)
  - `UCS X/Y/Z <angle>` rotates the UCS around its own axes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 14:16:06 +03:00
Hakan SevenandClaude Sonnet 4.6 ed9cbc2092 Refactor src/main.rs into modular src/app/ structure
Split the 4500-line main.rs into focused submodules under src/app/:
- mod.rs: H7CAD struct, Message enum, boot/new/run
- document.rs: DocumentTab, HistorySnapshot, HistoryState
- helpers.rs: pure helper functions
- history.rs: undo/redo logic
- properties.rs: properties panel logic
- layers.rs: layer sync
- commands.rs: dispatch_command
- cmd_result.rs: apply_cmd_result
- view.rs: view, subscription, tab bar
- update.rs: update message handler

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 22:44:32 +03:00