fix: 複合パスの外観と塗り規則を修正する #1197

This commit is contained in:
Ikki Nishihara
2026-10-12 00:12:52 +07:00
committed by bflatastic
parent bab78767ca
commit fcb1d81ba6
6 changed files with 124 additions and 9 deletions
+12 -2
View File
@@ -69,6 +69,16 @@ fn ruled<'a>(n: &'a Node, out: &mut Vec<(&'a Node, FillRule)>) {
}
}
/// A directly selected compound member fills with its compound's rule. Its other attributes
/// and path direction still belong to the member itself.
fn fill_rule_owner(d: &Document, id: NodeId) -> Option<&Node> {
let n = d.node(id)?;
match d.parent_of(id).and_then(|parent| d.node(parent)) {
Some(parent) if matches!(parent.kind, NodeKind::Compound { .. }) => Some(parent),
_ => Some(n),
}
}
/// The Attributes panel's values of `info.ids` beyond overprinting.
pub(super) fn fill_info(d: &Document, info: &mut AttributesInfo) {
let nodes: Vec<&Node> = info.ids.iter().filter_map(|id| d.node(*id)).collect();
@@ -78,7 +88,7 @@ pub(super) fn fill_info(d: &Document, info: &mut AttributesInfo) {
info.url = same(attrs().map(|a| a.url));
info.note = same(attrs().map(|a| a.note));
let mut all = vec![];
nodes.iter().for_each(|n| ruled(n, &mut all));
nodes.iter().filter_map(|n| fill_rule_owner(d, n.id)).for_each(|n| ruled(n, &mut all));
info.fill_rule = same(all.into_iter().map(|(_, r)| r));
let paths = paths_in(d, &info.ids);
let subpaths = paths.iter().filter_map(|id| d.node(*id)?.path_data()).flat_map(|p| &p.subpaths);
@@ -172,7 +182,7 @@ fn set_fill_rule(s: &mut Session, p: &Value) -> Result<Value> {
let ids = targets(s, p)?;
let changed = edit_counted(s, "Fill Rule", |d| {
let mut all = vec![];
ids.iter().filter_map(|id| d.node(*id)).for_each(|n| ruled(n, &mut all));
ids.iter().filter_map(|id| fill_rule_owner(d, *id)).for_each(|n| ruled(n, &mut all));
let todo: BTreeSet<_> = all.into_iter().filter(|(_, r)| *r != rule).map(|(n, _)| n.id).collect();
for id in &todo {
if let Some(NodeKind::Path { rule: r, .. } | NodeKind::Compound { rule: r, .. }) = d.node_mut(*id).map(|n| &mut n.kind) {
+3 -2
View File
@@ -627,9 +627,10 @@ fn show_all(s: &mut Session, _: &Value) -> Result<Value> {
fn compound_make(s: &mut Session, _: &Value) -> Result<Value> {
let ids = selected_roots(s)?;
let Some(top) = ids.last().copied() else { return Err(EngineError::Other("nothing selected".into())) };
let Some(backmost) = ids.first().copied() else { return Err(EngineError::Other("nothing selected".into())) };
let top = ids.last().copied().unwrap_or(backmost);
let id = s.edit("Make Compound Path", |d, sel| {
let appearance = d.node(top).map(|n| n.appearance.clone()).unwrap_or_default();
let appearance = d.node(backmost).ok_or(EngineError::NoNode(backmost))?.appearance.clone();
let (par, idx, _) = d.position(top).ok_or(EngineError::NoNode(top))?;
let mut paths: Vec<Arc<Node>> = vec![];
for id in &ids {
@@ -0,0 +1,99 @@
//! Compound appearance and fill-rule edits from a directly selected member (#1197).
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use serde_json::{Value, json};
use vectorcraft_doc::{NodeId, NodeKind};
use vectorcraft_engine::Session;
use vectorcraft_geom::{Affine, FillRule, PathData};
use vectorcraft_render::{RenderOptions, Renderer};
fn run(s: &mut Session, command: &str, params: Value) -> Value {
s.execute(command, &params).unwrap()
}
fn rectangles() -> (Session, NodeId, NodeId) {
let mut s = Session::new();
run(&mut s, "file.new", json!({"width": 100, "height": 100}));
let outer = NodeId(run(&mut s, "shape.rectangle", json!({"x": 10, "y": 10, "width": 80, "height": 80}))["id"].as_u64().unwrap());
run(&mut s, "paint.setFill", json!({"color": "#0000ff"}));
run(&mut s, "paint.setStroke", json!({"none": true}));
let inner = NodeId(run(&mut s, "shape.rectangle", json!({"x": 30, "y": 30, "width": 40, "height": 40}))["id"].as_u64().unwrap());
run(&mut s, "paint.setFill", json!({"color": "#ffffff"}));
(s, outer, inner)
}
fn make(s: &mut Session, outer: NodeId, inner: NodeId) -> NodeId {
// Selection order must not decide the compound's appearance or stacking order.
run(s, "select.set", json!({"ids": [inner.0, outer.0]}));
NodeId(run(s, "object.compoundPath.make", json!({}))["id"].as_u64().unwrap())
}
fn path(s: &Session, id: NodeId) -> PathData {
s.doc().unwrap().doc.node(id).unwrap().path_data().unwrap().clone()
}
fn pixels(s: &Session) -> ([u8; 4], [u8; 4]) {
let image = Renderer::new().render(&s.doc().unwrap().doc, 100, 100, Affine::IDENTITY, &RenderOptions::default());
(image.pixel(20, 20), image.pixel(50, 50))
}
#[test]
fn compound_inherits_the_backmost_appearance_and_preserves_contours() {
let (mut s, outer, inner) = rectangles();
let before_outer = path(&s, outer);
let mut reversed_inner = path(&s, inner);
reversed_inner.reverse();
let appearance = s.doc().unwrap().doc.node(outer).unwrap().appearance.clone();
let compound = make(&mut s, outer, inner);
let node = s.doc().unwrap().doc.node(compound).unwrap();
assert_eq!(node.appearance, appearance);
assert!(matches!(node.kind, NodeKind::Compound { rule: FillRule::NonZero, .. }));
assert_eq!(path(&s, outer), before_outer);
assert_eq!(path(&s, inner), reversed_inner);
let (edge, centre) = pixels(&s);
assert!(edge[2] > 200 && edge[0] < 20 && edge[3] > 240, "backmost blue fill: {edge:?}");
assert_eq!(centre[3], 0, "the inner contour cuts a hole");
run(&mut s, "edit.undo", json!({}));
assert!(s.doc().unwrap().doc.node(compound).is_none());
assert_eq!(s.doc().unwrap().doc.node(outer).unwrap().appearance, appearance);
run(&mut s, "edit.redo", json!({}));
assert_eq!(s.doc().unwrap().doc.node(compound).unwrap().appearance, appearance);
}
#[test]
fn a_selected_member_edits_and_reports_the_compounds_fill_rule() {
let (mut s, outer, inner) = rectangles();
let compound = make(&mut s, outer, inner);
run(&mut s, "select.set", json!({"ids": [inner.0]}));
// Give both contours the same direction, so non-zero fills and even-odd cuts a hole.
run(&mut s, "path.reverse", json!({}));
let before = (path(&s, outer), path(&s, inner));
assert!(pixels(&s).1[3] > 140);
assert_eq!(run(&mut s, "path.setFillRule", json!({"rule": "evenOdd"}))["changed"], 1);
assert!(matches!(s.doc().unwrap().doc.node(compound).unwrap().kind, NodeKind::Compound { rule: FillRule::EvenOdd, .. }));
assert!(matches!(s.doc().unwrap().doc.node(inner).unwrap().kind, NodeKind::Path { rule: FillRule::NonZero, .. }));
assert_eq!(run(&mut s, "attributes.info", json!({}))["fillRule"], "evenOdd");
assert_eq!(pixels(&s).1[3], 0);
assert_eq!((path(&s, outer), path(&s, inner)), before);
let revision = s.doc().unwrap().revision;
assert_eq!(run(&mut s, "path.setFillRule", json!({"rule": "evenOdd"}))["changed"], 0);
assert_eq!(s.doc().unwrap().revision, revision);
run(&mut s, "edit.undo", json!({}));
assert_eq!(run(&mut s, "attributes.info", json!({}))["fillRule"], "nonZero");
assert!(pixels(&s).1[3] > 140);
run(&mut s, "edit.redo", json!({}));
assert_eq!(pixels(&s).1[3], 0);
}
#[test]
fn explicit_members_and_their_compound_change_the_rule_once() {
let (mut s, outer, inner) = rectangles();
let compound = make(&mut s, outer, inner);
run(&mut s, "select.none", json!({}));
let params = json!({"ids": [outer.0, inner.0, compound.0], "rule": "evenOdd"});
assert_eq!(run(&mut s, "path.setFillRule", params.clone())["changed"], 1);
assert_eq!(run(&mut s, "attributes.info", json!({"ids": [outer.0, inner.0]}))["fillRule"], "evenOdd");
assert_eq!(run(&mut s, "path.setFillRule", params)["changed"], 0);
run(&mut s, "edit.undo", json!({}));
assert!(matches!(s.doc().unwrap().doc.node(compound).unwrap().kind, NodeKind::Compound { rule: FillRule::NonZero, .. }));
}
+3 -2
View File
@@ -1,6 +1,6 @@
# Roadmap: milestones and what's next
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-11 · **Change:** minor (M9: PDF raster-effect cap warnings; G11: #893 fixed, #937 open; M7: accurate character controls after scaling, #1034; estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (M6: compound appearance and member fill-rule fixes, #1197; estimates unchanged; M9: PDF raster-effect cap warnings; G11: #893 fixed, #937 open; M7: accurate character controls after scaling, #1034; estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
Forward-looking plan. Where VectorCraft stands is in [`ROADMAP.md`](../ROADMAP.md) and
[target-app-parity.md](target-app-parity.md); the ranked work list is [gaps.md](gaps.md). Hours are Opus 5.5
@@ -53,7 +53,7 @@ Checked 2026-10-10 from the code, tests and open issues; no workflow fails, so V
| M3 | Paint & appearance (swatches, color, gradient, stroke, appearance, transparency, styles) | ✅ done (M3.7–M3.98): swatches, groups, libraries, tints, Lab spots and Registration; Color Picker, Color panel, Color Guide, Color Themes, Edit Colors and Recolor Artwork; gradients that follow every transform, the annotator, linked stops, freeform gradients, focal points, gradients on strokes and Expand; stroke geometry, arrowheads, dashes fitted to corners, stroke on type, width profiles and the Width tool, Scale Strokes and preview bounds; Appearance targeting, container appearance, target circles, Expand Appearance; knockout, isolation, blend accuracy, CMYK blending, overprint, Attributes, masks and clipping, Flatten Transparency; graphic styles, links and libraries. Left: freeform and mixed spot/process gradients export as stops or process colours to SVG/PDF; confirm the Scale Strokes & Effects default | 4–8 |
| M4 | Files (native, SVG, PDF, raster, Export for Screens, clipboard interop) | ✅ done (M4.14–M4.98 on 2026-10-05): Save As/Save a Copy/Revert/templates, Data Recovery and background save, SVG import and export fidelity (units, text, symbols, filters, SVGZ, Preserve Editing, SVG Options), PDF import (colours, layers, masks, editable text, security) and export (presets, marks, bleed, ICC and output intent, raster effects, subset fonts, PDF/X-1a, X-3 and X-4, PDF layers, overprint, page thumbnails, fast web view, PDF 1.3), Place and Links, Package, EPS and DXF in and out, EMF/WMF, TIFF/BMP/Targa/PSD/GIF/PNG-8 export, Print and print presets, slices, Save for Web, Asset Export, clipboard flavours, File Info; AF.1–AF.4 add Affinity import (native layers, artboards, vectors, text and images from Affinity 1–3 files; the embedded preview only as a warned fallback; AF.4 expands current `.af` fixtures and every-board round-trip validation). Left: DWG (use DXF), PSD placement as layers, overprint read back from PDF, Affinity export, polish | 3–6 (existing scope; Affinity export unestimated) |
| M5 | Performance | 🟡 background render + caches + MT done; `vectorcraft-cli bench` and `vectorcraft-cli perf` (budget suite); file format v2 opens 3× faster (50k paths: 722 → 244 ms); raster effects (glows, shadows, blur, feather) no longer force the whole frame single-threaded (filtered offscreen per effect, verified equal to the single-threaded reference); effect-heavy demos need a clean-machine benchmark; dirty-region rendering, GPU backend spike pending | 15–25 |
| M6 | Path operations (Pathfinder, Shape Builder, offset…) | ✅ mostly done (Boolean precision on almost-horizontal edges fixed and the property tests made deterministic; Shape Builder gap detection, large-offset bug open) | 3–6 |
| M6 | Path operations (Pathfinder, Shape Builder, offset…) | ✅ mostly done (Compound appearance and member fill-rule edits verified (#1197); Boolean precision on almost-horizontal edges fixed and the property tests made deterministic; Shape Builder gap detection, large-offset bug open) | 3–6 |
| M7 | Type (point/area/path, editing, styles, OpenType, threading, glyphs) | 🟡 Document-point character controls after object/group scaling (#1034; optional font-size preservation remains separate), Character/Paragraph Styles, per-paragraph paragraph attributes, Area Type Options (with Width/Height, Auto Size and Shrink Text to Fit; overflow reported to agents and vertical Align), type areas resized by their handles and reshaped with Direct Selection, threaded text, Fit Headline, Glyphs, OpenType panel, Find Font, installed system fonts in every font menu (fonts registered with Windows outside its font folders, the XDG and Flatpak host font folders on Linux, and fonts installed while the app runs, listed when it comes back to the front; searchable; the Character and Properties menus show each family's sample of the selected text, preview it live under the pointer or ↑/↓, filter by script and kind, list a family's styles, and star favourites; the Glyphs panel and Find Font's Replace With use the same menu without the live preview) and found by name on open/import, variable fonts' named instances as styles, CJK names in the UI, input methods (marked text in place, candidate window at the caret; Japanese IME), Text Wrap (offset, invert, both sides of an object; follows edits), Type on a Path effects and Options with draggable start/centre/end brackets (slide, flip), tab stops + Tabs panel (Above Text floats it over the selected text, its ruler at the canvas zoom so stops line up with the columns, #729), inline symbols in text (beyond parity), Hebrew/Arabic bidirectional text, Paragraph Direction, Every-line/Single-line Composer per object (ragged text too) done; tab leaders, spell check, vertical type pending | 45–60 |
| M8 | Transform & distort (Puppet Warp, Liquify tools, Envelopes, Blends, Perspective Grid) | ✅ done (M8.1–M8.20 on 2026-10-06, fidelity pass): Liquify Tool Options with one shared brush, pen pressure, hold-to-apply, a safe scope and incremental strokes; tool options that last; Puppet Warp rotation, multi-select, Control bar and a rest-shape session that follows Undo; Width tool on compound paths; Envelope dialogs with preview, Reset with Warp/Mesh, full Envelope Options, type/images/symbols/appearance/gradients/patterns distorted on canvas and in every export, warps that keep their frame, Edit Contents, mesh handles and Control bar; Blend Options, anchor-targeted Blend tool, editable spine, keys picked on the canvas, faithful interpolation and knockout; Perspective Grid with Define Grid, presets, lock/snap/rulers, widgets, the 1–4 plane switch, movable planes, Perspective Selection scale/copy/perpendicular/Transform Again, and type and symbols truly projected with Edit Text. Left: Anti-Alias and Preserve Shape output, warp-envelope point editing, brush interpolation in blends, Free Transform/Scale/Reflect along a rotated box | 2–4 |
| M9 | Live effects (+ 3D & Materials) | 🟡 2D effects done incl. Effect → Pathfinder, Color Adjustments and Document Raster Effects Settings; PDF export warns when the pixel cap lowers raster-effect resolution; initial Revolve only; SVG Filters, remaining raster effects and 3D pending | 81–130 |
@@ -79,6 +79,7 @@ After placed documents (File › Place of a `.vectorcraft` file, linked), M4.14
| Date | Change | Summary |
|---|---|---|
| 2026-10-12 | minor | M6: compound paths inherit the backmost appearance; selected members read and change the compound fill rule (#1197); estimates unchanged |
| 2026-10-11 | minor | M9: PDF raster-effect resolution-cap warnings verified with real capped/uncapped exports; scores and hours unchanged |
| 2026-10-11 | minor | #893 (Shape Builder seams on grids of lines) fixed; G11 keeps #937 and its 8–15 h |
| 2026-10-10 | minor | M7: document-point character sizes, leading and baseline shift, and resettable horizontal stretching after object/group scaling (#1034). Optional group font-size preservation remains separate; scores/hours unchanged. |
+4 -2
View File
@@ -1,6 +1,6 @@
# Parity with Adobe Illustrator
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-11 · **Change:** minor (Type core: Middle Eastern digit types done, Character panel menu › Digits; PDF export warns when the pixel cap lowers raster-effect resolution; Drawing tools: Live Corners round corners between curved sides, #1144; Type advanced: proportional vertical metrics (`vpal`) done, #966; Shape Builder: #893 fixed, merged regions of crossing lines no longer keep seams; scores unchanged while #937 is open; #1034: document-point character controls after object/group scaling; full readiness recomputed as the standard's additive weighted sum, ~62%; per-audience hours; essentials-user score ~75% added; ready-for-real-work re-examined: 50% → 55%, mainstream 65% added; earlier the same day: full re-measure against Illustrator 2026 30.x; merged the ROADMAP's parity estimate, honest assessment and "Shipped so far" into this file) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (compound paths inherit the backmost appearance and member fill-rule edits reach their owner, #1197; scores unchanged; Type core: Middle Eastern digit types done, Character panel menu › Digits; PDF export warns when the pixel cap lowers raster-effect resolution; Drawing tools: Live Corners round corners between curved sides, #1144; Type advanced: proportional vertical metrics (`vpal`) done, #966; Shape Builder: #893 fixed, merged regions of crossing lines no longer keep seams; scores unchanged while #937 is open; #1034: document-point character controls after object/group scaling; full readiness recomputed as the standard's additive weighted sum, ~62%; per-audience hours; essentials-user score ~75% added; ready-for-real-work re-examined: 50% → 55%, mainstream 65% added; earlier the same day: full re-measure against Illustrator 2026 30.x; merged the ROADMAP's parity estimate, honest assessment and "Shipped so far" into this file) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
The authoritative parity assessment. [`ROADMAP.md`](../ROADMAP.md) summarizes it; [`gaps.md`](gaps.md) is
the work list made from it. Deep areas have their own checklists: [UI and interaction](ui-parity.md),
@@ -69,7 +69,7 @@ dominate; 3D, graphs and automation are specialist). They sum to 105. Feature de
|---|---:|---:|---|---:|---|
| Selection, transform & align tools | 6 | 85% | Free Transform/Scale/Reflect along a rotated box, Start Global Edit, transform nuances | 4–6 | estimated |
| Drawing tools (Pen, Curvature, Pencil, shapes, Shaper) | 7 | 85% | Shaper construction widget, Pencil/Pen nuances, Blob Brush options, Touch Type, Dimension tool, Live Corners' Relative rounding; see [ui-parity.md](ui-parity.md) | 8–13 | estimated |
| Path operations, Pathfinder, Shape Builder, Live Paint | 5 | 80% | Shape Builder misses planar regions in real files (#937), its gap detection and options; Live Paint Gap Options | 4–8 | estimated (lowered from 86%: open issues) |
| Path operations, Pathfinder, Shape Builder, Live Paint | 5 | 80% | Shape Builder misses planar regions in real files (#937), its gap detection and options; Live Paint Gap Options | 4–8 | estimated (lowered from 86%: open issues); compound attributes regression-tested (#1197) |
| Colour, swatches, gradients, patterns, mesh, recolor | 7 | 92% | 30.0's gradient dither, perceptual blending and gradient presets; freeform and mixed spot/process gradients in SVG/PDF | 4–7 | estimated (lowered from 96%: target moved) |
| Strokes, brushes, width profiles | 5 | 88% | Art brush Overlap, Pattern brush auto corners, brush libraries, pen pressure on macOS, tilt/bearing/rotation | 8–15 | estimated |
| Appearance, transparency, graphic styles, masks | 5 | 97% | raster effect reach in preview bounds | 1–2 | estimated |
@@ -400,6 +400,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
Agent selection commands validate every object id before changing the selection and report the resulting count and ids (#784).
Align and Distribute Spacing keep a compound path or selected group that contains the key object in place, as they keep the key object itself.
Clear deletes individual selected compound-path members without deleting their siblings, and explicit object ids work without a selection; Cut still removes the same objects it copies (#786).
- **Compound paths:** Make keeps the backmost object's appearance and each contour; selecting a member in Direct Selection reads and changes the compound's fill rule in Attributes. Direction remains a per-member edit. Rendered holes, unchanged contour geometry, no-op history and Undo/Redo are covered by `engine/tests/compound_attributes.rs` (#1197). Behaviour reference: [public compound-path documentation](https://helpx.adobe.com/illustrator/desktop/manage-objects/reshape-transform-objects/create-compound-paths.html).
- **Saved selections:** Select ▸ Save Selection… asks for a name and keeps the selection in the document (saved with the file, undoable, up to 25); each one is listed at the bottom of the Select menu and a click selects its objects again; Edit Selection… lists them to rename or delete, applied together on OK. A file's saved selections are capped and checked against its objects when it opens. Not yet: Update Selection and the Layers panel's saved-selection menu.
- **Drawing:** Pen, Curvature, anchor tools, Pencil, Paintbrush, Blob Brush, Smooth, Path Eraser, Join. Direction handles dragged with Direct Selection or the Anchor Point tool snap to smart guides, and Shift keeps them at 45° steps from their anchor. The Pen continues any open path from the end clicked, and while drawing, a click on an end of another open path joins the two into one (#776). The Curvature tool edits any selected path, whatever drew it, keeping its shape except where edited (#798): a click on a point makes it current and a drag moves it, re-curving only its two segments; Alt-click or double-click toggles it smooth or corner and Delete removes it; a click on a segment adds a point without changing the shape; with an end of an open path current, clicks go on from it and its other end closes it (`path.curvatureEdit`). Every point a drawing tool places snaps to Smart Guides through one snapper (#506): the Pen's and Curvature tool's anchors, the Pencil's ends, the shape tools' start and dragged corner or radius (Rectangle, Rounded Rectangle, Ellipse, Polygon, Star, Line Segment, Arc, Spiral, the grids), the Type tool's click and frame, a new artboard's corners.
- **Shapes:** all shape tools (including Flare) and the line, arc, spiral and grid tools. Space held while dragging out a shape, line, arc, spiral or grid moves it at its current size. While a grid is dragged out, Up/Down change its rows (concentric dividers) and Left/Right its columns (radial dividers). An ellipse's Pie Start and End Angle and Invert Pie (Transform and Properties panels' Ellipse Properties, `object.setLiveShape {pieStart, pieEnd, invertPie}`) cut it as a pie (#812; the on-canvas pie widget is still to come). A live polygon's Polygon Properties there: side count, angle, corner type and radius, radius, side length and Make Sides Equal once an uneven scale has made its sides differ (`object.setLiveShape {sides, polygonAngle, polygonRadius, sideLength, makeSidesEqual}`, part of #812); a star is a plain path, as in Illustrator. The Flare Tool Options last as the tool's options: the next flare dragged out uses them, a click's dialog shows them and double-clicking the tool opens them (part of #812).
@@ -461,6 +462,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
| Date | Change | Summary |
|---|---|---|
| 2026-10-12 | minor | Compound-path appearance and fill-rule edits through directly selected members (#1197) verified with rendered holes and Undo/Redo; scores and hours unchanged |
| 2026-10-11 | minor | Type core: Middle Eastern digit types (Character panel menu › Digits: Default, Arabic, Hindi, Farsi; `{digits}`) leave the missing list; score and hours unchanged at this rounding (estimated) |
| 2026-10-11 | minor | PDF raster-effect cap reductions are reported per original object with requested/achieved ppi; scores and hours unchanged |
| 2026-10-11 | minor | Platforms: Preferences › Performance › Linux Display Server opens the window through XWayland, where file drops work (#1151); score unchanged (estimated) |
+3 -1
View File
@@ -1,6 +1,6 @@
# UI and interaction parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-11 · **Change:** minor (a marquee needs to touch a path, not just lie inside its fill, #1103; earlier: Live Corners round corners between curved sides, #1144) · **Target:** Adobe Illustrator 2026 (30.x)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (Attributes fill-rule controls edit the compound when a member is selected, #1197; a marquee needs to touch a path, not just lie inside its fill, #1103; earlier: Live Corners round corners between curved sides, #1144) · **Target:** Adobe Illustrator 2026 (30.x)
How VectorCraft's tools, handles, snapping and panels behave next to Illustrator's. A vector illustrator's
trust is in the details: where a handle lands, what Shift and Alt do mid-drag, what a smart guide snaps to,
@@ -153,6 +153,7 @@ Layers panel.
| Properties panel per context (every selection type's Quick Actions) | partial | |
| Contextual task bar | done | |
| Control bar with Transform and Stroke popovers | done | |
| Attributes fill-rule controls with a compound member selected | done | Edits the compound's rule; direction still edits only the member; rendered-hole and Undo/Redo regression tests (#1197) |
| Canvas context menu | done | Layers rows, panels and type have none (gap) |
| Blend panel (30.7) | missing | Blend Options dialog exists |
| SVG Interactivity, Retype, Comments, Version History panels | missing | the last two are cloud features (out of scope) |
@@ -170,6 +171,7 @@ re-measure of the panels added since.
| Date | Change | Summary |
|---|---|---|
| 2026-10-12 | minor | Attributes: directly selected compound members read and change the compound fill rule; path direction stays member-local (#1197); score unchanged |
| 2026-10-11 | minor | A marquee inside a filled shape no longer selects it: only touching its path does (#1103); score unchanged (estimated) |
| 2026-10-11 | minor | Live Corners: corners between curved sides round too, the curve trimmed to what the cut leaves (#1144); score unchanged (estimated) |
| 2026-10-11 | minor | Artboards: Shift-click selects several, which move, copy and delete together (#1139); score unchanged (estimated) |