object.objectsOnPath.attach took a path inside a compound path, blend,
envelope, repeat or Intertwine group, or a clipping path, as the path
and put it into the new Objects on Path group, so it was no longer a
part of its container. As objects, a blend's key path, a repeat's
source art, an envelope's content and a clipping path were put into
the new group the same way.
The Objects on Path tool highlighted a blend's key path or a clipping
path and attached the selection to it.
objects_on_path::held now finds the container that holds an object.
The command refuses such a path or object with a reason, and the tool
does not highlight it or attach to it. A member of a compound path
given as an object still attaches as its whole compound path. The four
new messages have rows in every complete-message catalog.
Docs: docs/mcp.md (Objects on Path).
An edge hidden on its own (for example with Object > Hide) while its
Live Paint group stays visible could still be highlighted, selected or
stroked, and a hidden edge nearer the pointer kept a visible one from
being picked.
lp_part_at now takes the nearest visible edge (visible_edge_near), inside
and outside the faces, for the hover, the Live Paint Selection click and
the Bucket's Shift-click. The Bucket's Shift-click sends nothing where no
edge is highlighted, and livePaint.strokeEdge also skips hidden edges, so
the Bucket strokes the edge it highlights.
With Select Fills off, or with Alt held, the Live Paint Selection tool
highlighted an edge near the pointer even when the pointer was outside
every face, but a click there looked only for a face under the pointer
and cleared the selection. Outside the faces, the highlight came from
the front-most Live Paint group with an edge in reach, not from the
nearest edge, and it included hidden groups. The Live Paint Bucket's
Shift highlight and Shift-click used the same search.
The hover, the Live Paint Selection click and the Bucket's Shift-click
now call one function. Outside every face it picks the edge nearest
the pointer, within 4 screen pixels, among the visible Live Paint
groups. A click selects or strokes the edge that the highlight shows.
This change follows up #3052.
The selection tools' clipping-path outline hit was also used where the
Selection and Direct Selection tools act on what is drawn rather than
select. Near a layer's clipping mask edge, with art drawn over it:
loaded text threaded into the unpainted mask instead of the art, a
double-click on type entered isolation on the mask instead of editing
the type (Selection) or did nothing (Direct Selection), and Cmd/Ctrl+
Shift double-click could not release a guide path there. These now
take the ordinary hit (ToolContext::hit_options); a press or hover
that selects keeps the outline hit.
Tests (each fails with the selection hit options):
select::tests::loaded_text_threads_into_the_art_on_a_layer_masks_edge,
a_double_click_on_type_at_a_layer_masks_edge_edits_it (Selection and
Direct Selection) and a_guide_on_a_layer_masks_edge_is_released, over
the new testutil helpers add_masked_layer and
doc_with_type_on_a_layer_masks_edge. Recorded in
docs/target-app-parity.md.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A clip group answered a press only strictly inside its clipping path,
and there the art above the path came first in the hit test, so where
the clipped art extends past the mask's edge the clipping path's
segments could not be reached on the canvas.
With the new HitOptions::clip_outlines, which the Selection, Direct
Selection and Group Selection tools set
(ToolContext::selection_hit_options), a press within the selection
tolerance of a clipping path's outline, inside or just outside it,
hits the clipping path (HitKind::Outline) before the art it clips:
Direct Selection takes its segments and the Selection tool the clip
group. It applies to a clip group's clipping path and to a layer's
clipping mask, and only to one a click may hit (may_hit: visible,
editable, within reach). Inside the mask the art is hit as before.
The other tools, which act on what is drawn (the eyedropper, type,
gradients...), leave the option off, and hit_test_visible never
applies it, so they still take the art at the mask's edge rather
than the mask's empty appearance. Select Behind's one-pass walk takes
a layer mask's outline first too, so it lists what the repeated hit
test finds. Object Highlighting and a right-click's selection in the
canvas (selection_hit_at) take what those tools take; dropping a
swatch on art still hits what is drawn.
The reference app reshapes a clipping path with Direct Selection:
https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/edit-clipping-masks.html
Tests in doc: a_clipping_path_is_hit_on_its_outline_before_its_art,
only_the_selection_tools_hit_a_clipping_path_on_its_outline and
select_behind_takes_a_layer_masks_outline_first; the random Select
Behind test now clips layers and sublayers and sets clip_outlines,
and fails without the Select Behind change. In tools,
a_press_on_a_clipping_path_takes_its_segment_over_the_art fails
without the change; select::a_press_on_a_masks_edge_selects_its_clip_group
fails when the Selection tool uses the plain options; the eyedropper
copies the art on the edge. Recorded in docs/ui-parity.md,
docs/gaps.md and docs/target-app-parity.md.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
docs/control-protocol.md and the tool.options params text say that 0
turns the Pencil and Paintbrush options Edit selected paths within and
Close paths when ends are within off. A stroke that
started exactly on an end of a selected open path still continued that
path at 0, and a stroke that ended exactly where it started was still
closed at 0, because both checks compared the distance with <= 0.
At 0, a stroke that starts on an end of a selected open path now draws
a new path, as one that starts on the path away from its ends already
did, and a stroke that ends where it started stays open.
Docs: docs/target-app-parity.md.
testutil: :cx defaults measurement_labels on, so the aspect-ratio drag assertion must not expect a lone Marquee overlay.
Co-authored-by: Cursor <cursoragent@cursor.com>
Honor Show Measurement Labels on the file Place cursor so sizing drags get W/H readouts like other draw tools.
Co-authored-by: Cursor <cursoragent@cursor.com>
Found while testing crates/tools/src/xform/measure.rs:20 (shown at :66, reported at :80)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Match the Selection tool: Shift on an already selected hit removes it on
release without a drag, including one path of a compound path.
Co-authored-by: Cursor <cursoragent@cursor.com>
A lone object selected with ruler guides can be the key (the Selection
tool's click on it), and Align to the key moves the other selected
guides to its edge or centre: vertical guides to left, centre or right,
horizontal ones to top, centre or bottom. Parallel guides line up on a
key guide. Aligning to the selection or the artboard leaves guides alone.
A click with the Selection tool on a guide selected with objects makes
it the key (select.key {guide}), drawn thicker; a click on it again
cancels it. Align to Key Object is on with one object and a selected
guide and makes the guide the key. object.align lines the objects (or
selected anchors) up on the guide's line and leaves the guide where it
is: a vertical guide sets x only, a horizontal one y. The selection
keeps key_guide beside key, never both; it goes with the guide or the
objects, and follows the guide when earlier guides are deleted.
Distribute Spacing doesn't space from a key guide.
Honor Measurement Labels while moving gradient-mesh and mesh-envelope
points with the Mesh tool or Direct Selection.
Co-authored-by: Cursor <cursoragent@cursor.com>
Honor Measurement Labels during envelope-style corner drags on the Free Transform tool, matching the Move handle readout.
Co-authored-by: Cursor <cursoragent@cursor.com>
Wire the View menu toggle and add glyph baselines and bounds to drawing snap targets so point type placement lines up with existing text when Smart Guides are on.
Co-authored-by: Cursor <cursoragent@cursor.com>
Keyboard increment (Shift ×10) moves the active glyph through text.touchType, with vertical type remapping like the Type tool.
Co-authored-by: Cursor <cursoragent@cursor.com>
Hide the preview line and readout when Smart Guides › Measurement Labels is off; options still report the last drag.
Co-authored-by: Cursor <cursoragent@cursor.com>
Tools that showed distance or angle overlays during drags now respect the
Units preference, consistent with shape and selection tools.
Co-authored-by: Cursor <cursoragent@cursor.com>
Match corner-radius and pie-widget drags: the Selection tool reports a transforming drag so the canvas drops the sharp selection box during bracket moves.
Co-authored-by: Cursor <cursoragent@cursor.com>
Measurement labels while drawing now match the Measure tool (D, angle, dX/dY) when the preference is on.
Co-authored-by: Cursor <cursoragent@cursor.com>
Smart Guides spacing now applies when scaling with the Selection and Free Transform tools, not only on moves.
Co-authored-by: Cursor <cursoragent@cursor.com>
Zooming in on an inch ruler stepped through 0.2, 0.1 and 0.05 of an inch:
`Unit::ruler_step` picked from one decimal ladder for every unit, and the
rulers divided whatever it returned by ten. A tenth of an inch is not a mark an
inch ruler carries (#2827).
Below an inch the steps are binary fractions now, 1/2 down to 1/128, and
`Unit::ruler_minor` gives the unlabelled ticks between two labels: an eighth of
the step on an inch ruler, so every tick is a binary fraction of an inch and
they stay about as dense as the tenths elsewhere. An inch and wider keeps the
shared ladder, and the metric units, points, pixels and picas keep their
tenths. Ruler-tick snapping follows the ticks that are drawn, which moves an
inch document's Shift-drag of a guide onto the same grid.
`ruler_label` took its decimals from `(-step.log10()).ceil()`, which writes
1/16 as "0.06" and 1/8 as "0.13" -- wrong values, not just rounded ones. It
uses the fewest decimals that write the step exactly, and drops trailing zeros
as `Unit::number` does, so a sixteenth-inch ruler labels the inch `1` rather
than spelling `1.0000` down six lines of the vertical ruler.
With the Anchor Point tool, a drag from an anchor pulls out smooth handles.
Shift held at the press now moves the anchor instead, its handles keeping
their offsets; Shift kept down during the drag keeps the move at 45° steps
(the constrain angle), released it frees it. A Shift-click that doesn't
move leaves the anchor as it was. A selected blend's spine points follow
the same rule (object.blend.spine.moveAnchor).
The move goes through a new engine command, path.moveAnchor
{id, subpath?, anchor, x, y}, since path.moveAnchors only acts on the
direct-selected anchors and the Anchor Point tool keeps no anchor selection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VectorCraft had no Objects on Path. A click with the Objects on Path
tool attaches the selected objects to the path clicked. The tool is in
Edit Toolbar's Modify section and the Blend tool's flyout, and Object ›
Objects on Path › Attach and the context menu with two or more objects
selected choose it. The objects and the path go into an Objects on
Path group with the path at its bottom.
- object.objectsOnPath.attach orders the objects column by column from
left to right and top to bottom in each column. The first one sits
on the path's first anchor, the last one sits on the end of an open
path, and the rest are spread evenly by length; a closed path takes
them all the way round. Each object's attachment point (Pivot, one
of nine, default the center of its bounds without strokes) sits on
the path, and the object is turned to the path there. An object that
was rotated by an angle other than a multiple of 90° is first turned
to the nearest multiple of 90°. Rotate tilts every object by that
many degrees counter-clockwise. Symbol instances are measured by
their art. Guides, compound paths, type, paths without length and
graphs are refused with a reason.
- After every edit the objects go back on the path: editing the path
moves them, an object moved on its own goes back, and objects moved
into or out of the group in the Layers panel join the end of the
path or leave it. Scaling, shearing or reflecting the whole group
keeps each object's scale, shear or reflection, and the objects are
spread evenly along the transformed path again. Undo takes the path
and the objects back in one step, and a copied group stays live on
its own path.
- object.objectsOnPath.options changes Pivot and Rotate, and
object.objectsOnPath.expand leaves a plain group. Both act on the
groups that `ids` names, or on the groups of the selection when
`ids` is not given.
- The native file keeps the group live; SVG, PDF and the other exports
write plain art. The tool highlights the path under the pointer, has
a cursor entry, and Escape during a press cancels the click.
Part of #2545
Docs: docs/mcp.md, docs/target-app-parity.md.
Double-clicking the Live Paint Selection tool did nothing, and the tool
always selected faces and edges and highlighted them in red at 4 px.
It now opens Live Paint Selection Tool Options with Select Fills,
Select Strokes, Color and Width, the options the reference app's help
page on selecting objects lists.
- With Select Fills off, a click selects the edge nearest the pointer,
as Alt-click does. With Select Strokes off, Alt-click selects the
face under the pointer, as a click does. With both off, a click
selects no face or edge.
- Color (a layer color or a custom color) and Width (1 to 20 px) set
the highlight on the face or edge under the pointer.
- The defaults are the earlier behavior: both selections on, red
(#ff2a2a) and 4 px. The options are tool options (tool.setOption
{tool: "livePaintSelection"}): they last across tool switches and
are saved with the preferences.
Docs: docs/mcp.md, docs/target-app-parity.md.
While a Type tool edited text, a click on blank canvas ended the
editing and also placed new point type at the click. Area Type and
Type on a Path did the same with a click on a path.
Such a click now only ends the editing. The typing session is
committed, empty point type that a click placed is discarded as before,
and the Type tool in use stays active; the next click makes new type.
The same applies when the editing began with a double-click from a
selection tool. A click on other type still edits it at once, and a
drag that draws an area-type frame still makes the frame.
path.freehand takes redraw: {id, within}: the stroke's ends are placed on the path, anchors added there, and the section between them replaced by the fitted stroke, the shorter way round a closed subpath; a stroke that doesn't end on the path still draws a new one. The Pencil and Paintbrush send it for a stroke that starts on a selected path's outline away from its ends, within Edit Selected Paths' distance.