Commit graph cad-editor/src/app/automation.rs
Author SHA1 Message Date
Hakan Seven
1a78e6c7ff fix(rotate): apply a typed angle right after the centre (#159)
ROTATE's reference-point step prompts "or skip: type angle directly", but
on_text_input only handled the later Angle step. So picking a centre and
typing the angle did nothing, and the next Enter cancelled the command —
the objects never rotated, which reads as the rotate "erasing" / losing
them.

Accept a typed angle at the reference-point step too, rotating directly
about the centre (the reference point stays optional). Added an automation
regression test that rotates a line 90° with centre + angle only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 19:58:45 +03:00
Hakan Seven
df0e283eba feat(commands): interactive value prompts for PDMODE/PDSIZE/LTSCALE/CELTSCALE (F4)
Bare PDMODE / PDSIZE / LTSCALE / CELTSCALE now enter a generic
ValuePromptCommand front-end that prompts for the value and delegates to the
existing inline `<name> <value>` handler via CmdResult::Dispatch, so they
prompt step-by-step like UCS. A bare Enter reports the current value;
`PDMODE 3` still works as one typed / headless line.

Only pure single-value setting commands are converted. Dialog-opening
commands (LAYER, PURGE, DIMSTYLE, STYLE …) intentionally keep their
bare-name behaviour (open the panel), and subcommand-tree commands keep
their inline handlers — all already typable with arguments since F1.

Adds headless test value_prompt_commands_inline_args. Fourth phase of the
unified step driver.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 13:35:25 +03:00
Hakan Seven
52f6aed2f2 feat(cmdline): type whole command lines with spaces; unify with headless (F1, #169)
Space is now a literal character in the command line, so a whole command
line — `UCS Z 90`, `LINE 0,0 10,10`, `PDMODE 3` — is typed before Enter
instead of the first Space submitting the buffer. Enter tokenises the line
and runs it.

The command line and the headless automation feeder now share one runner,
`run_command_line` (moved to the non-wasm-gated cmd_result.rs): a single or
inline-argument command dispatches as-is; a multi-token line whose first
word starts an interactive tool feeds the rest as steps; plugin commands
get the whole line first (#162). The shared feeder is now UCS-aware —
typed coordinates are interpreted in the active UCS in both the GUI and
headless. `run_headless` is a thin wrapper over it.

A free-form text command (TEXT / MTEXT / a name with spaces) still receives
the whole line as one input, via the existing `wants_text_with_spaces`
guard. Unfocused Space still repeats the last command.

Closes the command-line half of #169 generically for every command, not
just UCS. Third phase of the unified step driver: every input source funnels
through feed_command.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 13:16:28 +03:00
Hakan Seven
36d2f33301 feat(ucs): interactive UCS command — UCS Z 90 typable everywhere (F2, #169)
Bare `UCS` now enters an interactive front-end (UcsCommand) that gathers the
option then its value as separate steps, instead of the command line
submitting `UCS` alone on the first Space. The front-end delegates execution
back to the existing inline `UCS <option> …` handler via a new
`CmdResult::Dispatch(String)`, so the coordinate-system math and the
save/persist path are untouched.

Now `UCS Z 90` works the same from the command line (Space-separated),
headless automation, and any source that feeds the active command — closing
the input half of #169. Inline `UCS Z 90` (one-shot / paste) still hits the
inline handler directly.

Second phase of routing input sources through the unified step driver: the
interactive command is driven by feed_command like any tool. Adds headless
test ucs_interactive_inline_args.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:36:49 +03:00
Hakan Seven
5fdd212592 refactor(commands): unified StepInput → feed_command step router (F0)
Introduce the single place every input source funnels through to drive the
active command's step machine: a `StepInput` vocabulary (one variant per
`CadCommand::on_*` method) and `OpenCADStudio::feed_command`, which routes
the input to the matching `on_*` and applies the result. Sources keep their
own classification (raw input → which StepInput); only the routing is
centralized.

Migrate the headless automation feeder onto it: feed_active_cmd builds a
StepInput, the terminator sends StepInput::Enter. No behavior change —
automation_ops_round_trip still passes.

Also fixes headless inline-argument loss for plugin commands (#162):
run_headless now offers the whole line to plugin dispatch first (plugins
parse their own inline args, e.g. `HC_PIPE 2B 2C 1.25 0.013`), falling back
to the first-word + fed-tokens path for built-in interactive tools.

First phase of routing all input sources (command line, dynamic input,
plugin API, viewport clicks) through one driver.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:24:17 +03:00
Sebastian
dcfaed0182 Initial commit 2026-06-25 00:01:36 +03:00
Hakan Seven
4f7a98a2ad feat(f64): command layer compiles on DVec3
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>
2026-06-24 03:51:50 +03:00
Hakan Seven
4aa1e90dcf feat(ucs): central WCS↔UCS converter; adopt file UCS, drive readout + icon
Introduce one bridge between WCS (how geometry and the file are stored)
and the active UCS (the coordinate system the user works in), so every
UCS-aware system routes through it instead of re-deriving axis math.

- UcsXform (app/helpers.rs): the converter — to_wcs/to_ucs (points),
  vec_to_wcs/vec_to_ucs (directions), axes(), is_identity(). Orthonormal
  axes, so the inverse is the transpose. The old ucs_to_wcs / ucs_rotate_vec
  free fns now delegate to it (one implementation).
- DocumentTab::ucs_xform() builds it from the tab's active_ucs (None = WCS).
- DocumentTab::adopt_active_ucs_from_header() loads the document's saved
  current UCS (header model-space UCS) into active_ucs on open, so the file's
  coordinate system is live immediately; called from both load paths.
- Status-bar coordinate readout reports the cursor in the active UCS.
- UCS icon tripod projects the active UCS axes, so it rotates to the UCS
  instead of always showing world X/Y/Z.

First slice of routing the app's coordinate systems through the converter;
input/picking, grid, ViewCube, and the remaining readouts follow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 09:57:39 +03:00
Hakan Seven
79bb1d67b1 feat(cli): full command-line interface via clap
Replace the ad-hoc `--serve` arg sniff with a clap-parsed CLI:

- positional <file> to open at startup (also the OS file-association path)
- --new, --read-only, --backend, --safe-mode/--no-gpu
- --serve/--port (headless JSON automation, unchanged behaviour)
- --export IN OUT (headless format convert via the automation load/save infra)
- --script FILE (run command lines at startup)
- --log/RUST_LOG, plus --version/--help for free

GUI-only options pass to app::boot through a OnceLock config; read-only
gates the save paths. The web build skips parsing (no args).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 13:59:20 +03:00
Hakan Seven
8bae517eec feat(automation): local TCP socket transport for --serve
OpenCADStudio --serve --port <N> listens on 127.0.0.1:<N> and serves the
same line-based JSON protocol over the socket (one client at a time; the
document session persists across reconnects), as an alternative to
stdin/stdout. ocs.py grows a port= option to connect either way.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 01:54:03 +03:00
Hakan Seven
d4f5ed3135 feat(automation): richer query geometry + layers/header introspection
query now emits geometry for Ellipse/Text/MText/Polyline/Insert (on top
of Line/Circle/Arc/Point). Add layers op (name, ACI/RGB colour, on/off,
frozen, locked + current layer) and header op (units, PDMODE/PDSIZE,
LTSCALE, annotation scale, current layer/text style). ocs.py gains
layers()/header(). Lets an agent read the drawing's structure and
settings, not just entity counts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 01:50:58 +03:00
Hakan Seven
707c143212 feat(plugin): object-pick acquisition for interactive commands
Extend InteractiveCommand with needs_object_pick / on_object_pick so a
plugin tool can prompt the user to pick an existing entity (handle + point)
rather than a free point — the structure-pick path Storm Sewer's SS_PIPE
needs to connect existing structures. The host adapter delegates to the
internal entity-pick flow; over --serve the pick is fed as a hex handle.
Stays API v2 (no released plugin implements InteractiveCommand yet).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 01:43:53 +03:00
Hakan Seven
8d3d0c30cc feat(automation): undo/redo ops (move/copy already work via select+run)
Add undo and redo ops to the headless server (synchronous via update()).
Combined with select, the modify commands MOVE/COPY work through the
existing dispatcher — "select type=Line" then run("MOVE 0,0 10,0")
translates the selection. ocs.py gains undo()/redo().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:31:15 +03:00
Hakan Seven
c78bc62056 feat(automation): select op — target entities by handle/type/layer
Add a select op that sets the document selection (by hex handles from
query, or by type/layer, or clear). Selection drives modify commands, so
an agent can query → select → run("ERASE") to act on specific entities.
Completes the modify half of the act/observe loop. ocs.py gains select().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:27:26 +03:00
Hakan Seven
6d29b8458c feat(automation): query op — list entities with detail and filters
Add a query op to the headless server: list entities with handle, type,
layer and basic geometry (Line start/end, Circle/Arc centre+radius, Point
location), filtered by type and/or layer and capped by limit. Closes the
observe half of an agent's act/observe loop. ocs.py gains ocs.query().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:22:46 +03:00
Hakan Seven
f1a59b1094 feat(automation): drive draw commands headless with coordinate args
run now feeds coordinate tokens to interactive tools: "LINE 0,0 10,10"
starts the tool, feeds the points, and terminates as if Enter were
pressed — creating real geometry through the existing command system. The
headless session also leaves the welcome (Start) tab so drawing commands
aren'\''t blocked. Verified end-to-end: LINE/CIRCLE create entities that
save and reopen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:17:17 +03:00
Hakan Seven
7f10b69e3c feat(automation): headless JSON server (--serve) + Python client
Add an external automation API (issue #29 / #100 track 2): `OpenCADStudio
--serve` runs without a GUI and is driven over a line-based JSON protocol
on stdin/stdout — open / new / run / entities / save. State persists
across requests so a script or AI agent can act, observe, and act again.

`run` drives the app's existing command system rather than a separate
binding, so coverage grows with the app; synchronous commands apply now,
pick-based interactive ones come once coordinate feeding is wired. Ships
a ~100-line example ocs.py client over the same protocol — no FFI to
maintain. Headless works because the app already constructs and dispatches
GUI-less in tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:05:12 +03:00