Remove serde from Bezier-rs web demos to reduce Wasm size

This commit is contained in:
Keavon Chambers 2024-09-24 01:20:39 -07:00
parent aa03dc8278
commit 0b0169a415
7 changed files with 152 additions and 73 deletions

3
Cargo.lock generated
View file

@ -533,9 +533,6 @@ dependencies = [
"glam",
"js-sys",
"log",
"serde",
"serde-wasm-bindgen",
"serde_json",
"wasm-bindgen",
]

View file

@ -11,7 +11,7 @@ const bezierFeatures = {
"bezier-through-points": {
name: "Bezier Through Points",
callback: (bezier: WasmBezierInstance, options: Record<string, number>): 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 = [

View file

@ -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

View file

@ -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<T: serde::Serialize>(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::<Array>().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::<Array>().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::<Array>().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::<Array>().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::<Array>().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::<Vec<DVec2>>())
JsValue::from(
self.0
.get_points()
.map(|point| [JsValue::from_f64(point.x), JsValue::from_f64(point.y)].iter().collect::<Array>())
.collect::<Array>(),
)
}
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::<Array>().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::<Array>().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::<Array>().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::<Array>().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::<Array>().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!(),
};

View file

@ -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<bool> = 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

View file

@ -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<DVec2>; 3]> = serde_wasm_bindgen::from_value(js_points).unwrap();
let point_triples = js_points
.dyn_into::<Array>()
.unwrap()
.iter()
.map(|manipulator_group| {
let triple = manipulator_group.dyn_into::<Array>().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::<Vec<_>>();
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::<Array>().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::<Array>().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::<Array>().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::<Array>().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 {

View file

@ -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::<Array>().unwrap();
let x = point.get(0).as_f64().unwrap();
let y = point.get(1).as_f64().unwrap();
DVec2::new(x, y)
}