mirror of
https://github.com/visioncortex/vtracer.git
synced 2026-09-26 05:51:21 -07:00
Relative & compound path
This commit is contained in:
+1
-1
@@ -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.2.0"
|
visioncortex = "0.3.0"
|
||||||
+9
-18
@@ -42,7 +42,7 @@ fn color_image_to_svg(config: ConverterConfig) -> Result<(), String> {
|
|||||||
let mut svg = SvgFile::new(width, height);
|
let mut svg = SvgFile::new(width, height);
|
||||||
for &cluster_index in view.clusters_output.iter().rev() {
|
for &cluster_index in view.clusters_output.iter().rev() {
|
||||||
let cluster = view.get_cluster(cluster_index);
|
let cluster = view.get_cluster(cluster_index);
|
||||||
let svg_path = cluster.to_svg_path(
|
let paths = cluster.to_compound_path(
|
||||||
&view,
|
&view,
|
||||||
false,
|
false,
|
||||||
config.mode,
|
config.mode,
|
||||||
@@ -51,22 +51,14 @@ fn color_image_to_svg(config: ConverterConfig) -> Result<(), String> {
|
|||||||
config.max_iterations,
|
config.max_iterations,
|
||||||
config.splice_threshold
|
config.splice_threshold
|
||||||
);
|
);
|
||||||
svg.add_path(svg_path, cluster.residue_color());
|
svg.add_path(paths, cluster.residue_color());
|
||||||
}
|
}
|
||||||
|
|
||||||
let out_file = File::create(config.output_path);
|
write_svg(svg, config.output_path)
|
||||||
let mut out_file = match out_file {
|
|
||||||
Ok(file) => file,
|
|
||||||
Err(_) => return Err(String::from("Cannot create output file.")),
|
|
||||||
};
|
|
||||||
|
|
||||||
out_file.write_all(&svg.to_svg_file().as_bytes()).unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn binary_image_to_svg(config: ConverterConfig) -> Result<(), String> {
|
fn binary_image_to_svg(config: ConverterConfig) -> Result<(), String> {
|
||||||
|
|
||||||
let (img, width, height);
|
let (img, width, height);
|
||||||
match read_image(config.input_path) {
|
match read_image(config.input_path) {
|
||||||
Ok(values) => {
|
Ok(values) => {
|
||||||
@@ -79,20 +71,19 @@ fn binary_image_to_svg(config: ConverterConfig) -> Result<(), String> {
|
|||||||
let img = img.to_binary_image(|x| x.r < 128);
|
let img = img.to_binary_image(|x| x.r < 128);
|
||||||
|
|
||||||
let clusters = img.to_clusters(false);
|
let clusters = img.to_clusters(false);
|
||||||
|
|
||||||
let mut svg = SvgFile::new(width, height);
|
let mut svg = SvgFile::new(width, height);
|
||||||
for i in 0..clusters.len() {
|
for i in 0..clusters.len() {
|
||||||
let cluster = clusters.get_cluster(i);
|
let cluster = clusters.get_cluster(i);
|
||||||
if cluster.size() >= config.filter_speckle_area {
|
if cluster.size() >= config.filter_speckle_area {
|
||||||
let svg_path = cluster.to_svg_path(
|
let paths = cluster.to_compound_path(
|
||||||
config.mode,
|
config.mode,
|
||||||
config.corner_threshold,
|
config.corner_threshold,
|
||||||
config.length_threshold,
|
config.length_threshold,
|
||||||
config.max_iterations,
|
config.max_iterations,
|
||||||
config.splice_threshold,
|
config.splice_threshold,
|
||||||
);
|
);
|
||||||
let color = Color::color(&ColorName::Black);
|
svg.add_path(paths, Color::color(&ColorName::Black));
|
||||||
svg.add_path(svg_path, color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,8 +109,8 @@ fn write_svg(svg: SvgFile, output_path: PathBuf) -> Result<(), String> {
|
|||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(_) => return Err(String::from("Cannot create output file.")),
|
Err(_) => return Err(String::from("Cannot create output file.")),
|
||||||
};
|
};
|
||||||
|
|
||||||
out_file.write_all(&svg.to_svg_file().as_bytes()).unwrap();
|
write!(&mut out_file, "{}", svg).expect("failed to write file.");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
+36
-21
@@ -1,42 +1,57 @@
|
|||||||
use visioncortex::Color;
|
use std::fmt;
|
||||||
|
use visioncortex::{Color, CompoundPath, PointF64};
|
||||||
pub struct SvgPath {
|
|
||||||
path: String,
|
|
||||||
color: Color,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SvgFile {
|
pub struct SvgFile {
|
||||||
patches: Vec<SvgPath>,
|
pub paths: Vec<SvgPath>,
|
||||||
width: usize,
|
pub width: usize,
|
||||||
height: usize,
|
pub height: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SvgPath {
|
||||||
|
pub path: CompoundPath,
|
||||||
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SvgFile {
|
impl SvgFile {
|
||||||
pub fn new(width: usize, height: usize) -> Self {
|
pub fn new(width: usize, height: usize) -> Self {
|
||||||
SvgFile {
|
SvgFile {
|
||||||
patches: vec![],
|
paths: vec![],
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_path(&mut self, path: String, color: Color) {
|
pub fn add_path(&mut self, path: CompoundPath, color: Color) {
|
||||||
self.patches.push(SvgPath {
|
self.paths.push(SvgPath {
|
||||||
path,
|
path,
|
||||||
color
|
color,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_svg_file(&self) -> String {
|
impl fmt::Display for SvgFile {
|
||||||
let mut result: Vec<String> = vec![format!(r#"<?xml version="1.0" encoding="UTF-8"?>
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="{}" height="{}">"#, self.width, self.height)];
|
writeln!(f, r#"<?xml version="1.0" encoding="UTF-8"?>"#)?;
|
||||||
|
writeln!(f,
|
||||||
|
r#"<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="{}" height="{}">"#,
|
||||||
|
self.width, self.height
|
||||||
|
)?;
|
||||||
|
|
||||||
for patch in &self.patches {
|
for path in &self.paths {
|
||||||
let color = patch.color;
|
path.fmt(f)?;
|
||||||
result.push(format!("<path d=\"{}\" fill=\"#{:02x}{:02x}{:02x}\"/>\n", patch.path, color.r, color.g, color.b));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
result.push(String::from("</svg>"));
|
writeln!(f, "</svg>")
|
||||||
result.concat()
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
writeln!(
|
||||||
|
f, "<path d=\"{}\" fill=\"{}\" transform=\"translate({},{})\"/>",
|
||||||
|
string, self.color.to_hex_string(),
|
||||||
|
offset.x, offset.y
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+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.2.0"
|
visioncortex = "0.3.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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use visioncortex::{clusters::Clusters, Color, ColorName, PointI32, PathSimplifyMode};
|
use visioncortex::{clusters::Clusters, Color, ColorName, PathSimplifyMode};
|
||||||
|
|
||||||
use crate::{canvas::*};
|
use crate::{canvas::*};
|
||||||
use crate::svg::*;
|
use crate::svg::*;
|
||||||
@@ -69,7 +69,7 @@ impl BinaryImageConverter {
|
|||||||
self.canvas.log(&format!("tick {}", self.counter));
|
self.canvas.log(&format!("tick {}", self.counter));
|
||||||
let cluster = self.clusters.get_cluster(self.counter);
|
let cluster = self.clusters.get_cluster(self.counter);
|
||||||
if cluster.size() >= self.params.filter_speckle {
|
if cluster.size() >= self.params.filter_speckle {
|
||||||
let svg_path = cluster.to_svg_path(
|
let paths = cluster.to_compound_path(
|
||||||
self.mode,
|
self.mode,
|
||||||
self.params.corner_threshold,
|
self.params.corner_threshold,
|
||||||
self.params.length_threshold,
|
self.params.length_threshold,
|
||||||
@@ -77,9 +77,8 @@ impl BinaryImageConverter {
|
|||||||
self.params.splice_threshold
|
self.params.splice_threshold
|
||||||
);
|
);
|
||||||
let color = Color::color(&ColorName::White);
|
let color = Color::color(&ColorName::White);
|
||||||
self.svg.prepend_path_with_fill(
|
self.svg.prepend_path(
|
||||||
&svg_path,
|
&paths,
|
||||||
&PointI32::default(),
|
|
||||||
&color,
|
&color,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use visioncortex::{PathSimplifyMode, PointI32};
|
use visioncortex::PathSimplifyMode;
|
||||||
use visioncortex::color_clusters::{IncrementalBuilder, Clusters, Runner, RunnerConfig};
|
use visioncortex::color_clusters::{IncrementalBuilder, Clusters, Runner, RunnerConfig};
|
||||||
|
|
||||||
use crate::canvas::*;
|
use crate::canvas::*;
|
||||||
@@ -94,16 +94,15 @@ impl ColorImageConverter {
|
|||||||
if self.counter < view.clusters_output.len() {
|
if self.counter < view.clusters_output.len() {
|
||||||
self.canvas.log("Vectorize tick");
|
self.canvas.log("Vectorize tick");
|
||||||
let cluster = view.get_cluster(view.clusters_output[self.counter]);
|
let cluster = view.get_cluster(view.clusters_output[self.counter]);
|
||||||
let svg_path = cluster.to_svg_path(
|
let paths = cluster.to_compound_path(
|
||||||
&view, false, self.mode,
|
&view, false, self.mode,
|
||||||
self.params.corner_threshold,
|
self.params.corner_threshold,
|
||||||
self.params.length_threshold,
|
self.params.length_threshold,
|
||||||
self.params.max_iterations,
|
self.params.max_iterations,
|
||||||
self.params.splice_threshold
|
self.params.splice_threshold
|
||||||
);
|
);
|
||||||
self.svg.prepend_path_with_fill(
|
self.svg.prepend_path(
|
||||||
&svg_path,
|
&paths,
|
||||||
&PointI32::new(0, 0),
|
|
||||||
&cluster.residue_color(),
|
&cluster.residue_color(),
|
||||||
);
|
);
|
||||||
self.counter += 1;
|
self.counter += 1;
|
||||||
|
|||||||
+4
-3
@@ -1,5 +1,5 @@
|
|||||||
use web_sys::Element;
|
use web_sys::Element;
|
||||||
use visioncortex::{Color, PointI32};
|
use visioncortex::{Color, CompoundPath, PointF64};
|
||||||
use super::common::document;
|
use super::common::document;
|
||||||
|
|
||||||
pub struct Svg {
|
pub struct Svg {
|
||||||
@@ -13,11 +13,12 @@ impl Svg {
|
|||||||
Self { element }
|
Self { element }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepend_path_with_fill(&mut self, path_string: &str, offset: &PointI32, color: &Color) {
|
pub fn prepend_path(&mut self, paths: &CompoundPath, color: &Color) {
|
||||||
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();
|
||||||
path.set_attribute("d", path_string).unwrap();
|
let (string, offset) = paths.to_svg_string(true, PointF64::default());
|
||||||
|
path.set_attribute("d", &string).unwrap();
|
||||||
path.set_attribute(
|
path.set_attribute(
|
||||||
"transform",
|
"transform",
|
||||||
format!("translate({},{})", offset.x, offset.y).as_str(),
|
format!("translate({},{})", offset.x, offset.y).as_str(),
|
||||||
|
|||||||
Reference in New Issue
Block a user