Browse / github.com/storytold/vectorcraft / patch
Image Trace: Expand can group the traced paths by color
Image Trace: Expand can group the traced paths by color ## What and why Expand puts every traced path into one group, so editing one color of a multi-color trace means finding its paths one by one, and Release to Layers splits by object, not by color. `imageTrace.expand` and `imageTrace.makeAndExpand` now take `byColor` (default `false`). With it, each color's paths go in a group of their own, named by the global swatch the color links to (a tint with its percentage, as `Sky 50%`) or as `#rrggbb`; gradient fills share a group named `Gradients`. The groups stack in the order of the lowest path in each and keep their paths' order, in one undo step.
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- dotCooCoo (imported from pull request #4058, open)
- Base
bccf4de7223ae78ea24f203b68ef2ffdbcaede74- Size
- +411 −2 in 23 files
- Review
- pending
- Shared
- 2026-10-28
- Id
59ec5ddf4b5f2edc10d3be85
How it went
No other operator has reported using it yet.
Diff
raw · crates/engine/src/cmd/buildcmds.rs crates/engine/src/tests_build.rs crates/ui-egui/src/i18n/cs.tsv crates/ui-egui/src/i18n/de.tsv crates/ui-egui/src/i18n/el.tsv crates/ui-egui/src/i18n/es.tsv crates/ui-egui/src/i18n/fi.tsv crates/ui-egui/src/i18n/fr.tsv crates/ui-egui/src/i18n/hu.tsv crates/ui-egui/src/i18n/it.tsv crates/ui-egui/src/i18n/ja.tsv crates/ui-egui/src/i18n/ka.tsv crates/ui-egui/src/i18n/ko.tsv crates/ui-egui/src/i18n/pl.tsv crates/ui-egui/src/i18n/pt-br.tsv crates/ui-egui/src/i18n/ru.tsv crates/ui-egui/src/i18n/sv.tsv crates/ui-egui/src/i18n/tr.tsv crates/ui-egui/src/i18n/uk.tsv crates/ui-egui/src/i18n/vi.tsv crates/ui-egui/src/i18n/zh-hans.tsv crates/ui-egui/src/i18n/zh-hant.tsv docs/mcp.md
@@ -138,7 +138,7 @@ pub fn specs() -> Vec<CommandSpec> { "Make and Expand", ["Object", "Image Trace"], None,- "{…as imageTrace.make} same as imageTrace.make, then expand → {id, paths, anchors, colors}",+ "{…as imageTrace.make, byColor?: false (each color's paths in a group of their own, as imageTrace.expand)} same as imageTrace.make, then expand → {id, paths, anchors, colors}", has_selection_or_ids, |s, p| trace_make(s, p, true) ),@@ -156,7 +156,7 @@ pub fn specs() -> Vec<CommandSpec> { "Expand", ["Object", "Image Trace"], None,- "{id?, ids?: [id…] (instead of id)} keep only the traced paths (drop the source image) → {ids}",+ "{id?, ids?: [id…] (instead of id), byColor?: false (each color's paths in a group of their own, named by the swatch the color links to or as #rrggbb, gradient fills in one group named Gradients; the groups stack by their lowest path and keep their paths' order; a color whose paths overlap another color's from above and below gets a group for each part, so the art draws the same; not for a trace made with autoGrouping)} keep only the traced paths (drop the source image) → {ids}", has_selection_or_ids, trace_expand ),@@ -1103,10 +1103,98 @@ fn grouped(d: &mut Document, nodes: Vec<Node>, parents: &[Option<usize>]) -> Vec roots.iter().filter_map(|&r| built.get_mut(r)?.take()).chain(ring).collect() } +/// The `byColor` param of an Expand (false when not given).+fn by_color_param(cmd: &str, p: &Value) -> Result<bool> {+ match p.get("byColor").filter(|v| !v.is_null()) {+ Some(v) => v.as_bool().ok_or_else(|| bad(cmd, "byColor must be true or false")),+ None => Ok(false),+ }+}++/// Whether a trace made with `params` can have paths that overlap, as in [`tr::trace_within`]'s+/// Auto Grouping: stacked layers, Flat Logo's shapes and stroked lines.+fn stacked_trace(params: &tr::TraceParams) -> bool {+ params.method == tr::Method::Overlapping || params.mode == tr::Mode::Logo || params.strokes+}++/// Expand by color: an expanded trace's `items` (bottom-most first) in a group for each paint, a+/// stroked line counted by its stroke. A solid color's group is named by the swatch it links to (a+/// tint with its percentage) or as `#rrggbb`, and gradient fills share one group, "Gradients". The+/// groups stack in the order of the lowest item in each, and each keeps its items' order. When the+/// paths can overlap (`stacked`), an item joins its paint's last group only if no item of a later+/// group overlaps it (by the boxes of everything they paint, effects included); otherwise it starts+/// another group of that paint, so the art draws as before. A group among `items` (Auto Grouping)+/// is refused.+fn color_groups(cmd: &str, d: &mut Document, items: Vec<Arc<Node>>, stacked: bool) -> Result<Vec<Arc<Node>>> {+ use vectorcraft_color::tint_percent;+ // Grouped by the paint: unlinked (its #rrggbb), linked (the swatch and its exact tint), a+ // gradient, or another paint (its label). The name is apart from it: a swatch may be named as+ // a color's `#rrggbb` or as "Gradients".+ type PaintKey = (u8, String, u32);+ struct Run {+ key: PaintKey,+ name: String,+ members: Vec<Arc<Node>>,+ boxes: Vec<Rect>,+ union: Option<Rect>,+ }+ let meet = |a: &Rect, b: &Rect| a.x0 < b.x1 && b.x0 < a.x1 && a.y0 < b.y1 && b.y0 < a.y1;+ let mut groups: Vec<Run> = vec![];+ for item in items {+ if matches!(item.kind, NodeKind::Group { .. }) {+ return Err(bad(cmd, "byColor can't split Auto Grouping groups: trace without Auto Grouping"));+ }+ let paint = match item.appearance.fill_paint() {+ Paint::None => item.appearance.stroke_paint(),+ fill => fill,+ };+ let (key, name) = match &paint {+ Paint::Solid { swatch: Some(n), tint, .. } => {+ let percent = tint_percent(*tint);+ let name = if *tint < 1.0 { format!("{n} {percent}%") } else { n.clone() };+ ((1, n.clone(), tint.to_bits()), name)+ }+ Paint::Solid { color, .. } => ((0, color.to_hex(), 0), color.to_hex()),+ Paint::Gradient(_) => ((2, String::new(), 0), "Gradients".to_string()),+ other => ((3, other.label(), 0), other.label()),+ };+ let bounds = if stacked { vectorcraft_render::painted_bounds(d, &item) } else { None };+ // Joining an earlier group moves the item under every later group's items.+ let hidden_by_later = |at: usize| {+ bounds.is_some_and(|b| groups.iter().skip(at + 1).any(|g| g.union.is_some_and(|u| meet(&u, &b)) && g.boxes.iter().any(|m| meet(m, &b))))+ };+ let at = groups.iter().rposition(|g| g.key == key).filter(|&at| !hidden_by_later(at));+ let mut started = Run { key, name, members: vec![], boxes: vec![], union: None };+ let joins = at.and_then(|at| groups.get_mut(at));+ let new = joins.is_none();+ let g = joins.unwrap_or(&mut started);+ if let Some(b) = bounds {+ g.boxes.push(b);+ g.union = Some(g.union.map_or(b, |u| u.union(b)));+ }+ g.members.push(item);+ if new {+ groups.push(started);+ }+ }+ Ok(groups+ .into_iter()+ .map(|run| {+ let mut g = Node::group(d.alloc_id(), run.members);+ g.name = Some(run.name);+ Arc::new(g)+ })+ .collect())+}+ fn trace_make(s: &mut Session, p: &Value, expand: bool) -> Result<Value> { let c = if expand { "imageTrace.makeAndExpand" } else { "imageTrace.make" }; let mut params = trace_params(c, p)?; let view = view_param(c, p)?;+ let by_color = expand && by_color_param(c, p)?;+ if by_color && params.auto_grouping {+ return Err(bad(c, "byColor can't split Auto Grouping groups: trace without Auto Grouping"));+ } let params_json = serde_json::to_value(¶ms).map_err(|e| EngineError::Other(e.to_string()))?; // The preset's name if the parameters are exactly that preset's or its enhanced version's, // else "Custom".@@ -1193,6 +1281,9 @@ fn trace_make(s: &mut Session, p: &Value, expand: bool) -> Result<Value> { traced.push(n); } children.extend(grouped(d, traced, &res.parents));+ if by_color {+ children = color_groups(c, d, children, stacked_trace(¶ms))?;+ } let gid = d.alloc_id(); let mut g = Node::group(gid, children); g.name = if expand { None } else { Some(TRACE_NAME.into()) };@@ -1291,7 +1382,17 @@ fn trace_release(s: &mut Session, p: &Value) -> Result<Value> { } fn trace_expand(s: &mut Session, p: &Value) -> Result<Value> {+ const C: &str = "imageTrace.expand";+ let by_color = by_color_param(C, p)?; let targets = trace_targets(s, p)?;+ if by_color {+ // A trace made with Auto Grouping is refused even when none of its paths were grouped.+ let d = &s.doc()?.doc;+ let grouped = |n: &Node| n.extras().trace.as_deref().and_then(|t| t.pointer("/params/autoGrouping")).and_then(Value::as_bool) == Some(true);+ if targets.iter().any(|g| d.node(*g).is_some_and(grouped)) {+ return Err(bad(C, "byColor can't split Auto Grouping groups: trace without Auto Grouping"));+ }+ } s.edit("Expand Image Trace", |d, sel| { for g in &targets { let n = d.node_mut(*g).ok_or(EngineError::NoNode(*g))?;@@ -1300,6 +1401,15 @@ fn trace_expand(s: &mut Session, p: &Value) -> Result<Value> { if let Some(ch) = n.children_mut() { ch.retain(|c| !matches!(c.kind, NodeKind::Image(_))); }+ if by_color {+ let items = d.node(*g).and_then(Node::children).cloned().unwrap_or_default();+ // The paths may have been moved or reshaped since the trace was made, so any trace's+ // paths are taken to overlap.+ let groups = color_groups(C, d, items, true)?;+ if let Some(ch) = d.node_mut(*g).and_then(Node::children_mut) {+ *ch = groups;+ }+ } } sel.set(targets.iter().copied()); Ok(())@@ -2517,6 +2517,256 @@ fn image_trace_release_and_expand() { assert!(s.execute("imageTrace.release", &json!({})).is_err()); } +/// Expand by color: each color's paths in a group of their own, named by the color, the groups+/// stacked by the lowest path in each and the art drawn as before; Release to Layers then gives a+/// sublayer for each color.+#[test]+fn image_trace_expand_by_color_groups_each_colors_paths() {+ // A light field with a black ring and a red square on it.+ let art = vectorcraft_trace::Raster::from_fn(200, 150, |x, y| {+ let r = (f64::from(x) + 0.5 - 70.0).hypot(f64::from(y) + 0.5 - 75.0);+ if (34.0..=40.0).contains(&r) {+ [0, 0, 0, 255]+ } else if (130..170).contains(&x) && (55..95).contains(&y) {+ [200, 40, 50, 255]+ } else {+ [240, 240, 220, 255]+ }+ });+ let names = |n: &Node| n.children().unwrap().iter().map(|c| c.name.clone().unwrap_or_default()).collect::<Vec<_>>();+ let hex = |n: &Node| n.appearance.fill_paint().color().unwrap().to_hex();+ let shot = |s: &Session| {+ let img = render_doc(s, false);+ (0..200).flat_map(|x| (0..150).map(move |y| (x, y))).map(|(x, y)| img.pixel(x, y)).collect::<Vec<_>>()+ };+ for method in ["abutting", "overlapping"] {+ let params = json!({"mode": "color", "palette": "limited", "colors": 3, "method": method});+ let expand = |by_color: bool| {+ let mut s = session();+ add_image(&mut s, &art, Affine::IDENTITY);+ let r = s.execute("imageTrace.makeAndExpand", &json!({"params": params, "byColor": by_color})).unwrap();+ let g = NodeId(r["id"].as_u64().unwrap());+ (s, g)+ };+ let (flat, fg) = expand(false);+ let (mut s, g) = expand(true);+ let paths = node(&flat, fg).children().unwrap().clone();+ // The colors in the order they first come in, bottom to top.+ let mut order: Vec<String> = vec![];+ for p in &paths {+ if !order.contains(&hex(p)) {+ order.push(hex(p));+ }+ }+ let mut sorted = order.clone();+ sorted.sort();+ assert_eq!(sorted, ["#000000", "#c82832", "#f0f0dc"], "{method}");+ let n = node(&s, g);+ assert_eq!(names(&n), order, "{method}");+ // Each group holds its color's paths, and in turn they are the paths of the trace without+ // byColor, in their order.+ let groups = n.children().unwrap();+ for c in groups {+ assert!(matches!(c.kind, NodeKind::Group { .. }), "{method}");+ assert!(c.children().unwrap().iter().all(|m| Some(hex(m)) == c.name), "{method}: {:?}", c.name);+ }+ let members: Vec<Arc<Node>> = groups.iter().flat_map(|c| c.children().unwrap().iter().cloned()).collect();+ assert!(members == paths, "{method}");+ assert!(shot(&s) == shot(&flat), "{method}: the art changed");+ // Release to Layers: a sublayer for each color, in the same order.+ let r = s.execute("layer.releaseToLayers", &json!({"id": g.0})).unwrap();+ let subs = ids(&r);+ assert_eq!(subs.len(), 3, "{method}");+ for (sub, name) in subs.iter().zip(&order) {+ let l = node(&s, *sub);+ assert!(matches!(l.kind, NodeKind::Layer { .. }), "{method}");+ assert_eq!(names(&l), std::slice::from_ref(name), "{method}");+ }+ }++ // imageTrace.expand takes it too, as one undo step.+ let mut s = session();+ add_image(&mut s, &art, Affine::IDENTITY);+ let params = json!({"mode": "color", "palette": "limited", "colors": 3});+ let g = NodeId(s.execute("imageTrace.make", &json!({"params": params})).unwrap()["id"].as_u64().unwrap());+ let steps = s.doc().unwrap().history.undo.len();+ s.execute("imageTrace.expand", &json!({"byColor": true})).unwrap();+ assert_eq!(s.doc().unwrap().history.undo.len(), steps + 1);+ let mut got = names(&node(&s, g));+ got.sort();+ assert_eq!(got, ["#000000", "#c82832", "#f0f0dc"]);+ s.execute("edit.undo", &json!({})).unwrap();+ assert_eq!(node(&s, g).name.as_deref(), Some("Image Trace"));++ // Auto Grouping's groups are not split: refused, and the trace stays as it was.+ let mut grouped = params.clone();+ grouped["autoGrouping"] = json!(true);+ let e = s.execute("imageTrace.makeAndExpand", &json!({"id": g.0, "params": grouped, "byColor": true})).unwrap_err().to_string();+ assert!(e.contains("Auto Grouping"), "{e}");+ let g = NodeId(s.execute("imageTrace.make", &json!({"id": g.0, "params": grouped})).unwrap()["id"].as_u64().unwrap());+ let e = s.execute("imageTrace.expand", &json!({"byColor": true})).unwrap_err().to_string();+ assert!(e.contains("Auto Grouping"), "{e}");+ assert_eq!(node(&s, g).name.as_deref(), Some("Image Trace"));+ let e = s.execute("imageTrace.expand", &json!({"byColor": "yes"})).unwrap_err().to_string();+ assert!(e.contains("true or false"), "{e}");+ // Refused too when Auto Grouping grouped nothing, as with side-by-side stripes.+ let mut s = session();+ let stripes = vectorcraft_trace::Raster::from_fn(60, 60, |x, _| match x / 20 {+ 0 => [200, 40, 40, 255],+ 1 => [40, 160, 60, 255],+ _ => [40, 70, 200, 255],+ });+ add_image(&mut s, &stripes, Affine::IDENTITY);+ let g = NodeId(s.execute("imageTrace.make", &json!({"params": grouped})).unwrap()["id"].as_u64().unwrap());+ assert!(node(&s, g).children().unwrap().iter().all(|c| !matches!(c.kind, NodeKind::Group { .. })));+ let e = s.execute("imageTrace.expand", &json!({"byColor": true})).unwrap_err().to_string();+ assert!(e.contains("Auto Grouping"), "{e}");+ assert_eq!(node(&s, g).name.as_deref(), Some("Image Trace"));++ // A color linked to a global swatch goes by the swatch's name, a tint with its percentage.+ let mut s = session();+ s.execute("swatch.new", &json!({"name": "Ink", "color": "#c82828", "global": true})).unwrap();+ s.execute("swatch.new", &json!({"name": "Sky", "color": "#2846c8", "global": true})).unwrap();+ let r = vectorcraft_trace::Raster::from_fn(60, 60, |x, y| match (x < 30, y < 30) {+ (true, _) => [200, 40, 40, 255],+ (false, true) => [40, 160, 60, 255],+ (false, false) => [40, 70, 200, 255],+ });+ add_image(&mut s, &r, Affine::IDENTITY);+ let limited = json!({"mode": "color", "palette": "limited", "colors": 3});+ let g = NodeId(s.execute("imageTrace.make", &json!({"params": limited})).unwrap()["id"].as_u64().unwrap());+ let paths: Vec<(u64, String)> =+ node(&s, g).children().unwrap().iter().filter(|c| !matches!(c.kind, NodeKind::Image(_))).map(|c| (c.id.0, hex(c))).collect();+ let mut colors: Vec<&str> = paths.iter().map(|(_, h)| h.as_str()).collect();+ colors.sort();+ colors.dedup();+ assert_eq!(colors, ["#2846c8", "#28a03c", "#c82828"]);+ // The red paths link to Ink and the blue ones to Sky at 50%; green stays a plain color.+ let pick = |want: &str| paths.iter().filter(|(_, h)| h == want).map(|(id, _)| *id).collect::<Vec<_>>();+ s.execute("paint.setFill", &json!({"ids": pick("#c82828"), "swatch": "Ink"})).unwrap();+ s.execute("paint.setFill", &json!({"ids": pick("#2846c8"), "swatch": "Sky", "tint": 50})).unwrap();+ s.execute("imageTrace.expand", &json!({"id": g.0, "byColor": true})).unwrap();+ let mut got = names(&node(&s, g));+ got.sort();+ assert_eq!(got, ["#28a03c", "Ink", "Sky 50%"]);+ // A swatch named as another color's #rrggbb still gets a group of its own.+ s.execute("edit.undo", &json!({})).unwrap();+ s.execute("swatch.edit", &json!({"name": "Ink", "newName": "#28a03c"})).unwrap();+ s.execute("imageTrace.expand", &json!({"id": g.0, "byColor": true})).unwrap();+ let mut got = names(&node(&s, g));+ got.sort();+ assert_eq!(got, ["#28a03c", "#28a03c", "Sky 50%"]);++ // Gradient fills share one group, and a stroked line goes by its stroke.+ let ramp = vectorcraft_trace::Raster::from_fn(100, 60, |x, _| {+ let t = (f64::from(x) + 0.5) / 100.0;+ [(20.0 + 200.0 * t) as u8, 40, (220.0 - 180.0 * t) as u8, 255]+ });+ let line =+ vectorcraft_trace::Raster::from_fn(100, 100, |x, y| if (10..90).contains(&x) && (49..52).contains(&y) { [0, 0, 0, 255] } else { [255; 4] });+ for (art, params, want) in [+ (ramp, json!({"preset": "6 Colors", "params": {"gradients": true}, "byColor": true}), "Gradients"),+ (line, json!({"params": {"ignoreWhite": true, "strokes": true, "strokeWidth": 5}, "byColor": true}), "#000000"),+ ] {+ let mut s = session();+ add_image(&mut s, &art, Affine::IDENTITY);+ let res = s.execute("imageTrace.makeAndExpand", ¶ms).unwrap();+ assert_eq!(names(&node(&s, NodeId(res["id"].as_u64().unwrap()))), [want]);+ }++ // Flat Logo stacks its shapes biggest first without cutting holes under white: a black ring's+ // outline, a white disc on it and a black dot on that. Black keeps a group below white and one+ // above it, and the art draws as before, through Make and Expand and through Expand.+ let target = vectorcraft_trace::Raster::from_fn(120, 120, |x, y| {+ let r = (f64::from(x) + 0.5 - 60.0).hypot(f64::from(y) + 0.5 - 60.0);+ if r < 15.0 || (30.0..50.0).contains(&r) {+ [0, 0, 0, 255]+ } else if r < 30.0 {+ [255, 255, 255, 255]+ } else {+ [0, 0, 0, 0]+ }+ });+ let logo = json!({"mode": "logo", "logoColors": ["#000000"]});+ let traced = |by_color: bool, expand_after: bool| {+ let mut s = session();+ add_image(&mut s, &target, Affine::IDENTITY);+ let g = if expand_after {+ let g = s.execute("imageTrace.make", &json!({"params": logo})).unwrap()["id"].as_u64().unwrap();+ s.execute("imageTrace.expand", &json!({"byColor": by_color})).unwrap();+ g+ } else {+ s.execute("imageTrace.makeAndExpand", &json!({"params": logo, "byColor": by_color})).unwrap()["id"].as_u64().unwrap()+ };+ (s, NodeId(g))+ };+ for expand_after in [false, true] {+ let (flat, fg) = traced(false, expand_after);+ let (s, g) = traced(true, expand_after);+ let paths = node(&flat, fg).children().unwrap().clone();+ assert_eq!(paths.iter().map(|p| hex(p)).collect::<Vec<_>>(), ["#000000", "#ffffff", "#000000"]);+ assert_eq!(names(&node(&s, g)), ["#000000", "#ffffff", "#000000"], "expand after: {expand_after}");+ let members: Vec<Arc<Node>> = node(&s, g).children().unwrap().iter().flat_map(|c| c.children().unwrap().iter().cloned()).collect();+ assert!(members == paths, "expand after: {expand_after}");+ assert!(shot(&s) == shot(&flat), "expand after: {expand_after}: the art changed");+ }++ // An Abutting trace's top path, given the bottom path's color and moved onto the middle one (by+ // a move or by a Transform effect), or moved clear of it with a drop shadow cast onto it, before+ // Expand, still draws on top of it.+ let stripes = vectorcraft_trace::Raster::from_fn(90, 30, |x, _| match x / 30 {+ 0 => [200, 40, 40, 255],+ 1 => [40, 160, 60, 255],+ _ => [40, 70, 200, 255],+ });+ let edited = |by_color: bool, how: &str| {+ let mut s = session();+ add_image(&mut s, &stripes, Affine::IDENTITY);+ let limited = json!({"mode": "color", "palette": "limited", "colors": 3, "method": "abutting"});+ let g = NodeId(s.execute("imageTrace.make", &json!({"params": limited})).unwrap()["id"].as_u64().unwrap());+ let paths: Vec<Arc<Node>> = node(&s, g).children().unwrap().iter().filter(|c| !matches!(c.kind, NodeKind::Image(_))).cloned().collect();+ assert_eq!(paths.len(), 3);+ let (first, middle, last) = (&paths[0], &paths[1], &paths[2]);+ s.execute("paint.setFill", &json!({"ids": [last.id.0], "color": hex(first)})).unwrap();+ let (to, from) = (middle.visual_bounds().unwrap(), last.visual_bounds().unwrap());+ let (dx, dy) = (to.x0 - from.x0, to.y0 - from.y0);+ match how {+ "move" => s.execute("object.move", &json!({"ids": [last.id.0], "dx": dx, "dy": dy})).unwrap(),+ "transform" => {+ s.execute("effect.apply", &json!({"ids": [last.id.0], "effect": "distort.transform", "params": {"moveH": dx, "moveV": dy}})).unwrap()+ }+ _ => {+ s.execute("object.move", &json!({"ids": [last.id.0], "dx": 40, "dy": 0})).unwrap();+ s.execute("effect.apply", &json!({"ids": [last.id.0], "effect": "stylize.dropShadow", "params": {"x": -70, "y": 0, "blur": 0}}))+ .unwrap()+ }+ };+ s.execute("imageTrace.expand", &json!({"id": g.0, "byColor": by_color})).unwrap();+ (s, g)+ };+ for how in ["move", "transform", "shadow"] {+ let (flat, _) = edited(false, how);+ let (s, g) = edited(true, how);+ assert_eq!(names(&node(&s, g)).len(), 3, "{how}");+ assert!(shot(&s) == shot(&flat), "{how}: the edited art changed");+ }++ // Two tints of one swatch that round to the same percentage are still two colors.+ let mut s = session();+ s.execute("swatch.new", &json!({"name": "Sky", "color": "#2846c8", "global": true})).unwrap();+ add_image(&mut s, &stripes, Affine::IDENTITY);+ let limited = json!({"mode": "color", "palette": "limited", "colors": 3});+ let g = NodeId(s.execute("imageTrace.make", &json!({"params": limited})).unwrap()["id"].as_u64().unwrap());+ let paths: Vec<u64> = node(&s, g).children().unwrap().iter().filter(|c| !matches!(c.kind, NodeKind::Image(_))).map(|c| c.id.0).collect();+ assert_eq!(paths.len(), 3);+ s.execute("paint.setFill", &json!({"ids": [paths[0]], "swatch": "Sky", "tint": 50.1})).unwrap();+ s.execute("paint.setFill", &json!({"ids": [paths[1], paths[2]], "swatch": "Sky", "tint": 50.4})).unwrap();+ s.execute("imageTrace.expand", &json!({"id": g.0, "byColor": true})).unwrap();+ let groups = node(&s, g).children().unwrap().clone();+ assert_eq!(names(&node(&s, g)), ["Sky 50%", "Sky 50%"]);+ assert_eq!(groups.iter().map(|c| c.children().unwrap().len()).collect::<Vec<_>>(), [1, 2]);+}+ #[test] fn image_trace_make_and_expand_and_color_presets() { let mut s = session();@@ -5393,6 +5393,8 @@ mark-style Japanese Japonské @msg too many library panels were opened in this session v této relaci bylo otevřeno příliš mnoho panelů knihoven @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Obraz má {_1} × {_2} pixelů, na jedné straně více než {LARGE_TRACE_SIDE}: vektorizuje se, ale pomaleji. Rychleji se vektorizuje s předvolbou nižší věrnosti nebo menší kopie obrazu. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Obraz byl vektorizován v rozlišení {_1} × {_2} pixelů, zmenšený z {_3} × {_4} (nejvýše {_5} megapixelů).+@msg byColor must be true or false byColor musí být true nebo false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor nerozdělí skupiny z Automatického seskupení: vektorizujte bez Automatického seskupení Snapping Přichytávání Snap to Tangent Přichytávat k tečně Snap to Perpendicular Přichytávat ke kolmici@@ -4972,6 +4972,8 @@ effect Soft Weich @msg too many library panels were opened in this session in dieser Sitzung wurden zu viele Bibliotheksbedienfelder geöffnet @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Das Bild hat {_1} × {_2} Pixel, mehr als {LARGE_TRACE_SIDE} auf einer Seite: Es wird nachgezeichnet, aber langsamer. Eine Vorgabe mit geringerer Detailtreue oder eine kleinere Kopie des Bildes geht schneller. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Das Bild wurde mit {_1} × {_2} Pixeln nachgezeichnet, verkleinert von {_3} × {_4} (höchstens {_5} Megapixel).+@msg byColor must be true or false byColor muss true oder false sein+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor kann Gruppen aus Automatisch gruppieren nicht aufteilen: ohne Automatisch gruppieren nachzeichnen Snapping Einrasten Snap to Tangent An Tangente ausrichten Snap to Perpendicular An Senkrechter ausrichten@@ -5345,6 +5345,8 @@ effect Points Σημεία @msg too many library panels were opened in this session άνοιξαν πάρα πολλοί πίνακες βιβλιοθηκών σε αυτή τη συνεδρία @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Η εικόνα είναι {_1} × {_2} εικονοστοιχεία, πάνω από {LARGE_TRACE_SIDE} σε μία πλευρά: ανιχνεύεται, αλλά πιο αργά. Μια προρύθμιση χαμηλότερης πιστότητας ή ένα μικρότερο αντίγραφο της εικόνας ανιχνεύεται γρηγορότερα. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Η εικόνα ανιχνεύτηκε σε {_1} × {_2} εικονοστοιχεία, σμικρυμένη από {_3} × {_4} (το πολύ {_5} megapixel).+@msg byColor must be true or false το byColor πρέπει να είναι true ή false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping το byColor δεν μπορεί να χωρίσει τις ομάδες της Αυτόματης ομαδοποίησης: ανιχνεύστε χωρίς Αυτόματη ομαδοποίηση Snapping Προσκόλληση Snap to Tangent Προσκόλληση σε εφαπτομένη Snap to Perpendicular Προσκόλληση σε κάθετο@@ -5335,6 +5335,8 @@ mark-style Japanese Japonesas @msg too many library panels were opened in this session se abrieron demasiados paneles de biblioteca en esta sesión @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. La imagen tiene {_1} × {_2} píxeles, más de {LARGE_TRACE_SIDE} en un lado: se calcará, pero más despacio. Un ajuste preestablecido de menor fidelidad o una copia más pequeña de la imagen se calcan más rápido. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). La imagen se calcó a {_1} × {_2} píxeles, reducida desde {_3} × {_4} ({_5} megapíxeles como máximo).+@msg byColor must be true or false byColor debe ser true o false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor no puede dividir los grupos de Agrupación automática: calque sin Agrupación automática Snapping Ajuste Snap to Tangent Ajustar a tangente Snap to Perpendicular Ajustar a perpendicular@@ -5357,6 +5357,8 @@ mark-style Japanese Japanilainen @msg too many library panels were opened in this session tässä istunnossa on avattu liian monta kirjastopaneelia @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Kuva on {_1} × {_2} kuvapistettä, yhdeltä sivultaan yli {LARGE_TRACE_SIDE}: se jäljitetään, mutta hitaammin. Pienemmän tarkkuuden esiasetus tai kuvan pienempi kopio jäljittyy nopeammin. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Kuva jäljitettiin kokoon {_1} × {_2} kuvapistettä pienennettynä koosta {_3} × {_4} (enintään {_5} megapikseliä).+@msg byColor must be true or false byColor-arvon on oltava true tai false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor ei voi jakaa Automaattisen ryhmittelyn ryhmiä: jäljitä ilman Automaattista ryhmittelyä Snapping Tartunta Snap to Tangent Kohdista tangenttiin Snap to Perpendicular Kohdista kohtisuoraan@@ -5392,6 +5392,8 @@ theme Appearance Apparence @msg too many library panels were opened in this session trop de panneaux de bibliothèque ont été ouverts dans cette session @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. L’image fait {_1} × {_2} pixels, plus de {LARGE_TRACE_SIDE} sur un côté : elle est vectorisée, mais plus lentement. Un paramètre prédéfini moins fidèle ou une copie plus petite de l’image se vectorise plus vite. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). L’image a été vectorisée à {_1} × {_2} pixels, réduite de {_3} × {_4} ({_5} mégapixels au plus).+@msg byColor must be true or false byColor doit être true ou false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor ne peut pas scinder les groupes du Groupement automatique : vectorisez sans Groupement automatique Snapping Magnétisme Snap to Tangent Magnétisme de la tangente Snap to Perpendicular Magnétisme de la perpendiculaire@@ -5379,6 +5379,8 @@ imageTrace Outline Körvonal @msg too many library panels were opened in this session ebben a munkamenetben túl sok könyvtárpanel nyílt meg @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. A kép {_1} × {_2} képpont, egyik oldalán több mint {LARGE_TRACE_SIDE}: a vektorizálás megtörténik, de lassabban. Kisebb hűségű előbeállítással vagy a kép kisebb másolatával gyorsabb. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). A kép vektorizálása {_1} × {_2} képponton történt, {_3} × {_4} méretről kicsinyítve (legfeljebb {_5} megapixel).+@msg byColor must be true or false a byColor értéke true vagy false lehet+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping a byColor nem bontja fel az Automatikus csoportosítás csoportjait: vektorizáljon Automatikus csoportosítás nélkül Snapping Illesztés Snap to Tangent Illesztés érintőre Snap to Perpendicular Illesztés merőlegesre@@ -5006,6 +5006,8 @@ lighting Light Luce @msg too many library panels were opened in this session in questa sessione sono stati aperti troppi pannelli libreria @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. L’immagine è di {_1} × {_2} pixel, più di {LARGE_TRACE_SIDE} su un lato: viene vettorializzata, ma più lentamente. Un predefinito a fedeltà più bassa o una copia più piccola dell’immagine è più veloce. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). L’immagine è stata vettorializzata a {_1} × {_2} pixel, ridotta da {_3} × {_4} (al massimo {_5} megapixel).+@msg byColor must be true or false byColor deve essere true o false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor non può dividere i gruppi di Raggruppamento automatico: vettorializza senza Raggruppamento automatico Snapping Aggancio Snap to Tangent Aggancia alla tangente Snap to Perpendicular Aggancia alla perpendicolare@@ -5046,6 +5046,8 @@ lighting Light ライト @msg too many library panels were opened in this session このセッションで開いたライブラリパネルが多すぎます @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. 画像は {_1} × {_2} ピクセルで、一辺が {LARGE_TRACE_SIDE} を超えています。トレースはできますが、時間がかかります。忠実度の低いプリセットや画像の小さいコピーを使うと速くなります。 @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). 画像は {_3} × {_4} から縮小した {_1} × {_2} ピクセルでトレースされました (最大 {_5} メガピクセル)。+@msg byColor must be true or false byColor は true または false にしてください+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor は自動グループ化のグループを分割できません: 自動グループ化なしでトレースしてください Snapping スナップ Snap to Tangent 接線にスナップ Snap to Perpendicular 垂線にスナップ@@ -5239,6 +5239,8 @@ imageTrace Outline კონტური @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. გამოსახულება არის {_1} × {_2} პიქსელი, ანუ ერთ მხარეზე {LARGE_TRACE_SIDE}-ზე მეტი: ის ვექტორიზდება, მაგრამ უფრო ნელა. ნაკლები სიზუსტის პრესეტი ან გამოსახულების უფრო პატარა ასლი უფრო სწრაფად ვექტორიზდება. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). გამოსახულება ვექტორიზდა {_1} × {_2} პიქსელზე, შემცირდა {_3} × {_4}-დან (მაქსიმუმ {_5} მეგაპიქსელი). @msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. ბრტყელი ლოგოს რეჟიმში გამოსახულება ვექტორიზდა {_1} × {_2} პიქსელზე, საშუალოს გამოთვლით შემცირდა {_3} × {_4}-დან.+@msg byColor must be true or false byColor უნდა იყოს true ან false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor ვერ დაყოფს ავტომატური დაჯგუფების ჯგუფებს: ვექტორიზება გააკეთეთ ავტომატური დაჯგუფების გარეშე @msg `{given}` isn't a library panel (see window.swatchLibrary) `{given}` ბიბლიოთეკის პანელი არ არის (იხ. window.swatchLibrary) @msg `{key}` must be a #rrggbb color `{key}` უნდა იყოს #rrggbb ფერი @msg a key object sits on that point ამ წერტილზე საკვანძო ობიექტი მდებარეობს@@ -4969,6 +4969,8 @@ lighting Light 광원 @msg too many library panels were opened in this session 이 세션에서 라이브러리 패널을 너무 많이 열었습니다 @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. 이미지가 {_1} × {_2}픽셀로 한 변이 {LARGE_TRACE_SIDE}을 넘습니다. 추적은 되지만 더 느립니다. 충실도가 낮은 사전 설정이나 이미지의 작은 사본을 사용하면 더 빠릅니다. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). 이미지를 {_3} × {_4}에서 축소한 {_1} × {_2}픽셀로 추적했습니다(최대 {_5}메가픽셀).+@msg byColor must be true or false byColor는 true 또는 false여야 합니다+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor는 자동 그룹화 그룹을 나눌 수 없습니다. 자동 그룹화 없이 추적하세요 Snapping 스냅 Snap to Tangent 접선에 스냅 Snap to Perpendicular 수직선에 스냅@@ -5245,6 +5245,8 @@ mark-style Japanese Japońskie @msg too many library panels were opened in this session w tej sesji otwarto zbyt wiele paneli bibliotek @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Obraz ma {_1} × {_2} pikseli, ponad {LARGE_TRACE_SIDE} na jednym boku: zostanie obrysowany, ale wolniej. Ustawienie wstępne o niższej wierności lub mniejsza kopia obrazu obrysuje się szybciej. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Obraz obrysowano w rozdzielczości {_1} × {_2} pikseli, po zmniejszeniu z {_3} × {_4} (najwyżej {_5} megapikseli).+@msg byColor must be true or false byColor musi mieć wartość true lub false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor nie może podzielić grup Automatycznego grupowania: obrysuj bez Automatycznego grupowania Snapping Przyciąganie Snap to Tangent Przyciągaj do stycznych Snap to Perpendicular Przyciągaj do prostopadłych@@ -5310,6 +5310,8 @@ mark-style Japanese Japonesas @msg too many library panels were opened in this session foram abertos painéis de biblioteca demais nesta sessão @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. A imagem tem {_1} × {_2} pixels, mais de {LARGE_TRACE_SIDE} em um lado: ela é traçada, mas mais devagar. Uma predefinição de menor fidelidade ou uma cópia menor da imagem traça mais rápido. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). A imagem foi traçada em {_1} × {_2} pixels, reduzida de {_3} × {_4} (no máximo {_5} megapixels).+@msg byColor must be true or false byColor deve ser true ou false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor não pode dividir os grupos de Agrupamento Automático: trace sem Agrupamento Automático Snapping Ajuste Snap to Tangent Ajustar à Tangente Snap to Perpendicular Ajustar à Perpendicular@@ -5002,6 +5002,8 @@ lighting Light Свет @msg too many library panels were opened in this session в этом сеансе открыто слишком много панелей библиотек @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Изображение {_1} × {_2} пикселей, больше {LARGE_TRACE_SIDE} по одной стороне: трассировка выполняется, но медленнее. Стиль с меньшей точностью или уменьшенная копия изображения трассируются быстрее. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Изображение трассировано в размере {_1} × {_2} пикселей, уменьшенное с {_3} × {_4} (не более {_5} мегапикселей).+@msg byColor must be true or false byColor должен иметь значение true или false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor не может разделить группы автогруппировки: трассируйте без автогруппировки Snapping Привязка Snap to Tangent Привязать к касательной Snap to Perpendicular Привязать к перпендикуляру@@ -5416,6 +5416,8 @@ swatch Yellow Green Gulgrön @msg “{name}” is not a file ”{name}” är inte en fil @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Bilden är {_1} × {_2} pixlar, mer än {LARGE_TRACE_SIDE} på en sida: den vektoriseras, men långsammare. En förinställning med lägre återgivning eller en mindre kopia av bilden går snabbare. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Bilden vektoriserades vid {_1} × {_2} pixlar, nedskalad från {_3} × {_4} (högst {_5} megapixlar).+@msg byColor must be true or false byColor måste vara true eller false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor kan inte dela upp grupper från Automatisk gruppering: vektorisera utan Automatisk gruppering Snapping Fästning Snap to Tangent Fäst mot tangent Snap to Perpendicular Fäst mot vinkelrät@@ -5391,6 +5391,8 @@ theme Appearance Görünüm @msg library panel `{given}` isn't missing its library `{given}` kitaplık panelinin kitaplığı eksik değil @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Görüntü {_1} × {_2} piksel, bir kenarı {LARGE_TRACE_SIDE} değerinden büyük: izlenir, ancak daha yavaş. Daha düşük doğruluklu bir ön ayar veya görüntünün daha küçük bir kopyası daha hızlı izlenir. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Görüntü {_3} × {_4} boyutundan küçültülerek {_1} × {_2} pikselde izlendi (en fazla {_5} megapiksel).+@msg byColor must be true or false byColor true veya false olmalıdır+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor, Otomatik Gruplama gruplarını bölemez: Otomatik Gruplama olmadan izleyin Gap Length: Boşluk Uzunluğu: @msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. Düz Logo modu görüntüyü {_3} × {_4} boyutundan ortalama alarak küçültüp {_1} × {_2} pikselde izledi. @plural {n} more export waiting|{n} more exports waiting {n} dışa aktarma daha bekliyor|{n} dışa aktarma daha bekliyor@@ -4986,6 +4986,8 @@ lighting Light Світло @msg too many library panels were opened in this session у цьому сеансі відкрито забагато панелей бібліотек @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Зображення {_1} × {_2} пікселів, понад {LARGE_TRACE_SIDE} з одного боку: трасування виконується, але повільніше. Стиль із нижчою точністю або менша копія зображення трасуються швидше. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Зображення трасовано в розмірі {_1} × {_2} пікселів, зменшене з {_3} × {_4} (щонайбільше {_5} мегапікселів).+@msg byColor must be true or false byColor має мати значення true або false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor не може розділити групи автогрупування: трасуйте без автогрупування Snapping Прив’язка Snap to Tangent Прив’язати до дотичної Snap to Perpendicular Прив’язати до перпендикуляра@@ -4986,6 +4986,8 @@ theme Appearance Giao diện @msg too many library panels were opened in this session đã mở quá nhiều bảng thư viện trong phiên này @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Ảnh có kích thước {_1} × {_2} điểm ảnh, hơn {LARGE_TRACE_SIDE} ở một cạnh: ảnh vẫn được dò, nhưng chậm hơn. Một thiết lập sẵn có độ trung thực thấp hơn hoặc một bản sao nhỏ hơn của ảnh sẽ dò nhanh hơn. @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Ảnh đã được dò ở {_1} × {_2} điểm ảnh, thu nhỏ từ {_3} × {_4} (tối đa {_5} megapixel).+@msg byColor must be true or false byColor phải là true hoặc false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor không thể tách các nhóm của Tự động nhóm: hãy dò mà không bật Tự động nhóm Snapping Hút Snap to Tangent Hút vào tiếp tuyến Snap to Perpendicular Hút vào đường vuông góc@@ -5225,6 +5225,8 @@ lighting Light 光源 @msg too many library panels were opened in this session 本次会话中打开的库面板过多 @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. 图像为 {_1} × {_2} 像素,某一边超过 {LARGE_TRACE_SIDE}:会进行描摹,但速度较慢。使用保真度较低的预设或图像的较小副本会更快。 @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). 图像已按 {_1} × {_2} 像素描摹,由 {_3} × {_4} 缩小而来(最多 {_5} 百万像素)。+@msg byColor must be true or false byColor 必须是 true 或 false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor 无法拆分自动编组生成的编组:请在不使用自动编组的情况下描摹 Snapping 对齐 Snap to Tangent 对齐切线 Snap to Perpendicular 对齐垂线@@ -2375,6 +2375,8 @@ file-filter Templates 範本 @msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. 影像為 {_1} × {_2} 像素,有一邊超過 {LARGE_TRACE_SIDE}:仍會描圖,但速度較慢。使用精確度較低的預設集或較小的影像拷貝,描圖會較快。 @msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). 影像已以 {_1} × {_2} 像素描圖,由 {_3} × {_4} 縮小而來 (最多 {_5} 百萬像素)。 @msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. 「扁平標誌」模式已以 {_1} × {_2} 像素描圖,由 {_3} × {_4} 平均縮小而來。+@msg byColor must be true or false byColor 必須是 true 或 false+@msg byColor can't split Auto Grouping groups: trace without Auto Grouping byColor 無法拆分自動群組產生的群組:請不使用自動群組來描圖 # Links and Attributes: files and URLs handed to other apps. @msg Edit Original opens image and document files only; “{name}” is not one 「編輯原稿」只能開啟影像與文件檔案;「{name}」不屬於這兩類 @msg only http, https and mailto URLs open in the browser, not “{url}” 只有 http、https 和 mailto URL 會在瀏覽器中開啟,「{url}」不會@@ -1496,6 +1496,15 @@ already walks Image Trace. Short agent entry points: - Image Trace: `imageTrace.presets`, `imageTrace.make {id?, preset?, params?, view?}` (or `imageTrace.makeAndExpand`), `imageTrace.setView`, `imageTrace.expand`, `imageTrace.release`. A key inside `params` that is not one of the trace settings, such as `colours`, is an error that names it and the nearest setting (`colors`), as for a command's own keys.+ With `byColor: true`, `imageTrace.expand` and `imageTrace.makeAndExpand` put each color's paths in a group of their+ own, named by the global swatch the color links to (a tint with its percentage, as `Sky 50%`) or as `#rrggbb`;+ gradient fills share a group named `Gradients`. The groups stack in the order of the lowest path in each, and each+ keeps its paths' order. With `imageTrace.expand`, and with `imageTrace.makeAndExpand` when the trace's paths can+ overlap (Overlapping, Flat Logo, Create Strokes), a path that would move under an overlapping path of another color+ (by the bounding boxes of what they paint, effects and shadows included) if it joined its color's group starts+ another group of its color instead, and the renderer draws the art unchanged. In a Flat Logo trace of a black ring+ around a white disc around a black dot, black gets two groups. `layer.releaseToLayers` on the expanded group then+ gives each group a sublayer. A trace made with `autoGrouping` is refused. - Live Paint: `livePaint.make`, `livePaint.merge`, `livePaint.fill {group?, point}`, `livePaint.strokeEdge`, `livePaint.info {group}`, `livePaint.release`, `livePaint.expand`, and `livePaint.gapOptions {ids?, detect?, size?, color?, closeGaps?}` (Object › Live Paint › Gap Options). With `detect: true`, paint stops at the gaps that run