mirror of
https://github.com/visioncortex/vtracer.git
synced 2026-09-25 05:25:57 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2a5626afa | ||
|
|
d0593e716a | ||
|
|
9ce7df176a | ||
|
|
5928c0a7f5 | ||
|
|
b2c95a50cd | ||
|
|
bcd3ffb40e | ||
|
|
20da3efd3c | ||
|
|
0c27d1f06a | ||
|
|
0c7c7fc808 |
@@ -0,0 +1 @@
|
|||||||
|
docs/** linguist-generated
|
||||||
@@ -26,7 +26,7 @@ Comparing to [Potrace]() which only accept binarized inputs (Black & White pixma
|
|||||||
|
|
||||||
Comparing to Adobe Illustrator's Live Trace, VTracer's output is much more compact (less shapes) as we adopt a stacking strategy and avoid producing shapes with holes.
|
Comparing to Adobe Illustrator's Live Trace, VTracer's output is much more compact (less shapes) as we adopt a stacking strategy and avoid producing shapes with holes.
|
||||||
|
|
||||||
VTracer is originally designed for processing high resolution scans of historic blueprints up to gigapixels. By setting both `filter_speckle` and `layer_difference` to `0`, VTracer can also handle low resolution pixel art, effectively achieving `image-rendering: pixelated` for retro game artworks.
|
VTracer is originally designed for processing high resolution scans of historic blueprints up to gigapixels. At the same time, VTracer can also handle low resolution pixel art, simulating `image-rendering: pixelated` for retro game artworks.
|
||||||
|
|
||||||
A technical description of the algorithm is on [visioncortex.org/vtracer-docs](//www.visioncortex.org/vtracer-docs).
|
A technical description of the algorithm is on [visioncortex.org/vtracer-docs](//www.visioncortex.org/vtracer-docs).
|
||||||
|
|
||||||
@@ -39,8 +39,9 @@ VTracer and its [core library](//github.com/visioncortex/visioncortex) is implem
|
|||||||

|

|
||||||
|
|
||||||
## Command Line
|
## Command Line
|
||||||
```
|
|
||||||
visioncortex VTracer
|
```sh
|
||||||
|
visioncortex VTracer 0.3.0
|
||||||
A cmd app to convert images into vector graphics.
|
A cmd app to convert images into vector graphics.
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
@@ -56,6 +57,9 @@ OPTIONS:
|
|||||||
-c, --corner_threshold <corner_threshold> Minimum momentary angle (degree) to be considered a corner
|
-c, --corner_threshold <corner_threshold> Minimum momentary angle (degree) to be considered a corner
|
||||||
-f, --filter_speckle <filter_speckle> Discard patches smaller than X px in size
|
-f, --filter_speckle <filter_speckle> Discard patches smaller than X px in size
|
||||||
-g, --gradient_step <gradient_step> Color difference between gradient layers
|
-g, --gradient_step <gradient_step> Color difference between gradient layers
|
||||||
|
--hierarchical <hierarchical>
|
||||||
|
Hierarchical clustering `stacked` (default) or non-stacked `cutout`. Only applies to color mode.
|
||||||
|
|
||||||
-i, --input <input> Path to input raster image
|
-i, --input <input> Path to input raster image
|
||||||
-m, --mode <mode> Curver fitting mode `pixel`, `polygon`, `spline`
|
-m, --mode <mode> Curver fitting mode `pixel`, `polygon`, `spline`
|
||||||
-o, --output <output> Path to output vector graphics
|
-o, --output <output> Path to output vector graphics
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "vtracer"
|
name = "vtracer"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
authors = ["Chris Tsang <tyt2y7@gmail.com>"]
|
authors = ["Chris Tsang <tyt2y7@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "A cmd app to convert images into vector graphics."
|
description = "A cmd app to convert images into vector graphics."
|
||||||
@@ -13,4 +13,4 @@ keywords = ["svg", "computer-graphics"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
image = "0.23.10"
|
image = "0.23.10"
|
||||||
visioncortex = "0.4.0"
|
visioncortex = "0.6.0"
|
||||||
+28
-7
@@ -33,6 +33,7 @@ pub struct Config {
|
|||||||
pub length_threshold: f64,
|
pub length_threshold: f64,
|
||||||
pub max_iterations: usize,
|
pub max_iterations: usize,
|
||||||
pub splice_threshold: i32,
|
pub splice_threshold: i32,
|
||||||
|
pub path_precision: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct ConverterConfig {
|
pub(crate) struct ConverterConfig {
|
||||||
@@ -48,6 +49,7 @@ pub(crate) struct ConverterConfig {
|
|||||||
pub length_threshold: f64,
|
pub length_threshold: f64,
|
||||||
pub max_iterations: usize,
|
pub max_iterations: usize,
|
||||||
pub splice_threshold: f64,
|
pub splice_threshold: f64,
|
||||||
|
pub path_precision: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@@ -65,6 +67,7 @@ impl Default for Config {
|
|||||||
length_threshold: 4.0,
|
length_threshold: 4.0,
|
||||||
splice_threshold: 45,
|
splice_threshold: 45,
|
||||||
max_iterations: 10,
|
max_iterations: 10,
|
||||||
|
path_precision: Some(8),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,6 +197,11 @@ impl Config {
|
|||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("Curver fitting mode `pixel`, `polygon`, `spline`"));
|
.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
|
// Extract matches
|
||||||
let matches = app.get_matches();
|
let matches = app.get_matches();
|
||||||
|
|
||||||
@@ -225,7 +233,7 @@ impl Config {
|
|||||||
} else if value == "spline" {
|
} else if value == "spline" {
|
||||||
"spline"
|
"spline"
|
||||||
} else {
|
} 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;
|
config.filter_speckle = value;
|
||||||
} else {
|
} 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;
|
config.color_precision = value;
|
||||||
} else {
|
} 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;
|
config.layer_difference = value;
|
||||||
} else {
|
} 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
|
config.corner_threshold = value
|
||||||
} else {
|
} 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;
|
config.length_threshold = value;
|
||||||
} else {
|
} 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;
|
config.splice_threshold = value;
|
||||||
} else {
|
} 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::<u32>().is_ok() { // is numeric
|
||||||
|
let value = value.trim().parse::<u32>().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,
|
length_threshold: 4.0,
|
||||||
max_iterations: 10,
|
max_iterations: 10,
|
||||||
splice_threshold: 45,
|
splice_threshold: 45,
|
||||||
|
path_precision: Some(8),
|
||||||
},
|
},
|
||||||
Preset::Poster => Self {
|
Preset::Poster => Self {
|
||||||
input_path,
|
input_path,
|
||||||
@@ -335,6 +353,7 @@ impl Config {
|
|||||||
length_threshold: 4.0,
|
length_threshold: 4.0,
|
||||||
max_iterations: 10,
|
max_iterations: 10,
|
||||||
splice_threshold: 45,
|
splice_threshold: 45,
|
||||||
|
path_precision: Some(8),
|
||||||
},
|
},
|
||||||
Preset::Photo => Self {
|
Preset::Photo => Self {
|
||||||
input_path,
|
input_path,
|
||||||
@@ -349,6 +368,7 @@ impl Config {
|
|||||||
length_threshold: 4.0,
|
length_threshold: 4.0,
|
||||||
max_iterations: 10,
|
max_iterations: 10,
|
||||||
splice_threshold: 45,
|
splice_threshold: 45,
|
||||||
|
path_precision: Some(8),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -367,6 +387,7 @@ impl Config {
|
|||||||
length_threshold: self.length_threshold,
|
length_threshold: self.length_threshold,
|
||||||
max_iterations: self.max_iterations,
|
max_iterations: self.max_iterations,
|
||||||
splice_threshold: deg2rad(self.splice_threshold),
|
splice_threshold: deg2rad(self.splice_threshold),
|
||||||
|
path_precision: self.path_precision,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ fn color_image_to_svg(config: ConverterConfig) -> Result<(), String> {
|
|||||||
config.max_iterations,
|
config.max_iterations,
|
||||||
config.splice_threshold
|
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)
|
write_svg(svg, config.output_path)
|
||||||
@@ -106,7 +106,7 @@ fn binary_image_to_svg(config: ConverterConfig) -> Result<(), String> {
|
|||||||
config.max_iterations,
|
config.max_iterations,
|
||||||
config.splice_threshold,
|
config.splice_threshold,
|
||||||
);
|
);
|
||||||
svg.add_path(paths, Color::color(&ColorName::Black));
|
svg.add_path(paths, Color::color(&ColorName::Black), config.path_precision);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -10,6 +10,7 @@ pub struct SvgFile {
|
|||||||
pub struct SvgPath {
|
pub struct SvgPath {
|
||||||
pub path: CompoundPath,
|
pub path: CompoundPath,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
|
pub path_precision: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SvgFile {
|
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<u32>) {
|
||||||
self.paths.push(SvgPath {
|
self.paths.push(SvgPath {
|
||||||
path,
|
path,
|
||||||
color,
|
color,
|
||||||
|
path_precision,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,7 +49,7 @@ impl fmt::Display for SvgFile {
|
|||||||
|
|
||||||
impl fmt::Display for SvgPath {
|
impl fmt::Display for SvgPath {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
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!(
|
writeln!(
|
||||||
f, "<path d=\"{}\" fill=\"{}\" transform=\"translate({},{})\"/>",
|
f, "<path d=\"{}\" fill=\"{}\" transform=\"translate({},{})\"/>",
|
||||||
string, self.color.to_hex_string(),
|
string, self.color.to_hex_string(),
|
||||||
|
|||||||
Generated
+1
-1
File diff suppressed because one or more lines are too long
Generated
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
Copyright (c) 2020 Tsang Hao Fung
|
Copyright (c) 2020 Tsang Hao Fung
|
||||||
|
|
||||||
All Rights Reserved
|
The content in the /docs directory is for GitHub pages, and is not covered under open source licenses.
|
||||||
|
|||||||
Generated
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 154 KiB |
Generated
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 243 KiB |
+1
-1
@@ -22,7 +22,7 @@ console_log = { version = "0.2", features = ["color"] }
|
|||||||
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
|
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
visioncortex = "0.4.0"
|
visioncortex = "0.6.0"
|
||||||
|
|
||||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||||
# logging them with `console.error`. This is great for development, but requires
|
# logging them with `console.error`. This is great for development, but requires
|
||||||
|
|||||||
@@ -185,6 +185,19 @@
|
|||||||
<input id="splice" type="range" min="0" max="180" step="1" value="45">
|
<input id="splice" type="range" min="0" max="180" step="1" value="45">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div style="display: none">
|
||||||
|
<div class="spline-options">
|
||||||
|
<div title="Number of decimal places used in svg path string">
|
||||||
|
Path Precision <span>(More digits)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="pathprecisionvalue" class="spline-options">
|
||||||
|
8
|
||||||
|
</div>
|
||||||
|
<div class="spline-options">
|
||||||
|
<input id="pathprecision" class="uk-range" type="range" min="0" max="16" step="1" value="8">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="./bootstrap.js"></script>
|
<script src="./bootstrap.js"></script>
|
||||||
|
|||||||
+19
-2
@@ -52,6 +52,7 @@ var presetConfigs = [
|
|||||||
clustering_hierarchical: 'stacked',
|
clustering_hierarchical: 'stacked',
|
||||||
filter_speckle: 4,
|
filter_speckle: 4,
|
||||||
color_precision: 6,
|
color_precision: 6,
|
||||||
|
path_precision: 8,
|
||||||
layer_difference: 16,
|
layer_difference: 16,
|
||||||
mode: 'spline',
|
mode: 'spline',
|
||||||
corner_threshold: 60,
|
corner_threshold: 60,
|
||||||
@@ -66,6 +67,7 @@ var presetConfigs = [
|
|||||||
clustering_hierarchical: 'stacked',
|
clustering_hierarchical: 'stacked',
|
||||||
filter_speckle: 4,
|
filter_speckle: 4,
|
||||||
color_precision: 8,
|
color_precision: 8,
|
||||||
|
path_precision: 8,
|
||||||
layer_difference: 25,
|
layer_difference: 25,
|
||||||
mode: 'spline',
|
mode: 'spline',
|
||||||
corner_threshold: 60,
|
corner_threshold: 60,
|
||||||
@@ -77,9 +79,10 @@ var presetConfigs = [
|
|||||||
{
|
{
|
||||||
src: 'assets/samples/Gum Tree Vector.jpg',
|
src: 'assets/samples/Gum Tree Vector.jpg',
|
||||||
clustering_mode: 'color',
|
clustering_mode: 'color',
|
||||||
clustering_hierarchical: 'cutout',
|
clustering_hierarchical: 'stacked',
|
||||||
filter_speckle: 4,
|
filter_speckle: 4,
|
||||||
color_precision: 8,
|
color_precision: 8,
|
||||||
|
path_precision: 8,
|
||||||
layer_difference: 28,
|
layer_difference: 28,
|
||||||
mode: 'spline',
|
mode: 'spline',
|
||||||
corner_threshold: 60,
|
corner_threshold: 60,
|
||||||
@@ -94,6 +97,7 @@ var presetConfigs = [
|
|||||||
clustering_hierarchical: 'stacked',
|
clustering_hierarchical: 'stacked',
|
||||||
filter_speckle: 8,
|
filter_speckle: 8,
|
||||||
color_precision: 7,
|
color_precision: 7,
|
||||||
|
path_precision: 8,
|
||||||
layer_difference: 64,
|
layer_difference: 64,
|
||||||
mode: 'spline',
|
mode: 'spline',
|
||||||
corner_threshold: 60,
|
corner_threshold: 60,
|
||||||
@@ -108,6 +112,7 @@ var presetConfigs = [
|
|||||||
clustering_hierarchical: 'stacked',
|
clustering_hierarchical: 'stacked',
|
||||||
filter_speckle: 10,
|
filter_speckle: 10,
|
||||||
color_precision: 8,
|
color_precision: 8,
|
||||||
|
path_precision: 8,
|
||||||
layer_difference: 48,
|
layer_difference: 48,
|
||||||
mode: 'spline',
|
mode: 'spline',
|
||||||
corner_threshold: 180,
|
corner_threshold: 180,
|
||||||
@@ -122,6 +127,7 @@ var presetConfigs = [
|
|||||||
clustering_hierarchical: 'stacked',
|
clustering_hierarchical: 'stacked',
|
||||||
filter_speckle: 0,
|
filter_speckle: 0,
|
||||||
color_precision: 8,
|
color_precision: 8,
|
||||||
|
path_precision: 8,
|
||||||
layer_difference: 0,
|
layer_difference: 0,
|
||||||
mode: 'none',
|
mode: 'none',
|
||||||
corner_threshold: 180,
|
corner_threshold: 180,
|
||||||
@@ -178,6 +184,9 @@ function loadConfig(config) {
|
|||||||
document.getElementById('layerdifferencevalue').innerHTML = globallayerdifference;
|
document.getElementById('layerdifferencevalue').innerHTML = globallayerdifference;
|
||||||
document.getElementById('layerdifference').value = globallayerdifference;
|
document.getElementById('layerdifference').value = globallayerdifference;
|
||||||
|
|
||||||
|
globalpathprecision = config.path_precision;
|
||||||
|
document.getElementById('pathprecisionvalue').innerHTML = globalpathprecision;
|
||||||
|
document.getElementById('pathprecision').value = globalpathprecision;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choose template from gallery
|
// Choose template from gallery
|
||||||
@@ -244,7 +253,8 @@ var globalcorner = parseInt(document.getElementById('corner').value),
|
|||||||
globalsplice = parseInt(document.getElementById('splice').value),
|
globalsplice = parseInt(document.getElementById('splice').value),
|
||||||
globalfilterspeckle = parseInt(document.getElementById('filterspeckle').value),
|
globalfilterspeckle = parseInt(document.getElementById('filterspeckle').value),
|
||||||
globalcolorprecision = parseInt(document.getElementById('colorprecision').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
|
// Load past inputs from localStorage
|
||||||
/*
|
/*
|
||||||
@@ -327,6 +337,12 @@ document.getElementById('splice').addEventListener('change', function (e) {
|
|||||||
restart();
|
restart();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById('pathprecision').addEventListener('change', function (e) {
|
||||||
|
globalpathprecision = parseInt(this.value);
|
||||||
|
document.getElementById('pathprecisionvalue').innerHTML = this.value;
|
||||||
|
restart();
|
||||||
|
});
|
||||||
|
|
||||||
// Save inputs before unloading
|
// Save inputs before unloading
|
||||||
/*
|
/*
|
||||||
window.addEventListener('beforeunload', function () {
|
window.addEventListener('beforeunload', function () {
|
||||||
@@ -404,6 +420,7 @@ function restart() {
|
|||||||
'filter_speckle': globalfilterspeckle*globalfilterspeckle,
|
'filter_speckle': globalfilterspeckle*globalfilterspeckle,
|
||||||
'color_precision': 8-globalcolorprecision,
|
'color_precision': 8-globalcolorprecision,
|
||||||
'layer_difference': globallayerdifference,
|
'layer_difference': globallayerdifference,
|
||||||
|
'path_precision': globalpathprecision,
|
||||||
});
|
});
|
||||||
if (runner) {
|
if (runner) {
|
||||||
runner.stop();
|
runner.stop();
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ pub struct BinaryImageConverterParams {
|
|||||||
pub max_iterations: usize,
|
pub max_iterations: usize,
|
||||||
pub splice_threshold: f64,
|
pub splice_threshold: f64,
|
||||||
pub filter_speckle: usize,
|
pub filter_speckle: usize,
|
||||||
|
pub path_precision: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
@@ -80,6 +81,7 @@ impl BinaryImageConverter {
|
|||||||
self.svg.prepend_path(
|
self.svg.prepend_path(
|
||||||
&paths,
|
&paths,
|
||||||
&color,
|
&color,
|
||||||
|
Some(self.params.path_precision),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.counter += 1;
|
self.counter += 1;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ pub struct ColorImageConverterParams {
|
|||||||
pub filter_speckle: usize,
|
pub filter_speckle: usize,
|
||||||
pub color_precision: i32,
|
pub color_precision: i32,
|
||||||
pub layer_difference: i32,
|
pub layer_difference: i32,
|
||||||
|
pub path_precision: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
@@ -137,6 +138,7 @@ impl ColorImageConverter {
|
|||||||
self.svg.prepend_path(
|
self.svg.prepend_path(
|
||||||
&paths,
|
&paths,
|
||||||
&cluster.residue_color(),
|
&cluster.residue_color(),
|
||||||
|
Some(self.params.path_precision),
|
||||||
);
|
);
|
||||||
self.counter += 1;
|
self.counter += 1;
|
||||||
false
|
false
|
||||||
|
|||||||
+2
-2
@@ -13,11 +13,11 @@ impl Svg {
|
|||||||
Self { element }
|
Self { element }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepend_path(&mut self, paths: &CompoundPath, color: &Color) {
|
pub fn prepend_path(&mut self, paths: &CompoundPath, color: &Color, precision: Option<u32>) {
|
||||||
let path = document()
|
let path = document()
|
||||||
.create_element_ns(Some("http://www.w3.org/2000/svg"), "path")
|
.create_element_ns(Some("http://www.w3.org/2000/svg"), "path")
|
||||||
.unwrap();
|
.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("d", &string).unwrap();
|
||||||
path.set_attribute(
|
path.set_attribute(
|
||||||
"transform",
|
"transform",
|
||||||
|
|||||||
Reference in New Issue
Block a user