mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 09:20:56 -06:00
Image Trace: keepColors traces only the listed palette colors
Image Trace had no way to trace some of an image's colors and leave the rest out. Ignore Color drops one color, so tracing only the line work of a multi-color image took a full trace and then deleting the other colors' paths by hand. With "keepColors": ["#rrggbb", ...] in the params of imageTrace.make and imageTrace.makeAndExpand, only the palette colors within about 30 levels of a listed color are traced, the distance Ignore Color matches by. With Method Abutting, the kept colors' paths are the same as in a trace of every color. With Method Overlapping, a kept color's paths do not reach under the colors left out, as with Ignore Color. An empty list traces every color, and a value that is not a six-digit hex color is refused. Flat Logo mode does not use the list. The reply's warnings name each listed color that matches none of the colors the trace found, and say when nothing was traced; the status bar shows the first. The warnings are translated in every complete catalog. The params help of imageTrace.make lists keepColors and the new warnings, and the help of imageTrace.makeAndExpand lists the warnings it returns.
This commit is contained in:
@@ -130,7 +130,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Make",
|
||||
["Object", "Image Trace"],
|
||||
None,
|
||||
"{id?: image or Image Trace group (default: selection), preset?: name (imageTrace.presets), enhanced?: false (the preset's enhanced version: with Auto Grouping, and with Shapes in all but High Fidelity Photo; Flat Logo has none and is traced as it is), params?: {mode: \"blackAndWhite\"|\"grayscale\"|\"color\"|\"logo\", threshold: 0-255, palette: \"limited\"|\"fullTone\"|\"automatic\"|\"documentLibrary\" (color mode; limited: the `colors` best colours, fullTone: as many as the tones need, more with colorDetail, automatic: flat art with its own colours, else fullTone, documentLibrary: at most `colors` of the library's colours, as they are), colors: 2-256, colorDetail: 0-100 (fullTone, automatic), library: \"document\" (the document's swatches) or a swatch library id or name (swatch.library.list; documentLibrary), paths: 0-100, corners: 0-100, noise: px, method: \"abutting\"|\"overlapping\", ignoreWhite (leave out white areas), ignoreColor?: \"#rrggbb\" (with ignoreWhite: the colour left out instead of white), snapCurvesToLines, shapes: false (filled outlines that are a circle, an ellipse, a rectangle or a regular polygon come out as that live shape), autoGrouping: false (each path that has others inside it is grouped with them), gradients: false (grayscale and color: an area whose colour changes evenly along one direction is one path with a linear gradient fill), fills: true, strokes: false (Create: lines up to strokeWidth wide become stroked centre lines; without fills, wider areas become stroked outlines), strokeWidth: px (10), logoColors?: [\"#rrggbb\"…] (mode logo: flat-colour logos only; colours found automatically or named here, white is always its own colour, at most ten; noise = areas under noise/10 px ignored; edges to a fraction of a pixel, sharp corners restored, lines and circles snapped; enclosed transparent areas are painted white unless ignoreWhite makes them holes; fills/strokes are ignored; gradients and photos are refused)}, view?: as imageTrace.setView (default: the traced object's own, else tracingResult), ids?: [id…] (instead of id)} → {id, paths, anchors, colors, warnings: [text…] (the image is over 1500 px on a side, so it traces more slowly; or it was traced scaled down)}",
|
||||
"{id?: image or Image Trace group (default: selection), preset?: name (imageTrace.presets), enhanced?: false (the preset's enhanced version: with Auto Grouping, and with Shapes in all but High Fidelity Photo; Flat Logo has none and is traced as it is), params?: {mode: \"blackAndWhite\"|\"grayscale\"|\"color\"|\"logo\", threshold: 0-255, palette: \"limited\"|\"fullTone\"|\"automatic\"|\"documentLibrary\" (color mode; limited: the `colors` best colours, fullTone: as many as the tones need, more with colorDetail, automatic: flat art with its own colours, else fullTone, documentLibrary: at most `colors` of the library's colours, as they are), colors: 2-256, colorDetail: 0-100 (fullTone, automatic), library: \"document\" (the document's swatches) or a swatch library id or name (swatch.library.list; documentLibrary), paths: 0-100, corners: 0-100, noise: px, method: \"abutting\"|\"overlapping\", ignoreWhite (leave out white areas), ignoreColor?: \"#rrggbb\" (with ignoreWhite: the colour left out instead of white), keepColors?: [\"#rrggbb\"…] (not mode logo: only the palette colors within about 30 levels of a listed color are traced; empty: every color), snapCurvesToLines, shapes: false (filled outlines that are a circle, an ellipse, a rectangle or a regular polygon come out as that live shape), autoGrouping: false (each path that has others inside it is grouped with them), gradients: false (grayscale and color: an area whose colour changes evenly along one direction is one path with a linear gradient fill), fills: true, strokes: false (Create: lines up to strokeWidth wide become stroked centre lines; without fills, wider areas become stroked outlines), strokeWidth: px (10), logoColors?: [\"#rrggbb\"…] (mode logo: flat-colour logos only; colours found automatically or named here, white is always its own colour, at most ten; noise = areas under noise/10 px ignored; edges to a fraction of a pixel, sharp corners restored, lines and circles snapped; enclosed transparent areas are painted white unless ignoreWhite makes them holes; fills/strokes are ignored; gradients and photos are refused)}, view?: as imageTrace.setView (default: the traced object's own, else tracingResult), ids?: [id…] (instead of id)} → {id, paths, anchors, colors, warnings: [text…] (the image is over 1500 px on a side, so it traces more slowly; or it was traced scaled down; or a keepColors color matches none the trace found, or nothing was traced)}",
|
||||
has_selection_or_ids,
|
||||
|s, p| trace_make(s, p, false)
|
||||
),
|
||||
@@ -139,7 +139,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} same as imageTrace.make, then expand → {id, paths, anchors, colors, warnings: [text…] (as imageTrace.make)}",
|
||||
has_selection_or_ids,
|
||||
|s, p| trace_make(s, p, true)
|
||||
),
|
||||
@@ -1121,7 +1121,8 @@ fn trace_make(s: &mut Session, p: &Value, expand: bool) -> Result<Value> {
|
||||
let raster = tr::Raster::decode_to_fit(&blob.bytes).map_err(|e| EngineError::Other(e.to_string()))?;
|
||||
let library = trace_library(s, &mut params)?;
|
||||
let res = tr::trace_within(&raster, ¶ms, MAX_TRACE_ANCHORS).map_err(|e| EngineError::Other(e.to_string()))?;
|
||||
let warnings = trace_warnings((img.width, img.height), (raster.width, raster.height));
|
||||
let mut warnings = keep_warnings(¶ms, &res);
|
||||
warnings.extend(trace_warnings((img.width, img.height), (raster.width, raster.height)));
|
||||
// Pixel space of the decoded raster → the image object's pixel space → document.
|
||||
let sx = img.width.max(1) as f64 / raster.width.max(1) as f64;
|
||||
let sy = img.height.max(1) as f64 / raster.height.max(1) as f64;
|
||||
@@ -1221,6 +1222,27 @@ pub(crate) fn trace_warnings(size: (u32, u32), traced: (u32, u32)) -> Vec<String
|
||||
out
|
||||
}
|
||||
|
||||
/// What a trace with Keep Colors tells the user: that nothing was traced because no color the
|
||||
/// trace found matches a listed one, or because Ignore Color left out the ones that match; each
|
||||
/// listed color that matches none of them; and that Flat Logo mode does not use the list.
|
||||
pub(crate) fn keep_warnings(params: &tr::TraceParams, res: &tr::TraceResult) -> Vec<String> {
|
||||
if params.keep_colors.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
if params.mode == tr::Mode::Logo {
|
||||
return vec!["Flat Logo mode does not use keepColors: it traces the colors named in logoColors, or the colors it finds.".to_string()];
|
||||
}
|
||||
if res.paths.is_empty() && res.keep_unmatched.len() == params.keep_colors.len() {
|
||||
return vec![format!("Nothing was traced: none of the colors the trace found matches keepColors ({}).", params.keep_colors.join(", "))];
|
||||
}
|
||||
let mut out = vec![];
|
||||
if res.paths.is_empty() && params.ignore_white {
|
||||
out.push("Nothing was traced: Ignore Color leaves out every area of the colors keepColors lists.".to_string());
|
||||
}
|
||||
out.extend(res.keep_unmatched.iter().map(|c| format!("keepColors: none of the colors the trace found matches {c}, so nothing of it is traced.")));
|
||||
out
|
||||
}
|
||||
|
||||
fn trace_targets(s: &Session, p: &Value) -> Result<Vec<NodeId>> {
|
||||
let d = &s.doc()?.doc;
|
||||
let v: Vec<NodeId> = root_ids(s, p)?.into_iter().filter(|i| d.node(*i).is_some_and(is_trace_group)).collect();
|
||||
|
||||
Reference in New Issue
Block a user