mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 05:37:23 -06:00
Merge pull request #4850 from Taha-Mahmoodi/feat/snap-tangent-perpendicular-tools
Snapping: a line end dragged with Direct Selection snaps square to straight paths and tangent to curves (#2530)
This commit is contained in:
@@ -180,6 +180,10 @@ pub struct DirectSelectionTool {
|
||||
guides: Vec<Overlay>,
|
||||
/// What the dragged anchors snap to, gathered when the drag begins.
|
||||
anchor_snap: Option<PointSnap>,
|
||||
/// With one open path's end anchor dragged along a straight segment, that segment's other end:
|
||||
/// the dragged end may land square to a straight path or grazing a curve (Smart Guides ›
|
||||
/// Geometric Guides). Set when the drag begins.
|
||||
line_end: Option<Leave>,
|
||||
/// What the anchor holding paths moved whole snaps to while Alt makes a copy: the original left
|
||||
/// behind too. Gathered with [`Self::anchor_snap`], before the previews move the original.
|
||||
copy_snap: Option<PointSnap>,
|
||||
@@ -227,6 +231,7 @@ impl DirectSelectionTool {
|
||||
mesh: MeshEdit::default(),
|
||||
guides: vec![],
|
||||
anchor_snap: None,
|
||||
line_end: None,
|
||||
copy_snap: None,
|
||||
handle_snap: HandleSnap::default(),
|
||||
move_snap: None,
|
||||
@@ -406,6 +411,38 @@ fn anchor_owners(cx: &ToolContext, f: fn(&Node) -> bool) -> Vec<NodeId> {
|
||||
v
|
||||
}
|
||||
|
||||
/// Where a line's end anchor `grab`bed for a drag leaves from, when it is one: the only anchor
|
||||
/// selected, the end of an open subpath whose one segment is straight, and no other art moving
|
||||
/// with it. That segment's other end stays put, so the end snaps square to straight paths and
|
||||
/// grazing curves; mid-line anchors and ends of curved segments do not (Illustrator limits Snap
|
||||
/// to Tangent and Snap to Perpendicular to line endpoints).
|
||||
fn line_end_leave(cx: &ToolContext, grab: Point) -> Option<Leave> {
|
||||
let sel = cx.selection;
|
||||
if !sel.segments.is_empty() || sel.anchors.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
let (&id, set) = sel.anchors.iter().next()?;
|
||||
if set.len() != 1 || sel.objects.iter().any(|o| *o != id) {
|
||||
return None;
|
||||
}
|
||||
let &(si, ai) = set.iter().next()?;
|
||||
let sp = cx.doc.node(id)?.path_data()?.subpaths.get(si)?;
|
||||
let n = sp.anchors.len();
|
||||
if sp.closed || ai >= n || sp.anchors[ai].p.distance(grab) > 1e-6 {
|
||||
return None;
|
||||
}
|
||||
let (other, seg) = if n < 2 {
|
||||
return None;
|
||||
} else if ai == 0 {
|
||||
(1, 0)
|
||||
} else if ai == n - 1 {
|
||||
(n - 2, n - 2)
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
sp.segment_is_line(seg).then(|| Leave::contact(sp.anchors[other].p))
|
||||
}
|
||||
|
||||
/// The anchor within `tol` of `p` nearest to it, of the topmost selected path or area type frame
|
||||
/// that has one, else of the topmost visible one: (id, si, ai, where it is). Selected objects come
|
||||
/// first so an object above can't take the press from the one being edited (#1508), as
|
||||
@@ -1230,6 +1267,7 @@ impl DirectSelectionTool {
|
||||
out.push(Action::Begin("Move".into()));
|
||||
self.state = State::MoveAnchors { start, grab, began: true };
|
||||
self.anchor_snap = Some(PointSnap::new(cx, || Targets::for_anchor_drag(cx.doc, cx.selection)));
|
||||
self.line_end = line_end_leave(cx, grab);
|
||||
}
|
||||
let mut d = move_delta(start, p, ev.mods.shift, cx.constrain_angle);
|
||||
let unsnapped = d;
|
||||
@@ -1238,7 +1276,7 @@ impl DirectSelectionTool {
|
||||
self.guides.clear();
|
||||
if let Some(snap) = &self.anchor_snap {
|
||||
let shift = ev.mods.shift.then(|| Leave::segment(cx, grab, true));
|
||||
let (q, guides) = snap.snap_from(cx, grab + (p - start), shift.as_ref());
|
||||
let (q, guides) = snap.snap_from(cx, grab + (p - start), shift.as_ref().or(self.line_end.as_ref()));
|
||||
(d, self.guides) = (q - grab, guides);
|
||||
}
|
||||
self.snapped = snapped(d, unsnapped, ev.mods.shift, &self.guides);
|
||||
|
||||
@@ -1477,6 +1477,101 @@ fn dragged_end_of_a_slanted_line_stays_on_its_direction() {
|
||||
assert!(matches!(guides[..], [(a, b)] if a == Point::new(300.0, 400.0) && (b - Point::new(420.0, 310.0)).hypot() < 1e-9), "{guides:?}");
|
||||
}
|
||||
|
||||
/// Drawn lines snap square to a straight path and grazing a curve (Smart Guides › Geometric
|
||||
/// Guides); so does an existing line whose end anchor is dragged with Direct Selection. Each
|
||||
/// option switches its own snap off. Only a line's end snaps so: the middle anchor of a
|
||||
/// polyline, an end of a curved segment, a closed path's anchor, several anchors dragged together,
|
||||
/// a Shift drag and an end out of reach do not, nor does Group Selection, which moves the path whole.
|
||||
#[test]
|
||||
fn dragged_line_ends_snap_perpendicular_to_segments_and_tangent_to_curves() {
|
||||
use vectorcraft_geom::{BezPath, PathData, SubPath, shapes};
|
||||
let near = |v: &serde_json::Value, (x, y): (f64, f64), eps: f64| (v["dx"].as_f64().unwrap() - x).hypot(v["dy"].as_f64().unwrap() - y) < eps;
|
||||
// The rect's right side (x = 200) and a circle (centre (500, 500), radius 50), with one more
|
||||
// path: its id, and the document.
|
||||
let with = |path: PathData| {
|
||||
let (mut d, _) = doc_with_rect();
|
||||
let l = d.layers[0].id;
|
||||
let (circle, id) = (d.alloc_id(), d.alloc_id());
|
||||
let ring =
|
||||
vectorcraft_doc::Node::path(circle, shapes::ellipse(Rect::new(450.0, 450.0, 550.0, 550.0)), vectorcraft_doc::Appearance::default_art());
|
||||
d.insert(Some(l), 1, ring).unwrap();
|
||||
d.insert(Some(l), 2, vectorcraft_doc::Node::path(id, path, vectorcraft_doc::Appearance::default_art())).unwrap();
|
||||
(d, id)
|
||||
};
|
||||
let p = paint();
|
||||
let none = Mods::default();
|
||||
let mut t = DirectSelectionTool::new(false);
|
||||
// A line (300, 160)–(260, 170), its end dragged to 3.6 pt from the foot of the perpendicular
|
||||
// from (300, 160) to the rect's right side: (200, 160).
|
||||
let (d, id) = with(shapes::line(Point::new(300.0, 160.0), Point::new(260.0, 170.0)));
|
||||
let end = anchors_of(id, &[(0, 1)]);
|
||||
let c = cx(&d, &end, &p);
|
||||
let (v, labels) = drag(&mut t, &c, (260.0, 170.0), (202.0, 163.0), none);
|
||||
assert!(near(&v, (-60.0, -10.0), 1e-6) && labels == ["perpendicular"], "{v} {labels:?}");
|
||||
// The other end snaps the same way about this one: square to the side at (200, 170).
|
||||
let start = anchors_of(id, &[(0, 0)]);
|
||||
let c = cx(&d, &start, &p);
|
||||
let (v, labels) = drag(&mut t, &c, (300.0, 160.0), (202.0, 172.0), none);
|
||||
assert!(near(&v, (-100.0, 10.0), 1e-6) && labels == ["perpendicular"], "{v} {labels:?}");
|
||||
// Perpendicular off: the end only lands on the path, where the pointer is; tangent off does
|
||||
// not change that.
|
||||
let c = ToolContext { snap_perpendicular: false, ..cx(&d, &end, &p) };
|
||||
let (v, labels) = drag(&mut t, &c, (260.0, 170.0), (202.0, 163.0), none);
|
||||
assert!(near(&v, (-60.0, -7.0), 1e-6) && labels == ["path"], "{v} {labels:?}");
|
||||
let c = ToolContext { snap_tangent: false, ..cx(&d, &end, &p) };
|
||||
assert_eq!(drag(&mut t, &c, (260.0, 170.0), (202.0, 163.0), none).1, ["perpendicular"]);
|
||||
// With Smart Guides off, Snap to Point alone finds no contact.
|
||||
let c = ToolContext { smart_guides: false, ..cx(&d, &end, &p) };
|
||||
assert_eq!(drag(&mut t, &c, (260.0, 170.0), (202.0, 163.0), none).1, Vec::<String>::new());
|
||||
// Out of reach: left alone.
|
||||
let c = cx(&d, &end, &p);
|
||||
let (v, labels) = drag(&mut t, &c, (260.0, 170.0), (240.0, 140.0), none);
|
||||
assert!(near(&v, (-20.0, -30.0), 1e-9) && labels.is_empty(), "{v} {labels:?}");
|
||||
// Shift holds the move to its 45° step: no contact.
|
||||
let shift = Mods { shift: true, ..none };
|
||||
assert!(!drag(&mut t, &c, (260.0, 170.0), (202.0, 163.0), shift).1.contains(&"perpendicular".to_string()));
|
||||
// Group Selection moves the path whole: no contact either.
|
||||
let mut group = DirectSelectionTool::new(true);
|
||||
assert!(!drag(&mut group, &c, (260.0, 170.0), (202.0, 163.0), none).1.contains(&"perpendicular".to_string()));
|
||||
|
||||
// A line (400, 500)–(420, 470) toward the circle: its end lands where the line from (400, 500)
|
||||
// grazes it, at (475, 456.7).
|
||||
let (d, id) = with(shapes::line(Point::new(400.0, 500.0), Point::new(420.0, 470.0)));
|
||||
let end = anchors_of(id, &[(0, 1)]);
|
||||
let c = cx(&d, &end, &p);
|
||||
let (v, labels) = drag(&mut t, &c, (420.0, 470.0), (477.0, 459.0), none);
|
||||
assert!(near(&v, (55.0, 500.0 - 1875f64.sqrt() - 470.0), 0.1) && labels == ["tangent"], "{v} {labels:?}");
|
||||
let c = ToolContext { snap_tangent: false, ..cx(&d, &end, &p) };
|
||||
let (v, labels) = drag(&mut t, &c, (420.0, 470.0), (477.0, 459.0), none);
|
||||
assert!(near(&v, (55.53, -13.61), 0.01) && labels == ["path"], "the end only lands on the circle: {v} {labels:?}");
|
||||
let c = ToolContext { snap_perpendicular: false, ..cx(&d, &end, &p) };
|
||||
assert_eq!(drag(&mut t, &c, (420.0, 470.0), (477.0, 459.0), none).1, ["tangent"]);
|
||||
|
||||
// Not a line's end: the middle anchor of a polyline, and an anchor of a closed path.
|
||||
let three = [Point::new(300.0, 160.0), Point::new(260.0, 170.0), Point::new(240.0, 230.0)];
|
||||
let (d, id) = with(PathData::single(SubPath::polyline(&three, false)));
|
||||
let mid = anchors_of(id, &[(0, 1)]);
|
||||
let c = cx(&d, &mid, &p);
|
||||
assert_eq!(drag(&mut t, &c, (260.0, 170.0), (202.0, 163.0), none).1, ["path"]);
|
||||
let (d, id) = with(PathData::single(SubPath::polyline(&three, true)));
|
||||
let corner = anchors_of(id, &[(0, 1)]);
|
||||
let c = cx(&d, &corner, &p);
|
||||
assert_eq!(drag(&mut t, &c, (260.0, 170.0), (202.0, 163.0), none).1, ["path"]);
|
||||
// The end of a curved segment: no contact either.
|
||||
let mut curve = BezPath::new();
|
||||
curve.move_to(Point::new(300.0, 160.0));
|
||||
curve.curve_to(Point::new(290.0, 150.0), Point::new(270.0, 160.0), Point::new(260.0, 170.0));
|
||||
let (d, id) = with(PathData::from_bezpath(&curve));
|
||||
let end = anchors_of(id, &[(0, 1)]);
|
||||
let c = cx(&d, &end, &p);
|
||||
assert_eq!(drag(&mut t, &c, (260.0, 170.0), (202.0, 163.0), none).1, ["path"]);
|
||||
// A line end dragged with another anchor selected: the pair moves rigidly, no contact.
|
||||
let (d, id) = with(shapes::line(Point::new(300.0, 160.0), Point::new(260.0, 170.0)));
|
||||
let both = anchors_of(id, &[(0, 0), (0, 1)]);
|
||||
let c = cx(&d, &both, &p);
|
||||
assert!(!drag(&mut t, &c, (260.0, 170.0), (202.0, 163.0), none).1.contains(&"perpendicular".to_string()));
|
||||
}
|
||||
|
||||
/// Group Selection (and Direct Selection on a whole path) moves objects snapping as the
|
||||
/// Selection tool does: B's left side lines up with A's right one.
|
||||
#[test]
|
||||
|
||||
@@ -230,6 +230,14 @@ impl Leave {
|
||||
self
|
||||
}
|
||||
|
||||
/// The straight segment from the anchor `at` to a point being dragged (an open path's end
|
||||
/// moved with Direct Selection): the point may land square to a segment or grazing a curve,
|
||||
/// but is not held to Shift's angles or the construction guides, which a moved anchor
|
||||
/// does not follow.
|
||||
pub fn contact(at: Point) -> Self {
|
||||
Self { at, shift: None, angles: &[], contact: true }
|
||||
}
|
||||
|
||||
/// A box dragged from its corner or centre `at` (Rectangle, Ellipse, Arc, the grids, and
|
||||
/// Artboard, which passes no Shift): Shift keeps the dragged corner on a diagonal, the box a
|
||||
/// square.
|
||||
|
||||
+15
-6
@@ -1,6 +1,6 @@
|
||||
# Gaps
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (G1: Grid Repeat Options has a Preview checkbox, #2651; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (G13: Direct Selection snaps a dragged line end square to straight paths and grazing curves; G13: gradient presets done; G1: Average & Join with a corner or smooth choice, Cmd+Alt+Shift+J, done; estimate unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
@@ -465,15 +465,23 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
|
||||
perceptual mix with dither's noise (an image with the noise in SVG and PDF); a stroke has no noise.
|
||||
- **Missing (asked for by users, from earlier versions):** radial gradients in Image Trace's
|
||||
Gradients option, which traces linear ones only (draft #1849). Inside the estimate below.
|
||||
- **Partial (#2530):** tangent snapping to curves and perpendicular snapping to straight target
|
||||
segments for drawn lines. Controls are saved under Preferences › Smart Guides › Geometric Guides
|
||||
and available from Control bar › Snapping; both default to on. Existing-line endpoint editing and
|
||||
other tool cases remain unverified. [Public source](https://helpx.adobe.com/illustrator/desktop/measure-and-align/plot-and-measure/snap-to-perpendicular.html).
|
||||
- **Done (#2530):** tangent snapping to curves and perpendicular snapping to straight target
|
||||
segments, as the public [perpendicular](https://helpx.adobe.com/illustrator/desktop/measure-and-align/plot-and-measure/snap-to-perpendicular.html)
|
||||
and [tangent](https://helpx.adobe.com/illustrator/desktop/measure-and-align/plot-and-measure/snap-to-tangent.html)
|
||||
pages describe, for the Line Segment and Pen tools' new lines and for existing lines: dragging the end
|
||||
anchor of an open path's straight segment with Direct Selection lands it square to
|
||||
another straight path (the foot of the perpendicular from the segment's other end) or grazing a curve, with
|
||||
the "perpendicular" or "tangent" label (and the square-corner mark) Smart Guides show for drawn lines. As
|
||||
the pages say, only line endpoints snap: the middle anchor of a path, the end of a curved segment, a closed
|
||||
path's anchor, several anchors dragged together and Shift-constrained drags (which keep their angle step) do
|
||||
not, and a curve is never a perpendicular target. Controls are saved under Preferences › Smart Guides ›
|
||||
Geometric Guides and available from Control bar › Snapping; both default to on. Tests:
|
||||
`dragged_line_ends_snap_perpendicular_to_segments_and_tangent_to_curves` in `crates/tools/src/direct/tests.rs`.
|
||||
- **Done (2026-10-30):** gradient presets (30.0): the Gradient panel's Presets menu applies one of the
|
||||
built-in gradient libraries' presets (by theme) or a saved one to the gradient behind the active proxy, keeping
|
||||
its placement, as one undo step; the panel menu saves the gradient as a preset (kept with the preferences) and
|
||||
deletes saved ones; `gradient.presets.*` commands.
|
||||
- **Missing:** remaining tangent/perpendicular snapping cases and the consolidated
|
||||
- **Missing:** the consolidated
|
||||
snapping options (30.0); artboard colors in exports and print, and managing artboards from the canvas other
|
||||
than locking and unlocking them from the Artboard tool's right-click menu (30.0); the Blend panel with ease
|
||||
in/out and separate colour acceleration (30.7);
|
||||
@@ -667,6 +675,7 @@ trap, and the defaults (0.25 pt, 100%, 40%) are VectorCraft's own, the pages giv
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-31 | minor | G13: Direct Selection snaps the end of an existing straight line, dragged by its end anchor, square to straight paths and grazing curves (Smart Guides › Geometric Guides: Snap to Perpendicular and Snap to Tangent), the case the public Snap to Perpendicular and Snap to Tangent pages add to drawing with the Line Segment and Pen tools (#2530). Scores and estimates unchanged. |
|
||||
| 2026-10-31 | minor | G8: a gradient mesh read from a file is budgeted: at most about a million quads per tessellation, and a mesh with more patches than that is malformed (#3592); estimate unchanged. |
|
||||
| 2026-10-30 | minor | G9: `.ai`/EPS editing data: a blend takes the transparency (opacity, blend mode, knockout) in its state block after its record, so its steps knock each other out as the page draws them; a user's file comes in 0.6% from its page instead of 1.3%. Scores and estimates unchanged. |
|
||||
| 2026-10-30 | minor | G1: Object › Path › Average & Join… (`path.averageJoin`, Cmd+Alt+Shift+J) moves two open path ends to their midpoint and joins them there, the joined anchor a corner or a smooth point as its dialog's Points choice asks, in one undo step; the "Average and Join in one step" entry is done. Estimate unchanged |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Parity with Adobe Illustrator
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (a width-profile stroke keeps its shape when the view zooms out, #995) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (G13: Direct Selection snaps a dragged line end square to straight paths and grazing curves; Path Precision Tools add-on: Shared Edge Merge Tool; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
@@ -141,10 +141,10 @@ dominate; 3D, graphs and automation are specialist). They sum to 105. Feature de
|
||||
| File formats | 6 | 75% | `.ai` files open with a flattened or wrong layer structure in real cases (#951), guides lost (#779), no writing of Illustrator's editing data (#868), FreeHand/CorelDRAW/DWG/HEIC/JPEG 2000 import, 3D export; see [file-format-parity.md](file-format-parity.md); EPS legacy mesh/opacity-mask and raster comparison follow-up verified | 10–20 | estimated (lowered from 85%: open issues); DXF control-point SPLINE import closure regression-tested (#1299 follow-up after #1320); PDF writer metadata, reader streaming and immutable backend checks in `crates/pdf/src/tests.rs`, `crates/pdf/src/tests/reader_streaming.rs` and `xtask/src/pdf_deps.rs`; PDF Place and clipboard paste retain live text (synthetic regressions); EPS ordinary Save retains the page frame and editable document (2026-10-26); `.ai`/EPS editing data checked against a user's files (2026-10-30): case setting (`ate::tests`, `tests_native::type_from_the_text_document_keeps_its_case_setting`), placed-file placements (`tests_native::a_small_placed_file_goes_where_it_draws_closer_to_the_page`), pages cropped to the art (`tests_native::type_on_the_copies_of_a_page_cropped_to_the_art_is_read_once`), mesh corner weights and state (`ai::mesh::tests::corners_with_handle_weights_of_zero_are_read`, `ai::tests::a_gradient_mesh_takes_the_transparency_written_after_it`), type on a path spacing (`tests_native::type_round_a_circle_is_spaced_as_the_file_lays_it_out`), blend state (`ai::tests::a_blends_transparency_follows_its_record`); scores and estimates unchanged. |
|
||||
| View & navigation | 3 | 70% | multiple windows and Arrange | 9–14 | estimated; document-tab artwork hover/copy and lossless transfer tested (2026-10-22; score/hours unchanged); cached canvas reprojection preserves scale during resize, rotation and DPI changes (regression and contributor original-file evidence below; scores/hours unchanged) |
|
||||
| View & navigation | 3 | 70% | multiple windows and Arrange | 9–14 | estimated |
|
||||
| Guides, grids, smart guides, snapping, rulers | 3 | 80% | 30.0's tangent snapping to curves and perpendicular snapping to straight segments for lines drawn with the Pen, Curvature and Line Segment tools (Preferences › Smart Guides › Geometric Guides and Control bar › Snapping, #2530; other tool and drag cases remain), "intersect" smart guides for a moved selection's anchors (drawn points and dragged anchors snap to intersections, #1658), distance readouts while drawing | 4–7 | estimated (lowered from 85%: target moved); new ruler-guide Alt orientation implemented; video rulers (View › Rulers › Show Video Rulers, #2849) done, score unchanged |
|
||||
| Guides, grids, smart guides, snapping, rulers | 3 | 80% | 30.0's tangent snapping to curves and perpendicular snapping to straight segments for lines drawn with the Pen, Curvature and Line Segment tools (Preferences › Smart Guides › Geometric Guides and Control bar › Snapping, #2530; other tool and drag cases remain), "intersect" smart guides for a moved selection's anchors (drawn points and dragged anchors snap to intersections, #1658), global/artboard ruler mode switch, distance readouts while drawing | 4–7 | estimated (lowered from 85%: target moved); new ruler-guide Alt orientation implemented; video rulers (View › Rulers › Show Video Rulers, #2849) done, score unchanged |
|
||||
| Guides, grids, smart guides, snapping, rulers | 3 | 80% | 30.0's tangent snapping to curves and perpendicular snapping to straight segments for lines drawn with the Pen, Curvature and Line Segment tools (Preferences › Smart Guides › Geometric Guides and Control bar › Snapping, #2530; other tool and drag cases remain), "intersect" smart guides for a moved selection's anchors (drawn points and dragged anchors snap to intersections, #1658), video rulers, distance readouts while drawing | 4–7 | estimated (lowered from 85%: target moved); new ruler-guide Alt orientation implemented; status-bar X/Y uses the active ruler origin and General units |
|
||||
| Guides, grids, smart guides, snapping, rulers | 3 | 80% | 30.0's tangent snapping to curves and perpendicular snapping to straight segments for lines drawn with the Pen, Curvature and Line Segment tools (Preferences › Smart Guides › Geometric Guides and Control bar › Snapping, #2530; other tool and drag cases remain), "intersect" smart guides for a moved selection's anchors (drawn points and dragged anchors snap to intersections, #1658), global/artboard ruler mode switch and video rulers, distance readouts while drawing | 4–7 | estimated (lowered from 85%: target moved); new ruler-guide Alt orientation implemented; status-bar X/Y uses the active ruler origin and General units |
|
||||
| Guides, grids, smart guides, snapping, rulers | 3 | 80% | "intersect" smart guides for a moved selection's anchors (drawn points and dragged anchors snap to intersections, #1658), distance readouts while drawing | 4–7 | estimated (lowered from 85%: target moved); new ruler-guide Alt orientation implemented; video rulers (View › Rulers › Show Video Rulers, #2849) done, score unchanged |
|
||||
| Guides, grids, smart guides, snapping, rulers | 3 | 80% | "intersect" smart guides for a moved selection's anchors (drawn points and dragged anchors snap to intersections, #1658), global/artboard ruler mode switch, distance readouts while drawing | 4–7 | estimated (lowered from 85%: target moved); new ruler-guide Alt orientation implemented; video rulers (View › Rulers › Show Video Rulers, #2849) done, score unchanged |
|
||||
| Guides, grids, smart guides, snapping, rulers | 3 | 80% | "intersect" smart guides for a moved selection's anchors (drawn points and dragged anchors snap to intersections, #1658), video rulers, distance readouts while drawing | 4–7 | estimated (lowered from 85%: target moved); new ruler-guide Alt orientation implemented; status-bar X/Y uses the active ruler origin and General units |
|
||||
| Guides, grids, smart guides, snapping, rulers | 3 | 80% | "intersect" smart guides for a moved selection's anchors (drawn points and dragged anchors snap to intersections, #1658), global/artboard ruler mode switch and video rulers, distance readouts while drawing | 4–7 | estimated (lowered from 85%: target moved); new ruler-guide Alt orientation implemented; status-bar X/Y uses the active ruler origin and General units |
|
||||
| Export for Screens, Asset Export, slices, Save for Web | 3 | 85% | anti-aliasing ghost borders (#983); TIFF choices and settings are implemented | 2–4 | estimated |
|
||||
| Print, colour management, separations, flattener | 3 | 80% | print preview fidelity | 2–4 | estimated |
|
||||
| Automation (Actions, Variables, scripting, batch) | 3 | 60% | a scripting surface (Illustrator: JavaScript/ExtendScript), Batch, image and graph variables, dataset import/export | 8–14 | estimated; MCP `add_text` exposes existing vertical type; score and hours unchanged |
|
||||
@@ -1361,6 +1361,7 @@ add-on.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-31 | minor | Guides and snapping: Snap to Perpendicular and Snap to Tangent apply to an existing line's end dragged with Direct Selection, as the public pages describe for editing existing lines (the end anchor of a straight segment, not mid-line anchors or curved segments); a regression test covers both snaps, both toggles and non-snapping controls (#2530); scores and estimates unchanged. |
|
||||
| 2026-10-30 | minor | File formats: `.ai`/EPS editing data: a blend takes the transparency (opacity, blend mode, knockout) in its state block after its record, so its steps knock each other out as the page draws them; a user's file comes in 0.6% from its page instead of 1.3%. Scores and estimates unchanged. |
|
||||
| 2026-10-31 | minor | Path Precision Tools add-on: the Shared Edge Merge Tool and Keep Shared Points Together; evidence added; scores unchanged. |
|
||||
| 2026-10-31 | minor | Path Precision Tools add-on: the Corner Tool and Slide Anchor Along Path; evidence added; scores unchanged. |
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
# UI and interaction parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (G1: Grid Repeat Options has a Preview checkbox, #2651; scores unchanged; previously: Path Precision Tools add-on: Shared Edge Merge Tool; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (Direct Selection snaps a dragged line end square to straight paths and grazing curves; Path Precision Tools add-on: Shared Edge Merge Tool; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
@@ -152,7 +152,7 @@ tools, never checked side by side) isn't captured row by row.
|
||||
| Snapping tolerance preference | done | |
|
||||
| Snap to Point, Snap to Grid, Snap to Pixel; Pixel Preview | done | #1840: with Snap to Grid on, anchors and objects snap to the grid even when Snap to Pixel is also ticked (#1816 waits for the reporter) |
|
||||
| Snap to Glyph | done | View › Snap to Glyph (`view.snapToGlyph`); new type and drawing snap to live-text baselines and glyph bounds when Smart Guides are on (#3124). Character panel menu › Show Snap to Glyph Options: a checkbox for each guide (Baseline, x-height, Glyph Bounds, Proximity Guides, Angular Guides, Anchor Point), dimmed while Snap to Glyph is off (#4232) |
|
||||
| Tangent and perpendicular snapping for line endpoints (new in 30.0) | partial | #2530: tangent snapping to curves and perpendicular snapping to straight segments for drawn lines; saved Preferences › Smart Guides › Geometric Guides controls, on by default. Other tool/drag cases remain. [Public source](https://helpx.adobe.com/illustrator/desktop/measure-and-align/plot-and-measure/snap-to-perpendicular.html) |
|
||||
| Tangent and perpendicular snapping for line endpoints (new in 30.0) | done | #2530: tangent snapping to curves and perpendicular snapping to straight segments for lines drawn with the Line Segment, Pen and Curvature tools, and for an existing line whose end anchor is dragged with Direct Selection (the only anchor selected, an open path's end on a straight segment; Shift keeps its angle step instead; Group Selection moves the path whole and does not snap so); mid-line anchors, curved segments' ends and curves as perpendicular targets are excluded, as the pages say. Saved Preferences › Smart Guides › Geometric Guides controls, on by default. [Perpendicular](https://helpx.adobe.com/illustrator/desktop/measure-and-align/plot-and-measure/snap-to-perpendicular.html) and [tangent](https://helpx.adobe.com/illustrator/desktop/measure-and-align/plot-and-measure/snap-to-tangent.html) pages; test `dragged_line_ends_snap_perpendicular_to_segments_and_tangent_to_curves` |
|
||||
| Snapping panel/popover consolidating the snap options (30.0) | partial | Control bar › Snapping provides quick access through existing view commands and prefs.set; no View › Snapping submenu. Floating-panel parity remains unverified. |
|
||||
| Customisable rotation snap angle (#973) | missing | Illustrator: Constrain Angle and construction angles only |
|
||||
| Ruler guides: drag out, select, move, delete, lock, Make/Release Guides | done | #414, #451; marquee selects unlocked ruler guides |
|
||||
@@ -376,6 +376,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-31 | minor | Smart guides and snapping: Snap to Perpendicular and Snap to Tangent for an existing line's end dragged with Direct Selection (#2530), so the tangent and perpendicular row is done; scores and estimates unchanged |
|
||||
| 2026-10-31 | minor | Grid Repeat Options has a Preview checkbox (#2651); scores and estimates unchanged |
|
||||
| 2026-10-31 | minor | Path Precision Tools add-on: the Shared Edge Merge Tool and Keep Shared Points Together; scores and estimates unchanged |
|
||||
| 2026-10-31 | minor | Path Precision Tools add-on: live Path Effects in Effect › Path Effects; scores and estimates unchanged |
|
||||
|
||||
Reference in New Issue
Block a user