diff --git a/ROADMAP.md b/ROADMAP.md index 3511fe3f2..c943bbb02 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -9,7 +9,7 @@ **Stage: alpha** · next: beta, ~13 points (ready for real work ~62% → ~75%, and reliable `.ai` exchange) and ~170–270 h away -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (Progress log rows for the Arrange Tools add-on, Export for Screens as the reference app's dialog and the 2026-10-31 community landings; headline numbers unchanged) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Progress log row for Group Overlapping Objects in the Arrange Tools add-on; headline numbers unchanged) · **Target:** Adobe Illustrator 2026 (30.x) VectorCraft is a clean-room, open-source, pure-Rust reimplementation of the Adobe Illustrator workflow. It runs on macOS, Windows, Linux, FreeBSD and the web (WASM), and agents can drive all of it over MCP, the CLI and a JSON @@ -129,6 +129,7 @@ Ranked; detail in [docs/roadmap.md](docs/roadmap.md) and [docs/gaps.md](docs/gap ## Progress log +- 2026-11-01: Group Overlapping Objects joins the Arrange Tools add-on (Object › Plug-ins › Arrange Tools): the selected objects whose visual bounds overlap or touch, chains included, go into one group each, within their own layer or group, at the stacking place of their frontmost member; objects that overlap nothing stay as they are. One undo step, a sweep along x so thousands of loose pieces group quickly (at most 20000 objects). A VectorCraft addition: headline numbers unchanged. - 2026-10-31: Export for Screens works as the reference app's dialog: Artboards and Assets tabs with a grid or list of thumbnails, Clear Selection and Prefix, Create Sub-folders by Scale or Format (on by default), Export PDFs As Single File (the chosen artboards as one PDF's pages, `pdfs: "single"`) or Multiple Files, iOS and Android rows, format presets in a ☰ menu, and a count of the files the export writes (#4895). About 55 community PRs and fixes landed the same day: Find Font, Replace Font, Outline Stroke and Expand leave hidden and locked art alone (with a warning); DXF export writes LINE and ELLIPSE entities and outlines only the strokes a CAD line can't draw; Blob Brush Angle and Roundness; Shift-constrained Perspective Selection; Grid Repeat Options Preview; a gradient mesh from a file budgeted at about a million quads; width-profile strokes keep their shape zoomed out; `.ai`/EPS import fidelity (area type, envelopes, transparency in dictionaries, character styles); PDF import finds hyphenated and CMap-suffixed font names; Windows renders follow the process's core affinity; Enter or Escape in a floating panel's field no longer reaches the canvas (#4803); Properties shows Blend Options for any selected blend (#4817); Artwork Cleanup finds doubled type (#4835) and joins stacked lines into one object (Join Point Text, #4826). Headline numbers unchanged. - 2026-10-31: Arrange Tools, a built-in add-on (Object › Plug-ins › Arrange Tools): Arrange in Grid lays the selected objects out in rows and columns, a row or a column with gaps, from the selection's top-left or centred on the artboard; Swap Objects swaps two objects' places (and, with Swap Objects and Sizes, their sizes); Replace with Top Object puts a copy of the topmost selected object in place of each of the others, kept, stretched or fitted in proportion; Set Size of Each gives every selected object an exact width and/or height about a reference point; Zoom to Selection fits the view to the selection. Each is one undo step with a previewed dialog where it has options. A VectorCraft addition: headline numbers unchanged. - 2026-10-30: Gradient panel presets: a Presets menu applies one of the built-in gradient libraries' presets (grouped 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 current gradient as a preset, kept with the preferences, and deletes saved ones (`gradient.presets.list/apply/save/delete`). @@ -204,6 +205,7 @@ The full earlier record of what landed is kept in [docs/roadmap.md](docs/roadmap | Date | Change | Summary | |---|---|---| +| 2026-11-01 | minor | Progress log row for Group Overlapping Objects (Arrange Tools add-on, #4928); headline numbers unchanged. | | 2026-10-31 | minor | Progress log row for Export for Screens laid out as the reference app's (#4895) and the 2026-10-31 community landings; headline numbers unchanged. | | 2026-10-31 | minor | Progress log row for the Arrange Tools add-on; headline numbers unchanged. | | 2026-10-29 | minor | Progress log row for the Linux Flatpak clipboard's core Wayland protocol text fallback; headline numbers unchanged. | diff --git a/crates/engine/src/cmd/addons/arrange.rs b/crates/engine/src/cmd/addons/arrange.rs index 2011559c3..986016213 100644 --- a/crates/engine/src/cmd/addons/arrange.rs +++ b/crates/engine/src/cmd/addons/arrange.rs @@ -2,14 +2,17 @@ //! out in rows and columns, a row or a column; Swap Objects swaps the places (and sizes) of two; //! Replace with Top Object puts a copy of the topmost selected object in place of each of the //! others; and Set Size of Each gives every selected object an exact width and height. Places and -//! sizes are measured and set as `object.setBounds` does. Its submenu (Object › Plug-ins › Arrange +//! sizes are measured and set as `object.setBounds` does. Group Overlapping Objects groups the +//! objects whose visual bounds overlap. Its submenu (Object › Plug-ins › Arrange //! Tools), its dialogs and Zoom to Selection (a view command) are the UI's. #[cfg(test)] mod tests; +use std::sync::Arc; + use serde_json::{Value, json}; -use vectorcraft_doc::{NodeId, OrientedBox}; +use vectorcraft_doc::{Node, NodeId, OrientedBox}; use vectorcraft_geom::{Affine, Point, Rect}; use vectorcraft_render::effects; @@ -25,6 +28,8 @@ const LIMIT: f64 = 1e6; const LEAST: f64 = 0.001; /// The most columns Arrange in Grid takes. const MAX_COLUMNS: u64 = 10_000; +/// The most objects Group Overlapping Objects looks at. +const MAX_OBJECTS: usize = 20_000; pub fn specs() -> Vec { vec![ @@ -64,6 +69,15 @@ pub fn specs() -> Vec { has_selection, size ), + cmd!( + "addon.arrange.groupOverlapping", + "Group Overlapping Objects", + ["Object", "Plug-ins", "Arrange Tools"], + None, + "{} group the selected objects (the top-level ones, at most 20000) wherever their visual bounds overlap or touch, chains included (when A overlaps B and B overlaps C, all three go in one group), as one undo step. Only objects in the same layer or group are grouped together, objects that overlap nothing stay as they are, and each new group goes where its frontmost member was in the stacking order; the new groups and the objects left alone stay selected → {groups: [id…], grouped (how many objects went into groups)}", + has_multi, + group_overlapping + ), ] } @@ -270,3 +284,75 @@ fn size(s: &mut Session, p: &Value) -> Result { transform_each(s, "Set Size of Each", strokes, &xfs)?; Ok(json!({ "count": xfs.len() })) } + +/// The sets of two or more of `items` (each in the order given) whose rects overlap or touch, +/// chains included, among items of the same key (parent). A sweep along x: only the rects still +/// open where each one starts are compared with it. +fn overlapping(items: &[(K, Rect)]) -> Vec> { + fn root(up: &mut [usize], mut i: usize) -> usize { + while up[i] != i { + up[i] = up[up[i]]; + i = up[i]; + } + i + } + let mut up: Vec = (0..items.len()).collect(); + let mut order: Vec = (0..items.len()).collect(); + order.sort_by(|a, b| items[*a].1.x0.total_cmp(&items[*b].1.x0)); + let mut open: Vec = vec![]; + for i in order { + let (key, r) = (&items[i].0, items[i].1); + open.retain(|&j| items[j].1.x1 >= r.x0); + for &j in &open { + let (k, o) = (&items[j].0, items[j].1); + if k == key && o.y0 <= r.y1 && r.y0 <= o.y1 { + let (a, b) = (root(&mut up, i), root(&mut up, j)); + up[a] = b; + } + } + open.push(i); + } + let mut sets: Vec> = vec![vec![]; items.len()]; + for i in 0..items.len() { + let r = root(&mut up, i); + sets[r].push(i); + } + sets.retain(|s| s.len() > 1); + sets.sort_by_key(|s| s.first().copied()); + sets +} + +fn group_overlapping(s: &mut Session, _: &Value) -> Result { + const C: &str = "addon.arrange.groupOverlapping"; + let roots = selected_roots(s)?; + if roots.len() > MAX_OBJECTS { + return Err(bad(C, "too many objects selected (at most 20000)")); + } + let d = &s.doc()?.doc; + let items: Vec<(NodeId, (Option, Rect))> = + roots.iter().filter_map(|id| Some((*id, (d.parent_of(*id), effects::preview::bounds_of(d, &[*id], true)?)))).collect(); + let keyed: Vec<(Option, Rect)> = items.iter().map(|(_, k)| *k).collect(); + let sets: Vec> = overlapping(&keyed).into_iter().map(|set| set.iter().filter_map(|i| Some(items.get(*i)?.0)).collect()).collect(); + if sets.is_empty() { + return Ok(json!({ "groups": [], "grouped": 0 })); + } + let grouped: std::collections::HashSet = sets.iter().flatten().copied().collect(); + let groups = s.edit("Group Overlapping Objects", |d, sel| { + // Every group at once: the members behind each front one go, then each front one is + // replaced by its group (one pass over the tree each, however many groups). + let (mut places, mut behind, mut groups) = (vec![], vec![], vec![]); + for set in &sets { + let Some((&front, rest)) = set.split_last() else { continue }; + let children: Vec> = set.iter().filter_map(|id| d.node(*id).cloned().map(Arc::new)).collect(); + let gid = d.alloc_id(); + places.push((front, vec![Node::group(gid, children)])); + behind.extend_from_slice(rest); + groups.push(gid); + } + d.remove_many(&behind)?; + d.insert_beside(places, true)?; + sel.set(groups.iter().copied().chain(roots.iter().copied().filter(|id| !grouped.contains(id)))); + Ok(groups) + })?; + Ok(json!({ "groups": groups.iter().map(|i| i.0).collect::>(), "grouped": grouped.len() })) +} diff --git a/crates/engine/src/cmd/addons/arrange/tests.rs b/crates/engine/src/cmd/addons/arrange/tests.rs index db19cc1e9..48f9ae4ec 100644 --- a/crates/engine/src/cmd/addons/arrange/tests.rs +++ b/crates/engine/src/cmd/addons/arrange/tests.rs @@ -256,8 +256,83 @@ fn off_without_a_selection() { let mut s = session(); rect(&mut s, 0.0, 0.0, 20.0, 10.0); s.execute("select.none", &json!({})).unwrap(); - for c in ["addon.arrange.grid", "addon.arrange.swap", "addon.arrange.replace", "addon.arrange.size"] { + for c in ["addon.arrange.grid", "addon.arrange.swap", "addon.arrange.replace", "addon.arrange.size", "addon.arrange.groupOverlapping"] { assert!(!enabled(&s, c), "{c}"); } assert!(!enabled(&Session::new(), "addon.arrange.swap")); } + +fn ids_of(v: &Value) -> Vec { + v.as_array().unwrap().iter().map(|i| NodeId(i.as_u64().unwrap())).collect() +} + +fn children(s: &Session, id: NodeId) -> Vec { + s.doc().unwrap().doc.node(id).unwrap().children().unwrap().iter().map(|n| n.id).collect() +} + +#[test] +fn group_overlapping_groups_chains_and_leaves_loners() { + let mut s = session(); + // a–b–c a chain (c only touches b), d alone, e over f. + let a = rect(&mut s, 0.0, 0.0, 20.0, 20.0); + let d = rect(&mut s, 200.0, 0.0, 10.0, 10.0); + let b = rect(&mut s, 15.0, 15.0, 20.0, 20.0); + let e = rect(&mut s, 100.0, 100.0, 30.0, 30.0); + let c = rect(&mut s, 35.0, 0.0, 10.0, 20.0); + let f = rect(&mut s, 110.0, 110.0, 5.0, 5.0); + select(&mut s, &[a, b, c, d, e, f]); + let n = steps(&s); + let r = s.execute("addon.arrange.groupOverlapping", &json!({})).unwrap(); + assert_eq!(steps(&s), n + 1, "one undo step"); + assert_eq!(r["grouped"], json!(5)); + let groups = ids_of(&r["groups"]); + assert_eq!(groups.len(), 2); + assert_eq!(children(&s, groups[0]), [a, b, c], "members keep their stacking order"); + assert_eq!(children(&s, groups[1]), [e, f]); + // Each group where its frontmost member was: a d [abc] [ef] → d, group(abc) above it, then (ef). + let layer: Vec = s.doc().unwrap().doc.layers[0].children().unwrap().iter().map(|n| n.id).collect(); + assert_eq!(layer, [d, groups[0], groups[1]]); + let sel = &s.doc().unwrap().selection.objects; + assert!(sel.contains(&groups[0]) && sel.contains(&groups[1]) && sel.contains(&d) && sel.len() == 3, "{sel:?}"); + s.execute("edit.undo", &json!({})).unwrap(); + assert_eq!(s.doc().unwrap().doc.layers[0].children().unwrap().len(), 6); +} + +#[test] +fn group_overlapping_keeps_layers_apart_and_measures_visual_bounds() { + let mut s = session(); + let a = rect(&mut s, 0.0, 0.0, 20.0, 20.0); + let gap = rect(&mut s, 22.0, 0.0, 20.0, 20.0); + s.execute("layer.new", &json!({})).unwrap(); + let other = rect(&mut s, 10.0, 10.0, 20.0, 20.0); + select(&mut s, &[a, gap, other]); + // 2 pt apart: nothing overlaps within a layer, and a and `other` are in different layers. + let n = steps(&s); + let r = s.execute("addon.arrange.groupOverlapping", &json!({})).unwrap(); + assert_eq!((r["grouped"].clone(), steps(&s)), (json!(0), n), "no groups, no step"); + // A 4 pt stroke reaches 2 pt out: the two in the first layer now touch. + select(&mut s, &[a, gap]); + s.execute("paint.setStroke", &json!({"color": "#000000"})).unwrap(); + s.execute("stroke.set", &json!({"weight": 4})).unwrap(); + select(&mut s, &[a, gap, other]); + let r = s.execute("addon.arrange.groupOverlapping", &json!({})).unwrap(); + let groups = ids_of(&r["groups"]); + assert_eq!(groups.len(), 1); + assert_eq!(children(&s, groups[0]), [a, gap]); + let doc = &s.doc().unwrap().doc; + assert_ne!(doc.layer_of(other), doc.layer_of(a), "left in its own layer"); +} + +#[test] +fn group_overlapping_is_off_with_one_object_and_scales_to_many() { + let mut s = session(); + let a = rect(&mut s, 0.0, 0.0, 20.0, 20.0); + select(&mut s, &[a]); + assert!(!enabled(&s, "addon.arrange.groupOverlapping")); + // A long row of pairs: each pair overlaps, neighbouring pairs don't. + let rects: Vec<(usize, Rect)> = + (0..4000).map(|i| (0, Rect::new(i as f64 * 10.0, 0.0, i as f64 * 10.0 + if i % 2 == 0 { 12.0 } else { 3.0 }, 5.0))).collect(); + let sets = super::overlapping(&rects); + assert_eq!(sets.len(), 2000); + assert!(sets.iter().all(|s| s.len() == 2 && s[1] == s[0] + 1)); +} diff --git a/crates/engine/tests/command_sweep.rs b/crates/engine/tests/command_sweep.rs index 69c69097e..14d7f9559 100644 --- a/crates/engine/tests/command_sweep.rs +++ b/crates/engine/tests/command_sweep.rs @@ -588,6 +588,7 @@ fn structured_junk() { ("addon.arrange.replace", json!({"fit": "bounds", "scaleStrokes": true})), ("addon.arrange.size", json!({"width": 1e6, "height": 0.001, "reference": 8, "scaleStrokes": true})), ("addon.arrange.size", json!({"width": 0.001, "proportional": true, "reference": u64::MAX})), + ("addon.arrange.groupOverlapping", json!({"ids": "x", "junk": f64::MAX})), // Smart Pen add-on. ("addon.pen.append", json!({"id": 2, "x": 1e308, "y": 0, "in": [1e308, -1e308]})), ("addon.pen.append", json!({"id": 2, "subpath": u64::MAX, "x": 0, "y": 0, "segment": "curved"})), diff --git a/crates/ui-egui/src/addons/arrange.rs b/crates/ui-egui/src/addons/arrange.rs index 8435483cb..f33757cee 100644 --- a/crates/ui-egui/src/addons/arrange.rs +++ b/crates/ui-egui/src/addons/arrange.rs @@ -1,6 +1,6 @@ //! Arrange Tools in the interface: its submenu (Object › Plug-ins › Arrange Tools); the dialogs of //! Arrange in Grid, Replace with Top Object and Set Size of Each, which preview on the canvas and -//! run their `addon.arrange.*` command on OK; and Zoom to Selection ([`ZOOM`]), which fits the +//! run their `addon.arrange.*` command on OK; Group Overlapping Objects; and Zoom to Selection ([`ZOOM`]), which fits the //! active view to the selection. use egui::Ui; @@ -38,6 +38,7 @@ pub fn menu() -> Vec { Item::Cmd("Swap Objects and Sizes", "addon.arrange.swap", json!({ "size": true })), Item::Cmd("Replace with Top Object…", "addon.arrange.replace", Value::Null), Item::Cmd("Set Size of Each…", "addon.arrange.size", Value::Null), + Item::Cmd("Group Overlapping Objects", "addon.arrange.groupOverlapping", json!({})), Item::Sep, Item::Cmd("Zoom to Selection", ZOOM, json!({})), ] diff --git a/crates/ui-egui/src/addons/arrange/tests.rs b/crates/ui-egui/src/addons/arrange/tests.rs index 63e58448b..72411f388 100644 --- a/crates/ui-egui/src/addons/arrange/tests.rs +++ b/crates/ui-egui/src/addons/arrange/tests.rs @@ -40,6 +40,7 @@ fn the_plugins_submenu_lists_the_tools() { ("Swap Objects and Sizes", "addon.arrange.swap"), ("Replace with Top Object…", "addon.arrange.replace"), ("Set Size of Each…", "addon.arrange.size"), + ("Group Overlapping Objects", "addon.arrange.groupOverlapping"), ("Zoom to Selection", ZOOM), ] { assert_eq!(under(label).and_then(|e| e.command.as_deref()), Some(id), "{label}"); @@ -125,3 +126,16 @@ fn zoom_to_selection_fits_the_selection_in_the_view() { assert!(!menus::enabled(&app, ZOOM)); assert!(app.run(ZOOM, json!({})).is_err()); } + +#[test] +fn group_overlapping_objects_runs_directly() { + let (mut app, a, b) = app(); + let c = NodeId(app.run("shape.rectangle", json!({"x": 10, "y": 5, "width": 20, "height": 20})).unwrap()["id"].as_u64().unwrap()); + app.run("select.all", json!({})).unwrap(); + click(&mut app, "Group Overlapping Objects"); + assert!(app.ui.dialog.is_none()); + let doc = &app.session.doc().unwrap().doc; + let g = doc.parent_of(a).unwrap(); + assert_eq!(doc.parent_of(c), Some(g), "a and c overlap: grouped"); + assert_ne!(doc.parent_of(b), Some(g), "b is left alone"); +} diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index d33addad4..a960e7dd2 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -5926,6 +5926,9 @@ breakText Glyphs Глифи Set Size of Each Задаване на размер на всеки @msg select exactly two objects изберете точно два обекта @msg give `width` or `height` задайте `width` или `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Групиране на припокриващи се обекти +@msg too many objects selected (at most 20000) избрани са твърде много обекти (най-много 20000) Marathi Маратхи Gujarati Гуджарати Bengali Бенгалски diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index f527ca255..6dba3afdf 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -5960,6 +5960,9 @@ shape Open Otvoren Set Size of Each Postavi veličinu svakog @msg select exactly two objects odaberite tačno dva objekta @msg give `width` or `height` zadajte `width` ili `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Grupiši preklapajuće objekte +@msg too many objects selected (at most 20000) odabrano je previše objekata (najviše 20000) Marathi Marathi Gujarati Gudžarati Bengali Bengalski diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 5873fbada..a102f7a7c 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -6033,6 +6033,9 @@ breakText Glyphs Glifs Set Size of Each Defineix la mida de cadascun @msg select exactly two objects selecciona exactament dos objectes @msg give `width` or `height` indica `width` o `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Agrupa els objectes superposats +@msg too many objects selected (at most 20000) s’han seleccionat massa objectes (com a màxim 20000) Marathi Marathi Gujarati Gujarati Bengali Bengalí diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 8f7a60710..b0c04ffe2 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -6043,6 +6043,9 @@ breakText Glyphs Glyfy Set Size of Each Nastavit velikost jednotlivě @msg select exactly two objects vyberte právě dva objekty @msg give `width` or `height` zadejte `width` nebo `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Seskupit překrývající se objekty +@msg too many objects selected (at most 20000) vybráno příliš mnoho objektů (nejvýše 20000) Marathi Maráthština Gujarati Gudžarátština Bengali Bengálština diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 8b67b907a..d85c865bf 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -5940,6 +5940,9 @@ breakText Glyphs Glyffer Set Size of Each Angiv størrelse for hver @msg select exactly two objects markér præcis to objekter @msg give `width` or `height` angiv `width` eller `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Gruppér overlappende objekter +@msg too many objects selected (at most 20000) for mange objekter markeret (højst 20000) Marathi Marathi Gujarati Gujarati Bengali Bengali diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 7400e6f73..b1e187de8 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -5628,6 +5628,9 @@ breakText Glyphs Glyphen Set Size of Each Größe einzeln festlegen @msg select exactly two objects wählen Sie genau zwei Objekte aus @msg give `width` or `height` geben Sie `width` oder `height` an +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Überlappende Objekte gruppieren +@msg too many objects selected (at most 20000) zu viele Objekte ausgewählt (höchstens 20000) Marathi Marathi Gujarati Gujarati Bengali Bengalisch diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 19ba17dd7..76d29ca75 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -5995,6 +5995,9 @@ breakText Glyphs Γλύφοι Set Size of Each Ορισμός μεγέθους καθενός @msg select exactly two objects επιλέξτε ακριβώς δύο αντικείμενα @msg give `width` or `height` δώστε `width` ή `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Ομαδοποίηση επικαλυπτόμενων αντικειμένων +@msg too many objects selected (at most 20000) επιλέχθηκαν πάρα πολλά αντικείμενα (το πολύ 20000) Marathi Μαραθικά Gujarati Γκουγιαράτι Bengali Βεγγαλικά diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 884ee8e7e..f1d2645dc 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -5985,6 +5985,9 @@ breakText Glyphs Glifos Set Size of Each Definir el tamaño de cada uno @msg select exactly two objects selecciona exactamente dos objetos @msg give `width` or `height` indica `width` o `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Agrupar objetos superpuestos +@msg too many objects selected (at most 20000) demasiados objetos seleccionados (como máximo 20000) Marathi Maratí Gujarati Guyaratí Bengali Bengalí diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 1b3528602..65c7df126 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -6073,6 +6073,9 @@ breakText Glyphs Merkit Set Size of Each Aseta kunkin koko @msg select exactly two objects valitse täsmälleen kaksi objektia @msg give `width` or `height` anna `width` tai `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Ryhmitä päällekkäiset objektit +@msg too many objects selected (at most 20000) liian monta objektia valittu (enintään 20000) Marathi Marathi Gujarati Gudžarati Bengali Bengali diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 46f1c08db..f0d74bcc7 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -6040,6 +6040,9 @@ breakText Glyphs Glyphes Set Size of Each Définir la taille de chaque objet @msg select exactly two objects sélectionnez exactement deux objets @msg give `width` or `height` indiquez `width` ou `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Grouper les objets qui se chevauchent +@msg too many objects selected (at most 20000) trop d’objets sélectionnés (20000 au plus) Marathi Marathi Gujarati Goudjarati Bengali Bengali diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index bf9ec10b2..bd2b64965 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -6029,6 +6029,9 @@ breakText Glyphs Glifák Set Size of Each Egyenkénti méret beállítása @msg select exactly two objects jelölj ki pontosan két objektumot @msg give `width` or `height` add meg a `width` vagy a `height` értéket +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Átfedő objektumok csoportosítása +@msg too many objects selected (at most 20000) túl sok objektum van kijelölve (legfeljebb 20000) Marathi Maráthi Gujarati Gudzsaráti Bengali Bengáli diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 99b747111..c7a2f0021 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -5940,6 +5940,9 @@ breakText Glyphs Glif Set Size of Each Atur ukuran masing-masing @msg select exactly two objects pilih tepat dua objek @msg give `width` or `height` berikan `width` atau `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Grupkan objek yang tumpang tindih +@msg too many objects selected (at most 20000) terlalu banyak objek dipilih (paling banyak 20000) Marathi Marathi Gujarati Gujarati Bengali Bengali diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index f3cbd7558..5f340b02b 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -5662,6 +5662,9 @@ breakText Glyphs Glifi Set Size of Each Imposta dimensione di ciascuno @msg select exactly two objects seleziona esattamente due oggetti @msg give `width` or `height` indica `width` o `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Raggruppa oggetti sovrapposti +@msg too many objects selected (at most 20000) troppi oggetti selezionati (al massimo 20000) Marathi Marathi Gujarati Gujarati Bengali Bengalese diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 5363c5d06..da7fd34f0 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -5729,6 +5729,9 @@ breakText Glyphs 字形 Set Size of Each 個別にサイズを設定 @msg select exactly two objects オブジェクトをちょうど 2 つ選択してください @msg give `width` or `height` `width` または `height` を指定してください +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects 重なり合うオブジェクトをグループ化 +@msg too many objects selected (at most 20000) 選択されたオブジェクトが多すぎます(最大 20000) Marathi マラーティー語 Gujarati グジャラート語 Bengali ベンガル語 diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 8f2c407c4..d71ee8b9b 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -6032,6 +6032,9 @@ breakText Glyphs გლიფები Set Size of Each თითოეულის ზომის დაყენება @msg select exactly two objects მონიშნეთ ზუსტად ორი ობიექტი @msg give `width` or `height` მიუთითეთ `width` ან `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects გადაფარვადი ობიექტების დაჯგუფება +@msg too many objects selected (at most 20000) არჩეულია ძალიან ბევრი ობიექტი (მაქსიმუმ 20000) Marathi მარათჰი Gujarati გუჯარათი Bengali ბენგალური diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index acf8135c3..6b9ef0524 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -5625,6 +5625,9 @@ breakText Glyphs 글리프 Set Size of Each 각각 크기 설정 @msg select exactly two objects 개체를 정확히 두 개 선택하세요 @msg give `width` or `height` `width` 또는 `height`를 지정하세요 +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects 겹치는 개체 그룹화 +@msg too many objects selected (at most 20000) 선택한 개체가 너무 많습니다(최대 20000개) Marathi 마라티어 Gujarati 구자라트어 Bengali 벵골어 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 51dd8b37e..c4d9c535e 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -5984,6 +5984,9 @@ breakText Glyphs Glyphs Set Size of Each Formaat van elk instellen @msg select exactly two objects selecteer precies twee objecten @msg give `width` or `height` geef `width` of `height` op +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Overlappende objecten groeperen +@msg too many objects selected (at most 20000) te veel objecten geselecteerd (hoogstens 20000) Marathi Marathi Gujarati Gujarati Bengali Bengaals diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 30203b1a1..2418eae3c 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -5898,6 +5898,9 @@ breakText Glyphs Glify Set Size of Each Ustaw rozmiar każdego @msg select exactly two objects zaznacz dokładnie dwa obiekty @msg give `width` or `height` podaj `width` lub `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Grupuj nakładające się obiekty +@msg too many objects selected (at most 20000) zaznaczono za dużo obiektów (najwyżej 20000) Marathi Marathi Gujarati Gudżarati Bengali Bengalski diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 6ede5c351..34b223428 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -5962,6 +5962,9 @@ breakText Glyphs Glifos Set Size of Each Definir Tamanho de Cada Um @msg select exactly two objects selecione exatamente dois objetos @msg give `width` or `height` informe `width` ou `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Agrupar objetos sobrepostos +@msg too many objects selected (at most 20000) objetos demais selecionados (no máximo 20000) Marathi Marati Gujarati Guzerate Bengali Bengali diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 2bf406c74..747c91585 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -6020,6 +6020,9 @@ breakText Glyphs Глифы Set Size of Each Задать размер каждого @msg select exactly two objects выделите ровно два объекта @msg give `width` or `height` укажите `width` или `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Сгруппировать перекрывающиеся объекты +@msg too many objects selected (at most 20000) выделено слишком много объектов (не более 20000) Marathi Маратхи Gujarati Гуджарати Bengali Бенгальский diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 788d45c8a..26b3a1d59 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -6095,6 +6095,9 @@ breakText Glyphs Glyfer Set Size of Each Ange storlek för vart och ett @msg select exactly two objects markera exakt två objekt @msg give `width` or `height` ange `width` eller `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Gruppera överlappande objekt +@msg too many objects selected (at most 20000) för många objekt markerade (högst 20000) Marathi Marathi Gujarati Gujarati Bengali Bengali diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 50b7f213b..a4851f2e3 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -6000,6 +6000,9 @@ breakText Glyphs Glifler Set Size of Each Her Birinin Boyutunu Ayarla @msg select exactly two objects tam olarak iki nesne seç @msg give `width` or `height` `width` veya `height` belirt +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Çakışan Nesneleri Grupla +@msg too many objects selected (at most 20000) çok fazla nesne seçili (en fazla 20000) Marathi Marathi dili Gujarati Gücerat dili Bengali Bengalce diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 03d912f32..a4c04383a 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -5642,6 +5642,9 @@ breakText Glyphs Гліфи Set Size of Each Задати розмір кожного @msg select exactly two objects виділіть рівно два об’єкти @msg give `width` or `height` укажіть `width` або `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Згрупувати об’єкти, що перекриваються +@msg too many objects selected (at most 20000) виділено забагато об’єктів (не більше 20000) Marathi Маратхі Gujarati Гуджараті Bengali Бенгальська diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index cdea7542b..ab593787b 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -5645,6 +5645,9 @@ breakText Glyphs Glyph Set Size of Each Đặt kích thước từng cái @msg select exactly two objects hãy chọn đúng hai đối tượng @msg give `width` or `height` hãy cho `width` hoặc `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects Nhóm các đối tượng chồng lên nhau +@msg too many objects selected (at most 20000) chọn quá nhiều đối tượng (tối đa 20000) Marathi Tiếng Marathi Gujarati Tiếng Gujarati Bengali Tiếng Bengali diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index c485086b1..9b506a128 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -5885,6 +5885,9 @@ breakText Glyphs 字形 Set Size of Each 分别设置大小 @msg select exactly two objects 请恰好选择两个对象 @msg give `width` or `height` 请提供 `width` 或 `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects 编组重叠对象 +@msg too many objects selected (at most 20000) 选择的对象过多(最多 20000 个) Marathi 马拉地语 Gujarati 古吉拉特语 Bengali 孟加拉语 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 06ea60498..3e645ca09 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -5682,6 +5682,9 @@ breakText Glyphs 字符 Set Size of Each 個別設定大小 @msg select exactly two objects 請選取正好兩個物件 @msg give `width` or `height` 請提供 `width` 或 `height` +# Arrange Tools add-on: Group Overlapping Objects. + Group Overlapping Objects 群組重疊物件 +@msg too many objects selected (at most 20000) 選取的物件過多(最多 20000 個) Marathi 馬拉地文 Gujarati 古吉拉特文 Bengali 孟加拉文 diff --git a/docs/mcp.md b/docs/mcp.md index baf64c4e2..705b9d54b 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -6251,6 +6251,7 @@ as `object.setBounds` measures them (Use Preview Bounds), except Arrange in Grid | `addon.arrange.swap` | `size` (false); `scaleStrokes` (the Scale Strokes & Effects preference) | Swaps the bounds centres of exactly two selected objects (off otherwise); with `size`, each also takes the other's width and height. | | `addon.arrange.replace` | `fit`: `keep`, `bounds` (the replaced object's width and height), `proportional` (fits inside them) (`keep`); `scaleStrokes`; `keep` (false) | Puts a copy of the topmost selected object centred on each of the other selected objects, in that object's place in the stacking order; the replaced objects are deleted unless `keep` (the copy then goes right above each). Needs two objects or more; the copies are selected. → `{ids}` | | `addon.arrange.size` | `width`, `height` pt, 0.001…1000000 (at least one); `proportional` (false: a side not given stays; true: it follows the given one in proportion); `reference` 0…8 (4, the centre); `scaleStrokes` | Gives every selected object that width and height, keeping its reference point in place (the 9-point grid row by row from the top-left). | +| `addon.arrange.groupOverlapping` | none | Groups the selected objects (at most 20000) wherever their visual bounds overlap or touch, chains included: when A overlaps B and B overlaps C, all three go in one group. Only objects in the same layer or group are grouped together; objects that overlap nothing stay as they are. Each new group goes where its frontmost member was in the stacking order, and the groups and the objects left alone stay selected; with nothing overlapping it makes no step. Needs two objects or more. → `{groups: [id…], grouped}` (how many objects went into groups) | Zoom to Selection is the UI command `addon.arrange.zoomSelection {}` (remote mode only): it zooms and centres the active view so the selection's visual bounds fill the window with a margin, as `view.fitArtboard` fits the artboard, diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 6dd6db0b2..1a0fe7d61 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1,6 +1,6 @@ # Parity with Adobe Illustrator -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Type core: text shaped in its language, a Language per character, Indian languages, #4940 #4946 #4943; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Arrange Tools add-on: Group Overlapping Objects, #4928; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026) ## Feature submission evidence @@ -1399,10 +1399,15 @@ scaling, replace with each fit and kept originals in stacking order, size with p undo step, off states, bad params refused), `addons::arrange::tests` in `vectorcraft-ui-egui` (the submenu, the dialogs, Preview and Cancel, Zoom to Selection) and `command_sweep`. Commands: `docs/mcp.md` › Arrange Tools add-on. +## Arrange Tools add-on: Group Overlapping Objects (2026-11-01) + +A VectorCraft addition, not a feature of the reference app, so no area score moves. Object › Plug-ins › Arrange Tools › Group Overlapping Objects groups the top-level selected objects wherever their visual bounds overlap or touch, chains included (A overlapping B and B overlapping C makes one group of three), only among objects of the same layer or group; objects that overlap nothing stay as they are, each new group takes its frontmost member's place in the stacking order, and the groups and the loose objects stay selected. One undo step; at most 20000 objects, compared by a sweep along x with union-find. Tests: `group_overlapping_*` in `cmd::addons::arrange::tests` in `vectorcraft-engine` (chains, loners, stacking order, layers kept apart, visual bounds, no step when nothing overlaps, many objects), `group_overlapping_objects_runs_directly` in `vectorcraft-ui-egui` and `command_sweep`. Commands: `docs/mcp.md` › Arrange Tools add-on. + ## Revision history | Date | Change | Summary | |---|---|---| +| 2026-11-01 | minor | Arrange Tools add-on (a VectorCraft addition): Group Overlapping Objects (#4928); evidence added; scores unchanged. | | 2026-11-01 | minor | Type core: a Language per character (Character panel › Language, `text.setFormat` / `text.setRangeStyle {language}`, character styles), new type in the document's language; text is shaped in its language so fonts use their language forms (OpenType `locl`: Turkish `fi`, #4940, #4946); Hindi, Marathi, Gujarati, Bengali, Punjabi, Tamil, Telugu, Kannada, Malayalam, Odia and Urdu in the language list (#4943); scores unchanged | | 2026-10-31 | minor | Selection tools: with Alt held at the press, a Direct Selection marquee selects every path it touches whole and keeps the Group Selection pointer (#4890); a press on a selected object's centre point selects the object whole and a drag from it moves it (#4891); regression tests in `direct/tests.rs`; scores and estimates unchanged. | | 2026-10-31 | minor | Arrange Tools add-on (a VectorCraft addition): evidence added; scores unchanged. | diff --git a/docs/ui-parity.md b/docs/ui-parity.md index a135d587c..6672e0f94 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -1,6 +1,6 @@ # UI and interaction parity -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Character panel › Language, Indian languages in Document Setup › Language, #4946 #4943) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Arrange Tools add-on: Group Overlapping Objects, #4928) · **Target:** Adobe Illustrator 2026 (30.x) ## Feature submission evidence @@ -329,6 +329,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po | Tabs panel: a stop drags from where the press landed, a stop just added included | done | #1458 | | Outline view keeps template layers in Preview, dimmed | done | #1486; a picture placed to be traced stays visible under the outlines | | Arrange Tools add-on (VectorCraft addition): Object › Plug-ins › Arrange Tools runs Arrange in Grid… (grid by row, one row or one column; columns, H and V spacing, Center on Artboard; Preview), Swap Objects, Swap Objects and Sizes, Replace with Top Object… (Actual Size, Bounds or Bounds (Fit Proportionally); Keep Original; Scale Strokes & Effects; Preview), Set Size of Each… (Width and Height each ticked on or off, Constrain Width and Height Proportions with one side, the reference point, Scale Strokes & Effects; Preview) and Zoom to Selection, each one undo step | done | `addon.arrange.*`, `crates/engine/src/cmd/addons/arrange.rs`, `crates/ui-egui/src/addons/arrange.rs` and their tests; not a feature of the reference app, so it is not scored | +| Arrange Tools add-on (VectorCraft addition): Object › Plug-ins › Arrange Tools › Group Overlapping Objects groups the selected objects whose visual bounds overlap or touch (chains included, within one layer or group), each group at its frontmost member's stacking place, one undo step | done | #4928; `addon.arrange.groupOverlapping`, `crates/engine/src/cmd/addons/arrange.rs` and its tests; not a feature of the reference app, so it is not scored | | Smart Pen add-on (VectorCraft addition): Object › Plug-ins › Smart Pen selects the Smart Pen Tool, which the All Tools drawer lists under its own heading but neither toolbar shows as shipped (no shortcut), and opens the Smart Pen panel (next segment straight or curved; Remove Points Keep Shape, Make Smooth, Make Corner, Retract Handles, Close or Join). The tool draws (click: straight or curved segment, a curve after a straight one tangent; drag: smooth anchor, Alt breaks the handle, Shift 45°, Space moves it; Tab switches a segment; rubber band; close, join, continue) and edits without switching tools (click selects a point, drag moves it, Alt-click removes it keeping the shape, Shift+Alt-click deletes it, double-click toggles smooth/corner; handle drags with Shift and Alt; segment drags reshape, Alt-click adds a point) | done | `addon.pen.*`, `crates/tools/src/addons/smart_pen.rs`, `crates/ui-egui/src/addons/pen.rs` and their tests; not a feature of the reference app, so it is not scored | | Path Precision Tools add-on (VectorCraft addition): Object › Plug-ins › Path Precision Tools selects its seven tools (Anchor Eraser Brush, Extend Path, Measure & Inspect, Parametric Shape, Corner Tool, Slide Anchor Along Path, Shared Edge Merge Tool), which the All Tools drawer lists under their own heading but neither toolbar shows as shipped, opens the Anchor Inspector panel (the selected anchor's X/Y and handle lengths and angles as fields; Make Smooth, Make Corner, Equalize Handles, Remove Points Keep Shape, Add Points, the three ways to close) and the Parametric Shape panel (kind, count, W/H, rotation, the kind's own values and corner radius of the selected parametric shape, or of the tool's next one), and runs Add Points at / Split at Crossings and Convert to Parametric Shape; the Parametric Shape Tool draws ten re-editable kinds from the centre (↑/↓ and ←/→ change their numbers while dragging) and edits a selected one with handles; the Corner Tool cuts editable corners on any path (hover shows the anchor, a click uses its radius and kind, a drag sizes the corner, Shift whole values; a corner's marker selects it and loads its values, drags to resize, to 0 or double-clicked removes it; never past what fits; Alt-marquee cuts every anchor inside) with a Corners section in the Anchor Inspector (radius, kind, selected count, Apply to Selected Anchors); Slide Anchor Along Path drags an anchor along its curve with the segments beside it refitted, Shift snapping where the tangent is a multiple of 45°; the Shared Edge Merge Tool shows, over two paths touching at two coincident anchors, the two stretches and the edge merging them, a click merges them (half way; Shift the upper path, Alt the lower), and near anchors meet or an anchor one path lacks is added to match (Shift+Alt asks for this inside a stretch), with a Shared Edges section in the Anchor Inspector (brush diameter, coincidence 0 to 3 pt, Keep Shared Points Together, which takes coincident anchors along with the add-on's own anchor moves); a double-click on the brush or Extend Path tool opens its options | done | `addon.pathtools.*`, `crates/tools/src/addons/`, `crates/ui-egui/src/addons/pathtools.rs`, `crates/ui-egui/src/addons/pathtools/` and their tests; not a feature of the reference app, so it is not scored | | Path Precision Tools add-on (VectorCraft addition): Effect › Path Effects lists its live effects (Dashes to Geometry, Add Anchors, Extend Path, Close Path, Smooth Anchors, Retract Handles, Reverse Direction, Simplify Anchors, Split at Anchors), each with the effect dialog's live Preview except Reverse Direction, which has no options, and listed in the Appearance panel like any effect | done | `addon.path.*`, `crates/effects/src/addons/path.rs`, `crates/ui-egui/src/addons/pathtools.rs` and their tests; not a feature of the reference app, so it is not scored | @@ -381,6 +382,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development | Date | Change | Summary | |---|---|---| +| 2026-11-01 | minor | Arrange Tools add-on (a VectorCraft addition): Group Overlapping Objects (#4928); scores and estimates unchanged | | 2026-11-01 | minor | Character panel › Language: the selected characters' language, from Document Setup's list, which gains Hindi, Marathi, Gujarati, Bengali, Punjabi, Tamil, Telugu, Kannada, Malayalam, Odia and Urdu (#4946, #4943); the Digits menu's numeral names keep their own translations; scores unchanged | | 2026-11-01 | minor | VectorCraft Options' Version is a menu of the versions VectorCraft writes, each named by the releases that read it, and an uncompressible version turns Use Compression off (#4931); a save refused before writing shows the Can't save alert with the reason (#4933); About › Contributors' table keeps its header row above the scrolling rows (#4944); the Gradient panel's freeform point swatch opens the point's popover (#4953); scores unchanged | | 2026-10-31 | minor | A file opened from the file manager while VectorCraft runs opens as a new tab in that window, which comes to the front, on Windows and Linux (#4892); `--new-instance` opts out; scores unchanged |