Expose simplify in the Python and Node bindings

Config(simplify=...) / property in Python; simplify option in Node,
with the tolerance documented in .pyi, index.d.ts, and both READMEs.
The Node README's option list also catches up with the clustering
rename and the binary/watershed options, and test.js drops the stale
colorMode key (silently ignored since the rename, so its bw assertion
was testing the default path) and asserts simplify shrinks output.
This commit is contained in:
Chris Tsang
2026-07-27 23:19:42 +01:00
parent 640270a608
commit f4fe428038
7 changed files with 41 additions and 10 deletions
+2 -1
View File
@@ -51,10 +51,11 @@ properties, plus the presets `Config.bw()`, `Config.poster()`, `Config.photo()`:
| `length_threshold` | `4.0` | px |
| `max_iterations` | `10` | |
| `splice_threshold` | `45` | degrees |
| `simplify` | `None` | curve simplification tolerance in px (try 12.5) |
| `path_precision` | `2` | output decimal places |
| `palette` | `None` | list of `#rrggbb` strings |
| `max_colors` | `None` | auto-quantize target |
| `optimize` | `1` | `0` off, `1` quantize+simplify, `2` + shorthands |
| `optimize` | `1` | `0` off, `1` quantize+cleanup, `2` + shorthands |
| `binary_threshold` | `128` | bw: fixed cutoff, foreground below it |
| `adaptive` | `False` | bw: BradleyRoth adaptive thresholding |
| `adaptive_window` | `0` | bw adaptive: window px (`0` = auto) |
+12 -2
View File
@@ -140,6 +140,7 @@ impl PyConfig {
length_threshold = 4.0,
max_iterations = 10,
splice_threshold = 45,
simplify = None,
path_precision = 2,
palette = None,
max_colors = None,
@@ -162,6 +163,7 @@ impl PyConfig {
length_threshold: f64,
max_iterations: usize,
splice_threshold: i32,
simplify: Option<f64>,
path_precision: u32,
palette: Option<Vec<String>>,
max_colors: Option<usize>,
@@ -188,8 +190,7 @@ impl PyConfig {
length_threshold,
max_iterations,
splice_threshold,
// Curve simplification is not surfaced in the Python API yet.
simplify: None,
simplify,
path_precision: Some(path_precision),
palette,
max_colors,
@@ -331,6 +332,15 @@ impl PyConfig {
self.inner.splice_threshold = v;
}
#[getter]
fn simplify(&self) -> Option<f64> {
self.inner.simplify
}
#[setter]
fn set_simplify(&mut self, v: Option<f64>) {
self.inner.simplify = v;
}
#[getter]
fn path_precision(&self) -> Option<u32> {
self.inner.path_precision
+2
View File
@@ -18,6 +18,7 @@ class Config:
length_threshold: float = 4.0,
max_iterations: int = 10,
splice_threshold: int = 45,
simplify: Optional[float] = None, # curve simplification tolerance in px (None = off)
path_precision: int = 2,
palette: Optional[list[str]] = None, # e.g. ["#112233", "#445566"]
max_colors: Optional[int] = None, # auto-quantize target
@@ -46,6 +47,7 @@ class Config:
length_threshold: float
max_iterations: int
splice_threshold: int
simplify: Optional[float]
path_precision: Optional[int]
palette: list[str]
max_colors: Optional[int]
+8 -5
View File
@@ -37,11 +37,14 @@ const svg2 = vtracer.convertPixels(rgba, width, height, { clustering: 'bw' });
### `Options` (all optional, camelCase)
`preset` (`"bw" | "poster" | "photo"`, applied first), `clustering`
(`"color" | "bw"`), `hierarchical` (`"stacked" | "cutout"` for the seam-free
mosaic), `mode` (`"pixel" | "polygon" | "spline"`), `filterSpeckle`,
`colorPrecision`, `layerDifference`, `cornerThreshold`, `lengthThreshold`,
`maxIterations`, `spliceThreshold`, `pathPrecision`, `palette` (list of
`#rrggbb`), `maxColors`, `optimize` (`0 | 1 | 2`).
(`"color-cluster" | "bw" | "watershed"`), `hierarchical` (`"stacked" | "cutout"`
for the seam-free mosaic), `mode` (`"pixel" | "polygon" | "spline"`),
`filterSpeckle`, `colorPrecision`, `layerDifference`, `cornerThreshold`,
`lengthThreshold`, `maxIterations`, `spliceThreshold`, `simplify` (curve
simplification tolerance in px, try 12.5), `pathPrecision`, `palette` (list of
`#rrggbb`), `maxColors`, `optimize` (`0 | 1 | 2`), `binaryThreshold` /
`adaptive` / `adaptiveWindow` / `adaptiveT` (binary mode), `watershedDetail`
(0..=255).
## Build from source
+2
View File
@@ -13,6 +13,8 @@ export interface Options {
lengthThreshold?: number;
maxIterations?: number;
spliceThreshold?: number;
/** Curve simplification tolerance in px (omit = off; try 1-2.5). */
simplify?: number;
pathPrecision?: number;
/** Fixed palette: `#rrggbb` strings. */
palette?: string[];
+5
View File
@@ -26,6 +26,8 @@ struct Options {
length_threshold: Option<f64>,
max_iterations: Option<usize>,
splice_threshold: Option<i32>,
/// Curve simplification tolerance in px (omit = off).
simplify: Option<f64>,
path_precision: Option<u32>,
palette: Option<Vec<String>>,
max_colors: Option<usize>,
@@ -104,6 +106,9 @@ fn config_from(options: JsValue) -> Result<Config, JsValue> {
if let Some(v) = opts.splice_threshold {
config.splice_threshold = v;
}
if let Some(v) = opts.simplify {
config.simplify = Some(v);
}
if let Some(v) = opts.path_precision {
config.path_precision = Some(v);
}
+10 -2
View File
@@ -12,11 +12,19 @@ let svg = vtracer.convertBuffer(data);
assert(svg.includes('<svg') && svg.includes('<path'), 'default convertBuffer');
console.log('convertBuffer default:', (svg.match(/<path/g) || []).length, 'paths');
// options: bw preset -> all black
svg = vtracer.convertBuffer(data, { colorMode: 'bw' });
// options: bw clustering -> all black
svg = vtracer.convertBuffer(data, { clustering: 'bw' });
assert(svg.includes('fill="#000000"'), 'bw produces black');
console.log('convertBuffer bw:', (svg.match(/<path/g) || []).length, 'paths');
// curve simplification shrinks the output
{
const plain = vtracer.convertBuffer(data);
const simplified = vtracer.convertBuffer(data, { simplify: 2 });
assert(simplified.length < plain.length, 'simplify shrinks output');
console.log('convertBuffer simplify:', plain.length, '->', simplified.length, 'bytes');
}
// options: mosaic + polygon + palette
svg = vtracer.convertBuffer(data, { hierarchical: 'cutout', mode: 'polygon', palette: ['#000000', '#ffffff'], optimize: 2 });
assert(svg.includes('<svg'), 'mosaic+palette');