From 84336967aa0bc8ccf6af993a9793442a0bef3aa6 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Sun, 1 Nov 2026 08:56:29 +0700 Subject: [PATCH] Fix #4922: Expand Dashes into Paths --- ROADMAP.md | 4 +- crates/engine/src/cmd/addons/pathtools.rs | 113 +++++++++++++++- .../engine/src/cmd/addons/pathtools/tests.rs | 123 ++++++++++++++++++ crates/engine/tests/command_sweep.rs | 3 + crates/ui-egui/src/addons/pathtools.rs | 3 +- crates/ui-egui/src/addons/pathtools/tests.rs | 10 ++ crates/ui-egui/src/i18n/bg.tsv | 3 + crates/ui-egui/src/i18n/bs.tsv | 3 + crates/ui-egui/src/i18n/ca.tsv | 3 + crates/ui-egui/src/i18n/cs.tsv | 3 + crates/ui-egui/src/i18n/da.tsv | 3 + crates/ui-egui/src/i18n/de.tsv | 3 + crates/ui-egui/src/i18n/el.tsv | 3 + crates/ui-egui/src/i18n/es.tsv | 3 + crates/ui-egui/src/i18n/fi.tsv | 3 + crates/ui-egui/src/i18n/fr.tsv | 3 + crates/ui-egui/src/i18n/hu.tsv | 3 + crates/ui-egui/src/i18n/id.tsv | 3 + crates/ui-egui/src/i18n/it.tsv | 3 + crates/ui-egui/src/i18n/ja.tsv | 3 + crates/ui-egui/src/i18n/ka.tsv | 3 + crates/ui-egui/src/i18n/ko.tsv | 3 + crates/ui-egui/src/i18n/nl.tsv | 3 + crates/ui-egui/src/i18n/pl.tsv | 3 + crates/ui-egui/src/i18n/pt-br.tsv | 3 + crates/ui-egui/src/i18n/ru.tsv | 3 + crates/ui-egui/src/i18n/sv.tsv | 3 + crates/ui-egui/src/i18n/tr.tsv | 3 + crates/ui-egui/src/i18n/uk.tsv | 3 + crates/ui-egui/src/i18n/vi.tsv | 3 + crates/ui-egui/src/i18n/zh-hans.tsv | 3 + crates/ui-egui/src/i18n/zh-hant.tsv | 3 + docs/mcp.md | 11 ++ docs/target-app-parity.md | 7 +- docs/ui-parity.md | 4 +- 35 files changed, 349 insertions(+), 7 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index cbce1fbfa..c85be5aef 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-11-01 · **Change:** minor (Progress log row for the Arrange Tools add-on's Sort Objects; 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 the Path Precision Tools add-on's Expand Dashes into Paths; 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: Expand Dashes into Paths joins the Path Precision Tools add-on (Object › Plug-ins › Path Precision Tools): each selected path's dashed stroke becomes separate open paths, one per dash, laid out exactly as the stroke draws them (pattern, offset, Align Dashes to Corners and Path Ends), each keeping the stroke with the dashes off, so the gaps are real gaps for cutters and plotters, as one undo step (`addon.pathtools.expandDashes`, #4922). - 2026-11-01: Randomize Opacity joins the Arrange Tools add-on (Object › Plug-ins › Arrange Tools): each selected object gets its own opacity at random between a minimum and a maximum percent, with a seed that repeats the result, as one undo step (`addon.arrange.randomizeOpacity`, #4925). - 2026-11-01: Sort Objects joins the Arrange Tools add-on (Object › Plug-ins › Arrange Tools): the selected objects are sorted by width, height, area, position, opacity or stacking order (small to large, large to small or at random) and take another of those in that order, trading their values or stepping them evenly, as one undo step (`addon.arrange.sort`, #4917). - 2026-11-01: New Symbols from Layers joins the Document Tools add-on (Object › Plug-ins › Document Tools): one symbol of each top-level layer's art (the layers highlighted in the Layers panel when two or more are, else every layer), named after the layer, made as New Symbol makes one; Convert Layers to Symbols also puts an instance of each in place of its layer's art. Empty layers are skipped and reported; one undo step. A VectorCraft addition: headline numbers unchanged. @@ -210,6 +211,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 the Path Precision Tools add-on's Expand Dashes into Paths (#4922); headline numbers unchanged. | | 2026-11-01 | minor | Progress log row for the Arrange Tools add-on's Randomize Opacity (#4925); headline numbers unchanged. | | 2026-11-01 | minor | Progress log row for the Arrange Tools add-on's Sort Objects (#4917); headline numbers unchanged. | | 2026-11-01 | minor | Progress log row for the Document Tools add-on's New Symbols from Layers (#4927); headline numbers unchanged. | diff --git a/crates/engine/src/cmd/addons/pathtools.rs b/crates/engine/src/cmd/addons/pathtools.rs index 94114a59c..5d0e5723f 100644 --- a/crates/engine/src/cmd/addons/pathtools.rs +++ b/crates/engine/src/cmd/addons/pathtools.rs @@ -2,7 +2,8 @@ //! the Extend Path tool's extension, the Measure & Inspect tool's readouts, the Anchor Inspector's //! anchor edits, Split / Add Points at Crossings, Slide Anchor Along Path, the Corner Tool's corners //! ([`corners`]), the Parametric Shape Tool's shapes ([`shape`]), the Shared Edge Merge Tool's -//! merges with Keep Shared Points Together ([`shared_edge`]), and Divide into Dashes. +//! merges with Keep Shared Points Together ([`shared_edge`]), Divide into Dashes and Expand Dashes +//! into Paths. //! Its tools, panels and submenu (Object › Plug-ins › Path Precision Tools) are the tools crate's //! and the UI's. @@ -17,7 +18,7 @@ mod tests; use std::collections::{BTreeMap, BTreeSet}; use serde_json::{Value, json}; -use vectorcraft_doc::{AnchorRef, Document, NodeId, NodeKind}; +use vectorcraft_doc::{AnchorRef, Appearance, AppearanceItem, Document, LineCap, NodeId, NodeKind}; use vectorcraft_geom::{PathData, Point, SubPath}; use vectorcraft_pathops::addons::{self as po, pathtools as pt}; @@ -40,7 +41,7 @@ const MIN_RADIUS: f64 = 0.05; const MAX_DIAMETER: f64 = 2500.0; /// The farthest an end is extended at once (pt). const MAX_EXTEND: f64 = 1e6; -/// Most pieces Divide into Dashes makes at once. +/// Most pieces Divide into Dashes and Expand Dashes into Paths make at once. const MAX_PIECES: usize = 100_000; /// Most anchors Add Points puts on each segment. const MAX_ADD: u64 = pt::MAX_ADDED_PER_SEGMENT as u64; @@ -164,6 +165,15 @@ pub fn specs() -> Vec { has_selection, divide_dashes ), + cmd!( + "addon.pathtools.expandDashes", + "Expand Dashes into Paths", + ["Object", "Plug-ins", "Path Precision Tools"], + None, + "{ids?: [id…] (default: the selection)} turn the dashed stroke of each path and compound path under the objects into separate open paths, one per dash, laid out as the stroke draws them (its dash pattern, offset and Align Dashes to Corners and Path Ends), each keeping the path's appearance with the dashes off, so the gaps are real gaps: a dash running all the way round a closed path stays closed, and with a round or projecting cap a dash of no length (a dot) becomes a path on its point. The top stroke's dashes are the ones expanded; its arrowheads go, and the path's fills stay on it, below its dashes. Paths without a dashed stroke stay as they are. One undo step; the dashes are selected; at most 100000 → {ids, dashes}", + has_selection, + expand_dashes + ), ]; v.extend(shape::specs()); v.extend(corners::specs()); @@ -793,3 +803,100 @@ fn divide_dashes(s: &mut Session, p: &Value) -> Result { })?; Ok(json!({ "ids": ids.iter().map(|i| i.0).collect::>(), "dashes": dashes })) } + +// ---------- Expand Dashes into Paths ---------- + +/// The paths and compound paths under `roots` whose top stroke is dashed: a compound path's +/// members stand for it, which holds their paint. Clipping paths are left alone. +fn dashed_paths(d: &Document, roots: &[NodeId]) -> Vec { + let editable = d.editable(); + let mut seen = BTreeSet::new(); + let mut out = vec![]; + for id in paths_under(d, roots) { + let compound = d.parent_of(id).and_then(|p| d.node(p)).filter(|p| matches!(p.kind, NodeKind::Compound { .. })); + let Some(n) = compound.or_else(|| d.node(id)) else { continue }; + let dashed = n.appearance.stroke().and_then(|st| st.dash.as_ref()).is_some_and(|dash| dash.is_dashed()); + if dashed && !matches!(n.kind, NodeKind::Path { clipping: true, .. }) && editable(n.id) && seen.insert(n.id) { + out.push(n.id); + } + } + out +} + +fn expand_dashes(s: &mut Session, p: &Value) -> Result { + const C: &str = "addon.pathtools.expandDashes"; + let too_many = || bad(C, "that makes too many pieces (at most 100000): make the dashes longer"); + let roots = checked_ids_or_selection(s, p, C)?; + let d = &s.doc()?.doc; + // Each dashed path and its dashes, as the canvas draws them. + let mut changed: Vec<(NodeId, Vec)> = vec![]; + let mut count = 0; + for id in dashed_paths(d, &roots) { + let Some(n) = d.node(id) else { continue }; + let (Some(st), Some(bp)) = (n.appearance.stroke(), n.stroke_path()) else { continue }; + let Some(dash) = &st.dash else { continue }; + // The line the stroke draws: the path, its ends trimmed under any arrowheads. + let line = vectorcraft_render::effects::stroke::stroke_pieces(&bp, st).line; + // A pattern too fine to draw is drawn solid: it has too many dashes to expand. + let dashed = vectorcraft_render::effects::stroke::dash(&line, dash).ok_or_else(too_many)?; + let mut pieces = PathData::from_bezpath(&dashed.path).subpaths; + // A dot paints its cap; a path that stays on its point is drawn so too. Under a butt cap it + // paints nothing. + if st.cap != LineCap::Butt { + pieces.extend(dashed.dots.iter().map(|dot| SubPath::polyline(&[dot.at, dot.at], false))); + } + count += pieces.len(); + if count > MAX_PIECES { + return Err(too_many()); + } + if !pieces.is_empty() { + changed.push((id, pieces)); + } + } + if changed.is_empty() { + return Err(bad(C, "select paths with dashed strokes to expand")); + } + let ids = s.edit("Expand Dashes into Paths", |d, sel| { + let mut all = vec![]; + let mut beside = Beside::replacing(d, &changed.iter().map(|(id, _)| *id).collect::>()); + for (id, pieces) in changed { + let n = d.node(id).cloned().ok_or(EngineError::NoNode(id))?; + let (NodeKind::Path { rule, .. } | NodeKind::Compound { rule, .. }) = &n.kind else { continue }; + let rule = *rule; + let filled = |i: &AppearanceItem| matches!(i, AppearanceItem::Fill(f) if f.visible && !f.paint.is_none()); + // The dashes: open paths with the strokes (the top one's dashes and arrowheads off). + let mut look = Appearance::clone(&n.appearance); + look.items.retain(|i| !matches!(i, AppearanceItem::Fill(_))); + if let Some(st) = look.stroke_mut() { + (st.dash, st.start_arrow, st.end_arrow) = (None, None, None); + } + let mut dash = n.clone(); + dash.kind = NodeKind::Path { path: PathData::default(), rule, live: None, clipping: false, guide: false }; + dash.appearance = look.into(); + // The fills stay on the path, below its dashes; without fills it goes. + let mut parts = vec![]; + if n.appearance.items.iter().any(filled) { + let mut fill = n.clone(); + let mut look = Appearance::clone(&n.appearance); + look.items.retain(filled); + fill.appearance = look.into(); + parts.push(fill); + } + for sp in pieces { + let mut piece = d.reid(&dash); + if let NodeKind::Path { path, .. } = &mut piece.kind { + *path = PathData::single(sp); + } + all.push(piece.id); + parts.push(piece); + } + beside.put(d, id, parts)?; + } + beside.finish(d)?; + // The dashes join the objects selected (whole, in one go: there may be many). + let before = std::mem::take(&mut sel.objects); + sel.set(before.into_iter().filter(|id| d.node(*id).is_some()).chain(all.iter().copied())); + Ok(all) + })?; + Ok(json!({ "ids": ids.iter().map(|i| i.0).collect::>(), "dashes": ids.len() })) +} diff --git a/crates/engine/src/cmd/addons/pathtools/tests.rs b/crates/engine/src/cmd/addons/pathtools/tests.rs index cc745385c..32bcc0c3b 100644 --- a/crates/engine/src/cmd/addons/pathtools/tests.rs +++ b/crates/engine/src/cmd/addons/pathtools/tests.rs @@ -748,3 +748,126 @@ fn divide_into_dashes_refuses_bad_params_and_too_many_pieces() { s.execute("select.none", &json!({})).unwrap(); refuses(&mut s, "addon.pathtools.divideDashes", &[json!({})]); } + +/// The dashes made of `r`'s ids: (first x, last x) of each, by its anchors. +fn dash_spans(s: &Session, r: &Value) -> Vec<(f64, f64)> { + ids(r).iter().map(|id| path(s, *id).bounds().map(|b| (b.x0, b.x1)).unwrap()).collect() +} + +fn near(a: &[(f64, f64)], b: &[(f64, f64)]) -> bool { + a.len() == b.len() && a.iter().zip(b).all(|(a, b)| (a.0 - b.0).abs() < 1e-6 && (a.1 - b.1).abs() < 1e-6) +} + +#[test] +fn expand_dashes_makes_an_open_path_of_each_dash_keeping_the_stroke() { + let mut s = session(); + let l = line(&mut s, &[(0.0, 0.0), (100.0, 0.0)], false); + select(&mut s, &[l]); + s.execute("paint.setFill", &json!({"none": true})).unwrap(); + s.execute("stroke.set", &json!({"weight": 3, "cap": "round", "dash": [30, 10]})).unwrap(); + let n = steps(&s); + let r = s.execute("addon.pathtools.expandDashes", &json!({})).unwrap(); + assert_eq!(steps(&s), n + 1, "one undo step"); + assert!(near(&dash_spans(&s, &r), &[(0.0, 30.0), (40.0, 70.0), (80.0, 100.0)]), "{:?}", dash_spans(&s, &r)); + assert_eq!(r["dashes"], json!(3)); + let st = s.doc().unwrap(); + for id in ids(&r) { + let node = st.doc.node(id).unwrap(); + let stroke = node.appearance.stroke().unwrap(); + assert_eq!((stroke.width, stroke.cap, stroke.dash.clone()), (3.0, vectorcraft_doc::LineCap::Round, None), "the stroke stays, dashes off"); + assert!(!path(&s, id).subpaths[0].closed); + } + assert_eq!(st.selection.objects, ids(&r)); + s.execute("edit.undo", &json!({})).unwrap(); + assert!(s.doc().unwrap().doc.node(l).unwrap().appearance.stroke().unwrap().dash.is_some()); + // The offset shifts the pattern as the stroke draws it: 20 into the first dash. + s.execute("stroke.set", &json!({"dashOffset": 20})).unwrap(); + let r = s.execute("addon.pathtools.expandDashes", &json!({"ids": [l.0]})).unwrap(); + assert!(near(&dash_spans(&s, &r), &[(0.0, 10.0), (20.0, 50.0), (60.0, 90.0)]), "{:?}", dash_spans(&s, &r)); +} + +#[test] +fn expand_dashes_keeps_the_fill_below_and_follows_aligned_corners() { + let mut s = session(); + let rect = id_of(&s.execute("shape.rectangle", &json!({"x": 0, "y": 0, "width": 100, "height": 60})).unwrap()); + select(&mut s, &[rect]); + s.execute("stroke.set", &json!({"dash": [20, 20]})).unwrap(); + let had_fill = !s.doc().unwrap().doc.node(rect).unwrap().appearance.fill_paint().is_none(); + let r = s.execute("addon.pathtools.expandDashes", &json!({})).unwrap(); + // 320 round: eight dashes of 20, a dash bending round each corner it meets. + let pieces = ids(&r); + assert_eq!(pieces.len(), 8); + let st = s.doc().unwrap(); + let layer: Vec = st.doc.layers[0].children().unwrap().iter().map(|n| n.id).collect(); + if had_fill { + let kept = st.doc.node(rect).unwrap(); + assert!(kept.appearance.stroke().is_none() && kept.appearance.fill().is_some(), "the fill stays on the rectangle"); + assert_eq!(layer, [vec![rect], pieces.clone()].concat(), "the fill below its dashes"); + } else { + assert_eq!(layer, pieces); + } + assert!(pieces.iter().all(|id| st.doc.node(*id).unwrap().appearance.fill().is_none()), "an open dash isn't filled"); + s.execute("edit.undo", &json!({})).unwrap(); + // Aligned to corners: a dash centred on each corner, the four sides each starting and ending + // on half a dash; 12 dashes along the sides. + s.execute("stroke.set", &json!({"dash": [10, 10], "alignDashes": true})).unwrap(); + let r = s.execute("addon.pathtools.expandDashes", &json!({})).unwrap(); + let corners = [Point::new(0.0, 0.0), Point::new(100.0, 0.0), Point::new(100.0, 60.0), Point::new(0.0, 60.0)]; + let mut centred = 0; + for id in ids(&r) { + let sp = path(&s, id).subpaths[0].clone(); + if let [a, b, c] = sp.anchors.as_slice() + && corners.iter().any(|k| b.p.distance(*k) < 1e-6) + { + assert!((a.p.distance(b.p) - 5.0).abs() < 1e-6 && (c.p.distance(b.p) - 5.0).abs() < 1e-6, "{sp:?}"); + centred += 1; + } + } + assert_eq!((ids(&r).len(), centred), (16, 4)); +} + +#[test] +fn expand_dashes_turns_dots_into_paths_on_their_point_and_releases_compound_paths() { + let mut s = session(); + let l = line(&mut s, &[(0.0, 0.0), (100.0, 0.0)], false); + select(&mut s, &[l]); + s.execute("stroke.set", &json!({"dash": [0, 20], "cap": "round"})).unwrap(); + let r = s.execute("addon.pathtools.expandDashes", &json!({})).unwrap(); + assert_eq!(ids(&r).len(), 6, "a dot every 20 from 0 to 100"); + assert!(ids(&r).iter().all(|id| path(&s, *id).bounds().unwrap().width() == 0.0)); + s.execute("edit.undo", &json!({})).unwrap(); + // Under a butt cap a dot paints nothing, so there is nothing to expand. + s.execute("stroke.set", &json!({"cap": "butt"})).unwrap(); + refuses(&mut s, "addon.pathtools.expandDashes", &[json!({})]); + // A compound path's dashes are paths of their own with its stroke. + let a = line(&mut s, &[(0.0, 100.0), (50.0, 100.0)], false); + let b = line(&mut s, &[(0.0, 120.0), (50.0, 120.0)], false); + select(&mut s, &[a, b]); + let c = id_of(&s.execute("object.compoundPath.make", &json!({})).unwrap()); + s.execute("stroke.set", &json!({"weight": 2, "dash": [20, 10], "cap": "butt"})).unwrap(); + let r = s.execute("addon.pathtools.expandDashes", &json!({})).unwrap(); + // Each 50 line: 20, gap, 20. + let st = s.doc().unwrap(); + assert_eq!(ids(&r).len(), 4); + for id in ids(&r) { + let n = st.doc.node(id).unwrap(); + assert!(matches!(n.kind, vectorcraft_doc::NodeKind::Path { .. }), "a plain path"); + assert_eq!(n.appearance.stroke_width(), 2.0); + assert!(n.appearance.stroke().unwrap().dash.is_none()); + } + assert!(st.doc.node(c).is_none_or(|n| n.appearance.stroke().is_none()), "the compound path's dashes are gone from it"); +} + +#[test] +fn expand_dashes_refuses_without_dashes_and_past_the_limit() { + let mut s = session(); + let l = line(&mut s, &[(0.0, 0.0), (1e5, 0.0)], false); + refuses(&mut s, "addon.pathtools.expandDashes", &[json!({})]); + select(&mut s, &[l]); + refuses(&mut s, "addon.pathtools.expandDashes", &[json!({}), json!({"ids": "x"}), json!({"zzz": 1})]); + // 125000 dashes refused; 1000 are fine. + s.execute("stroke.set", &json!({"dash": [0.4, 0.4]})).unwrap(); + refuses(&mut s, "addon.pathtools.expandDashes", &[json!({})]); + s.execute("stroke.set", &json!({"dash": [50, 50]})).unwrap(); + assert_eq!(s.execute("addon.pathtools.expandDashes", &json!({})).unwrap()["dashes"], json!(1000)); +} diff --git a/crates/engine/tests/command_sweep.rs b/crates/engine/tests/command_sweep.rs index 330fd4540..38a60e239 100644 --- a/crates/engine/tests/command_sweep.rs +++ b/crates/engine/tests/command_sweep.rs @@ -600,6 +600,9 @@ fn structured_junk() { ("addon.pathtools.divideDashes", json!({"dash": 1e-308, "gap": -1e308, "fit": "x"})), ("addon.pathtools.divideDashes", json!({"dash": 0.01, "gap": 1e6, "fit": true, "removeGaps": false})), ("addon.pathtools.divideDashes", json!({"dash": f64::MAX, "gap": [0], "removeGaps": null})), + ("addon.pathtools.expandDashes", json!({})), + ("addon.pathtools.expandDashes", json!({"ids": [0, 1, 2, 3, u64::MAX]})), + ("addon.pathtools.expandDashes", json!({"ids": "x", "junk": f64::MAX})), // Refused before anything is written: no format of that name, a time that is no time. ("addon.document.saveTimestampedCopy", json!({"format": "png", "folder": 7})), ("addon.document.saveTimestampedCopy", json!({"format": "svg", "time": [1e308], "folder": null})), diff --git a/crates/ui-egui/src/addons/pathtools.rs b/crates/ui-egui/src/addons/pathtools.rs index 9503a35e4..2e5306677 100644 --- a/crates/ui-egui/src/addons/pathtools.rs +++ b/crates/ui-egui/src/addons/pathtools.rs @@ -1,6 +1,6 @@ //! Path Precision Tools in the interface: its submenu (Object › Plug-ins › Path Precision Tools), //! which selects its tools, opens its panels and runs Split / Add Points at Crossings, Divide into -//! Dashes ([`dashes`]) and Convert to Parametric Shape; the Anchor Inspector panel (the selected +//! Dashes ([`dashes`]), Expand Dashes into Paths and Convert to Parametric Shape; the Anchor Inspector panel (the selected //! anchor's position and handles as numbers, the add-on's anchor edits, the Corner Tool's values, //! [`corner`], and the Shared Edge Merge Tool's, [`shared_edge`]); the Parametric Shape panel //! ([`shape`]); and its tools' options dialog (a double-click on the tool). Everything acts through `addon.pathtools.*` and @@ -65,6 +65,7 @@ pub fn menu() -> Vec { Item::Cmd("Add Points at Crossings", "addon.pathtools.crossings", json!({})), Item::Cmd("Split at Crossings", "addon.pathtools.crossings", json!({ "cut": true })), Item::Cmd("Divide into Dashes…", dashes::COMMAND, Value::Null), + Item::Cmd("Expand Dashes into Paths", "addon.pathtools.expandDashes", json!({})), Item::Cmd("Convert to Parametric Shape", "addon.pathtools.shape.convert", json!({})), ] } diff --git a/crates/ui-egui/src/addons/pathtools/tests.rs b/crates/ui-egui/src/addons/pathtools/tests.rs index e69c93042..d477fa036 100644 --- a/crates/ui-egui/src/addons/pathtools/tests.rs +++ b/crates/ui-egui/src/addons/pathtools/tests.rs @@ -33,6 +33,16 @@ fn the_plugins_submenu_selects_the_tools_and_opens_the_anchor_inspector() { assert_eq!(e.command.as_deref(), Some("addon.pathtools.crossings")); assert_eq!(e.params.get("cut").and_then(|v| v.as_bool()).unwrap_or(false), cut); } + // Expand Dashes into Paths runs at once: a dashed line becomes its dashes. + let line = app.run("path.create", json!({"anchors": [{"x": 0, "y": 0}, {"x": 100, "y": 0}]})).unwrap()["id"].clone(); + app.run("select.set", json!({"ids": [line]})).unwrap(); + app.run("stroke.set", json!({"dash": [30, 10]})).unwrap(); + let expand = under("Expand Dashes into Paths").expect("listed"); + let (cmd, p) = menus::click_target(&expand.label, expand.command.as_deref().unwrap(), &expand.params); + menus::invoke(&mut app, &cmd, p); + let selected = app.session.doc().unwrap().selection.objects.len(); + assert!(selected >= 3, "three dashes selected: {selected}"); + app.run("edit.undo", json!({})).unwrap(); // Artwork Cleanup's submenu and Install Plug-in… are still there. assert!(entries.iter().any(|e| e.path == ["Object", "Plug-ins", "Artwork Cleanup"])); assert!(entries.iter().any(|e| e.path == ["Object", "Plug-ins"] && e.label == "Install Plug-in…")); diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index b3bc7e860..f69921d5d 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -5965,6 +5965,9 @@ breakText Glyphs Глифи Remove Gaps Премахване на разстоянията @msg that makes too many pieces (at most 100000): make the dashes longer така се получават твърде много части (най-много 100000): удължете тиретата @msg select paths, or segments, to divide изберете пътища или сегменти за разделяне +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Разширяване на тиретата в пътища +@msg select paths with dashed strokes to expand изберете пътища с прекъснат щрих за разширяване # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Инструменти за документи Save a Timestamped Copy Запазване на копие с дата и час diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index aaa5ec00f..904856098 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -5998,6 +5998,9 @@ shape Open Otvoren Remove Gaps Ukloni razmake @msg that makes too many pieces (at most 100000): make the dashes longer to daje previše dijelova (najviše 100000): produžite crtice @msg select paths, or segments, to divide odaberite putanje ili segmente za dijeljenje +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Proširi crtice u putanje +@msg select paths with dashed strokes to expand odaberite putanje s isprekidanim potezom za proširivanje # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Alati za dokument Save a Timestamped Copy Sačuvaj kopiju s datumom i vremenom diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 5e309464e..2eadca7c1 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -6071,6 +6071,9 @@ breakText Glyphs Glifs Remove Gaps Elimina les separacions @msg that makes too many pieces (at most 100000): make the dashes longer això fa massa trossos (com a màxim 100000): allargueu els guions @msg select paths, or segments, to divide seleccioneu camins o segments per dividir +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Expandeix els guions en camins +@msg select paths with dashed strokes to expand seleccioneu camins amb traç discontinu per expandir # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Eines del document Save a Timestamped Copy Desa una còpia amb data i hora diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 700dcf26d..d45c83723 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -6081,6 +6081,9 @@ breakText Glyphs Glyfy Remove Gaps Odstranit mezery @msg that makes too many pieces (at most 100000): make the dashes longer tím vznikne příliš mnoho dílů (nejvýše 100000): prodlužte čárky @msg select paths, or segments, to divide vyberte cesty nebo segmenty k rozdělení +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Rozložit čárky na cesty +@msg select paths with dashed strokes to expand vyberte cesty s čárkovaným tahem k rozložení # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Nástroje dokumentu Save a Timestamped Copy Uložit kopii s datem a časem diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index b4cb18ce0..74d5e43d6 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -5980,6 +5980,9 @@ breakText Glyphs Glyffer Remove Gaps Fjern mellemrum @msg that makes too many pieces (at most 100000): make the dashes longer det giver for mange stykker (højst 100000): gør stregerne længere @msg select paths, or segments, to divide vælg stier eller segmenter, der skal opdeles +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Udvid streger til stier +@msg select paths with dashed strokes to expand vælg stier med stiplet streg, der skal udvides # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Dokumentværktøjer Save a Timestamped Copy Gem en kopi med dato og klokkeslæt diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 0f9001fa4..f1e17a4d3 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -5666,6 +5666,9 @@ breakText Glyphs Glyphen Remove Gaps Lücken entfernen @msg that makes too many pieces (at most 100000): make the dashes longer das ergibt zu viele Teile (höchstens 100000): machen Sie die Striche länger @msg select paths, or segments, to divide wählen Sie Pfade oder Segmente zum Aufteilen aus +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Striche in Pfade umwandeln +@msg select paths with dashed strokes to expand wählen Sie Pfade mit gestrichelter Kontur zum Umwandeln aus # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Dokumentwerkzeuge Save a Timestamped Copy Kopie mit Zeitstempel speichern diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 931624d4d..9c046d74f 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -6033,6 +6033,9 @@ breakText Glyphs Γλύφοι Remove Gaps Αφαίρεση κενών @msg that makes too many pieces (at most 100000): make the dashes longer αυτό δίνει πάρα πολλά κομμάτια (το πολύ 100000): κάντε τις παύλες μεγαλύτερες @msg select paths, or segments, to divide επιλέξτε διαδρομές ή τμήματα για διαίρεση +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Επέκταση παυλών σε διαδρομές +@msg select paths with dashed strokes to expand επιλέξτε διαδρομές με διακεκομμένο περίγραμμα για επέκταση # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Εργαλεία εγγράφου Save a Timestamped Copy Αποθήκευση αντιγράφου με ημερομηνία και ώρα diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 7bb969e1b..eaa3f66d7 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -6023,6 +6023,9 @@ breakText Glyphs Glifos Remove Gaps Quitar separaciones @msg that makes too many pieces (at most 100000): make the dashes longer eso da demasiados trozos (como máximo 100000): alargue los guiones @msg select paths, or segments, to divide seleccione trazados o segmentos para dividir +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Expandir guiones en trazados +@msg select paths with dashed strokes to expand seleccione trazados con trazo discontinuo para expandir # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Herramientas de documento Save a Timestamped Copy Guardar una copia con fecha y hora diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index a0017ed4d..a4c7a8774 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -6111,6 +6111,9 @@ breakText Glyphs Merkit Remove Gaps Poista välit @msg that makes too many pieces (at most 100000): make the dashes longer siitä tulee liian monta osaa (enintään 100000): pidennä viivoja @msg select paths, or segments, to divide valitse jaettavat polut tai segmentit +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Laajenna viivat poluiksi +@msg select paths with dashed strokes to expand valitse laajennettavat polut, joissa on katkoviiva # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Asiakirjatyökalut Save a Timestamped Copy Tallenna kopio aikaleimalla diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index a4c3483b7..774b000ba 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -6078,6 +6078,9 @@ breakText Glyphs Glyphes Remove Gaps Supprimer les écarts @msg that makes too many pieces (at most 100000): make the dashes longer cela donne trop de morceaux (100000 au plus) : allongez les tirets @msg select paths, or segments, to divide sélectionnez des tracés ou des segments à diviser +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Décomposer les tirets en tracés +@msg select paths with dashed strokes to expand sélectionnez des tracés à contour pointillé à décomposer # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Outils de document Save a Timestamped Copy Enregistrer une copie horodatée diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index c44637ba9..46bb0c991 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -6067,6 +6067,9 @@ breakText Glyphs Glifák Remove Gaps Hézagok eltávolítása @msg that makes too many pieces (at most 100000): make the dashes longer ez túl sok darabot adna (legfeljebb 100000): hosszabbítsa meg a szaggatásokat @msg select paths, or segments, to divide jelöljön ki felosztandó útvonalakat vagy szakaszokat +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Szaggatások kibontása útvonalakká +@msg select paths with dashed strokes to expand jelöljön ki szaggatott körvonalú útvonalakat a kibontáshoz # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Dokumentumeszközök Save a Timestamped Copy Másolat mentése időbélyeggel diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index e1b620277..8e87dd93a 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -5980,6 +5980,9 @@ breakText Glyphs Glif Remove Gaps Hapus celah @msg that makes too many pieces (at most 100000): make the dashes longer itu menghasilkan terlalu banyak potongan (paling banyak 100000): perpanjang garisnya @msg select paths, or segments, to divide pilih jalur atau segmen yang akan dibagi +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Perluas garis putus menjadi jalur +@msg select paths with dashed strokes to expand pilih jalur dengan stroke putus-putus yang akan diperluas # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Alat dokumen Save a Timestamped Copy Simpan salinan bercap waktu diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 77d8c0a20..2cf474260 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -5700,6 +5700,9 @@ breakText Glyphs Glifi Remove Gaps Rimuovi le distanze @msg that makes too many pieces (at most 100000): make the dashes longer così si ottengono troppi pezzi (al massimo 100000): allunga i trattini @msg select paths, or segments, to divide seleziona tracciati o segmenti da dividere +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Espandi trattini in tracciati +@msg select paths with dashed strokes to expand seleziona tracciati con traccia tratteggiata da espandere # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Strumenti documento Save a Timestamped Copy Salva una copia con data e ora diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index fea333848..8cdb79a67 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -5767,6 +5767,9 @@ breakText Glyphs 字形 Remove Gaps 間隔を削除 @msg that makes too many pieces (at most 100000): make the dashes longer 分割数が多すぎます(最大 100000):ダッシュを長くしてください @msg select paths, or segments, to divide 分割するパスまたはセグメントを選択してください +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths 破線をパスに拡張 +@msg select paths with dashed strokes to expand 拡張する破線のパスを選択してください # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools ドキュメントツール Save a Timestamped Copy 日時付きのコピーを保存 diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 586b59cc5..22894ea3b 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -6070,6 +6070,9 @@ breakText Glyphs გლიფები Remove Gaps ღრეჩოების მოცილება @msg that makes too many pieces (at most 100000): make the dashes longer ეს ძალიან ბევრ ნაწილს იძლევა (მაქსიმუმ 100000): გაზარდეთ ტირეების სიგრძე @msg select paths, or segments, to divide აირჩიეთ დასაყოფი კონტურები ან სეგმენტები +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths ტირეების კონტურებად გაშლა +@msg select paths with dashed strokes to expand აირჩიეთ გასაშლელი ტირეებიანი ხაზის კონტურები # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools დოკუმენტის ხელსაწყოები Save a Timestamped Copy ასლის შენახვა თარიღითა და დროით diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 05d5ed4e6..9c9d8dbd3 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -5663,6 +5663,9 @@ breakText Glyphs 글리프 Remove Gaps 간격 제거 @msg that makes too many pieces (at most 100000): make the dashes longer 조각이 너무 많아집니다(최대 100000개). 대시를 더 길게 하십시오 @msg select paths, or segments, to divide 나눌 패스나 세그먼트를 선택하십시오 +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths 대시를 패스로 확장 +@msg select paths with dashed strokes to expand 확장할 점선 획의 패스를 선택하십시오 # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools 문서 도구 Save a Timestamped Copy 날짜와 시간이 붙은 사본 저장 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 98a89cc6b..4918f150f 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -6024,6 +6024,9 @@ breakText Glyphs Glyphs Remove Gaps Tussenruimten verwijderen @msg that makes too many pieces (at most 100000): make the dashes longer dat geeft te veel stukken (hoogstens 100000): maak de streepjes langer @msg select paths, or segments, to divide selecteer paden of segmenten om te verdelen +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Streepjes uitbreiden tot paden +@msg select paths with dashed strokes to expand selecteer paden met een streepjeslijn om uit te breiden # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Documentgereedschappen Save a Timestamped Copy Een kopie met tijdstempel opslaan diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 08ab910c4..8d9e8ba9d 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -5936,6 +5936,9 @@ breakText Glyphs Glify Remove Gaps Usuń odstępy @msg that makes too many pieces (at most 100000): make the dashes longer to daje za dużo części (najwyżej 100000): wydłuż kreski @msg select paths, or segments, to divide zaznacz ścieżki lub segmenty do podzielenia +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Rozszerz kreski na ścieżki +@msg select paths with dashed strokes to expand zaznacz ścieżki z przerywanym konturem do rozszerzenia # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Narzędzia dokumentu Save a Timestamped Copy Zapisz kopię z datą i godziną diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 4c8c88572..b7aaabd1d 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -6000,6 +6000,9 @@ breakText Glyphs Glifos Remove Gaps Remover distâncias @msg that makes too many pieces (at most 100000): make the dashes longer isso gera peças demais (no máximo 100000): aumente os traços @msg select paths, or segments, to divide selecione demarcadores ou segmentos para dividir +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Expandir traços em demarcadores +@msg select paths with dashed strokes to expand selecione demarcadores com traçado tracejado para expandir # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Ferramentas de Documento Save a Timestamped Copy Salvar uma Cópia com Data e Hora diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index d330e4ccc..d9d95ae35 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -6058,6 +6058,9 @@ breakText Glyphs Глифы Remove Gaps Убрать зазоры @msg that makes too many pieces (at most 100000): make the dashes longer так получится слишком много частей (не более 100000): удлините штрихи @msg select paths, or segments, to divide выделите контуры или сегменты для разделения +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Разобрать штрихи на контуры +@msg select paths with dashed strokes to expand выделите контуры со штриховой обводкой для разбора # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Инструменты документа Save a Timestamped Copy Сохранить копию с датой и временем diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index d18203ddf..e378d833f 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -6133,6 +6133,9 @@ breakText Glyphs Glyfer Remove Gaps Ta bort mellanrum @msg that makes too many pieces (at most 100000): make the dashes longer det blir för många delar (högst 100000): gör strecken längre @msg select paths, or segments, to divide markera banor eller segment att dela upp +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Expandera streck till banor +@msg select paths with dashed strokes to expand markera banor med streckad kontur att expandera # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Dokumentverktyg Save a Timestamped Copy Spara en kopia med tidsstämpel diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 7bf6aa584..981f2a3ec 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -6038,6 +6038,9 @@ breakText Glyphs Glifler Remove Gaps Boşlukları Kaldır @msg that makes too many pieces (at most 100000): make the dashes longer bu çok fazla parça oluşturur (en fazla 100000): tireleri uzatın @msg select paths, or segments, to divide bölünecek yolları veya bölümleri seçin +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Tireleri Yollara Genişlet +@msg select paths with dashed strokes to expand genişletilecek kesikli konturlu yolları seçin # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Belge Araçları Save a Timestamped Copy Zaman Damgalı Bir Kopya Kaydet diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index f4ed70ce2..4b4bda6bc 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -5680,6 +5680,9 @@ breakText Glyphs Гліфи Remove Gaps Прибрати проміжки @msg that makes too many pieces (at most 100000): make the dashes longer так вийде забагато частин (не більше 100000): подовжте штрихи @msg select paths, or segments, to divide виділіть контури або сегменти для розділення +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Розібрати штрихи на контури +@msg select paths with dashed strokes to expand виділіть контури зі штриховим обведенням для розбору # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Інструменти документа Save a Timestamped Copy Зберегти копію з датою і часом diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 82e7600d2..6b8c54330 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -5683,6 +5683,9 @@ breakText Glyphs Glyph Remove Gaps Gỡ các khoảng hở @msg that makes too many pieces (at most 100000): make the dashes longer như vậy tạo ra quá nhiều mảnh (tối đa 100000): hãy kéo dài nét @msg select paths, or segments, to divide chọn các đường dẫn hoặc đoạn để chia +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths Mở rộng nét đứt thành đường dẫn +@msg select paths with dashed strokes to expand chọn các đường dẫn có nét đứt để mở rộng # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools Công cụ tài liệu Save a Timestamped Copy Lưu bản sao có ngày giờ diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 9cba58ad3..a9d0563da 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -5923,6 +5923,9 @@ breakText Glyphs 字形 Remove Gaps 移除间隙 @msg that makes too many pieces (at most 100000): make the dashes longer 这会产生过多片段(最多 100000 个):请加长短划线 @msg select paths, or segments, to divide 请选择要分割的路径或线段 +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths 将短划线扩展为路径 +@msg select paths with dashed strokes to expand 请选择要扩展的虚线描边路径 # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools 文档工具 Save a Timestamped Copy 保存带时间戳的副本 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index ec99ae2e2..ceec87f9d 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -5720,6 +5720,9 @@ breakText Glyphs 字符 Remove Gaps 移除間隙 @msg that makes too many pieces (at most 100000): make the dashes longer 這會產生過多片段(最多 100000 個):請加長短破折號 @msg select paths, or segments, to divide 請選取要分割的路徑或線段 +# Path Precision Tools add-on: Expand Dashes into Paths. + Expand Dashes into Paths 將短破折號展開為路徑 +@msg select paths with dashed strokes to expand 請選取要展開的虛線筆畫路徑 # Document Tools add-on (Object › Plug-ins › Document Tools): Save a Timestamped Copy. Document Tools 文件工具 Save a Timestamped Copy 儲存附時間戳記的拷貝 diff --git a/docs/mcp.md b/docs/mcp.md index 333d7e85b..b1ca3a347 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -6071,6 +6071,17 @@ Commands: last piece of each run is what is left. The pieces are selected; more than 100,000 is refused. → `{ids, dashes}`. Divide into Dashes… opens a dialog (`ui.menuDialog {command}`, kind `divideDashes`) whose fields are these parameters, with Preview. +- `addon.pathtools.expandDashes {ids?}` turns the dashed stroke of each path and compound path under `ids` (default: + the selection) into separate open paths, one per dash, as one undo step (Object › Plug-ins › Path Precision Tools › + Expand Dashes into Paths). The dashes are laid out exactly as the stroke draws them: its dash pattern (an odd one + repeats with dashes and gaps swapped), its dash offset and Align Dashes to Corners and Path Ends, restarting on each + subpath and running on through the start of a closed one; a dash running all the way round a closed path stays + closed. Each dash keeps the path's appearance with the top stroke's dashes off, so the gaps become real gaps; with a + round or projecting cap a dash of no length (a dot) becomes a path on its point, which draws the cap (under a butt + cap it paints nothing, so it makes nothing). The stroke's arrowheads go, the line is first trimmed under them as + drawn, and the path's fills stay on it, below its dashes (a path without fills goes). A compound path's dashes are + plain paths with its appearance. Paths without a dashed stroke stay as they are, and it is an error if none has one. + The dashes are selected; more than 100,000 (or a pattern too fine to draw) is refused. → `{ids, dashes}`. - `addon.pathtools.cornerSet {ids?, anchors?, items?, radius, kind?}` cuts live corners, the ones Direct Selection's corner widgets and the Corners dialog edit, as one undo step. `ids` (default: the selection) are the paths and `anchors` the anchor indices of each path's outline with no corners cut; without `anchors`, a path's direct-selected diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index dd2f9c55a..8bfe80641 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 (Glyphs panel highlights the character selected with the Type tool, #4908; 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 (Path Precision Tools add-on: Expand Dashes into Paths, #4922; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026) ## Feature submission evidence @@ -1430,6 +1430,10 @@ A VectorCraft addition, not a feature of the reference app, so no area score mov A VectorCraft addition, not a feature of the reference app, so no area score moves. Object › Plug-ins › Path Precision Tools › Divide into Dashes… cuts the selected paths into separate open paths along the curve (arc length, not straight distance): each `dash` long (0.01 to 1000000 pt, 10 by default) with `gap` between them (0 by default: the pieces touch); the gap pieces are left out unless Remove Gaps is off. With direct-selected segments (or anchors) only those runs are divided, the pattern running on across segments that follow one another, and the rest of such a path stays as pieces of its own. Fit to Corners and Ends gives every run between corners and ends a whole number of dashes, all stretched or squeezed alike so it starts and ends on a dash (a closed path without corners holds whole periods); otherwise the lengths are exact and each run's last piece is what is left. Every piece keeps the path's appearance and is selected; one undo step; at most 100000 pieces (more is refused). The dialog takes lengths in any unit and previews on the canvas. Tests: `dashes::tests` in `vectorcraft-pathops` (exact and fitted lengths, curves measured along them, picked segments on open and closed paths, the piece cap and bad patterns), `divide_into_dashes_*` in `cmd::addons::pathtools::tests` in `vectorcraft-engine` (gaps removed or kept, fitted lengths, the stroke kept, one undo step, selected segments only, bad params and too many pieces refused), `divide_into_dashes_opens_its_dialog_previews_and_runs_on_ok` in `vectorcraft-ui-egui` and `command_sweep`. Commands: `docs/mcp.md` › Path Precision Tools add-on. +## Path Precision Tools add-on: Expand Dashes into Paths (2026-11-01) + +A VectorCraft addition, not a feature of the reference app, so no area score moves. Object › Plug-ins › Path Precision Tools › Expand Dashes into Paths turns the dashed stroke of each selected path and compound path into separate open paths, one per dash, for cutters and plotters that need the centre lines rather than Outline Stroke's filled outlines. The dashes are laid out by the renderer's own dash code, so they match what the canvas draws: the dash pattern, its offset and Align Dashes to Corners and Path Ends, a dash running on through the start of a closed path, a dash all the way round staying closed, and under a round or projecting cap each dot (a dash of no length) as a path on its point. Each dash keeps the path's appearance with the top stroke's dashes and arrowheads off; the path's fills stay on it, below its dashes; a compound path's dashes are plain paths. One undo step; the dashes are selected; at most 100000 (more, or a pattern too fine to draw, is refused). Tests: `expand_dashes_*` in `cmd::addons::pathtools::tests` in `vectorcraft-engine` (the stroke kept with dashes off, the offset, one undo step, the fill kept below, dashes centred on corners when aligned, dots under round and butt caps, compound paths, refusals and the piece cap), `the_plugins_submenu_selects_the_tools_and_opens_the_anchor_inspector` in `vectorcraft-ui-egui` and `command_sweep`. Commands: `docs/mcp.md` › Path Precision Tools add-on. + ## Document Tools add-on: Save a Timestamped Copy (2026-11-01) A VectorCraft addition, not a feature of the reference app, so no area score moves. Object › Plug-ins › Document Tools › Save a Timestamped Copy writes a copy of the document named `_YYYY-MM-DD_HH-MM.` (the date and time in UTC, through chrono; `-2`, `-3`… added when that name is taken) next to the document, or into a folder picked (the folder picker) when it was never saved, in the document's own format or another save format. It writes through File › Save a Copy's save, so the document keeps its path, title and modified state, no undo step is made, and the write stays inside the automation roots in force. It is off on the web, which has no clock. Tests: `cmd::addons::document::tests` in `vectorcraft-engine` (the name and place, numbered names, the document unchanged and its content in the copy, an unsaved document needing a folder, other formats, bad params refused, automation roots), `addons::document::tests` in `vectorcraft-ui-egui` (the submenu, beside the document, the folder picked or cancelled) and `command_sweep`. Commands: `docs/mcp.md` › Document Tools add-on. @@ -1446,6 +1450,7 @@ Symbols area (#4938), from the public symbol documentation ([create and place sy | Date | Change | Summary | |---|---|---| +| 2026-11-01 | minor | Path Precision Tools add-on (a VectorCraft addition): Expand Dashes into Paths (#4922); evidence added; scores unchanged. | | 2026-11-01 | minor | Type core: the Glyphs panel highlights the character selected with the Type tool and scrolls it into view (#4908); evidence added; scores unchanged. | | 2026-11-01 | minor | Arrange Tools add-on (a VectorCraft addition): Randomize Opacity (#4925); evidence added; scores unchanged. | | 2026-11-01 | minor | Arrange Tools add-on (a VectorCraft addition): Sort Objects (#4917); evidence added; scores unchanged. | diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 6dc32fc85..221211281 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 (Arrange Tools add-on: Sort Objects, #4917) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Path Precision Tools add-on: Expand Dashes into Paths, #4922) · **Target:** Adobe Illustrator 2026 (30.x) ## Feature submission evidence @@ -341,6 +341,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po | 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): Object › Plug-ins › Path Precision Tools › Divide into Dashes… (Dash and Gap in any unit, Fit to Corners and Ends, Remove Gaps; Preview) cuts the selected paths, or only their direct-selected segments, into open pieces along the curve, keeping their appearance, one undo step | done | #4950; `addon.pathtools.divideDashes`, `crates/pathops/src/addons/pathtools/dashes.rs`, `crates/ui-egui/src/addons/pathtools/dashes.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 › Expand Dashes into Paths turns each selected path's dashed stroke into separate open paths, one per dash, laid out as the stroke draws them (pattern, offset, Align Dashes to Corners and Path Ends), each keeping the stroke with the dashes off; one undo step | done | #4922; `addon.pathtools.expandDashes` (in `crates/engine/src/cmd/addons/pathtools.rs`, using the renderer's dash layout) and its 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 | | Path Precision Tools add-on (VectorCraft addition): Object › Plug-ins › Path Precision Tools selects its four tools (Anchor Eraser Brush, Extend Path, Measure & Inspect, Parametric Shape), 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; 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/shape.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 six tools (Anchor Eraser Brush, Extend Path, Measure & Inspect, Parametric Shape, Corner Tool, Slide Anchor Along Path), 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°; 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 | @@ -391,6 +392,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development | Date | Change | Summary | |---|---|---| +| 2026-11-01 | minor | Path Precision Tools add-on (a VectorCraft addition): Expand Dashes into Paths (#4922); scores and estimates unchanged | | 2026-11-01 | minor | Arrange Tools add-on (a VectorCraft addition): Randomize Opacity (#4925); scores and estimates unchanged | | 2026-11-01 | minor | Arrange Tools add-on (a VectorCraft addition): Sort Objects (#4917); scores and estimates unchanged | | 2026-11-01 | minor | Blob Brush and Eraser: the tip's outline (size, angle, roundness, at the zoom) follows the pointer before and while painting, around a small centre dot, updating at once with `[` / `]` and Tool Options; precise cursors (Caps Lock) show only the crosshair (#4977); scores and estimates unchanged (estimated) |