diff --git a/cmdapp/Cargo.toml b/cmdapp/Cargo.toml index d6e0ca3..8ed8a56 100644 --- a/cmdapp/Cargo.toml +++ b/cmdapp/Cargo.toml @@ -13,4 +13,4 @@ keywords = ["svg", "computer-graphics"] [dependencies] clap = "2.33.3" image = "0.23.10" -visioncortex = "0.4.0" \ No newline at end of file +visioncortex = "0.6.0" \ No newline at end of file diff --git a/cmdapp/src/config.rs b/cmdapp/src/config.rs index 13df1ec..8a1fa3e 100644 --- a/cmdapp/src/config.rs +++ b/cmdapp/src/config.rs @@ -33,6 +33,7 @@ pub struct Config { pub length_threshold: f64, pub max_iterations: usize, pub splice_threshold: i32, + pub path_precision: Option, } pub(crate) struct ConverterConfig { @@ -48,6 +49,7 @@ pub(crate) struct ConverterConfig { pub length_threshold: f64, pub max_iterations: usize, pub splice_threshold: f64, + pub path_precision: Option, } impl Default for Config { @@ -65,6 +67,7 @@ impl Default for Config { length_threshold: 4.0, splice_threshold: 45, max_iterations: 10, + path_precision: Some(8), } } } @@ -194,6 +197,11 @@ impl Config { .takes_value(true) .help("Curver fitting mode `pixel`, `polygon`, `spline`")); + let app = app.arg(Arg::with_name("path_precision") + .long("path_precision") + .takes_value(true) + .help("Number of deciaml places to use in path string")); + // Extract matches let matches = app.get_matches(); @@ -225,7 +233,7 @@ impl Config { } else if value == "spline" { "spline" } else { - panic!("Parser Error: Curve fitting mode is invalid with value {}", value); + panic!("Parser Error: Curve fitting mode is invalid: {}", value); }); } @@ -237,7 +245,7 @@ impl Config { } config.filter_speckle = value; } else { - panic!("Parser Error: Filter speckle is not a positive integer with value {}.", value); + panic!("Parser Error: Filter speckle is not a positive integer: {}.", value); } } @@ -249,7 +257,7 @@ impl Config { } config.color_precision = value; } else { - panic!("Parser Error: Color precision is not an integer with value {}.", value); + panic!("Parser Error: Color precision is not an integer: {}.", value); } } @@ -261,7 +269,7 @@ impl Config { } config.layer_difference = value; } else { - panic!("Parser Error: Gradient step is not an integer with value {}.", value); + panic!("Parser Error: Gradient step is not an integer: {}.", value); } } @@ -273,7 +281,7 @@ impl Config { } config.corner_threshold = value } else { - panic!("Parser Error: Corner threshold is not numeric with value {}.", value); + panic!("Parser Error: Corner threshold is not numeric: {}.", value); } } @@ -285,7 +293,7 @@ impl Config { } config.length_threshold = value; } else { - panic!("Parser Error: Segment length is not numeric with value {}.", value); + panic!("Parser Error: Segment length is not numeric: {}.", value); } } @@ -297,7 +305,16 @@ impl Config { } config.splice_threshold = value; } else { - panic!("Parser Error: Segment length is not numeric with value {}.", value); + panic!("Parser Error: Segment length is not numeric: {}.", value); + } + } + + if let Some(value) = matches.value_of("path_precision") { + if value.trim().parse::().is_ok() { // is numeric + let value = value.trim().parse::().ok(); + config.path_precision = value; + } else { + panic!("Parser Error: Path precision is not an unsigned integer: {}.", value); } } @@ -321,6 +338,7 @@ impl Config { length_threshold: 4.0, max_iterations: 10, splice_threshold: 45, + path_precision: Some(8), }, Preset::Poster => Self { input_path, @@ -335,6 +353,7 @@ impl Config { length_threshold: 4.0, max_iterations: 10, splice_threshold: 45, + path_precision: Some(8), }, Preset::Photo => Self { input_path, @@ -349,6 +368,7 @@ impl Config { length_threshold: 4.0, max_iterations: 10, splice_threshold: 45, + path_precision: Some(8), } } } @@ -367,6 +387,7 @@ impl Config { length_threshold: self.length_threshold, max_iterations: self.max_iterations, splice_threshold: deg2rad(self.splice_threshold), + path_precision: self.path_precision, } } } diff --git a/cmdapp/src/converter.rs b/cmdapp/src/converter.rs index 70b2f06..b5f6ef3 100644 --- a/cmdapp/src/converter.rs +++ b/cmdapp/src/converter.rs @@ -74,7 +74,7 @@ fn color_image_to_svg(config: ConverterConfig) -> Result<(), String> { config.max_iterations, config.splice_threshold ); - svg.add_path(paths, cluster.residue_color()); + svg.add_path(paths, cluster.residue_color(), config.path_precision); } write_svg(svg, config.output_path) @@ -106,7 +106,7 @@ fn binary_image_to_svg(config: ConverterConfig) -> Result<(), String> { config.max_iterations, config.splice_threshold, ); - svg.add_path(paths, Color::color(&ColorName::Black)); + svg.add_path(paths, Color::color(&ColorName::Black), config.path_precision); } } diff --git a/cmdapp/src/svg.rs b/cmdapp/src/svg.rs index 6026d2d..db23835 100644 --- a/cmdapp/src/svg.rs +++ b/cmdapp/src/svg.rs @@ -10,6 +10,7 @@ pub struct SvgFile { pub struct SvgPath { pub path: CompoundPath, pub color: Color, + pub path_precision: Option, } impl SvgFile { @@ -21,10 +22,11 @@ impl SvgFile { } } - pub fn add_path(&mut self, path: CompoundPath, color: Color) { + pub fn add_path(&mut self, path: CompoundPath, color: Color, path_precision: Option) { self.paths.push(SvgPath { path, color, + path_precision, }) } } @@ -47,7 +49,7 @@ impl fmt::Display for SvgFile { impl fmt::Display for SvgPath { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let (string, offset) = self.path.to_svg_string(true, PointF64::default()); + let (string, offset) = self.path.to_svg_string(true, PointF64::default(), self.path_precision); writeln!( f, "", string, self.color.to_hex_string(), diff --git a/webapp/Cargo.toml b/webapp/Cargo.toml index 90fe003..11a3827 100644 --- a/webapp/Cargo.toml +++ b/webapp/Cargo.toml @@ -22,7 +22,7 @@ console_log = { version = "0.2", features = ["color"] } wasm-bindgen = { version = "0.2", features = ["serde-serialize"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -visioncortex = "0.4.0" +visioncortex = "0.6.0" # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires diff --git a/webapp/app/index.html b/webapp/app/index.html index efe2544..3932461 100644 --- a/webapp/app/index.html +++ b/webapp/app/index.html @@ -185,6 +185,19 @@ +
+
+
+ Path Precision (More digits) +
+
+
+ 8 +
+
+ +
+
diff --git a/webapp/app/index.js b/webapp/app/index.js index 82146ae..2a45f98 100644 --- a/webapp/app/index.js +++ b/webapp/app/index.js @@ -52,6 +52,7 @@ var presetConfigs = [ clustering_hierarchical: 'stacked', filter_speckle: 4, color_precision: 6, + path_precision: 8, layer_difference: 16, mode: 'spline', corner_threshold: 60, @@ -66,6 +67,7 @@ var presetConfigs = [ clustering_hierarchical: 'stacked', filter_speckle: 4, color_precision: 8, + path_precision: 8, layer_difference: 25, mode: 'spline', corner_threshold: 60, @@ -77,9 +79,10 @@ var presetConfigs = [ { src: 'assets/samples/Gum Tree Vector.jpg', clustering_mode: 'color', - clustering_hierarchical: 'cutout', + clustering_hierarchical: 'stacked', filter_speckle: 4, color_precision: 8, + path_precision: 8, layer_difference: 28, mode: 'spline', corner_threshold: 60, @@ -94,6 +97,7 @@ var presetConfigs = [ clustering_hierarchical: 'stacked', filter_speckle: 8, color_precision: 7, + path_precision: 8, layer_difference: 64, mode: 'spline', corner_threshold: 60, @@ -108,6 +112,7 @@ var presetConfigs = [ clustering_hierarchical: 'stacked', filter_speckle: 10, color_precision: 8, + path_precision: 8, layer_difference: 48, mode: 'spline', corner_threshold: 180, @@ -122,6 +127,7 @@ var presetConfigs = [ clustering_hierarchical: 'stacked', filter_speckle: 0, color_precision: 8, + path_precision: 8, layer_difference: 0, mode: 'none', corner_threshold: 180, @@ -178,6 +184,9 @@ function loadConfig(config) { document.getElementById('layerdifferencevalue').innerHTML = globallayerdifference; document.getElementById('layerdifference').value = globallayerdifference; + globalpathprecision = config.path_precision; + document.getElementById('pathprecisionvalue').innerHTML = globalpathprecision; + document.getElementById('pathprecision').value = globalpathprecision; } // Choose template from gallery @@ -244,7 +253,8 @@ var globalcorner = parseInt(document.getElementById('corner').value), globalsplice = parseInt(document.getElementById('splice').value), globalfilterspeckle = parseInt(document.getElementById('filterspeckle').value), globalcolorprecision = parseInt(document.getElementById('colorprecision').value), - globallayerdifference = parseInt(document.getElementById('layerdifference').value); + globallayerdifference = parseInt(document.getElementById('layerdifference').value), + globalpathprecision = parseInt(document.getElementById('pathprecision').value); // Load past inputs from localStorage /* @@ -327,6 +337,12 @@ document.getElementById('splice').addEventListener('change', function (e) { restart(); }); +document.getElementById('pathprecision').addEventListener('change', function (e) { + globalpathprecision = parseInt(this.value); + document.getElementById('pathprecisionvalue').innerHTML = this.value; + restart(); +}); + // Save inputs before unloading /* window.addEventListener('beforeunload', function () { @@ -404,6 +420,7 @@ function restart() { 'filter_speckle': globalfilterspeckle*globalfilterspeckle, 'color_precision': 8-globalcolorprecision, 'layer_difference': globallayerdifference, + 'path_precision': globalpathprecision, }); if (runner) { runner.stop(); diff --git a/webapp/src/conversion/binary_image.rs b/webapp/src/conversion/binary_image.rs index 3dada50..22c8d9a 100644 --- a/webapp/src/conversion/binary_image.rs +++ b/webapp/src/conversion/binary_image.rs @@ -17,6 +17,7 @@ pub struct BinaryImageConverterParams { pub max_iterations: usize, pub splice_threshold: f64, pub filter_speckle: usize, + pub path_precision: u32, } #[wasm_bindgen] @@ -80,6 +81,7 @@ impl BinaryImageConverter { self.svg.prepend_path( &paths, &color, + Some(self.params.path_precision), ); } self.counter += 1; diff --git a/webapp/src/conversion/color_image.rs b/webapp/src/conversion/color_image.rs index 447b4c8..b196601 100644 --- a/webapp/src/conversion/color_image.rs +++ b/webapp/src/conversion/color_image.rs @@ -21,6 +21,7 @@ pub struct ColorImageConverterParams { pub filter_speckle: usize, pub color_precision: i32, pub layer_difference: i32, + pub path_precision: u32, } #[wasm_bindgen] @@ -137,6 +138,7 @@ impl ColorImageConverter { self.svg.prepend_path( &paths, &cluster.residue_color(), + Some(self.params.path_precision), ); self.counter += 1; false diff --git a/webapp/src/svg.rs b/webapp/src/svg.rs index e3b2153..10449cd 100644 --- a/webapp/src/svg.rs +++ b/webapp/src/svg.rs @@ -13,11 +13,11 @@ impl Svg { Self { element } } - pub fn prepend_path(&mut self, paths: &CompoundPath, color: &Color) { + pub fn prepend_path(&mut self, paths: &CompoundPath, color: &Color, precision: Option) { let path = document() .create_element_ns(Some("http://www.w3.org/2000/svg"), "path") .unwrap(); - let (string, offset) = paths.to_svg_string(true, PointF64::default()); + let (string, offset) = paths.to_svg_string(true, PointF64::default(), precision); path.set_attribute("d", &string).unwrap(); path.set_attribute( "transform",