Direct distance entry (#1429) turned every bare number at a point prompt
into a distance, so ROTATE's angle and the values automation types were
read as coordinates. The step reads the token first now — `feed_command`
reports whether it claimed it — and only an unclaimed one becomes a
distance. That also puts the token back on the unified step driver, which
owns the P / L selection keywords and the dimension-snap bookkeeping, and
stops `on_text_input` running twice for the same token. A tracking ray
carries its own base, so it no longer demands an anchor that the first
point of a command does not have yet.
Plugin command execution (#1425) re-implemented the command line inside
the plugin host: it dispatched families directly, skipping the teardown
that starts a command (grip edits, selection gestures, `last_point`, the
accepted snaps), and made every `CmdResult::Dispatch` in the application
skip plugin dispatch to avoid its own re-entrancy. A scripted line now
runs through `run_command_line`, with the trailing Enter left off while
the plugin streams the command, and the re-entrancy is handled by a flag
set for the length of the plugin's call. A pause parks the line's Enter
with the rest of its tokens instead of dropping it.
A font reference (#1432) tried the family lookup before the face-file
index, and that lookup ends in a prefix match a longer file name loses to
(`ISOCPEUI.TTF` would take `ISOCP`). The exact file answers first.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Select arc or circle / Dimension text / dimension line location / value or
name prompts with a radN parameter, invalid-selection, miss, duplicate and
invalid-expression messages, negative values as magnitudes, a dynamic radial
dimension on the constraints layer drawn as the reference does (mid-radius
text on the center-to-rim line, no hook, no center mark, one text grip), the
stored text position following the radius, center-anchored resizing, the
Properties title and Constraint section, DIMCONSTRAINT's Radius default,
the small-button Dimensional ribbon grid and tooltip descriptions.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Dynamic dimensions carry DIMEXO/DIMEXE/DIMASZ overrides in text-height
ratios, a two-line angular dimension draws extension lines only beyond
its sides, a selected dynamic angle shows triangle grips at the arc ends
with squares at the arc point and text, and the constraint dimension's
Properties follow the reference: kind titles, General before Constraint,
angular-precision Value and Measurement, drawn text position, reference
row order for Fit, Primary Units and Tolerances, and text/dimension line
alignment rows reading On as the aligned state.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Three-point angular dimensions take their sweep from the two rays and the
arc point (extension lines follow their own rays, straight and reflex
angles draw), dynamic angular dimensions store no text point so DIMTAD
places the text beside an unbroken arc, and dynamic dimensions carry a
DIMGAP override of a quarter text height.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
DCANGULAR picks two lines, an arc or three points, takes the dimension
line location to choose the sector and the value or name, and creates the
dynamic two-line or three-point angular dimension driven by an angN
parameter. Reference messages for invalid, repeated and parallel sides,
existing names and invalid names; angle values display within one turn at
the angular precision; the first side (or vertex and first point) stays
when the value changes; the ribbon Dimensional panel follows the reference
order.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Placements (entity lookups, intersections, String labels, projections)
recomputed per view() and per mouse-move hit-test, with overlay re-clones
and 2-3x offset recomputes. New constraints_epoch bumped on every set
mutation/visibility/undo/DWG-load (plus new set_constraint_enabled API);
single-slot Arc<[GlyphEntry]> memo on an 11-field key (hover/selection
excluded); overlay de-cloned/de-duped to the single geometry source;
hit paths reuse the cache.
Tests:
- cached_glyph_placements_hit_returns_same_arc: same-key Arc identity
- cached_glyph_placements_invalidated_by_metadata_edit: edit + selection change yield new Arc
- glyph_hit_test_entries_matches_precomputed_boxes: shared hit-test helper
Bench (cargo bench glyph, 60 constraints): 8.18us -> 0.05-0.15us (~55x).
A/B reference deleted.
Only the first line's ends are pinned while the second line is made
parallel, the way the Parallel constraint itself moves it; the distance
is then measured on the solved geometry.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Point & line and 2Lines take a text baseline or an ellipse axis as the
line; a zero value folds the points onto the first one; 2Lines turns the
second line about its start. The Properties Constraint section edits
Constraint Form (Dynamic on the *ADSK_CONSTRAINTS layer, Annotational on
the current layer), Reference (driven constraint in parentheses whose
parameter follows the measurement) and Description (stored with the
parameter and the native variable). Dynamic dimensions keep a 12 px text
and 8 px arrows at any zoom, drop trailing zeros and show formulas as
`fx: len=d1*2`; the hidden constraints layer stays out of the layer lists.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
DCFORM sets the annotational or dynamic form and continues into the
DIMCONSTRAINT option prompt, whose wording and last-used default match the
reference. -PARAMETERS manages parameters from the command line; a
double-clicked constraint dimension opens its value prompt. Dynamic
dimensions rescale on every camera change and their display overrides stay
out of the saved file. A driving dimension between an entity's own ends no
longer has its length retained, so a new value moves the second point along
the dimension axis; parameter edits hold the first constraint point.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
DCALIGNED opens with `[Object/Point & line/2Lines] <Object>`. Point & line
asks `Specify constraint point or [Line] <Line>:` then `Select line:` (or the
line first and `Select constraint point:`), measures perpendicular to the
line from the constraint point to the line end nearest to it, and stores a
directed distance with the line as direction reference. 2Lines asks
`Select first line:` and `Select second line to make parallel:`, adds a
Parallel constraint solved with the first line pinned, then measures on the
solved geometry between the nearest ends. Unsupported line picks report the
reference's message; the same measurement again reports `The constraint
already exists on the selected objects.` and ends the command; an invalid
expression reports both reference lines and asks again.
The offered default and seeded expression carry twelve significant digits;
a typed value moves the second point (or the other object) while the first
point and its measured line stay, negative values keep their sign in the
parameter and use their magnitude, zero folds the points together, and the
dynamic dimension's origins follow every solve. The lock mark sits after the
text, a selected dynamic dimension shows triangle grips at its points and the
text square, and its Properties show the Constraint section (Name and
Expression editable) plus Text rotation.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A plain number shows as typed (d1=100), an expression under the fx: prefix
(fx: d2=d1*2); the text stays horizontal through DIMTIH/DIMTOH overrides
and the lock mark follows the text.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every dynamic dimension gets a DIMSCALE override for 12 px text, refreshed
per zoom band, at creation and after load. A SCALE/ROTATE of an entity whose
own end points a driving dimension measures now pins its second constraint
point so the dimension moves the first end, as the reference does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The dynamic dimension of a dimensional constraint is now linked in the
drawing file (AcDbAssocDependency + AcDbAssocDimDependencyBody on the
constraint node), reread on load, hidden by DCHIDE/DYNCONSTRAINTDISPLAY
and never plotted; it carries a lock mark before its text. New
CONSTRAINTNAMEFORMAT and DYNCONSTRAINTDISPLAY variables and the
DIMCONSTRAINT option front end. A transformed entity whose own end points
a driving dimension measures keeps only the first constraint point, so a
scaled line returns to its dimension value.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
DCLINEAR, DCHORIZONTAL, DCVERTICAL and DCALIGNED follow the reference flow:
`Specify first constraint point or [Object] <Object>:`, `Specify second
constraint point:`, `Specify dimension line location:`, then `Dimension
text = …` and `Enter value or name and value <d1=100>:`. Linear reads
horizontal or vertical from the dimension line location the way a linear
dimension does; Object takes a line's, an arc's or a polyline segment's
ends. A miss reports `No valid constraint point found.` / `No object
found.` and asks again; another object type reports `Invalid selection for
Linear. Select a line, polyline segment or arc.`.
Each constraint creates a drawing parameter (`d1`, `d2`, …, or the typed
`name=expression`), drives the constraint through it, and draws a dynamic
dimension in constraint gray whose text follows CONSTRAINTNAMEFORMAT
(`d1=100.0000`); the dimension is associated with the constrained objects
and its text is refreshed after every solve. A typed value other than the
measured one moves the second point while the first stays, and so does a
later parameter edit. Deleting the constraint removes its dimension and,
when nothing else reads it, its parameter.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A fresh reference session shows the "=" bar of every object made equal
through Multiple right away; the earlier hidden state was not the rule.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A second object of the wrong kind (a circle after a line, a line after a
circle) or a pair that already carries Equal now reports and asks for the
second object again instead of ending the command. The mismatch message
names what the first object asks for — `Select a line segment, polyline
segment, text, MText, major or minor axis of ellipse or elliptical arc.`
after a length object, `Select a circle or arc.` after a radius object —
and the same-pick message carries the reference's double space.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- An object that carries several Equal relations shows a single "="
badge, as the reference does, instead of one stacked badge per relation.
- A pick made in the Multiple flow leaves its new constraint bar unshown
until the object is selected or bars are shown, matching the reference;
the single flow still shows the bar at once.
- A typed coordinate at the Fix `Select object:` prompt and at the
Horizontal/Vertical `Select an object or [2Points]` prompt picks the
object under that point, like a click, instead of being read as a point.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An OTRACK crossing is the meeting of two tracking vectors, but
`OtrackHit` carried only the one a typed distance is measured along, so
the overlay had a single guide to draw. Snapping to the crossing of two
vectors worked and looked like an unexplained point on one line.
Carry the crossed ray out of `otrack_snap` as `OtrackHit::cross`, keep it
on the app beside `otrack_active`, and let the overlay take a list of
guides rather than one. `otrack_active` still drives typed-distance
entry on its own, so distance entry is unchanged.
FixesHakanSeven12/OpenCADStudio#1313
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQzYZsXBwPZ5HwBaGitEUV
(cherry picked from commit 2f6caacece)
`run_command_line` feeds the tokens after the command verb to the interactive
command and then terminates it as if Enter were pressed. Two gaps left a
headless caller unable to tell what actually happened.
In-place text editor takes over, remaining tokens were dropped
----------------------------------------------------------------
The `TEXT` content step hands over to the in-place editor, at which point the
command has already ended (`active_cmd` is `None`). The feed loop stopped
there, so the rest of the line — including the text itself — vanished:
`TEXT 0,0 5 0 hello` reported `completed` with `added: 0` and created nothing.
The tail is now typed into that editor and committed, and the commit is done
synchronously so its result is observable: `text_inline_commit()` returning
false leaves the tokens in `unconsumed` instead of reporting a successful line.
Dispatch the commit as a queued message instead and a failed commit would look
exactly like a good one — the text dropped, `added: 0`, `unconsumed: []`.
Only an editor this line opens is taken over, and after a successful commit the
line is closed with an escape: `TEXT` re-arms for the next line
(`open_next_line`), which would otherwise let the *next* batch line be eaten as
the next text position.
Leftover tokens are dropped
---------------------------
Any token no prompt ever asked for is recorded in `CommandLine::unconsumed` and
surfaced by the automation `run` response, so over-typed or misplaced input is
visible instead of silently ignored: `CIRCLE 0,0 5 9` reports
`"unconsumed": ["9"]` while a fully consumed line reports an empty list.
Pending surfaces are reported instead of `completed`
----------------------------------------------------
`run` now carries `blocked_by` — `"command"`, `"text_editor"`,
`"mtext_editor"`, `"modal:<Kind>"`, or `null` — and answers `waiting_input`
when something is still open rather than claiming `completed`.
`blocked_by` is attributed to the line that opened the surface: a condition and
its snapshot are taken before the command runs, so an editor left open by an
earlier line, another tab, or a startup dialog does not turn a finished command
into `waiting_input`. Without that attribution a caller polling for `completed`
would wait forever on someone else's editor. `docs/automation` documented
`waiting_input` as "more input is required", which is exactly what a pending
surface is; the two new fields are documented there as well.
Tests
-----
* `batch_text_line_creates_text_and_consumes_every_token` — the regression case
creates exactly one `Text` entity and leaves nothing unconsumed.
* `multi_word_text_keeps_the_spaces` — asserts the committed string, so an
off-by-one in the consumed index or a wrong join cannot pass.
* `run_reports_leftover_tokens_instead_of_dropping_them`.
* `text_without_content_reports_the_open_editor`.
* `a_stray_editor_is_not_hijacked_by_the_next_line` — the next line neither
types into the stray editor nor inherits its blocker (`blocked_by: null`,
`status: completed`).
* `a_command_still_waiting_is_named_as_the_blocker`.
Verified over the `--serve` protocol: `TEXT 0,0 5 0 hello world` → `completed`,
`added: 1`, and the entity really holds `hello world`; `TEXT 40,0 5 0` →
`waiting_input` + `blocked_by: "text_editor"`; the following `CIRCLE 0,0 5 9` →
`completed`, `blocked_by: null`, `unconsumed: ["9"]`; `LINE` → `waiting_input`
+ `blocked_by: "command"`.
Full lib suite: 1497 passed, 2 failed — both failures
(`io::plot_style::…named_tables_and_missing_files_get_a_reason`,
`scene::parametric_constraints::…arc_grips_drive_center_start_and_end_but_not_midpoint`)
reproduce on the unmodified tree; run with `LC_ALL=C` since several xref tests
otherwise fail on a non-English locale.
Known remaining gap, unchanged here: `MTEXT` / `HATCH` still answer `completed`
with `added: 0` in this path (they need interactive surfaces the feeder has no
tokens to fill). Reported separately in #1390.
CmdResult::Dispatch and CmdResult::Relaunch end the active command and run the
assembled line through dispatch_command, then discarded the Task it returned
(`let _ =`). A command whose effect is a message carried by that Task was
announced but never applied. VSCURRENT's keyword picker is one: choosing
"Gouraud Shaded" printed "Visual style: Gouraud Shaded." and left the
drawing in wireframe, because SetRenderMode travels in that Task.
Hold the Task and batch it with the focus task that apply_cmd_result_inner
returns. A regression test drives both results through VSCURRENT and checks the
tab's render mode.
Multiple now follows the reference flow: every pick after the first is
made equal right away under `Select object to make equal to first:` and
Enter ends the command with `Length of objects made equal` (or `Radius
…`). A typed coordinate at one of the command's object prompts picks the
object under that point instead of being refused as a point.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Equal was applied through a generic gather-then-apply flow and, with the
default size-retaining solve mode, never changed anything: the solver
held both sizes and the relation could not be met. Now:
- GCEQUAL joins ECONSTRAINT and GEOMCONSTRAINT Equal; both objects are
picked inside the command (`Select first object or [Multiple]:`,
`Select second object:`; Multiple gathers `Select objects to make equal
to the first:`), a selection made beforehand is not used, a miss says
`No object found.` and re-asks, an unsupported object or the same object
twice is reported and re-asked, a repeat is refused.
- The follower is resized in place before the relation is added: a line or
straight polyline segment keeps its start and direction and its end
moves to the first object's length; a circle or arc keeps its center and
takes the radius.
- During edits the side that is not being edited follows the same rule:
its size is not retained, its start (or center) and other vertices are
held and its direction kept, so only its length or radius changes; a
chain of Equal relations follows along.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The solver cannot start from an axis lying exactly across the datum: the
distance and direction equations are singular there, so a horizontal line,
a text at 0 degrees or an ellipse whose major axis is horizontal stayed
put when Vertical was applied. The command now turns the object about its
anchor (line start, picked segment's first vertex, text insertion point,
ellipse center) onto the axis first — a polyline moves only the segment's
end vertex — and lets the constraint hold what already fits. Two points on
one entity follow the same rule about the first point.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The first empty-space click of the 2Points flow was only resolved against
the document together with the second one, so "No valid constraint point
found." appeared one pick late. The command now hands the first pick to
the host (`CmdResult::CheckHorizontalPoint`), which resolves it right away:
a miss reports and re-asks for the first point, a hit moves on to the
second point.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- #1352 pinned cadkernel/cadkernel-constraints to ramox81's fork. Its one
extra commit (cadkernel #67, SymmetricLineDirections) is now on
HakanSeven12/cadkernel main as the same SHA, so the manifests and lock
point back at the upstream repo at rev 0493b59.
- SYCONSTRAINT reports a repeated relation with the same message Fix and
Vertical use, as an error instead of plain output.
- The knot-less spline test consumes the add_entity results.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1) VCONSTRAINT (and the new GCVERTICAL name) uses the axis constraint flow:
"Select an object or [2Points] <2Points>:", 2Points mode with the
reference's re-prompts and messages, and the same object types as
Horizontal — line, polyline segment, text, MText, ellipse axis.
2) Object mode turns the object about its first vertex; a two-point relation
across two objects slides the second object rigidly onto the axis.
3) Re-applying an axis constraint reports that the constraint already
exists; Vertical follows the UCS Y direction.
4) The constraint bar draws Vertical as the reference's axis mark, with the
point marker for a two-point relation.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1) FXCONSTRAINT (and the new GCFIX name) prompts "Select point or [Object]
<Object>:" — a pick fixes that constraint point; Enter or O asks for one
line, polyline segment, circle, arc, ellipse or spline; misses and
unsupported objects report the reference messages and re-prompt.
2) Point, block, text, MText, attribute and table insertion points are
addressable constraint points.
3) Re-applying Fixed to the same point or object reports that the constraint
already exists instead of adding a duplicate.
4) A persistent Fixed holds its pre-edit geometry through MOVE, ROTATE,
SCALE, STRETCH, grip and Properties edits: fixed points stay, free points
of a partly fixed entity follow the edit, and size retention skips fixed
entities.
5) Object mode on a polyline fixes the picked segment (natively persisted)
instead of the whole polyline; arc segments pin endpoints, center and
radius.
6) Ribbon label "Fix" with a padlock icon.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>