mirror of
https://github.com/visioncortex/vtracer.git
synced 2026-10-17 16:01:46 -07:00
Add a curve-simplification stage (--simplify), paper.js style
A new pipeline slot between curve fitting and composition: CurvePasses rewrite each fitted contour, so mosaic mode transforms every shared boundary segment exactly once and the tessellation stays seam-free by construction. SimplifyCurves re-fits each smooth run between corners with the fewest cubics within the tolerance (Schneider's algorithm via a current flo_curves — visioncortex's copy is pinned to an old one and block-splits at 200 points), with tangents from the chain's own ends, corners kept in place, junction endpoints pinned bit-for-bit, and rings seamed at their sharpest junction. Off by default; polylines pass through untouched. Cityscape at tolerance 1: 229 -> 138 KB stacked, 103 -> 36 KB watershed cutout, with render diffs under golden noise. CurveFitter now returns Vec<FittedGeom> (promoted from mosaic::fit) so stacked contours flow through the same pass machinery; the optimizer's SimplifyPass is renamed CleanupPass to free the word.
This commit is contained in:
@@ -71,6 +71,11 @@ struct Args {
|
||||
#[arg(short = 's', long, value_parser = clap::value_parser!(i64).range(0..=180))]
|
||||
splice_threshold: Option<i64>,
|
||||
|
||||
/// Simplify curves (spline mode): re-fit with the fewest cubics staying
|
||||
/// within this tolerance in px (try 1–2.5; paper.js uses 2.5).
|
||||
#[arg(long, value_name = "TOLERANCE", value_parser = parse_simplify_tolerance)]
|
||||
simplify: Option<f64>,
|
||||
|
||||
/// Decimal places to use in path coordinates.
|
||||
#[arg(long)]
|
||||
path_precision: Option<u32>,
|
||||
@@ -87,7 +92,7 @@ struct Args {
|
||||
#[arg(long)]
|
||||
max_colors: Option<usize>,
|
||||
|
||||
/// Optimization level: 0 = off, 1 = quantize+simplify, 2 = + shorthands/grouping.
|
||||
/// Optimization level: 0 = off, 1 = quantize+cleanup, 2 = + shorthands/grouping.
|
||||
#[arg(long, value_parser = clap::value_parser!(u8).range(0..=2))]
|
||||
optimize: Option<u8>,
|
||||
|
||||
@@ -112,6 +117,14 @@ struct Args {
|
||||
watershed_detail: Option<u8>,
|
||||
}
|
||||
|
||||
fn parse_simplify_tolerance(s: &str) -> Result<f64, String> {
|
||||
let v: f64 = s.parse().map_err(|_| format!("`{s}` is not a number"))?;
|
||||
if !v.is_finite() || v <= 0.0 {
|
||||
return Err(format!("simplify tolerance {v} must be positive"));
|
||||
}
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn parse_segment_length(s: &str) -> Result<f64, String> {
|
||||
let v: f64 = s.parse().map_err(|_| format!("`{s}` is not a number"))?;
|
||||
if !(3.5..=10.0).contains(&v) {
|
||||
@@ -177,6 +190,9 @@ fn build_config(args: &Args) -> Result<Config, String> {
|
||||
if let Some(v) = args.splice_threshold {
|
||||
config.splice_threshold = v as i32;
|
||||
}
|
||||
if args.simplify.is_some() {
|
||||
config.simplify = args.simplify;
|
||||
}
|
||||
if args.path_precision.is_some() {
|
||||
config.path_precision = args.path_precision;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user