Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7af5790b71 | ||
|
|
5bfe774420 | ||
|
|
e918615347 | ||
|
|
17b4396a97 | ||
|
|
5adeada26d | ||
|
|
175ad4a1e3 | ||
|
|
95304ad7f6 | ||
|
|
02da9bbdc9 | ||
|
|
331fc48b24 |
No files matched your search
@@ -249,7 +249,7 @@ pub fn effect_catalog() -> Vec<EffectInfo> {
|
||||
"distort.freeDistort",
|
||||
"Free Distort…",
|
||||
DT,
|
||||
"{corners: [[x,y]×4] new positions of the bounding-box corners TL, TR, BR, BL in unit box coordinates (identity [[0,0],[1,0],[1,1],[0,1]])}",
|
||||
"{corners: [[x,y]×4] (new positions of the bounding-box corners TL, TR, BR, BL in unit box coordinates; identity [[0,0],[1,0],[1,1],[0,1]])}",
|
||||
json!({"corners": [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]}),
|
||||
),
|
||||
g("distort.puckerBloat", "Pucker & Bloat…", DT, "{amount: % (-200 pucker … 200 bloat, default 0)}", json!({"amount": 0.0})),
|
||||
|
||||
@@ -280,6 +280,20 @@ fn move_effect(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(json!({ "ids": ids_json(&ids), "index": index, "item": item.flatten() }))
|
||||
}
|
||||
|
||||
/// The ids of the effects `effect.setParams` with `p` edits, for the check of their parameters
|
||||
/// (none when `p` doesn't say which: running it reports why).
|
||||
pub(crate) fn set_params_effects(s: &Session, p: &Value) -> Vec<String> {
|
||||
const C: &str = "effect.setParams";
|
||||
let (Ok(index), Ok(item), Ok(ids), Ok(st)) = (index_param(p, "index", C), item_target(s, p, C), appearance_targets(s, p), s.doc()) else {
|
||||
return vec![];
|
||||
};
|
||||
let effect = |id: &NodeId| {
|
||||
let ap = &st.doc.node(*id)?.appearance;
|
||||
Some(ap.effects_at(item.effects_item(ap, C).ok()?)?.get(index)?.id.clone())
|
||||
};
|
||||
ids.iter().filter_map(effect).collect()
|
||||
}
|
||||
|
||||
fn set_params(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
const C: &str = "effect.setParams";
|
||||
let index = index_param(p, "index", C)?;
|
||||
@@ -566,7 +580,7 @@ mod tests {
|
||||
for other in ["threeD.revolve", "threeD.extrude", "threeD.rotate"] {
|
||||
assert!(s.execute("effect.apply", &json!({"effect": other, "ids": [id.0], "item": null})).is_err(), "{effect} + {other}");
|
||||
}
|
||||
s.execute("effect.setParams", &json!({"index": 0, "params": {"rotationY": 50, "depth": 120}, "ids": [id.0], "item": null})).unwrap();
|
||||
s.execute("effect.setParams", &json!({"index": 0, "params": {"rotationY": 50, "rotationX": 10}, "ids": [id.0], "item": null})).unwrap();
|
||||
assert_ne!(effects::solid_art(&node(&s, id)).unwrap(), art, "{effect}: options change the art");
|
||||
assert!(s.execute("effect.setParams", &json!({"index": 0, "params": {"bevel": "nope"}, "ids": [id.0], "item": null})).is_err());
|
||||
let svg = s.execute("document.serialize", &json!({"format": "svg"})).unwrap();
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
//!
|
||||
//! Callers outside the app (the control channel, MCP, the CLI) are answered with a parameter error
|
||||
//! for a key the description doesn't name ([`check_params`]): a misspelt `widht` no longer runs the
|
||||
//! command with its default. [`Session::execute`] itself takes what it is given, so the journal of
|
||||
//! an older version still replays.
|
||||
//! command with its default. So are the `params` of an effect (#1592): `effect.apply` and
|
||||
//! `effect.setParams` answer a key the effect doesn't take, or a value of another type than its
|
||||
//! default, with a parameter error ([`check_effect_params`]). [`Session::execute`] itself takes
|
||||
//! what it is given, so the journal of an older version still replays.
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -189,15 +191,56 @@ pub fn check_keys(id: &str, doc: &'static str, params: &Value) -> Result<()> {
|
||||
return Ok(());
|
||||
}
|
||||
let Some(keys) = accepted(doc, 0) else { return Ok(()) };
|
||||
let Some(key) = given.keys().find(|k| !keys.contains(&k.as_str())) else { return Ok(()) };
|
||||
Err(bad(
|
||||
match given.keys().find(|k| !keys.contains(&k.as_str())) {
|
||||
Some(key) => Err(unknown(id, key, &keys)),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// The parameter error of command `id` for `key`, which isn't one of `keys`: with the nearest of
|
||||
/// them, or all of them.
|
||||
fn unknown(id: &str, key: &str, keys: &[&str]) -> crate::EngineError {
|
||||
bad(
|
||||
id,
|
||||
match nearest(key, &keys) {
|
||||
match nearest(key, keys) {
|
||||
Some(near) => format!("unknown parameter `{key}`; did you mean `{near}`?"),
|
||||
None if keys.is_empty() => format!("unknown parameter `{key}`; the command takes no parameters"),
|
||||
None => format!("unknown parameter `{key}`; the command takes: {}", keys.join(", ")),
|
||||
},
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
/// `params` of built-in effect `effect`, as command `id` was given them (#1592): only keys the
|
||||
/// effect takes (those of its dialog defaults and of its params description), each of its
|
||||
/// default's type. Numbers out of range pass: the effect clamps them when it draws. An effect
|
||||
/// plug-in resolves its own against its manifest, and an unknown effect is the command's to report.
|
||||
pub fn check_effect_params(id: &str, effect: &str, params: &Value) -> Result<()> {
|
||||
let Value::Object(given) = params else { return Ok(()) };
|
||||
if effect.starts_with(vectorcraft_plugins::effect::PREFIX) {
|
||||
return Ok(());
|
||||
}
|
||||
let Some(info) = vectorcraft_render::effects::effect_info(effect) else { return Ok(()) };
|
||||
let defaults = info.defaults.as_object();
|
||||
let mut keys: Vec<&str> = defaults.into_iter().flat_map(|d| d.keys().map(String::as_str)).collect();
|
||||
// A description that doesn't parse names no more keys (the tests rule that out).
|
||||
for key in doc_keys(info.params).map(|d| d.keys).unwrap_or_default() {
|
||||
if !keys.contains(&key) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
for (key, v) in given {
|
||||
if !keys.contains(&key.as_str()) {
|
||||
return Err(unknown(id, key, &keys));
|
||||
}
|
||||
match defaults.and_then(|d| d.get(key)) {
|
||||
Some(Value::Number(_)) if !v.is_number() => return Err(bad(id, format!("`{key}` must be a number"))),
|
||||
Some(Value::Bool(_)) if !v.is_boolean() => return Err(bad(id, format!("`{key}` must be true or false"))),
|
||||
// (A colour is also taken as `[r, g, b]` or as a colour object.)
|
||||
Some(Value::String(_)) if v.is_number() || v.is_boolean() => return Err(bad(id, format!("`{key}` must be a string"))),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Commands whose params aren't checked yet (#1442): they read keys in ways the audit of the
|
||||
@@ -333,10 +376,16 @@ pub const UNCHECKED: &[&str] = &[
|
||||
"webExport.settings",
|
||||
];
|
||||
|
||||
/// [`check_keys`] for an engine command, the steps of a `command.batch` included. A command the
|
||||
/// engine doesn't have passes: running it reports that.
|
||||
/// [`check_keys`] for an engine command, the steps of a `command.batch` included, and
|
||||
/// [`check_effect_params`] for the effect `effect.apply` adds. A command the engine doesn't have
|
||||
/// passes: running it reports that.
|
||||
pub fn check_params(id: &str, params: &Value) -> Result<()> {
|
||||
let Some(spec) = find_command(id) else { return Ok(()) };
|
||||
if let ("effect.apply" | "appearance.addEffect", Some(effect), Some(given)) =
|
||||
(id, params.get("effect").or_else(|| params.get("id")).and_then(Value::as_str), params.get("params"))
|
||||
{
|
||||
check_effect_params(id, effect, given)?;
|
||||
}
|
||||
if UNCHECKED.contains(&id) {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -350,3 +399,18 @@ pub fn check_params(id: &str, params: &Value) -> Result<()> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl crate::Session {
|
||||
/// [`check_params`], and [`check_effect_params`] for the effects `effect.setParams` edits in
|
||||
/// this session's document (not as a step of a `command.batch`: what it edits is only there
|
||||
/// once the steps before it ran).
|
||||
pub fn check_params(&self, id: &str, params: &Value) -> Result<()> {
|
||||
check_params(id, params)?;
|
||||
if let ("effect.setParams", Some(given)) = (id, params.get("params")) {
|
||||
for effect in super::effectcmd::set_params_effects(self, params) {
|
||||
check_effect_params(id, &effect, given)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1317,7 +1317,7 @@ impl Session {
|
||||
// The crate's own tests are the audit of the params descriptions (#1442): a key a test
|
||||
// passes that its command's description doesn't name fails the test.
|
||||
#[cfg(test)]
|
||||
cmd::params::check_params(id, params)?;
|
||||
self.check_params(id, params)?;
|
||||
// Clear's menu needs a selection; an agent supplying explicit targets only needs a
|
||||
// document. The command validates those targets before making any edit.
|
||||
let enabled = if id == "edit.clear" && params.get("ids").is_some() {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use serde_json::json;
|
||||
|
||||
use crate::cmd::params::{UNCHECKED, check_keys, check_params, doc_keys};
|
||||
use crate::cmd::params::{UNCHECKED, check_effect_params, check_keys, check_params, doc_keys};
|
||||
use crate::{EngineError, Session, command_specs, find_command};
|
||||
|
||||
fn keys(doc: &str) -> Vec<&str> {
|
||||
@@ -95,6 +95,50 @@ fn a_batch_checks_its_steps() {
|
||||
assert_eq!(refusal("command.batch", json!({"steps": []})), "unknown parameter `steps`; the command takes: label, commands, artboard");
|
||||
}
|
||||
|
||||
/// #1592: the `params` of an effect are held to the effect's own keys and types, where the effect
|
||||
/// is named (`effect.apply`) and where the document says which it is (`effect.setParams`).
|
||||
#[test]
|
||||
fn effect_params_are_the_effects_own() {
|
||||
use vectorcraft_render::effects::effect_catalog;
|
||||
for e in effect_catalog() {
|
||||
doc_keys(e.params).unwrap_or_else(|why| panic!("{}: {why}", e.id));
|
||||
check_effect_params("effect.apply", e.id, &e.defaults).unwrap_or_else(|why| panic!("{}'s defaults: {why}", e.id));
|
||||
}
|
||||
let apply = |effect: &str, params| json!({"effect": effect, "params": params});
|
||||
assert_eq!(refusal("effect.apply", apply("blur.gaussian", json!({"blurr": 3}))), "unknown parameter `blurr`; the command takes: radius");
|
||||
assert_eq!(
|
||||
refusal("appearance.addEffect", apply("stylize.dropShadow", json!({"opacty": 50}))),
|
||||
"unknown parameter `opacty`; did you mean `opacity`?"
|
||||
);
|
||||
assert_eq!(refusal("effect.apply", apply("blur.gaussian", json!({"radius": "big"}))), "`radius` must be a number");
|
||||
assert_eq!(refusal("effect.apply", apply("threeD.revolve", json!({"shade": 1}))), "`shade` must be true or false");
|
||||
assert_eq!(refusal("effect.apply", apply("threeD.revolve", json!({"edge": 1}))), "`edge` must be a string");
|
||||
// Out of range is the effect's to clamp; a key only its description names is one it takes;
|
||||
// a plug-in's are its manifest's; an unknown effect is the command's to report.
|
||||
for (effect, params) in [
|
||||
("stylize.dropShadow", json!({"opacity": 500, "x": -3, "color": "#ff0000"})),
|
||||
("stylize.dropShadow", json!({"color": [1, 0, 0]})),
|
||||
("threeD.revolve", json!({"segments": 100000})),
|
||||
("path.outlineStroke", json!({"width": 2})),
|
||||
("plugin.anything", json!({"whatever": 1})),
|
||||
("no.such", json!({"whatever": 1})),
|
||||
] {
|
||||
assert!(check_params("effect.apply", &apply(effect, params.clone())).is_ok(), "{effect} {params}");
|
||||
}
|
||||
// `effect.setParams` names an index: the session knows the effect there.
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({})).unwrap();
|
||||
s.execute("shape.rectangle", &json!({"x": 10, "y": 10, "width": 50, "height": 40})).unwrap();
|
||||
s.execute("effect.apply", &apply("blur.gaussian", json!({"radius": 2}))).unwrap();
|
||||
for (params, why) in [(json!({"blurr": 3}), "unknown parameter `blurr`"), (json!({"radius": true}), "`radius` must be a number")] {
|
||||
let e = s.execute("effect.setParams", &json!({"index": 0, "params": params})).unwrap_err().to_string();
|
||||
assert!(e.contains(why), "{e}");
|
||||
}
|
||||
s.execute("effect.setParams", &json!({"index": 0, "params": {"radius": 1e9}, "visible": false})).unwrap();
|
||||
// No effect at the index: the command says so, not the check.
|
||||
assert!(s.check_params("effect.setParams", &json!({"index": 7, "params": {"blurr": 3}})).is_ok());
|
||||
}
|
||||
|
||||
/// What a command notes for its journal entry is a key its description names, so the entry
|
||||
/// passes the check a caller's params get.
|
||||
#[test]
|
||||
|
||||
@@ -106,10 +106,19 @@ fn listed_applied_edited_and_undone() {
|
||||
assert_eq!((&e["raster"], &e["menu"]), (&json!(true), &json!(["Effect", menu])), "{id}");
|
||||
}
|
||||
}
|
||||
// Junk parameters are stored as given and read clamped; the canvas still draws.
|
||||
// Numbers far out of range are stored as given and read clamped; the canvas still draws.
|
||||
for (id, _) in EFFECTS {
|
||||
s.execute("effect.apply", &json!({"effect": id, "ids": [g], "params": {"amount": 1e308, "radius": -1e308, "threshold": "x", "quality": 7}}))
|
||||
.unwrap();
|
||||
let defaults = vectorcraft_render::effects::default_params(id).unwrap();
|
||||
let junk: serde_json::Map<String, Value> = defaults
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|(_, v)| v.is_number())
|
||||
.enumerate()
|
||||
.map(|(i, (k, _))| (k.clone(), json!(if i % 2 == 0 { 1e308 } else { -1e308 })))
|
||||
.collect();
|
||||
assert!(!junk.is_empty(), "{id}");
|
||||
s.execute("effect.apply", &json!({"effect": id, "ids": [g], "params": junk})).unwrap();
|
||||
}
|
||||
let _ = render(&s);
|
||||
let n = s.doc().unwrap().doc.node(NodeId(g)).unwrap().clone();
|
||||
|
||||
@@ -22,6 +22,28 @@ fn a_journal_with_a_key_no_longer_read_still_replays() {
|
||||
assert_eq!((st.doc.artboards[0].rect.width(), st.doc.layers[0].children().unwrap().len()), (300.0, 1));
|
||||
}
|
||||
|
||||
/// So do an effect's own parameters (#1592): a journal or a file with a key the effect doesn't
|
||||
/// take, or a value of another type, replays and opens; the effect reads what it knows, clamped.
|
||||
#[test]
|
||||
fn effect_params_no_longer_taken_still_replay_and_open() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({})).unwrap();
|
||||
s.execute("shape.rectangle", &json!({"x": 10, "y": 10, "width": 50, "height": 40})).unwrap();
|
||||
let old = [
|
||||
("effect.apply", json!({"effect": "blur.gaussian", "params": {"blurr": 3, "radius": "big"}})),
|
||||
("effect.setParams", json!({"index": 0, "params": {"amount": 1e308}})),
|
||||
];
|
||||
for (id, params) in &old {
|
||||
assert!(s.check_params(id, params).is_err(), "{id}");
|
||||
s.execute(id, params).unwrap();
|
||||
}
|
||||
let doc = &s.active().unwrap().doc;
|
||||
let back = vectorcraft_format::load(&vectorcraft_format::save(doc, false)).unwrap();
|
||||
let stored = |d: &vectorcraft_doc::Document| d.layers[0].children().unwrap()[0].appearance.effects[0].params.clone();
|
||||
assert_eq!(stored(&back), stored(doc));
|
||||
assert_eq!(stored(&back)["blurr"], json!(3));
|
||||
}
|
||||
|
||||
/// What a command adds to its own journal entry passes the check, so a recorded journal can be
|
||||
/// sent back through the control channel or MCP.
|
||||
#[test]
|
||||
|
||||
@@ -128,7 +128,7 @@ impl Headless {
|
||||
// A key the command's description doesn't name is an error, not a default (#1442).
|
||||
match HOST_COMMANDS.iter().find(|c| c.0 == id) {
|
||||
Some((_, _, doc)) => vectorcraft_engine::check_keys(id, doc, params),
|
||||
None => vectorcraft_engine::check_params(id, params),
|
||||
None => self.session.check_params(id, params),
|
||||
}
|
||||
.map_err(|e| e.to_string())?;
|
||||
match id {
|
||||
|
||||
@@ -1241,11 +1241,50 @@ impl<'p> Builder<'p> {
|
||||
}
|
||||
|
||||
fn flush_text(&mut self) {
|
||||
let Some((t, opacity, facts)) = self.text.take().and_then(TextLine::finish) else { return };
|
||||
let mut n = Node::new(self.id(), NodeKind::Text(Box::new(t)));
|
||||
n.opacity = opacity;
|
||||
self.lines.insert(n.id, facts);
|
||||
self.push_node(n);
|
||||
let Some(mut line) = self.text.take() else { return };
|
||||
let held = std::mem::take(&mut line.held);
|
||||
if let Some((t, opacity, facts)) = line.finish() {
|
||||
let mut n = Node::new(self.id(), NodeKind::Text(Box::new(t)));
|
||||
n.opacity = opacity;
|
||||
self.lines.insert(n.id, facts);
|
||||
self.push_node(n);
|
||||
}
|
||||
// The strokes of its glyphs' outlines, right after it ([`import_lines`] makes them its stroke).
|
||||
for n in held {
|
||||
self.push_node(n);
|
||||
}
|
||||
}
|
||||
|
||||
/// A stroke of the outline of the glyph just drawn, in the glyph's own paint, while every glyph
|
||||
/// before it on the open line was stroked so too, is held with the line instead of ending it
|
||||
/// (an editor writes type on a path glyph by glyph, each followed by the stroke added to the
|
||||
/// type). `false`: it isn't one.
|
||||
fn hold_glyph_stroke(&mut self, path: &BezPath, transform: Affine, paint: &hayro_interpret::Paint<'_>, draw_mode: &PathDrawMode) -> bool {
|
||||
let PathDrawMode::Stroke(props) = draw_mode else { return false };
|
||||
if self.mask.is_some() || self.blend != BlendMode::Normal || matches!(paint, hayro_interpret::Paint::Pattern(_)) {
|
||||
return false;
|
||||
}
|
||||
let (p, opacity) = self.paint(paint, true);
|
||||
let Some(line) = &self.text else { return false };
|
||||
let Some(ink) = line.ink.last() else { return false };
|
||||
if line.held.len() + 1 != line.ink.len() || opacity < 0.999 || !line.last_fill().is_some_and(|f| *f == p) {
|
||||
return false;
|
||||
}
|
||||
let mut bp = path.clone();
|
||||
bp.apply_affine(transform);
|
||||
let b = bp.bounding_box();
|
||||
let tol = 0.01 * (ink.width() + ink.height()) + 0.01;
|
||||
if ![(b.x0, ink.x0), (b.y0, ink.y0), (b.x1, ink.x1), (b.y1, ink.y1)].iter().all(|(a, c)| (a - c).abs() <= tol) {
|
||||
return false;
|
||||
}
|
||||
let mut st = stroke_layer(p, opacity, props, mean_scale(transform));
|
||||
// Every painted path takes its turn in the scan's overprint list, a held one too.
|
||||
st.overprint = self.path_overprint();
|
||||
let n = Node::path(self.id(), PathData::from_bezpath(&bp), Appearance { items: vec![AppearanceItem::Stroke(st)], ..Default::default() });
|
||||
if let Some(line) = &mut self.text {
|
||||
line.held.push(n);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Convert a hayro paint to a VectorCraft paint and opacity (`stroke`: for a stroke).
|
||||
@@ -1667,6 +1706,10 @@ impl<'a> Device<'a> for Builder<'_> {
|
||||
|
||||
fn draw_path(&mut self, path: &BezPath, transform: Affine, paint: &hayro_interpret::Paint<'a>, draw_mode: &PathDrawMode) {
|
||||
self.flush_glyphs();
|
||||
if self.hold_glyph_stroke(path, transform, paint, draw_mode) {
|
||||
self.pending = false;
|
||||
return;
|
||||
}
|
||||
self.flush_text();
|
||||
self.pending = false;
|
||||
let overprint = self.path_overprint();
|
||||
|
||||
@@ -11,7 +11,7 @@ use std::collections::HashMap;
|
||||
|
||||
use kurbo::{Affine, BezPath, Point, Rect, Vec2};
|
||||
use vectorcraft_color::Paint;
|
||||
use vectorcraft_doc::{CharStyle, ParaDirection, TextKind, TextObject, TextRun};
|
||||
use vectorcraft_doc::{CharStyle, Node, ParaDirection, TextKind, TextObject, TextRun};
|
||||
use vectorcraft_text::TextLayout;
|
||||
|
||||
/// One glyph's placement: its baseline origin, advance direction, size and horizontal scale.
|
||||
@@ -144,6 +144,9 @@ pub(crate) struct TextLine {
|
||||
gaps: Vec<(usize, f64)>,
|
||||
/// Each glyph's advance in the file's font (points), with [`Self::baseline`].
|
||||
advances: Vec<f64>,
|
||||
/// The strokes of its glyphs' outlines drawn between them, one per glyph with ink so far
|
||||
/// (written right after the line).
|
||||
pub held: Vec<Node>,
|
||||
}
|
||||
|
||||
/// What a finished line of type tells beyond its text object (see [`crate::import_lines`]).
|
||||
@@ -177,9 +180,15 @@ impl TextLine {
|
||||
ink: vec![],
|
||||
gaps: vec![],
|
||||
advances: vec![],
|
||||
held: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// The paint the last glyph is filled with.
|
||||
pub fn last_fill(&self) -> Option<&Paint> {
|
||||
self.runs.last()?.0.fill.as_ref()
|
||||
}
|
||||
|
||||
/// Add glyph `text` set vertically (`upright`) if it continues this column (or makes this
|
||||
/// line's single glyph a column): below the last one, in the same column. `false`: it starts
|
||||
/// another line.
|
||||
@@ -350,6 +359,20 @@ impl TextLine {
|
||||
t.runs = tracked_each(std::mem::take(&mut t.runs), own, self.at.size);
|
||||
}
|
||||
if let Some(path) = path {
|
||||
// Evenly spaced type keeps its length along the path: the path runs through the
|
||||
// glyphs' origins, one curve each, to the end of the last glyph, and tracking makes up
|
||||
// the difference from the advances.
|
||||
if own.is_none() {
|
||||
let length: f64 = path.segments().take(self.baseline.len()).map(|seg| kurbo::ParamCurveArclen::arclen(&seg, 1e-3)).sum();
|
||||
let laid = vectorcraft_text::layout(db, &t);
|
||||
let natural: f64 = laid.glyphs.iter().map(|g| g.advance).sum();
|
||||
let chars: usize = t.runs.iter().map(|r| r.text.chars().count()).sum();
|
||||
let size = self.at.size;
|
||||
if chars > 1 && length.is_finite() && (length - natural).abs() > size * 0.01 {
|
||||
let (tracking, spaces) = spacing(&laid, &[], length - natural, chars, size);
|
||||
t.runs = tracked(std::mem::take(&mut t.runs), tracking, &spaces);
|
||||
}
|
||||
}
|
||||
t.kind = TextKind::OnPath { path: vectorcraft_geom::PathData::from_bezpath(&path), start: 0.0, end: None };
|
||||
t.xf = Affine::IDENTITY;
|
||||
t.cached_bounds = Some(vectorcraft_text::layout(db, &t).bounds);
|
||||
|
||||
@@ -153,6 +153,25 @@ fn a_stroke_on_the_type_of_a_moved_ai_page_is_still_the_types_stroke() {
|
||||
assert!(matches!(art[1].appearance.items.as_slice(), [AppearanceItem::Stroke(_)]), "{:?}", art[1].appearance);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn glyphs_each_followed_by_their_stroked_outline_stay_one_object() {
|
||||
// Type on a path is written glyph by glyph: each glyph, then its outline stroked in the same
|
||||
// colour (a shadow under the type).
|
||||
let glyph = |c: char, x: f64, stroke: &str| format!("BT 0 0 1 rg /F1 24 Tf {x} 50 Td ({c}) Tj ET {stroke} 0.5 w {}", outline(c, x));
|
||||
let i_at = 10.0 + 0.722 * 24.0;
|
||||
let d = import(&page(&(glyph('H', 10.0, "0 0 1 RG") + &glyph('i', i_at, "0 0 1 RG")))).unwrap();
|
||||
let art = d.layers[0].children().unwrap();
|
||||
assert_eq!(art.len(), 1, "one object: {art:?}");
|
||||
let NodeKind::Text(t) = &art[0].kind else { panic!("{:?}", art[0].kind) };
|
||||
assert_eq!(t.plain_text(), "Hi");
|
||||
let [AppearanceItem::Stroke(st)] = art[0].appearance.items.as_slice() else { panic!("{:?}", art[0].appearance) };
|
||||
assert_eq!(st.paint.color(), Some(Color::rgb(0.0, 0.0, 1.0)));
|
||||
// A stroke of another colour drawn over each glyph in turn would also cover the next one's
|
||||
// neighbour as one object's stroke: the glyphs stay as the file draws them.
|
||||
let d = import(&page(&(glyph('H', 10.0, "1 1 1 RG") + &glyph('i', i_at, "1 1 1 RG")))).unwrap();
|
||||
assert_eq!(texts(&d).len(), 2);
|
||||
}
|
||||
|
||||
/// A 300 × 200 pt page with `lines` of (x, y from the top, size, text) in Source Sans 3.
|
||||
fn source_sans(lines: &[(f32, f32, f32, &str)]) -> Vec<u8> {
|
||||
crate::tests_live_text::text_pdf(lines, false)
|
||||
|
||||
@@ -424,3 +424,22 @@ fn glyphs_spaced_each_their_own_way_keep_their_place() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Evenly tracked type on a path comes back with its tracking: each glyph where it was along the
|
||||
/// curve, not closed up to the font's advances.
|
||||
#[test]
|
||||
fn evenly_tracked_type_on_a_path_keeps_its_length() {
|
||||
let mut arc = BezPath::new();
|
||||
arc.move_to((40.0, 200.0));
|
||||
arc.curve_to((100.0, 80.0), (260.0, 80.0), (320.0, 200.0));
|
||||
let mut t = TextObject::point(Point::ORIGIN, "ARCHING TYPE", CharStyle { tracking: 80.0, ..style(24.0) });
|
||||
t.kind = TextKind::OnPath { path: PathData::from_bezpath(&arc), start: 0.0, end: None };
|
||||
let d = doc(vec![t]);
|
||||
let want = origins(&d);
|
||||
let back = import_as(&pdf(&d, false).bytes, TextAs::default());
|
||||
let got = origins(&back);
|
||||
assert_eq!(got.len(), want.len(), "{:?}", texts(&back));
|
||||
for (g, w) in got.iter().zip(&want) {
|
||||
assert!(g.distance(*w) < 0.3, "{got:?}\nvs {want:?}");
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -54,9 +54,13 @@ fn before(a: Point, b: Point) -> std::cmp::Ordering {
|
||||
/// Lines within this of level or upright are made exactly so (the tangent of 2.5°).
|
||||
const NEARLY_UPRIGHT: f64 = 0.0437;
|
||||
|
||||
/// The farthest (px) levelling a line may move either of its ends.
|
||||
const LEVEL_REACH: f64 = 1.6;
|
||||
|
||||
/// Snap Curves To Lines: the straight pieces of `segs` (a chain; `closed` when its end joins its
|
||||
/// start) that are within 2.5° of level or upright and at least 2 px long are made exactly level
|
||||
/// or upright, by moving their ends across them: both to their mean, or, in an open chain, the
|
||||
/// or upright (unless that moves an end by more than [`LEVEL_REACH`]), by moving their ends
|
||||
/// across them: both to their mean, or, in an open chain, the
|
||||
/// free end to the end that is fixed (with `fixed_ends`, a chain's own ends stay where they are:
|
||||
/// they are junctions other regions share). The pieces next to a moved end follow it.
|
||||
fn snap_upright(segs: &mut [Cubic], closed: bool, fixed_ends: bool) {
|
||||
@@ -96,6 +100,11 @@ fn snap_upright(segs: &mut [Cubic], closed: bool, fixed_ends: bool) {
|
||||
(false, true) => pick(b),
|
||||
(false, false) => (pick(a) + pick(b)) / 2.0,
|
||||
};
|
||||
// Not when an end would go farther than a pixel and a half or so from where the image has it: a long line a
|
||||
// degree or two off is that line, not a level one drawn badly.
|
||||
if (pick(a) - to).abs().max((pick(b) - to).abs()) > LEVEL_REACH {
|
||||
continue;
|
||||
}
|
||||
let moved = |p: Point| if level { Point::new(p.x, to) } else { Point::new(to, p.y) };
|
||||
if !fixed(ia) {
|
||||
put(segs, ia, moved(a));
|
||||
|
||||
@@ -1250,3 +1250,18 @@ fn a_hard_edged_circle_is_a_circle() {
|
||||
let [TracedPath { shape: Some(TracedShape::Ellipse { center, rx, ry, .. }), .. }] = res.paths.as_slice() else { panic!("{:?}", res.paths) };
|
||||
assert!(rx == ry && (rx - 30.0).abs() < 0.3 && center.distance(Point::new(50.0, 50.0)) < 0.1, "{center:?} {rx} {ry}");
|
||||
}
|
||||
|
||||
/// Levelling moves no end far: a long line two degrees off level stays where the image has it.
|
||||
#[test]
|
||||
fn a_long_line_two_degrees_askew_is_not_levelled() {
|
||||
let slope = 2f64.to_radians().tan();
|
||||
let img = Raster::from_fn(240, 40, |x, y| {
|
||||
let top = 12.0 + slope * (f64::from(x) - 20.0);
|
||||
let inside = (20..220).contains(&x) && (f64::from(y) + 0.5 - top - 4.0).abs() <= 4.0;
|
||||
if inside { [0, 0, 0, 255] } else { [255, 255, 255, 255] }
|
||||
});
|
||||
let res = trace(&img, &TraceParams { ignore_white: true, snap_curves_to_lines: true, ..Default::default() });
|
||||
let b = res.paths[0].path.bounds().unwrap();
|
||||
// 200 px at 2° rises 7 px: levelled, the bar would be 8 px tall, not 15.
|
||||
assert!(b.height() > 13.0, "{b:?}");
|
||||
}
|
||||
@@ -144,7 +144,7 @@ pub fn handle(app: &mut VectorcraftApp, ctx: &egui::Context, req: &ControlReques
|
||||
let params = p.get("params").cloned().unwrap_or(json!({}));
|
||||
let params = if params.is_null() { json!({}) } else { params };
|
||||
// A key the command's description doesn't name is an error, not a default (#1442).
|
||||
if let Err(e) = crate::menus::check_params(id, ¶ms) {
|
||||
if let Err(e) = crate::menus::check_params(&app.session, id, ¶ms) {
|
||||
return err(e);
|
||||
}
|
||||
wrap(app.run(id, params))
|
||||
|
||||
@@ -623,7 +623,7 @@ impl VectorcraftApp {
|
||||
// In debug builds the app's own calls are held to the params descriptions too (#1442): a
|
||||
// panel passing a key its command doesn't read shows up in the tests.
|
||||
if cfg!(debug_assertions) {
|
||||
menus::check_params(id, ¶ms)?;
|
||||
menus::check_params(&self.session, id, ¶ms)?;
|
||||
}
|
||||
if let Some(r) = menus::run_ui_command(self, id, ¶ms) {
|
||||
return r;
|
||||
|
||||
@@ -905,11 +905,11 @@ pub(crate) fn opt_bool(p: &Value, key: &str) -> Result<Option<bool>, String> {
|
||||
/// `params` has only keys the params description of engine command `id` names, or the parameter
|
||||
/// error naming the one it doesn't (#1442). The UI's own commands aren't checked yet: their
|
||||
/// descriptions haven't been audited against what they read.
|
||||
pub fn check_params(id: &str, params: &Value) -> Result<(), String> {
|
||||
pub fn check_params(session: &vectorcraft_engine::Session, id: &str, params: &Value) -> Result<(), String> {
|
||||
if UI_COMMANDS.iter().any(|c| c.0 == id) {
|
||||
return Ok(());
|
||||
}
|
||||
vectorcraft_engine::check_params(id, params).map_err(|e| e.to_string())
|
||||
session.check_params(id, params).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
pub fn run_ui_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<Result<Value, String>> {
|
||||
|
||||
+139
-138
@@ -1,6 +1,6 @@
|
||||
# Image Trace quality
|
||||
|
||||
> **Last reviewed:** 2026-10-13 · **Last updated:** 2026-10-13 · **Change:** minor (Gradients: on linear gradients 6 Colors goes from ΔE 2.12 in 6.4 paths to 0.62 in 3.4)
|
||||
> **Last reviewed:** 2026-10-13 · **Last updated:** 2026-10-13 · **Change:** minor (levelling moves no line end more than 1.6 px: Technical Drawing's largest distance 4.21 px to 1.46)
|
||||
|
||||
What Image Trace (`crates/trace`) makes of images whose right answer is known, measured by a harness and
|
||||
compared with the numbers before each change. Nothing here is a judgement by eye, and nothing is compared
|
||||
@@ -81,7 +81,7 @@ Each preset on the inputs it is meant for, all sizes and variants.
|
||||
| Technical Drawing | polygons, star, letter A, lines, askew shapes and lines | 2.73 | 13.67 | 0.210 | 1.43 | 105.76 | 4.7 | 31 | 2.01 | 100 | 9 | 85 | 5.9 | – | – | – | – | 0.1 | 0.0 | – | – | – |
|
||||
| Flat Logo | two touching halves, four-colour badge | 0.43 | 3.53 | 3.106 | 88.50 | 0.78 | 69.9 | 352 | 38.92 | 38 | 10 | 80 | 237.0 | 0.0 | 0 | 13.8 | – | 59.1 | 0.2 | – | – | – |
|
||||
|
||||
### Now (with Gradients)
|
||||
### Now (with levelling bounded)
|
||||
|
||||
| preset | inputs | ΔE | edge ΔE | off mean | off max | missed max | paths | anchors | × art | lines % | upright % | corners % | kinks | odd colours | odd px | gap px | overlap px | specks | outside px | centre off | width off | anchors a stroke | shapes % | grouped % | gradient paths | found % | angle off | ramp off |
|
||||
|:--|:--|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|
|
||||
@@ -97,7 +97,7 @@ Each preset on the inputs it is meant for, all sizes and variants.
|
||||
| Sketched Art | circle, ellipses, badge with a hole, letter D, lines | 2.71 | 12.02 | 0.185 | 1.00 | 105.61 | 1.8 | 16 | 1.42 | 19 | 8 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Silhouettes | circle, ellipses, badge with a hole, letter D | 2.98 | 19.30 | 0.524 | 2.19 | 2.19 | 1.2 | 14 | 1.64 | 12 | 9 | 93 | 0.4 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Line Art | lines | 2.62 | 9.49 | 0.056 | 0.99 | 105.72 | 5.4 | 43 | 2.14 | 40 | 0 | – | – | – | – | – | – | 0.1 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | polygons, star, letter A, lines, askew shapes and lines | 2.71 | 12.14 | 0.178 | 4.21 | 105.64 | 4.7 | 38 | 2.55 | 81 | 24 | 78 | 0.8 | – | – | – | – | 0.5 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | polygons, star, letter A, lines, askew shapes and lines | 2.65 | 10.88 | 0.126 | 1.46 | 105.64 | 4.7 | 38 | 2.55 | 81 | 19 | 78 | 0.8 | – | – | – | – | 0.5 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Outline | lines | 2.60 | 9.16 | 0.055 | 2.94 | 105.75 | 5.0 | 14 | 0.71 | 95 | 4 | – | – | – | – | – | – | 0.0 | 0.0 | 0.053 | 0.02 | 2.4 | – | – | 0.0 | – | – | – |
|
||||
| Flat Logo | two touching halves, four-colour badge | 0.43 | 3.53 | 3.106 | 88.50 | 0.78 | 69.9 | 352 | 38.92 | 38 | 10 | 80 | 237.0 | 0.0 | 0 | 13.8 | – | 59.1 | 0.2 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | plain shapes | 3.18 | 9.53 | 0.102 | 1.00 | 1.00 | 6.0 | 40 | 1.32 | 41 | 27 | – | – | – | – | 15.8 | 11.5 | 0.0 | 0.0 | – | – | – | 57 | – | 0.0 | – | – | – |
|
||||
@@ -110,7 +110,7 @@ Each preset on the inputs it is meant for, all sizes and variants.
|
||||
| High Fidelity Photo, no Gradients | two-stop gradient, three-stop gradient in a disc | 0.79 | 2.98 | – | – | – | 38.1 | 3827 | – | 31 | 30 | – | – | – | – | 0.0 | – | 1.1 | 0.0 | – | – | – | – | – | 0.0 | 0 | – | – |
|
||||
| High Fidelity Photo with Gradients | two-stop gradient, three-stop gradient in a disc | 0.46 | 1.31 | – | – | – | 20.6 | 759 | – | 9 | 5 | – | – | – | – | 0.0 | – | 2.9 | 0.0 | – | – | – | – | – | 1.0 | 86 | 1.5 | 21 |
|
||||
|
||||
The sections below are the numbers now (with Gradients).
|
||||
The sections below are the numbers now (with levelling bounded).
|
||||
|
||||
## 1. Each preset on the inputs it is meant for
|
||||
|
||||
@@ -130,7 +130,7 @@ All sizes and variants (clean, aliased, blurred, noisy, scanned) of those inputs
|
||||
| Sketched Art | circle, ellipses, badge with a hole, letter D, lines | 2.71 | 12.02 | 0.185 | 1.00 | 105.61 | 1.8 | 16 | 1.42 | 19 | 8 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Silhouettes | circle, ellipses, badge with a hole, letter D | 2.98 | 19.30 | 0.524 | 2.19 | 2.19 | 1.2 | 14 | 1.64 | 12 | 9 | 93 | 0.4 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Line Art | lines | 2.62 | 9.49 | 0.056 | 0.99 | 105.72 | 5.4 | 43 | 2.14 | 40 | 0 | – | – | – | – | – | – | 0.1 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | polygons, star, letter A, lines, askew shapes and lines | 2.71 | 12.14 | 0.178 | 4.21 | 105.64 | 4.7 | 38 | 2.55 | 81 | 24 | 78 | 0.8 | – | – | – | – | 0.5 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | polygons, star, letter A, lines, askew shapes and lines | 2.65 | 10.88 | 0.126 | 1.46 | 105.64 | 4.7 | 38 | 2.55 | 81 | 19 | 78 | 0.8 | – | – | – | – | 0.5 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Outline | lines | 2.60 | 9.16 | 0.055 | 2.94 | 105.75 | 5.0 | 14 | 0.71 | 95 | 4 | – | – | – | – | – | – | 0.0 | 0.0 | 0.053 | 0.02 | 2.4 | – | – | 0.0 | – | – | – |
|
||||
| Flat Logo | two touching halves, four-colour badge | 0.43 | 3.53 | 3.106 | 88.50 | 0.78 | 69.9 | 352 | 38.92 | 38 | 10 | 80 | 237.0 | 0.0 | 0 | 13.8 | – | 59.1 | 0.2 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | plain shapes | 3.18 | 9.53 | 0.102 | 1.00 | 1.00 | 6.0 | 40 | 1.32 | 41 | 27 | – | – | – | – | 15.8 | 11.5 | 0.0 | 0.0 | – | – | – | 57 | – | 0.0 | – | – | – |
|
||||
@@ -214,7 +214,7 @@ All sizes and variants (clean, aliased, blurred, noisy, scanned) of those inputs
|
||||
| Black and White Logo | curved shapes | 0.03 | 1.48 | 0.024 | 0.16 | 0.16 | 1.2 | 20 | 2.12 | 21 | 21 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Black and White Logo | polygons | 0.06 | 2.36 | 0.040 | 0.47 | 1.26 | 1.7 | 14 | 1.24 | 93 | 8 | 95 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Black and White Logo | thin lines | 0.59 | 6.00 | 0.105 | 0.50 | 77.21 | 10.3 | 44 | 2.18 | 90 | 10 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Black and White Logo | askew shapes | 0.89 | 20.99 | 0.613 | 4.21 | 9.63 | 4.0 | 25 | 1.03 | 98 | 98 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Black and White Logo | askew shapes | 0.49 | 11.14 | 0.234 | 1.46 | 9.63 | 4.0 | 25 | 1.03 | 98 | 70 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Black and White Logo | plain shapes | 0.08 | 1.56 | 0.033 | 0.31 | 0.30 | 6.0 | 55 | 1.84 | 58 | 52 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | 0 | – | 0.0 | – | – | – |
|
||||
| Black and White Logo | flat colour art | 19.90 | 17.76 | 0.157 | 0.30 | 179.51 | 1.0 | 18 | 1.12 | 23 | 23 | 0 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Black and White Logo | gradient, photo | 38.97 | 38.53 | – | – | – | 9.0 | 634 | – | 73 | 54 | – | – | – | – | – | – | 1.2 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
@@ -246,7 +246,7 @@ All sizes and variants (clean, aliased, blurred, noisy, scanned) of those inputs
|
||||
| Technical Drawing | curved shapes | 0.03 | 1.48 | 0.024 | 0.16 | 0.16 | 1.2 | 20 | 2.12 | 21 | 21 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | polygons | 0.06 | 2.36 | 0.040 | 0.47 | 1.26 | 1.7 | 14 | 1.23 | 94 | 8 | 95 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | thin lines | 0.50 | 5.46 | 0.109 | 0.50 | 55.93 | 15.3 | 62 | 3.12 | 86 | 14 | – | – | – | – | – | – | 3.3 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | askew shapes | 0.88 | 20.92 | 0.611 | 4.21 | 4.19 | 4.3 | 26 | 1.08 | 98 | 98 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | askew shapes | 0.48 | 11.07 | 0.233 | 1.46 | 2.11 | 4.3 | 26 | 1.08 | 98 | 70 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | plain shapes | 0.08 | 1.63 | 0.035 | 0.33 | 0.33 | 6.0 | 55 | 1.82 | 60 | 52 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | 0 | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | flat colour art | 19.90 | 17.76 | 0.157 | 0.30 | 179.51 | 1.0 | 18 | 1.12 | 23 | 23 | 0 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | gradient, photo | 38.94 | 38.51 | – | – | – | 20.0 | 659 | – | 73 | 51 | – | – | – | – | – | – | 11.7 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
@@ -404,10 +404,10 @@ All sizes and variants (clean, aliased, blurred, noisy, scanned) of those inputs
|
||||
| Line Art | blurred | 1.56 | 16.06 | 0.061 | 0.99 | 105.72 | 3.0 | 53 | 2.65 | 22 | 0 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Line Art | noisy | 1.03 | 4.10 | 0.035 | 0.44 | 56.07 | 8.0 | 50 | 2.50 | 43 | 0 | – | – | – | – | – | – | 1.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Line Art | scanned | 13.54 | 19.55 | 0.062 | 0.92 | 105.68 | 3.0 | 51 | 2.55 | 8 | 0 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | clean | 0.31 | 6.70 | 0.168 | 4.21 | 55.93 | 4.9 | 26 | 1.58 | 93 | 27 | 95 | 0.0 | – | – | – | – | 0.7 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | aliased | 0.56 | 20.39 | 0.275 | 2.42 | 55.44 | 6.2 | 31 | 1.85 | 96 | 28 | 94 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | blurred | 1.12 | 17.47 | 0.168 | 2.41 | 105.64 | 2.4 | 49 | 3.84 | 47 | 19 | 30 | 2.7 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | noisy | 1.68 | 6.90 | 0.160 | 2.13 | 31.79 | 7.0 | 34 | 1.98 | 93 | 26 | 95 | 0.0 | – | – | – | – | 1.4 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | clean | 0.23 | 4.73 | 0.092 | 1.46 | 55.93 | 4.9 | 26 | 1.58 | 93 | 21 | 95 | 0.0 | – | – | – | – | 0.7 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | aliased | 0.50 | 19.60 | 0.238 | 1.37 | 55.44 | 6.2 | 31 | 1.85 | 96 | 23 | 94 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | blurred | 1.06 | 16.51 | 0.130 | 1.16 | 105.64 | 2.4 | 49 | 3.84 | 47 | 13 | 30 | 2.7 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | noisy | 1.59 | 5.74 | 0.103 | 1.17 | 31.79 | 7.0 | 34 | 1.98 | 93 | 21 | 95 | 0.0 | – | – | – | – | 1.4 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Technical Drawing | scanned | 14.69 | 20.15 | 0.136 | 1.17 | 105.57 | 2.4 | 73 | 5.42 | 49 | 12 | 39 | 3.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | – | 0.0 | – | – | – |
|
||||
| Outline | clean | 0.52 | 3.89 | 0.038 | 2.94 | 105.73 | 3.3 | 7 | 0.35 | 100 | 7 | – | – | – | – | – | – | 0.0 | 0.0 | 0.056 | 0.02 | 2.1 | – | – | 0.0 | – | – | – |
|
||||
| Outline | aliased | 0.62 | 13.33 | 0.169 | 0.88 | 55.55 | 11.0 | 51 | 2.55 | 64 | 9 | – | – | – | – | – | – | 0.0 | 0.0 | 0.154 | 0.01 | 4.6 | – | – | 0.0 | – | – | – |
|
||||
@@ -505,155 +505,156 @@ One trace at a time on this machine (all cores); memory is the peak allocated ab
|
||||
|
||||
| preset | image | megapixels | result |
|
||||
|:--|:--|--:|:--|
|
||||
| Default | photo-like | 1 | 0.05 s, 29 paths, 6278 anchors, 12 MB |
|
||||
| Default | four-colour badge | 1 | 0.03 s, 3 paths, 64 anchors, 12 MB |
|
||||
| Auto Color | photo-like | 1 | 0.18 s, 732 paths, 45265 anchors, 25 MB |
|
||||
| Auto Color | four-colour badge | 1 | 0.05 s, 5 paths, 84 anchors, 12 MB |
|
||||
| High Fidelity Photo | photo-like | 1 | 0.23 s, 968 paths, 117041 anchors, 170 MB |
|
||||
| High Fidelity Photo | four-colour badge | 1 | 0.07 s, 6 paths, 172 anchors, 23 MB |
|
||||
| Low Fidelity Photo | photo-like | 1 | 0.21 s, 129 paths, 23014 anchors, 103 MB |
|
||||
| Low Fidelity Photo | four-colour badge | 1 | 0.06 s, 6 paths, 136 anchors, 23 MB |
|
||||
| 3 Colors | photo-like | 1 | 0.09 s, 9 paths, 3341 anchors, 18 MB |
|
||||
| 3 Colors | four-colour badge | 1 | 0.05 s, 4 paths, 88 anchors, 13 MB |
|
||||
| Default | photo-like | 1 | 0.04 s, 29 paths, 6278 anchors, 12 MB |
|
||||
| Default | four-colour badge | 1 | 0.02 s, 3 paths, 64 anchors, 12 MB |
|
||||
| Auto Color | photo-like | 1 | 0.16 s, 732 paths, 45265 anchors, 23 MB |
|
||||
| Auto Color | four-colour badge | 1 | 0.06 s, 5 paths, 84 anchors, 12 MB |
|
||||
| High Fidelity Photo | photo-like | 1 | 0.25 s, 968 paths, 117041 anchors, 164 MB |
|
||||
| High Fidelity Photo | four-colour badge | 1 | 0.06 s, 6 paths, 172 anchors, 23 MB |
|
||||
| Low Fidelity Photo | photo-like | 1 | 0.15 s, 129 paths, 23014 anchors, 104 MB |
|
||||
| Low Fidelity Photo | four-colour badge | 1 | 0.07 s, 6 paths, 136 anchors, 23 MB |
|
||||
| 3 Colors | photo-like | 1 | 0.11 s, 9 paths, 3341 anchors, 18 MB |
|
||||
| 3 Colors | four-colour badge | 1 | 0.07 s, 4 paths, 88 anchors, 13 MB |
|
||||
| 6 Colors | photo-like | 1 | 0.10 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| 6 Colors | four-colour badge | 1 | 0.06 s, 5 paths, 84 anchors, 12 MB |
|
||||
| 16 Colors | photo-like | 1 | 0.12 s, 178 paths, 20470 anchors, 31 MB |
|
||||
| 16 Colors | four-colour badge | 1 | 0.06 s, 5 paths, 88 anchors, 12 MB |
|
||||
| Shades of Gray | photo-like | 1 | 0.10 s, 81 paths, 6698 anchors, 40 MB |
|
||||
| Shades of Gray | four-colour badge | 1 | 0.08 s, 10 paths, 725 anchors, 35 MB |
|
||||
| 6 Colors | four-colour badge | 1 | 0.07 s, 5 paths, 84 anchors, 12 MB |
|
||||
| 16 Colors | photo-like | 1 | 0.18 s, 178 paths, 20470 anchors, 32 MB |
|
||||
| 16 Colors | four-colour badge | 1 | 0.07 s, 5 paths, 88 anchors, 12 MB |
|
||||
| Shades of Gray | photo-like | 1 | 0.11 s, 81 paths, 6698 anchors, 40 MB |
|
||||
| Shades of Gray | four-colour badge | 1 | 0.08 s, 10 paths, 725 anchors, 34 MB |
|
||||
| Black and White Logo | photo-like | 1 | 0.05 s, 71 paths, 4933 anchors, 8 MB |
|
||||
| Black and White Logo | four-colour badge | 1 | 0.02 s, 1 paths, 47 anchors, 6 MB |
|
||||
| Sketched Art | photo-like | 1 | 0.04 s, 5 paths, 2873 anchors, 8 MB |
|
||||
| Sketched Art | photo-like | 1 | 0.05 s, 5 paths, 2873 anchors, 8 MB |
|
||||
| Sketched Art | four-colour badge | 1 | 0.02 s, 2 paths, 114 anchors, 6 MB |
|
||||
| Silhouettes | photo-like | 1 | 0.04 s, 2 paths, 556 anchors, 9 MB |
|
||||
| Silhouettes | four-colour badge | 1 | 0.02 s, 1 paths, 24 anchors, 6 MB |
|
||||
| Line Art | photo-like | 1 | 0.04 s, 54 paths, 4495 anchors, 8 MB |
|
||||
| Line Art | photo-like | 1 | 0.05 s, 54 paths, 4495 anchors, 8 MB |
|
||||
| Line Art | four-colour badge | 1 | 0.02 s, 1 paths, 38 anchors, 6 MB |
|
||||
| Technical Drawing | photo-like | 1 | 0.05 s, 221 paths, 6258 anchors, 8 MB |
|
||||
| Technical Drawing | four-colour badge | 1 | 0.02 s, 1 paths, 47 anchors, 6 MB |
|
||||
| Outline | photo-like | 1 | 0.05 s, 45 paths, 4110 anchors, 16 MB |
|
||||
| Outline | photo-like | 1 | 0.06 s, 45 paths, 4110 anchors, 16 MB |
|
||||
| Outline | four-colour badge | 1 | 0.03 s, 1 paths, 38 anchors, 11 MB |
|
||||
| Flat Logo | photo-like | 1 | refused after 0.10 s (not flat art), 40 MB |
|
||||
| Flat Logo | four-colour badge | 1 | 0.69 s, 5 paths, 78 anchors, 146 MB |
|
||||
| Default with Shapes | photo-like | 1 | 0.04 s, 29 paths, 6278 anchors, 12 MB |
|
||||
| Default with Shapes | four-colour badge | 1 | 0.03 s, 3 paths, 63 anchors, 12 MB |
|
||||
| 6 Colors with Shapes | photo-like | 1 | 0.24 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| 6 Colors with Shapes | four-colour badge | 1 | 0.06 s, 5 paths, 84 anchors, 12 MB |
|
||||
| 6 Colors with Auto Grouping | photo-like | 1 | 0.10 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| 6 Colors with Auto Grouping | four-colour badge | 1 | 0.05 s, 5 paths, 84 anchors, 12 MB |
|
||||
| Shades of Gray with Auto Grouping | photo-like | 1 | 0.10 s, 81 paths, 6698 anchors, 44 MB |
|
||||
| Flat Logo | photo-like | 1 | refused after 0.11 s (not flat art), 40 MB |
|
||||
| Flat Logo | four-colour badge | 1 | 0.80 s, 5 paths, 78 anchors, 146 MB |
|
||||
| Default with Shapes | photo-like | 1 | 0.11 s, 29 paths, 6278 anchors, 12 MB |
|
||||
| Default with Shapes | four-colour badge | 1 | 0.04 s, 3 paths, 63 anchors, 12 MB |
|
||||
| 6 Colors with Shapes | photo-like | 1 | 0.09 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| 6 Colors with Shapes | four-colour badge | 1 | 0.05 s, 5 paths, 84 anchors, 12 MB |
|
||||
| 6 Colors with Auto Grouping | photo-like | 1 | 0.08 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| 6 Colors with Auto Grouping | four-colour badge | 1 | 0.05 s, 5 paths, 84 anchors, 13 MB |
|
||||
| Shades of Gray with Auto Grouping | photo-like | 1 | 0.14 s, 81 paths, 6698 anchors, 40 MB |
|
||||
| Shades of Gray with Auto Grouping | four-colour badge | 1 | 0.07 s, 10 paths, 725 anchors, 35 MB |
|
||||
| High Fidelity Photo with Auto Grouping | photo-like | 1 | 0.28 s, 968 paths, 117041 anchors, 167 MB |
|
||||
| High Fidelity Photo with Auto Grouping | four-colour badge | 1 | 0.09 s, 6 paths, 172 anchors, 23 MB |
|
||||
| 6 Colors, no Gradients | photo-like | 1 | 0.10 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| High Fidelity Photo with Auto Grouping | photo-like | 1 | 0.25 s, 968 paths, 117041 anchors, 169 MB |
|
||||
| High Fidelity Photo with Auto Grouping | four-colour badge | 1 | 0.08 s, 6 paths, 172 anchors, 23 MB |
|
||||
| 6 Colors, no Gradients | photo-like | 1 | 0.49 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| 6 Colors, no Gradients | four-colour badge | 1 | 0.05 s, 5 paths, 84 anchors, 12 MB |
|
||||
| 6 Colors with Gradients | photo-like | 1 | 0.14 s, 78 paths, 9076 anchors, 32 MB |
|
||||
| 6 Colors with Gradients | four-colour badge | 1 | 0.07 s, 5 paths, 84 anchors, 12 MB |
|
||||
| High Fidelity Photo, no Gradients | photo-like | 1 | 0.24 s, 968 paths, 117041 anchors, 169 MB |
|
||||
| 6 Colors with Gradients | photo-like | 1 | 0.18 s, 78 paths, 9076 anchors, 32 MB |
|
||||
| 6 Colors with Gradients | four-colour badge | 1 | 0.11 s, 5 paths, 84 anchors, 12 MB |
|
||||
| High Fidelity Photo, no Gradients | photo-like | 1 | 0.23 s, 968 paths, 117041 anchors, 163 MB |
|
||||
| High Fidelity Photo, no Gradients | four-colour badge | 1 | 0.07 s, 6 paths, 172 anchors, 23 MB |
|
||||
| High Fidelity Photo with Gradients | photo-like | 1 | 0.43 s, 968 paths, 117041 anchors, 166 MB |
|
||||
| High Fidelity Photo with Gradients | four-colour badge | 1 | 0.09 s, 6 paths, 172 anchors, 23 MB |
|
||||
| Default | photo-like | 4 | 0.25 s, 48 paths, 14438 anchors, 49 MB |
|
||||
| Default | four-colour badge | 4 | 0.16 s, 3 paths, 72 anchors, 48 MB |
|
||||
| Auto Color | photo-like | 4 | 0.72 s, 1469 paths, 149389 anchors, 113 MB |
|
||||
| Auto Color | four-colour badge | 4 | 0.30 s, 5 paths, 92 anchors, 46 MB |
|
||||
| High Fidelity Photo | photo-like | 4 | 1.05 s, 4479 paths, 337884 anchors, 638 MB |
|
||||
| High Fidelity Photo | four-colour badge | 4 | 0.35 s, 6 paths, 200 anchors, 96 MB |
|
||||
| Low Fidelity Photo | photo-like | 4 | 0.54 s, 299 paths, 61601 anchors, 392 MB |
|
||||
| Low Fidelity Photo | four-colour badge | 4 | 0.33 s, 6 paths, 152 anchors, 97 MB |
|
||||
| 3 Colors | photo-like | 4 | 0.38 s, 20 paths, 8473 anchors, 72 MB |
|
||||
| 3 Colors | four-colour badge | 4 | 0.28 s, 4 paths, 122 anchors, 53 MB |
|
||||
| 6 Colors | photo-like | 4 | 0.42 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors | four-colour badge | 4 | 0.27 s, 5 paths, 92 anchors, 46 MB |
|
||||
| 16 Colors | photo-like | 4 | 0.36 s, 353 paths, 52770 anchors, 129 MB |
|
||||
| 16 Colors | four-colour badge | 4 | 0.24 s, 5 paths, 96 anchors, 47 MB |
|
||||
| Shades of Gray | photo-like | 4 | 0.34 s, 137 paths, 18823 anchors, 158 MB |
|
||||
| Shades of Gray | four-colour badge | 4 | 0.27 s, 10 paths, 1256 anchors, 135 MB |
|
||||
| Black and White Logo | photo-like | 4 | 0.21 s, 208 paths, 13546 anchors, 32 MB |
|
||||
| Black and White Logo | four-colour badge | 4 | 0.09 s, 1 paths, 57 anchors, 24 MB |
|
||||
| Sketched Art | photo-like | 4 | 0.26 s, 9 paths, 5387 anchors, 32 MB |
|
||||
| Sketched Art | four-colour badge | 4 | 0.13 s, 2 paths, 196 anchors, 24 MB |
|
||||
| Silhouettes | photo-like | 4 | 0.32 s, 2 paths, 1465 anchors, 36 MB |
|
||||
| Silhouettes | four-colour badge | 4 | 0.13 s, 1 paths, 28 anchors, 24 MB |
|
||||
| Line Art | photo-like | 4 | 0.30 s, 127 paths, 11330 anchors, 32 MB |
|
||||
| Line Art | four-colour badge | 4 | 0.12 s, 1 paths, 42 anchors, 24 MB |
|
||||
| Technical Drawing | photo-like | 4 | 0.31 s, 931 paths, 20628 anchors, 32 MB |
|
||||
| Technical Drawing | four-colour badge | 4 | 0.13 s, 1 paths, 57 anchors, 24 MB |
|
||||
| Outline | photo-like | 4 | 0.39 s, 113 paths, 10815 anchors, 64 MB |
|
||||
| Outline | four-colour badge | 4 | 0.20 s, 1 paths, 42 anchors, 44 MB |
|
||||
| Flat Logo | photo-like | 4 | refused after 0.19 s (not flat art), 40 MB |
|
||||
| Flat Logo | four-colour badge | 4 | 1.31 s, 5 paths, 69 anchors, 146 MB |
|
||||
| Default with Shapes | photo-like | 4 | 0.30 s, 48 paths, 14438 anchors, 49 MB |
|
||||
| Default with Shapes | four-colour badge | 4 | 0.15 s, 3 paths, 72 anchors, 48 MB |
|
||||
| 6 Colors with Shapes | photo-like | 4 | 0.45 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors with Shapes | four-colour badge | 4 | 0.31 s, 5 paths, 92 anchors, 46 MB |
|
||||
| 6 Colors with Auto Grouping | photo-like | 4 | 0.44 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors with Auto Grouping | four-colour badge | 4 | 0.30 s, 5 paths, 92 anchors, 47 MB |
|
||||
| Shades of Gray with Auto Grouping | photo-like | 4 | 0.47 s, 137 paths, 18823 anchors, 162 MB |
|
||||
| Shades of Gray with Auto Grouping | four-colour badge | 4 | 0.39 s, 10 paths, 1256 anchors, 137 MB |
|
||||
| High Fidelity Photo with Auto Grouping | photo-like | 4 | 1.34 s, 4479 paths, 337884 anchors, 615 MB |
|
||||
| High Fidelity Photo with Gradients | photo-like | 1 | 0.32 s, 968 paths, 117041 anchors, 168 MB |
|
||||
| High Fidelity Photo with Gradients | four-colour badge | 1 | 0.10 s, 6 paths, 172 anchors, 23 MB |
|
||||
| Default | photo-like | 4 | 0.21 s, 48 paths, 14438 anchors, 49 MB |
|
||||
| Default | four-colour badge | 4 | 0.11 s, 3 paths, 72 anchors, 48 MB |
|
||||
| Auto Color | photo-like | 4 | 0.44 s, 1469 paths, 149389 anchors, 115 MB |
|
||||
| Auto Color | four-colour badge | 4 | 0.21 s, 5 paths, 92 anchors, 46 MB |
|
||||
| High Fidelity Photo | photo-like | 4 | 0.78 s, 4479 paths, 337884 anchors, 623 MB |
|
||||
| High Fidelity Photo | four-colour badge | 4 | 0.29 s, 6 paths, 200 anchors, 97 MB |
|
||||
| Low Fidelity Photo | photo-like | 4 | 0.41 s, 299 paths, 61601 anchors, 406 MB |
|
||||
| Low Fidelity Photo | four-colour badge | 4 | 0.24 s, 6 paths, 152 anchors, 97 MB |
|
||||
| 3 Colors | photo-like | 4 | 0.31 s, 20 paths, 8473 anchors, 73 MB |
|
||||
| 3 Colors | four-colour badge | 4 | 0.24 s, 4 paths, 122 anchors, 53 MB |
|
||||
| 6 Colors | photo-like | 4 | 0.34 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors | four-colour badge | 4 | 0.22 s, 5 paths, 92 anchors, 46 MB |
|
||||
| 16 Colors | photo-like | 4 | 0.37 s, 353 paths, 52770 anchors, 130 MB |
|
||||
| 16 Colors | four-colour badge | 4 | 0.22 s, 5 paths, 96 anchors, 46 MB |
|
||||
| Shades of Gray | photo-like | 4 | 0.50 s, 137 paths, 18823 anchors, 162 MB |
|
||||
| Shades of Gray | four-colour badge | 4 | 0.33 s, 10 paths, 1256 anchors, 137 MB |
|
||||
| Black and White Logo | photo-like | 4 | 0.25 s, 208 paths, 13546 anchors, 32 MB |
|
||||
| Black and White Logo | four-colour badge | 4 | 0.11 s, 1 paths, 57 anchors, 24 MB |
|
||||
| Sketched Art | photo-like | 4 | 0.24 s, 9 paths, 5387 anchors, 32 MB |
|
||||
| Sketched Art | four-colour badge | 4 | 0.11 s, 2 paths, 196 anchors, 24 MB |
|
||||
| Silhouettes | photo-like | 4 | 0.24 s, 2 paths, 1465 anchors, 36 MB |
|
||||
| Silhouettes | four-colour badge | 4 | 0.09 s, 1 paths, 28 anchors, 24 MB |
|
||||
| Line Art | photo-like | 4 | 0.22 s, 127 paths, 11330 anchors, 32 MB |
|
||||
| Line Art | four-colour badge | 4 | 0.09 s, 1 paths, 42 anchors, 24 MB |
|
||||
| Technical Drawing | photo-like | 4 | 0.21 s, 931 paths, 20628 anchors, 32 MB |
|
||||
| Technical Drawing | four-colour badge | 4 | 0.08 s, 1 paths, 57 anchors, 24 MB |
|
||||
| Outline | photo-like | 4 | 0.25 s, 113 paths, 10815 anchors, 64 MB |
|
||||
| Outline | four-colour badge | 4 | 0.13 s, 1 paths, 42 anchors, 44 MB |
|
||||
| Flat Logo | photo-like | 4 | refused after 0.10 s (not flat art), 40 MB |
|
||||
| Flat Logo | four-colour badge | 4 | 0.67 s, 5 paths, 69 anchors, 146 MB |
|
||||
| Default with Shapes | photo-like | 4 | 0.20 s, 48 paths, 14438 anchors, 49 MB |
|
||||
| Default with Shapes | four-colour badge | 4 | 0.10 s, 3 paths, 72 anchors, 48 MB |
|
||||
| 6 Colors with Shapes | photo-like | 4 | 0.31 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors with Shapes | four-colour badge | 4 | 0.21 s, 5 paths, 92 anchors, 46 MB |
|
||||
| 6 Colors with Auto Grouping | photo-like | 4 | 0.40 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors with Auto Grouping | four-colour badge | 4 | 0.22 s, 5 paths, 92 anchors, 46 MB |
|
||||
| Shades of Gray with Auto Grouping | photo-like | 4 | 0.38 s, 137 paths, 18823 anchors, 165 MB |
|
||||
| Shades of Gray with Auto Grouping | four-colour badge | 4 | 0.29 s, 10 paths, 1256 anchors, 138 MB |
|
||||
| High Fidelity Photo with Auto Grouping | photo-like | 4 | 1.06 s, 4479 paths, 337884 anchors, 622 MB |
|
||||
| High Fidelity Photo with Auto Grouping | four-colour badge | 4 | 0.28 s, 6 paths, 200 anchors, 97 MB |
|
||||
| 6 Colors, no Gradients | photo-like | 4 | 0.35 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors, no Gradients | photo-like | 4 | 0.36 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors, no Gradients | four-colour badge | 4 | 0.26 s, 5 paths, 92 anchors, 46 MB |
|
||||
| 6 Colors with Gradients | photo-like | 4 | 0.66 s, 115 paths, 23295 anchors, 128 MB |
|
||||
| 6 Colors with Gradients | four-colour badge | 4 | 0.39 s, 5 paths, 92 anchors, 46 MB |
|
||||
| High Fidelity Photo, no Gradients | photo-like | 4 | 0.79 s, 4479 paths, 337884 anchors, 617 MB |
|
||||
| High Fidelity Photo, no Gradients | four-colour badge | 4 | 0.27 s, 6 paths, 200 anchors, 97 MB |
|
||||
| High Fidelity Photo with Gradients | photo-like | 4 | 1.14 s, 4479 paths, 337884 anchors, 636 MB |
|
||||
| High Fidelity Photo with Gradients | four-colour badge | 4 | 0.40 s, 6 paths, 200 anchors, 97 MB |
|
||||
| Default | photo-like | 16 | 1.15 s, 127 paths, 34034 anchors, 199 MB |
|
||||
| Default | four-colour badge | 16 | 0.64 s, 3 paths, 88 anchors, 184 MB |
|
||||
| Auto Color | photo-like | 16 | 2.06 s, 5390 paths, 454670 anchors, 280 MB |
|
||||
| Auto Color | four-colour badge | 16 | 1.31 s, 5 paths, 112 anchors, 185 MB |
|
||||
| High Fidelity Photo | photo-like | 16 | 5.28 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo | four-colour badge | 16 | 1.36 s, 6 paths, 264 anchors, 257 MB |
|
||||
| Low Fidelity Photo | photo-like | 16 | 3.01 s, 2036 paths, 182778 anchors, 754 MB |
|
||||
| Low Fidelity Photo | four-colour badge | 16 | 1.55 s, 6 paths, 180 anchors, 263 MB |
|
||||
| 3 Colors | photo-like | 16 | 1.82 s, 76 paths, 23024 anchors, 290 MB |
|
||||
| 3 Colors | four-colour badge | 16 | 1.30 s, 4 paths, 190 anchors, 205 MB |
|
||||
| 6 Colors | photo-like | 16 | 1.76 s, 470 paths, 60831 anchors, 298 MB |
|
||||
| 6 Colors | four-colour badge | 16 | 1.27 s, 5 paths, 112 anchors, 185 MB |
|
||||
| 16 Colors | photo-like | 16 | 1.93 s, 1973 paths, 145642 anchors, 303 MB |
|
||||
| 16 Colors | four-colour badge | 16 | 1.26 s, 5 paths, 114 anchors, 185 MB |
|
||||
| Shades of Gray | photo-like | 16 | 2.01 s, 686 paths, 53654 anchors, 407 MB |
|
||||
| Shades of Gray | four-colour badge | 16 | 1.76 s, 9 paths, 2208 anchors, 270 MB |
|
||||
| Black and White Logo | photo-like | 16 | 1.45 s, 1128 paths, 45273 anchors, 129 MB |
|
||||
| Black and White Logo | four-colour badge | 16 | 0.88 s, 1 paths, 69 anchors, 88 MB |
|
||||
| Sketched Art | photo-like | 16 | 1.60 s, 15 paths, 11486 anchors, 128 MB |
|
||||
| Sketched Art | four-colour badge | 16 | 0.75 s, 2 paths, 361 anchors, 88 MB |
|
||||
| Silhouettes | photo-like | 16 | 2.21 s, 3 paths, 3350 anchors, 144 MB |
|
||||
| Silhouettes | four-colour badge | 16 | 0.51 s, 1 paths, 32 anchors, 95 MB |
|
||||
| Line Art | photo-like | 16 | 1.21 s, 713 paths, 37370 anchors, 129 MB |
|
||||
| Line Art | four-colour badge | 16 | 0.44 s, 1 paths, 49 anchors, 88 MB |
|
||||
| Technical Drawing | photo-like | 16 | 1.52 s, 4577 paths, 76559 anchors, 130 MB |
|
||||
| Technical Drawing | four-colour badge | 16 | 0.54 s, 1 paths, 69 anchors, 88 MB |
|
||||
| 6 Colors with Gradients | photo-like | 4 | 0.73 s, 115 paths, 23295 anchors, 128 MB |
|
||||
| 6 Colors with Gradients | four-colour badge | 4 | 0.40 s, 5 paths, 92 anchors, 46 MB |
|
||||
| High Fidelity Photo, no Gradients | photo-like | 4 | 0.98 s, 4479 paths, 337884 anchors, 626 MB |
|
||||
| High Fidelity Photo, no Gradients | four-colour badge | 4 | 0.29 s, 6 paths, 200 anchors, 97 MB |
|
||||
| High Fidelity Photo with Gradients | photo-like | 4 | 1.22 s, 4479 paths, 337884 anchors, 629 MB |
|
||||
| High Fidelity Photo with Gradients | four-colour badge | 4 | 0.43 s, 6 paths, 200 anchors, 97 MB |
|
||||
| Default | photo-like | 16 | 1.24 s, 127 paths, 34034 anchors, 199 MB |
|
||||
| Default | four-colour badge | 16 | 1.54 s, 3 paths, 88 anchors, 184 MB |
|
||||
| Auto Color | photo-like | 16 | 2.44 s, 5390 paths, 454670 anchors, 272 MB |
|
||||
| Auto Color | four-colour badge | 16 | 1.08 s, 5 paths, 112 anchors, 184 MB |
|
||||
| High Fidelity Photo | photo-like | 16 | 4.70 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo | four-colour badge | 16 | 1.27 s, 6 paths, 264 anchors, 257 MB |
|
||||
| Low Fidelity Photo | photo-like | 16 | 2.21 s, 2036 paths, 182778 anchors, 754 MB |
|
||||
| Low Fidelity Photo | four-colour badge | 16 | 1.25 s, 6 paths, 180 anchors, 257 MB |
|
||||
| 3 Colors | photo-like | 16 | 1.38 s, 76 paths, 23024 anchors, 290 MB |
|
||||
| 3 Colors | four-colour badge | 16 | 0.93 s, 4 paths, 190 anchors, 205 MB |
|
||||
| 6 Colors | photo-like | 16 | 1.38 s, 470 paths, 60831 anchors, 298 MB |
|
||||
| 6 Colors | four-colour badge | 16 | 0.91 s, 5 paths, 112 anchors, 184 MB |
|
||||
| 16 Colors | photo-like | 16 | 1.40 s, 1973 paths, 145642 anchors, 304 MB |
|
||||
| 16 Colors | four-colour badge | 16 | 0.99 s, 5 paths, 114 anchors, 184 MB |
|
||||
| Shades of Gray | photo-like | 16 | 1.94 s, 686 paths, 53654 anchors, 407 MB |
|
||||
| Shades of Gray | four-colour badge | 16 | 1.59 s, 9 paths, 2208 anchors, 270 MB |
|
||||
| Black and White Logo | photo-like | 16 | 1.32 s, 1128 paths, 45273 anchors, 129 MB |
|
||||
| Black and White Logo | four-colour badge | 16 | 0.54 s, 1 paths, 69 anchors, 88 MB |
|
||||
| Sketched Art | photo-like | 16 | 1.30 s, 15 paths, 11486 anchors, 128 MB |
|
||||
| Sketched Art | four-colour badge | 16 | 0.56 s, 2 paths, 361 anchors, 88 MB |
|
||||
| Silhouettes | photo-like | 16 | 1.94 s, 3 paths, 3350 anchors, 144 MB |
|
||||
| Silhouettes | four-colour badge | 16 | 0.61 s, 1 paths, 32 anchors, 95 MB |
|
||||
| Line Art | photo-like | 16 | 1.55 s, 713 paths, 37370 anchors, 129 MB |
|
||||
| Line Art | four-colour badge | 16 | 0.60 s, 1 paths, 49 anchors, 88 MB |
|
||||
| Technical Drawing | photo-like | 16 | 1.78 s, 4577 paths, 76559 anchors, 130 MB |
|
||||
| Technical Drawing | four-colour badge | 16 | 0.59 s, 1 paths, 69 anchors, 88 MB |
|
||||
| Outline | photo-like | 16 | 1.83 s, 632 paths, 34884 anchors, 256 MB |
|
||||
| Outline | four-colour badge | 16 | 0.91 s, 1 paths, 49 anchors, 176 MB |
|
||||
| Flat Logo | photo-like | 16 | refused after 0.19 s (not flat art), 40 MB |
|
||||
| Flat Logo | four-colour badge | 16 | 1.04 s, 5 paths, 67 anchors, 146 MB |
|
||||
| Default with Shapes | photo-like | 16 | 1.38 s, 127 paths, 34034 anchors, 199 MB |
|
||||
| Default with Shapes | four-colour badge | 16 | 0.64 s, 3 paths, 88 anchors, 184 MB |
|
||||
| 6 Colors with Shapes | photo-like | 16 | 1.66 s, 470 paths, 60831 anchors, 298 MB |
|
||||
| 6 Colors with Shapes | four-colour badge | 16 | 1.19 s, 5 paths, 112 anchors, 185 MB |
|
||||
| 6 Colors with Auto Grouping | photo-like | 16 | 2.10 s, 470 paths, 60831 anchors, 298 MB |
|
||||
| 6 Colors with Auto Grouping | four-colour badge | 16 | 1.08 s, 5 paths, 112 anchors, 184 MB |
|
||||
| Shades of Gray with Auto Grouping | photo-like | 16 | 3.20 s, 686 paths, 53654 anchors, 407 MB |
|
||||
| Shades of Gray with Auto Grouping | four-colour badge | 16 | 2.32 s, 9 paths, 2208 anchors, 270 MB |
|
||||
| High Fidelity Photo with Auto Grouping | photo-like | 16 | 10.42 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo with Auto Grouping | four-colour badge | 16 | 1.11 s, 6 paths, 264 anchors, 257 MB |
|
||||
| 6 Colors, no Gradients | photo-like | 16 | 1.21 s, 470 paths, 60831 anchors, 298 MB |
|
||||
| 6 Colors, no Gradients | four-colour badge | 16 | 0.86 s, 5 paths, 112 anchors, 184 MB |
|
||||
| 6 Colors with Gradients | photo-like | 16 | 3.03 s, 470 paths, 60831 anchors, 512 MB |
|
||||
| 6 Colors with Gradients | four-colour badge | 16 | 1.28 s, 5 paths, 112 anchors, 185 MB |
|
||||
| High Fidelity Photo, no Gradients | photo-like | 16 | 4.74 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo, no Gradients | four-colour badge | 16 | 1.12 s, 6 paths, 264 anchors, 257 MB |
|
||||
| High Fidelity Photo with Gradients | photo-like | 16 | 7.70 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo with Gradients | four-colour badge | 16 | 2.65 s, 6 paths, 264 anchors, 257 MB |
|
||||
| Flat Logo | photo-like | 16 | refused after 0.20 s (not flat art), 40 MB |
|
||||
| Flat Logo | four-colour badge | 16 | 1.17 s, 5 paths, 67 anchors, 146 MB |
|
||||
| Default with Shapes | photo-like | 16 | 1.89 s, 127 paths, 34034 anchors, 199 MB |
|
||||
| Default with Shapes | four-colour badge | 16 | 1.81 s, 3 paths, 88 anchors, 184 MB |
|
||||
| 6 Colors with Shapes | photo-like | 16 | 3.30 s, 470 paths, 60831 anchors, 298 MB |
|
||||
| 6 Colors with Shapes | four-colour badge | 16 | 1.73 s, 5 paths, 112 anchors, 185 MB |
|
||||
| 6 Colors with Auto Grouping | photo-like | 16 | 2.14 s, 470 paths, 60831 anchors, 299 MB |
|
||||
| 6 Colors with Auto Grouping | four-colour badge | 16 | 1.50 s, 5 paths, 112 anchors, 185 MB |
|
||||
| Shades of Gray with Auto Grouping | photo-like | 16 | 2.71 s, 686 paths, 53654 anchors, 407 MB |
|
||||
| Shades of Gray with Auto Grouping | four-colour badge | 16 | 1.85 s, 9 paths, 2208 anchors, 270 MB |
|
||||
| High Fidelity Photo with Auto Grouping | photo-like | 16 | 10.82 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo with Auto Grouping | four-colour badge | 16 | 1.69 s, 6 paths, 264 anchors, 257 MB |
|
||||
| 6 Colors, no Gradients | photo-like | 16 | 1.81 s, 470 paths, 60831 anchors, 299 MB |
|
||||
| 6 Colors, no Gradients | four-colour badge | 16 | 1.26 s, 5 paths, 112 anchors, 185 MB |
|
||||
| 6 Colors with Gradients | photo-like | 16 | 4.20 s, 470 paths, 60831 anchors, 512 MB |
|
||||
| 6 Colors with Gradients | four-colour badge | 16 | 1.76 s, 5 paths, 112 anchors, 185 MB |
|
||||
| High Fidelity Photo, no Gradients | photo-like | 16 | 5.32 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo, no Gradients | four-colour badge | 16 | 1.66 s, 6 paths, 264 anchors, 257 MB |
|
||||
| High Fidelity Photo with Gradients | photo-like | 16 | 7.38 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo with Gradients | four-colour badge | 16 | 1.79 s, 6 paths, 264 anchors, 263 MB |
|
||||
|
||||
## Revision history
|
||||
|
||||
| date | change |
|
||||
|:--|:--|
|
||||
| 2026-10-13 | Snap Curves To Lines moves no end of a line by more than 1.6 px: long lines a degree or two off stay as the image has them (Technical Drawing: largest distance 4.21 px to 1.46) |
|
||||
| 2026-10-13 | Gradients (off unless asked for): areas that are a linear gradient are found before the palette is made and traced as one path with a gradient fill; linear-gradient inputs with known lines and stops, and four columns for them |
|
||||
| 2026-10-13 | Auto Grouping (off unless asked for): each path that has others inside it is grouped with them; a "grouped %" column, and three presets measured with the option on |
|
||||
| 2026-10-13 | Shapes (off unless asked for): filled outlines that are a circle, an ellipse, a rectangle or a regular polygon come out as that shape; corners whose pixel is mostly outside the shape are corners; plain-shape inputs and a "shapes %" column |
|
||||
|
||||
+14
-2
@@ -675,8 +675,20 @@ is refused before the command runs:
|
||||
→ invalid parameters for `file.new`: unknown parameter `widht`; did you mean `width`?
|
||||
```
|
||||
|
||||
Only the top level is checked: the keys of an object inside a value (`artboardLayout: {…}`, an effect's `params`) are
|
||||
the command's to read. A description with a `…` entry takes more keys than it lists: those of the command it names
|
||||
Only the top level is checked: the keys of an object inside a value (`artboardLayout: {…}`) are the command's to
|
||||
read. The one exception is an effect's `params`: `effect.apply` (and `appearance.addEffect`) and `effect.setParams`
|
||||
check them against the effect's own entry in `effect.list`, its `defaults` and the keys its `params` text names. A key
|
||||
the effect doesn't take, or a value of another type than its default (a string for a number, a number for a switch),
|
||||
is refused the same way:
|
||||
|
||||
```json
|
||||
{"id": "effect.apply", "params": {"effect": "blur.gaussian", "params": {"blurr": 3}}}
|
||||
→ invalid parameters for `effect.apply`: unknown parameter `blurr`; the command takes: radius
|
||||
```
|
||||
|
||||
A number outside the range the effect documents is still stored as given and clamped when the effect draws. A
|
||||
plug-in effect's parameters are resolved against its manifest instead, and `effect.setParams` as a step of
|
||||
`command.batch` isn't checked (the effect it edits may only exist once the steps before it ran). A description with a `…` entry takes more keys than it lists: those of the command it names
|
||||
(`…settings as file.new`), or any key when it names none, as `document.export` and `document.save` do for the options
|
||||
of the format they write (one object can carry several formats' options). A command that takes its own reply back
|
||||
(`document.setup`, `text.areaOptions`) says so the same way. The app's own journal isn't checked: an action or
|
||||
|
||||
Reference in new issue
Block a user