Source Release

This commit is contained in:
Chris Tsang
2020-10-25 14:22:42 +08:00
parent f0a1369dc4
commit f6f4839185
23 changed files with 6333 additions and 3 deletions

View File

@@ -0,0 +1,97 @@
use wasm_bindgen::prelude::*;
use visioncortex::{clusters::Clusters, Color, ColorName, PointI32, PathSimplifyMode};
use crate::{canvas::*};
use crate::svg::*;
use serde::Deserialize;
use super::util;
#[derive(Debug, Deserialize)]
pub struct BinaryImageConverterParams {
pub canvas_id: String,
pub svg_id: String,
pub mode: String,
pub corner_threshold: f64,
pub length_threshold: f64,
pub max_iterations: usize,
pub splice_threshold: f64,
pub filter_speckle: usize,
}
#[wasm_bindgen]
pub struct BinaryImageConverter {
canvas: Canvas,
svg: Svg,
clusters: Clusters,
counter: usize,
mode: PathSimplifyMode,
params: BinaryImageConverterParams,
}
impl BinaryImageConverter {
pub fn new(params: BinaryImageConverterParams) -> Self {
let canvas = Canvas::new_from_id(&params.canvas_id);
let svg = Svg::new_from_id(&params.svg_id);
Self {
canvas,
svg,
clusters: Clusters::default(),
counter: 0,
mode: util::path_simplify_mode(&params.mode),
params,
}
}
}
#[wasm_bindgen]
impl BinaryImageConverter {
pub fn new_with_string(params: String) -> Self {
let params: BinaryImageConverterParams = serde_json::from_str(params.as_str()).unwrap();
Self::new(params)
}
pub fn init(&mut self) {
let width = self.canvas.width() as u32;
let height = self.canvas.height() as u32;
let image = self.canvas.get_image_data_as_image(0, 0, width, height);
let binary_image = image.to_binary_image(|x| x.r < 128);
self.clusters = binary_image.to_clusters(false);
self.canvas.log(&format!(
"clusters.len() = {}, self.clusters.rect.left = {}",
self.clusters.len(),
self.clusters.rect.left
));
}
pub fn tick(&mut self) -> bool {
if self.counter < self.clusters.len() {
self.canvas.log(&format!("tick {}", self.counter));
let cluster = self.clusters.get_cluster(self.counter);
if cluster.size() >= self.params.filter_speckle {
let svg_path = cluster.to_svg_path(
self.mode,
self.params.corner_threshold,
self.params.length_threshold,
self.params.max_iterations,
self.params.splice_threshold
);
let color = Color::color(&ColorName::White);
self.svg.prepend_path_with_fill(
&svg_path,
&PointI32::default(),
&color,
);
}
self.counter += 1;
false
} else {
self.canvas.log("done");
true
}
}
pub fn progress(&self) -> u32 {
100 * self.counter as u32 / self.clusters.len() as u32
}
}

View File

@@ -0,0 +1,133 @@
use wasm_bindgen::prelude::*;
use visioncortex::{PathSimplifyMode, PointI32};
use visioncortex::color_clusters::{IncrementalBuilder, Clusters, Runner, RunnerConfig};
use crate::canvas::*;
use crate::svg::*;
use serde::Deserialize;
use super::util;
#[derive(Debug, Deserialize)]
pub struct ColorImageConverterParams {
pub canvas_id: String,
pub svg_id: String,
pub mode: String,
pub corner_threshold: f64,
pub length_threshold: f64,
pub max_iterations: usize,
pub splice_threshold: f64,
pub filter_speckle: usize,
pub color_precision: i32,
pub layer_difference: i32,
}
#[wasm_bindgen]
pub struct ColorImageConverter {
canvas: Canvas,
svg: Svg,
stage: Stage,
counter: usize,
mode: PathSimplifyMode,
params: ColorImageConverterParams,
}
pub enum Stage {
New,
Clustering(IncrementalBuilder),
Vectorize(Clusters),
}
impl ColorImageConverter {
pub fn new(params: ColorImageConverterParams) -> Self {
let canvas = Canvas::new_from_id(&params.canvas_id);
let svg = Svg::new_from_id(&params.svg_id);
Self {
canvas,
svg,
stage: Stage::New,
counter: 0,
mode: util::path_simplify_mode(&params.mode),
params,
}
}
}
#[wasm_bindgen]
impl ColorImageConverter {
pub fn new_with_string(params: String) -> Self {
let params: ColorImageConverterParams = serde_json::from_str(params.as_str()).unwrap();
Self::new(params)
}
pub fn init(&mut self) {
let width = self.canvas.width() as u32;
let height = self.canvas.height() as u32;
let image = self.canvas.get_image_data_as_image(0, 0, width, height);
let runner = Runner::new(RunnerConfig {
batch_size: 25600,
good_min_area: self.params.filter_speckle,
good_max_area: (width * height) as usize,
is_same_color_a: self.params.color_precision,
is_same_color_b: 1,
deepen_diff: self.params.layer_difference,
hollow_neighbours: 1,
}, image);
self.stage = Stage::Clustering(runner.start());
}
pub fn tick(&mut self) -> bool {
match &mut self.stage {
Stage::New => {
panic!("uninitialized");
},
Stage::Clustering(builder) => {
self.canvas.log("Clustering tick");
if builder.tick() {
self.stage = Stage::Vectorize(builder.result())
}
false
},
Stage::Vectorize(clusters) => {
let view = clusters.view();
if self.counter < view.clusters_output.len() {
self.canvas.log("Vectorize tick");
let cluster = view.get_cluster(view.clusters_output[self.counter]);
let svg_path = cluster.to_svg_path(
&view, false, self.mode,
self.params.corner_threshold,
self.params.length_threshold,
self.params.max_iterations,
self.params.splice_threshold
);
self.svg.prepend_path_with_fill(
&svg_path,
&PointI32::new(0, 0),
&cluster.residue_color(),
);
self.counter += 1;
false
} else {
self.canvas.log("done");
true
}
}
}
}
pub fn progress(&self) -> i32 {
(match &self.stage {
Stage::New => {
0
},
Stage::Clustering(builder) => {
builder.progress() / 2
},
Stage::Vectorize(clusters) => {
50 + 50 * self.counter as u32 / clusters.view().clusters_output.len() as u32
}
}) as i32
}
}

6
webapp/src/conversion/mod.rs Executable file
View File

@@ -0,0 +1,6 @@
mod binary_image;
mod color_image;
mod util;
pub use binary_image::*;
pub use color_image::*;

10
webapp/src/conversion/util.rs Executable file
View File

@@ -0,0 +1,10 @@
use visioncortex::PathSimplifyMode;
pub fn path_simplify_mode(s: &str) -> PathSimplifyMode {
match s {
"polygon" => PathSimplifyMode::Polygon,
"spline" => PathSimplifyMode::Spline,
"none" => PathSimplifyMode::None,
_ => panic!("unknown PathSimplifyMode {}", s),
}
}