diff --git a/Cargo.lock b/Cargo.lock index ea30b27d..7529aaf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -533,9 +533,6 @@ dependencies = [ "glam", "js-sys", "log", - "serde", - "serde-wasm-bindgen", - "serde_json", "wasm-bindgen", ] diff --git a/website/other/bezier-rs-demos/src/features/bezier-features.ts b/website/other/bezier-rs-demos/src/features/bezier-features.ts index 98d17098..82d0ae52 100644 --- a/website/other/bezier-rs-demos/src/features/bezier-features.ts +++ b/website/other/bezier-rs-demos/src/features/bezier-features.ts @@ -11,7 +11,7 @@ const bezierFeatures = { "bezier-through-points": { name: "Bezier Through Points", callback: (bezier: WasmBezierInstance, options: Record): string => { - const points = JSON.parse(bezier.get_points()); + const points = bezier.get_points(); if (Object.values(options).length === 1) { return WasmBezier.quadratic_through_points(points, options.t); } @@ -514,7 +514,7 @@ const bezierFeatures = { join: { name: "Join", callback: (bezier: WasmBezierInstance): string => { - const points = JSON.parse(bezier.get_points()); + const points = bezier.get_points(); let examplePoints = []; if (points.length === 2) { examplePoints = [ diff --git a/website/other/bezier-rs-demos/wasm/Cargo.toml b/website/other/bezier-rs-demos/wasm/Cargo.toml index 7131a27b..1d746e38 100644 --- a/website/other/bezier-rs-demos/wasm/Cargo.toml +++ b/website/other/bezier-rs-demos/wasm/Cargo.toml @@ -13,16 +13,20 @@ license = "Apache-2.0" [lib] crate-type = ["cdylib", "rlib"] +[features] +default = [] +logging = ["log"] + [dependencies] # Workspace dependencies bezier-rs = { workspace = true } -log = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde-wasm-bindgen = { workspace = true } wasm-bindgen = { workspace = true } -serde_json = { workspace = true } js-sys = { workspace = true } -glam = { workspace = true, features = ["serde"] } +glam = { workspace = true } +log = { workspace = true, optional = true } + +[dev-dependencies] +log = { workspace = true } [package.metadata.wasm-pack.profile.dev] wasm-opt = false @@ -33,7 +37,7 @@ demangle-name-section = true dwarf-debug-info = true [package.metadata.wasm-pack.profile.release] -wasm-opt = ["-Os"] +wasm-opt = ["-Oz"] [package.metadata.wasm-pack.profile.release.wasm-bindgen] debug-js-glue = false diff --git a/website/other/bezier-rs-demos/wasm/src/bezier.rs b/website/other/bezier-rs-demos/wasm/src/bezier.rs index 92661709..1bb9b047 100644 --- a/website/other/bezier-rs-demos/wasm/src/bezier.rs +++ b/website/other/bezier-rs-demos/wasm/src/bezier.rs @@ -1,10 +1,12 @@ use crate::svg_drawing::*; -use crate::utils::parse_cap; +use crate::utils::{parse_cap, parse_point}; use bezier_rs::{ArcStrategy, ArcsOptions, Bezier, Identifier, TValue, TValueType}; use glam::DVec2; +use js_sys::Array; use wasm_bindgen::prelude::*; +use wasm_bindgen::{JsCast, JsValue}; #[wasm_bindgen] pub enum WasmMaximizeArcs { @@ -20,11 +22,6 @@ const SCALE_UNIT_VECTOR_FACTOR: f64 = 50.; #[derive(Clone)] pub struct WasmBezier(Bezier); -/// Serialize some data and then convert it to a JsValue. -fn to_js_value(data: T) -> JsValue { - serde_wasm_bindgen::to_value(&serde_json::to_string(&data).unwrap()).unwrap() -} - fn convert_wasm_maximize_arcs(wasm_enum_value: WasmMaximizeArcs) -> ArcStrategy { match wasm_enum_value { WasmMaximizeArcs::Automatic => ArcStrategy::Automatic, @@ -53,22 +50,30 @@ impl Identifier for EmptyId { #[wasm_bindgen] impl WasmBezier { - /// Expect js_points to be a list of 2 pairs. pub fn new_linear(js_points: JsValue) -> WasmBezier { - let points: [DVec2; 2] = serde_wasm_bindgen::from_value(js_points).unwrap(); - WasmBezier(Bezier::from_linear_dvec2(points[0], points[1])) + let array = js_points.dyn_into::().unwrap(); + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + WasmBezier(Bezier::from_linear_dvec2(point1, point2)) } /// Expect js_points to be a list of 3 pairs. pub fn new_quadratic(js_points: JsValue) -> WasmBezier { - let points: [DVec2; 3] = serde_wasm_bindgen::from_value(js_points).unwrap(); - WasmBezier(Bezier::from_quadratic_dvec2(points[0], points[1], points[2])) + let array = js_points.dyn_into::().unwrap(); + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + let point3 = parse_point(&array.get(2)); + WasmBezier(Bezier::from_quadratic_dvec2(point1, point2, point3)) } /// Expect js_points to be a list of 4 pairs. pub fn new_cubic(js_points: JsValue) -> WasmBezier { - let points: [DVec2; 4] = serde_wasm_bindgen::from_value(js_points).unwrap(); - WasmBezier(Bezier::from_cubic_dvec2(points[0], points[1], points[2], points[3])) + let array = js_points.dyn_into::().unwrap(); + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + let point3 = parse_point(&array.get(2)); + let point4 = parse_point(&array.get(3)); + WasmBezier(Bezier::from_cubic_dvec2(point1, point2, point3, point4)) } fn draw_bezier_through_points(bezier: Bezier, through_point: DVec2) -> String { @@ -86,15 +91,21 @@ impl WasmBezier { } pub fn quadratic_through_points(js_points: JsValue, t: f64) -> String { - let points: [DVec2; 3] = serde_wasm_bindgen::from_value(js_points).unwrap(); - let bezier = Bezier::quadratic_through_points(points[0], points[1], points[2], Some(t)); - WasmBezier::draw_bezier_through_points(bezier, points[1]) + let array = js_points.dyn_into::().unwrap(); + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + let point3 = parse_point(&array.get(2)); + let bezier = Bezier::quadratic_through_points(point1, point2, point3, Some(t)); + WasmBezier::draw_bezier_through_points(bezier, point2) } pub fn cubic_through_points(js_points: JsValue, t: f64, midpoint_separation: f64) -> String { - let points: [DVec2; 3] = serde_wasm_bindgen::from_value(js_points).unwrap(); - let bezier = Bezier::cubic_through_points(points[0], points[1], points[2], Some(t), Some(midpoint_separation)); - WasmBezier::draw_bezier_through_points(bezier, points[1]) + let array = js_points.dyn_into::().unwrap(); + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + let point3 = parse_point(&array.get(2)); + let bezier = Bezier::cubic_through_points(point1, point2, point3, Some(t), Some(midpoint_separation)); + WasmBezier::draw_bezier_through_points(bezier, point2) } pub fn set_start(&mut self, x: f64, y: f64) { @@ -114,7 +125,12 @@ impl WasmBezier { } pub fn get_points(&self) -> JsValue { - to_js_value(self.0.get_points().collect::>()) + JsValue::from( + self.0 + .get_points() + .map(|point| [JsValue::from_f64(point.x), JsValue::from_f64(point.y)].iter().collect::()) + .collect::(), + ) } fn get_bezier_path(&self) -> String { @@ -434,8 +450,10 @@ impl WasmBezier { } pub fn intersect_line_segment(&self, js_points: JsValue) -> String { - let points: [DVec2; 2] = serde_wasm_bindgen::from_value(js_points).unwrap(); - let line = Bezier::from_linear_dvec2(points[0], points[1]); + let array = js_points.dyn_into::().unwrap(); + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + let line = Bezier::from_linear_dvec2(point1, point2); let bezier_curve_svg = self.get_bezier_path(); @@ -454,8 +472,11 @@ impl WasmBezier { } pub fn intersect_quadratic_segment(&self, js_points: JsValue, error: f64, minimum_separation: f64) -> String { - let points: [DVec2; 3] = serde_wasm_bindgen::from_value(js_points).unwrap(); - let quadratic = Bezier::from_quadratic_dvec2(points[0], points[1], points[2]); + let array = js_points.dyn_into::().unwrap(); + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + let point3 = parse_point(&array.get(2)); + let quadratic = Bezier::from_quadratic_dvec2(point1, point2, point3); let bezier_curve_svg = self.get_bezier_path(); @@ -474,8 +495,12 @@ impl WasmBezier { } pub fn intersect_cubic_segment(&self, js_points: JsValue, error: f64, minimum_separation: f64) -> String { - let points: [DVec2; 4] = serde_wasm_bindgen::from_value(js_points).unwrap(); - let cubic = Bezier::from_cubic_dvec2(points[0], points[1], points[2], points[3]); + let array = js_points.dyn_into::().unwrap(); + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + let point3 = parse_point(&array.get(2)); + let point4 = parse_point(&array.get(3)); + let cubic = Bezier::from_cubic_dvec2(point1, point2, point3, point4); let bezier_curve_svg = self.get_bezier_path(); @@ -511,23 +536,25 @@ impl WasmBezier { } pub fn intersect_rectangle(&self, js_points: JsValue) -> String { - let points: [DVec2; 2] = serde_wasm_bindgen::from_value(js_points).unwrap(); + let array = js_points.dyn_into::().unwrap(); + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); let bezier_curve_svg = self.get_bezier_path(); let mut rectangle_svg = String::new(); [ - Bezier::from_linear_coordinates(points[0].x, points[0].y, points[1].x, points[0].y), - Bezier::from_linear_coordinates(points[1].x, points[0].y, points[1].x, points[1].y), - Bezier::from_linear_coordinates(points[1].x, points[1].y, points[0].x, points[1].y), - Bezier::from_linear_coordinates(points[0].x, points[1].y, points[0].x, points[0].y), + Bezier::from_linear_coordinates(point1.x, point1.y, point2.x, point1.y), + Bezier::from_linear_coordinates(point2.x, point1.y, point2.x, point2.y), + Bezier::from_linear_coordinates(point2.x, point2.y, point1.x, point2.y), + Bezier::from_linear_coordinates(point1.x, point2.y, point1.x, point1.y), ] .iter() .for_each(|line| line.to_svg(&mut rectangle_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), String::new(), String::new(), String::new())); let intersections_svg = self .0 - .rectangle_intersections(points[0], points[1]) + .rectangle_intersections(point1, point2) .iter() .map(|intersection_t| { let point = &self.0.evaluate(TValue::Parametric(*intersection_t)); @@ -650,18 +677,25 @@ impl WasmBezier { } pub fn join(&self, js_points: &JsValue) -> String { + let array = js_points.to_owned().dyn_into::().unwrap(); let other_bezier: Bezier = match self.0.get_points().count() { 2 => { - let points: [DVec2; 2] = serde_wasm_bindgen::from_value(js_points.into()).unwrap(); - Bezier::from_linear_dvec2(points[0], points[1]) + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + Bezier::from_linear_dvec2(point1, point2) } 3 => { - let points: [DVec2; 3] = serde_wasm_bindgen::from_value(js_points.into()).unwrap(); - Bezier::from_quadratic_dvec2(points[0], points[1], points[2]) + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + let point3 = parse_point(&array.get(2)); + Bezier::from_quadratic_dvec2(point1, point2, point3) } 4 => { - let points: [DVec2; 4] = serde_wasm_bindgen::from_value(js_points.into()).unwrap(); - Bezier::from_cubic_dvec2(points[0], points[1], points[2], points[3]) + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + let point3 = parse_point(&array.get(2)); + let point4 = parse_point(&array.get(3)); + Bezier::from_cubic_dvec2(point1, point2, point3, point4) } _ => unreachable!(), }; diff --git a/website/other/bezier-rs-demos/wasm/src/lib.rs b/website/other/bezier-rs-demos/wasm/src/lib.rs index 8ce59354..2cc69439 100644 --- a/website/other/bezier-rs-demos/wasm/src/lib.rs +++ b/website/other/bezier-rs-demos/wasm/src/lib.rs @@ -5,28 +5,34 @@ mod utils; use wasm_bindgen::prelude::*; +#[cfg(feature = "logging")] pub static LOGGER: WasmLog = WasmLog; +#[cfg(feature = "logging")] thread_local! { pub static HAS_CRASHED: std::cell::RefCell = const { std::cell::RefCell::new(false) } } /// Initialize the backend #[wasm_bindgen(start)] pub fn init() { - // Set up the logger with a default level of debug - log::set_logger(&LOGGER).expect("Failed to set logger"); - log::set_max_level(log::LevelFilter::Trace); + #[cfg(feature = "logging")] + { + // Set up the logger with a default level of debug + log::set_logger(&LOGGER).expect("Failed to set logger"); + log::set_max_level(log::LevelFilter::Trace); - fn panic_hook(info: &std::panic::PanicInfo<'_>) { - // Skip if we have already panicked - if HAS_CRASHED.with(|cell| cell.replace(true)) { - return; + fn panic_hook(info: &std::panic::PanicInfo<'_>) { + // Skip if we have already panicked + if HAS_CRASHED.with(|cell| cell.replace(true)) { + return; + } + log::error!("{}", info); } - log::error!("{}", info); - } - std::panic::set_hook(Box::new(panic_hook)); + std::panic::set_hook(Box::new(panic_hook)); + } } /// Logging to the JS console +#[cfg(feature = "logging")] #[wasm_bindgen] extern "C" { #[wasm_bindgen(js_namespace = console)] @@ -39,9 +45,11 @@ extern "C" { fn error(msg: &str, format: &str); } +#[cfg(feature = "logging")] #[derive(Default)] pub struct WasmLog; +#[cfg(feature = "logging")] impl log::Log for WasmLog { fn enabled(&self, metadata: &log::Metadata) -> bool { metadata.level() <= log::Level::Info diff --git a/website/other/bezier-rs-demos/wasm/src/subpath.rs b/website/other/bezier-rs-demos/wasm/src/subpath.rs index ecf62d7c..4cb738d4 100644 --- a/website/other/bezier-rs-demos/wasm/src/subpath.rs +++ b/website/other/bezier-rs-demos/wasm/src/subpath.rs @@ -1,12 +1,14 @@ use crate::svg_drawing::*; -use crate::utils::{parse_cap, parse_join}; +use crate::utils::{parse_cap, parse_join, parse_point}; use bezier_rs::{Bezier, ManipulatorGroup, Subpath, SubpathTValue, TValueType}; use glam::DVec2; +use js_sys::Array; use js_sys::Math; use std::fmt::Write; use wasm_bindgen::prelude::*; +use wasm_bindgen::{JsCast, JsValue}; #[derive(Clone, PartialEq, Hash)] pub(crate) struct EmptyId; @@ -34,8 +36,21 @@ fn parse_t_variant(t_variant: &String, t: f64) -> SubpathTValue { #[wasm_bindgen] impl WasmSubpath { /// Expects js_points to be an unbounded list of triples, where each item is a tuple of floats. + /// The input TypeScript type is: (number[] | undefined)[][] pub fn from_triples(js_points: JsValue, closed: bool) -> WasmSubpath { - let point_triples: Vec<[Option; 3]> = serde_wasm_bindgen::from_value(js_points).unwrap(); + let point_triples = js_points + .dyn_into::() + .unwrap() + .iter() + .map(|manipulator_group| { + let triple = manipulator_group.dyn_into::().unwrap(); + let anchor = parse_point(&triple.get(0)); + let in_handle = if triple.get(1).is_falsy() { None } else { Some(parse_point(&triple.get(1))) }; + let out_handle = if triple.get(2).is_falsy() { None } else { Some(parse_point(&triple.get(2))) }; + [Some(anchor), in_handle, out_handle] + }) + .collect::>(); + let manipulator_groups = point_triples .into_iter() .map(|point_triple| ManipulatorGroup { @@ -273,8 +288,10 @@ impl WasmSubpath { } pub fn intersect_line_segment(&self, js_points: JsValue, error: f64, minimum_separation: f64) -> String { - let points: [DVec2; 2] = serde_wasm_bindgen::from_value(js_points).unwrap(); - let line = Bezier::from_linear_dvec2(points[0], points[1]); + let array = js_points.dyn_into::().unwrap(); + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + let line = Bezier::from_linear_dvec2(point1, point2); let subpath_svg = self.to_default_svg(); @@ -305,8 +322,11 @@ impl WasmSubpath { } pub fn intersect_quadratic_segment(&self, js_points: JsValue, error: f64, minimum_separation: f64) -> String { - let points: [DVec2; 3] = serde_wasm_bindgen::from_value(js_points).unwrap(); - let line = Bezier::from_quadratic_dvec2(points[0], points[1], points[2]); + let array = js_points.dyn_into::().unwrap(); + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + let point3 = parse_point(&array.get(2)); + let line = Bezier::from_quadratic_dvec2(point1, point2, point3); let subpath_svg = self.to_default_svg(); @@ -337,8 +357,12 @@ impl WasmSubpath { } pub fn intersect_cubic_segment(&self, js_points: JsValue, error: f64, minimum_separation: f64) -> String { - let points: [DVec2; 4] = serde_wasm_bindgen::from_value(js_points).unwrap(); - let line = Bezier::from_cubic_dvec2(points[0], points[1], points[2], points[3]); + let array = js_points.dyn_into::().unwrap(); + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); + let point3 = parse_point(&array.get(2)); + let point4 = parse_point(&array.get(3)); + let line = Bezier::from_cubic_dvec2(point1, point2, point3, point4); let subpath_svg = self.to_default_svg(); @@ -387,23 +411,25 @@ impl WasmSubpath { } pub fn intersect_rectangle(&self, js_points: JsValue, error: f64, minimum_separation: f64) -> String { - let points: [DVec2; 2] = serde_wasm_bindgen::from_value(js_points).unwrap(); + let array = js_points.dyn_into::().unwrap(); + let point1 = parse_point(&array.get(0)); + let point2 = parse_point(&array.get(1)); let subpath_svg = self.to_default_svg(); let mut rectangle_svg = String::new(); [ - Bezier::from_linear_coordinates(points[0].x, points[0].y, points[1].x, points[0].y), - Bezier::from_linear_coordinates(points[1].x, points[0].y, points[1].x, points[1].y), - Bezier::from_linear_coordinates(points[1].x, points[1].y, points[0].x, points[1].y), - Bezier::from_linear_coordinates(points[0].x, points[1].y, points[0].x, points[0].y), + Bezier::from_linear_coordinates(point1.x, point1.y, point2.x, point1.y), + Bezier::from_linear_coordinates(point2.x, point1.y, point2.x, point2.y), + Bezier::from_linear_coordinates(point2.x, point2.y, point1.x, point2.y), + Bezier::from_linear_coordinates(point1.x, point2.y, point1.x, point1.y), ] .iter() .for_each(|line| line.to_svg(&mut rectangle_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), String::new(), String::new(), String::new())); let intersections_svg = self .0 - .rectangle_intersections(points[0], points[1], Some(error), Some(minimum_separation)) + .rectangle_intersections(point1, point2, Some(error), Some(minimum_separation)) .iter() .map(|(segment_index, intersection_t)| { let point = self.0.evaluate(SubpathTValue::Parametric { diff --git a/website/other/bezier-rs-demos/wasm/src/utils.rs b/website/other/bezier-rs-demos/wasm/src/utils.rs index b9b7ce60..1a33b14a 100644 --- a/website/other/bezier-rs-demos/wasm/src/utils.rs +++ b/website/other/bezier-rs-demos/wasm/src/utils.rs @@ -1,4 +1,7 @@ use bezier_rs::{Cap, Join}; +use glam::DVec2; +use js_sys::Array; +use wasm_bindgen::{JsCast, JsValue}; pub fn parse_join(join: i32, miter_limit: f64) -> Join { match join { @@ -17,3 +20,10 @@ pub fn parse_cap(cap: i32) -> Cap { _ => panic!("Unexpected Cap value: '{cap}'"), } } + +pub fn parse_point(js_point: &JsValue) -> DVec2 { + let point = js_point.to_owned().dyn_into::().unwrap(); + let x = point.get(0).as_f64().unwrap(); + let y = point.get(1).as_f64().unwrap(); + DVec2::new(x, y) +}