diff --git a/libraries/bezier-rs/src/bezier/core.rs b/libraries/bezier-rs/src/bezier/core.rs
index 43572ecb..2fb48d97 100644
--- a/libraries/bezier-rs/src/bezier/core.rs
+++ b/libraries/bezier-rs/src/bezier/core.rs
@@ -1,4 +1,5 @@
use super::*;
+use std::fmt::Write;
/// Functionality relating to core `Bezier` operations, such as constructors and `abs_diff_eq`.
impl Bezier {
@@ -142,14 +143,44 @@ impl Bezier {
}
}
- /// Convert `Bezier` to SVG `path`.
- pub fn to_svg(&self) -> String {
- format!(
- r#""#,
- self.start.x,
- self.start.y,
- self.svg_curve_argument()
- )
+ /// Appends to the `svg` mutable string with an SVG shape representation of the curve.
+ pub fn curve_to_svg(&self, svg: &mut String, attributes: String) {
+ let _ = write!(svg, r#""#, self.start.x, self.start.y, self.svg_curve_argument(), attributes);
+ }
+
+ /// Appends to the `svg` mutable string with an SVG shape representation of the handle lines.
+ pub fn handle_lines_to_svg(&self, svg: &mut String, attributes: String) {
+ let _ = write!(svg, r#""#, self.svg_handle_line_argument().unwrap_or_else(|| "".to_string()), attributes);
+ }
+
+ /// Appends to the `svg` mutable string with an SVG shape representation of the anchors.
+ pub fn anchors_to_svg(&self, svg: &mut String, attributes: String) {
+ let _ = write!(
+ svg,
+ r#""#,
+ self.start.x, self.start.y, self.end.x, self.end.y
+ );
+ }
+
+ /// Appends to the `svg` mutable string with an SVG shape representation of the handles.
+ pub fn handles_to_svg(&self, svg: &mut String, attributes: String) {
+ if let BezierHandles::Quadratic { handle } = self.handles {
+ let _ = write!(svg, r#""#, handle.x, handle.y);
+ } else if let BezierHandles::Cubic { handle_start, handle_end } = self.handles {
+ let _ = write!(
+ svg,
+ r#""#,
+ handle_start.x, handle_start.y, handle_end.x, handle_end.y
+ );
+ };
+ }
+
+ /// Appends to the `svg` mutable string with an SVG shape representation that includes the curve, the handle lines, the anchors, and the handles.
+ pub fn to_svg(&self, svg: &mut String, curve_attributes: String, anchor_attributes: String, handle_attributes: String, handle_line_attributes: String) {
+ self.curve_to_svg(svg, curve_attributes);
+ self.handle_lines_to_svg(svg, handle_line_attributes);
+ self.anchors_to_svg(svg, anchor_attributes);
+ self.handles_to_svg(svg, handle_attributes);
}
/// Returns true if the corresponding points of the two `Bezier`s are within the provided absolute value difference from each other.
diff --git a/libraries/bezier-rs/src/lib.rs b/libraries/bezier-rs/src/lib.rs
index 3b0d26b2..0c2482af 100644
--- a/libraries/bezier-rs/src/lib.rs
+++ b/libraries/bezier-rs/src/lib.rs
@@ -3,7 +3,9 @@
mod bezier;
mod consts;
mod subpath;
+mod svg;
mod utils;
pub use bezier::*;
pub use subpath::*;
+pub use svg::*;
diff --git a/libraries/bezier-rs/src/subpath/core.rs b/libraries/bezier-rs/src/subpath/core.rs
index 7dc43f35..d466d1ac 100644
--- a/libraries/bezier-rs/src/subpath/core.rs
+++ b/libraries/bezier-rs/src/subpath/core.rs
@@ -1,5 +1,6 @@
use super::*;
use crate::consts::*;
+use crate::ToSVGOptions;
/// Functionality relating to core `Subpath` operations, such as constructors and `iter`.
impl Subpath {
diff --git a/libraries/bezier-rs/src/subpath/structs.rs b/libraries/bezier-rs/src/subpath/structs.rs
index f2edb960..1ad55dac 100644
--- a/libraries/bezier-rs/src/subpath/structs.rs
+++ b/libraries/bezier-rs/src/subpath/structs.rs
@@ -6,78 +6,3 @@ pub struct ManipulatorGroup {
pub in_handle: Option,
pub out_handle: Option,
}
-
-/// Structure to represent optional parameters that can be passed to the `into_svg` function.
-pub struct ToSVGOptions {
- /// Color of the line segments along the `Subpath`. Defaulted to `black`.
- pub curve_stroke_color: String,
- /// Width of the line segments along the `Subpath`. Defaulted to `2.`.
- pub curve_stroke_width: f64,
- /// Stroke color outlining circles marking anchors on the `Subpath`. Defaulted to `black`.
- pub anchor_stroke_color: String,
- /// Stroke width outlining circles marking anchors on the `Subpath`. Defaulted to `2.`.
- pub anchor_stroke_width: f64,
- /// Radius of the circles marking anchors on the `Subpath`. Defaulted to `4.`.
- pub anchor_radius: f64,
- /// Fill color of the circles marking anchors on the `Subpath`. Defaulted to `white`.
- pub anchor_fill: String,
- /// Color of the line segments connecting anchors to handle points. Defaulted to `gray`.
- pub handle_line_stroke_color: String,
- /// Width of the line segments connecting anchors to handle points. Defaulted to `1.`.
- pub handle_line_stroke_width: f64,
- /// Stroke color outlining circles marking the handles of `Subpath`. Defaulted to `gray`.
- pub handle_point_stroke_color: String,
- /// Stroke color outlining circles marking the handles of `Subpath`. Defaulted to `1.5`.
- pub handle_point_stroke_width: f64,
- /// Radius of the circles marking the handles of `Subpath`. Defaulted to `3.`.
- pub handle_point_radius: f64,
- /// Fill color of the circles marking the handles of `Subpath`. Defaulted to `white`.
- pub handle_point_fill: String,
-}
-
-impl ToSVGOptions {
- /// Combine and format curve styling options for an SVG path.
- pub(crate) fn formatted_curve_arguments(&self) -> String {
- format!(r#"stroke="{}" stroke-width="{}" fill="none""#, self.curve_stroke_color, self.curve_stroke_width)
- }
-
- /// Combine and format anchor styling options an SVG circle.
- pub(crate) fn formatted_anchor_arguments(&self) -> String {
- format!(
- r#"r="{}", stroke="{}" stroke-width="{}" fill="{}""#,
- self.anchor_radius, self.anchor_stroke_color, self.anchor_stroke_width, self.anchor_fill
- )
- }
-
- /// Combine and format handle point styling options for an SVG circle.
- pub(crate) fn formatted_handle_point_arguments(&self) -> String {
- format!(
- r#"r="{}", stroke="{}" stroke-width="{}" fill="{}""#,
- self.handle_point_radius, self.handle_point_stroke_color, self.handle_point_stroke_width, self.handle_point_fill
- )
- }
-
- /// Combine and format handle line styling options an SVG path.
- pub(crate) fn formatted_handle_line_arguments(&self) -> String {
- format!(r#"stroke="{}" stroke-width="{}" fill="none""#, self.handle_line_stroke_color, self.handle_line_stroke_width)
- }
-}
-
-impl Default for ToSVGOptions {
- fn default() -> Self {
- ToSVGOptions {
- curve_stroke_color: String::from("black"),
- curve_stroke_width: 2.,
- anchor_stroke_color: String::from("black"),
- anchor_stroke_width: 2.,
- anchor_radius: 4.,
- anchor_fill: String::from("white"),
- handle_line_stroke_color: String::from("gray"),
- handle_line_stroke_width: 1.,
- handle_point_stroke_color: String::from("gray"),
- handle_point_stroke_width: 1.5,
- handle_point_radius: 3.,
- handle_point_fill: String::from("white"),
- }
- }
-}
diff --git a/libraries/bezier-rs/src/svg.rs b/libraries/bezier-rs/src/svg.rs
new file mode 100644
index 00000000..d7352c01
--- /dev/null
+++ b/libraries/bezier-rs/src/svg.rs
@@ -0,0 +1,74 @@
+/// Structure to represent optional parameters that can be passed to the `into_svg` function.
+pub struct ToSVGOptions {
+ /// Color of the line segments along the `Subpath`. Defaulted to `black`.
+ pub curve_stroke_color: String,
+ /// Width of the line segments along the `Subpath`. Defaulted to `2.`.
+ pub curve_stroke_width: f64,
+ /// Stroke color outlining circles marking anchors on the `Subpath`. Defaulted to `black`.
+ pub anchor_stroke_color: String,
+ /// Stroke width outlining circles marking anchors on the `Subpath`. Defaulted to `2.`.
+ pub anchor_stroke_width: f64,
+ /// Radius of the circles marking anchors on the `Subpath`. Defaulted to `4.`.
+ pub anchor_radius: f64,
+ /// Fill color of the circles marking anchors on the `Subpath`. Defaulted to `white`.
+ pub anchor_fill: String,
+ /// Color of the line segments connecting anchors to handle points. Defaulted to `gray`.
+ pub handle_line_stroke_color: String,
+ /// Width of the line segments connecting anchors to handle points. Defaulted to `1.`.
+ pub handle_line_stroke_width: f64,
+ /// Stroke color outlining circles marking the handles of `Subpath`. Defaulted to `gray`.
+ pub handle_point_stroke_color: String,
+ /// Stroke color outlining circles marking the handles of `Subpath`. Defaulted to `1.5`.
+ pub handle_point_stroke_width: f64,
+ /// Radius of the circles marking the handles of `Subpath`. Defaulted to `3.`.
+ pub handle_point_radius: f64,
+ /// Fill color of the circles marking the handles of `Subpath`. Defaulted to `white`.
+ pub handle_point_fill: String,
+}
+
+impl ToSVGOptions {
+ /// Combine and format curve styling options for an SVG path.
+ pub(crate) fn formatted_curve_arguments(&self) -> String {
+ format!(r#"stroke="{}" stroke-width="{}" fill="none""#, self.curve_stroke_color, self.curve_stroke_width)
+ }
+
+ /// Combine and format anchor styling options an SVG circle.
+ pub(crate) fn formatted_anchor_arguments(&self) -> String {
+ format!(
+ r#"r="{}", stroke="{}" stroke-width="{}" fill="{}""#,
+ self.anchor_radius, self.anchor_stroke_color, self.anchor_stroke_width, self.anchor_fill
+ )
+ }
+
+ /// Combine and format handle point styling options for an SVG circle.
+ pub(crate) fn formatted_handle_point_arguments(&self) -> String {
+ format!(
+ r#"r="{}", stroke="{}" stroke-width="{}" fill="{}""#,
+ self.handle_point_radius, self.handle_point_stroke_color, self.handle_point_stroke_width, self.handle_point_fill
+ )
+ }
+
+ /// Combine and format handle line styling options an SVG path.
+ pub(crate) fn formatted_handle_line_arguments(&self) -> String {
+ format!(r#"stroke="{}" stroke-width="{}" fill="none""#, self.handle_line_stroke_color, self.handle_line_stroke_width)
+ }
+}
+
+impl Default for ToSVGOptions {
+ fn default() -> Self {
+ ToSVGOptions {
+ curve_stroke_color: String::from("black"),
+ curve_stroke_width: 2.,
+ anchor_stroke_color: String::from("black"),
+ anchor_stroke_width: 2.,
+ anchor_radius: 4.,
+ anchor_fill: String::from("white"),
+ handle_line_stroke_color: String::from("gray"),
+ handle_line_stroke_width: 1.,
+ handle_point_stroke_color: String::from("gray"),
+ handle_point_stroke_width: 1.5,
+ handle_point_radius: 3.,
+ handle_point_fill: String::from("white"),
+ }
+ }
+}
diff --git a/website/other/bezier-rs-demos/vue.config.js b/website/other/bezier-rs-demos/vue.config.js
index a29ed969..dc251f5a 100644
--- a/website/other/bezier-rs-demos/vue.config.js
+++ b/website/other/bezier-rs-demos/vue.config.js
@@ -23,7 +23,7 @@ module.exports = defineConfig({
crateDirectory: path.resolve(__dirname, "wasm"),
// Remove when this issue is resolved: https://github.com/wasm-tool/wasm-pack-plugin/issues/93
outDir: path.resolve(__dirname, "wasm/pkg"),
- watchDirectories: ["../../lib"].map((folder) => path.resolve(__dirname, folder)),
+ watchDirectories: ["../../../libraries/bezier-rs"].map((folder) => path.resolve(__dirname, folder)),
})
)
.end();
diff --git a/website/other/bezier-rs-demos/wasm/src/bezier.rs b/website/other/bezier-rs-demos/wasm/src/bezier.rs
new file mode 100644
index 00000000..9d4deaf9
--- /dev/null
+++ b/website/other/bezier-rs-demos/wasm/src/bezier.rs
@@ -0,0 +1,264 @@
+use crate::svg_drawing::*;
+use bezier_rs::{ArcStrategy, ArcsOptions, Bezier, ProjectionOptions, ToSVGOptions};
+use glam::DVec2;
+use serde::{Deserialize, Serialize};
+use wasm_bindgen::prelude::*;
+
+#[derive(Serialize, Deserialize)]
+struct CircleSector {
+ center: Point,
+ radius: f64,
+ #[serde(rename = "startAngle")]
+ start_angle: f64,
+ #[serde(rename = "endAngle")]
+ end_angle: f64,
+}
+
+#[derive(Serialize, Deserialize)]
+struct Point {
+ x: f64,
+ y: f64,
+}
+
+#[wasm_bindgen]
+pub enum WasmMaximizeArcs {
+ Automatic, // 0
+ On, // 1
+ Off, // 2
+}
+
+/// Wrapper of the `Bezier` struct to be used in JS.
+#[wasm_bindgen]
+#[derive(Clone)]
+pub struct WasmBezier(Bezier);
+
+/// Convert a `DVec2` into a `Point`.
+fn vec_to_point(p: &DVec2) -> Point {
+ Point { x: p.x, y: p.y }
+}
+
+/// Convert a bezier to a list of points.
+fn bezier_to_points(bezier: Bezier) -> Vec {
+ bezier.get_points().map(|point| Point { x: point.x, y: point.y }).collect()
+}
+
+/// Serialize some data and then convert it to a JsValue.
+fn to_js_value(data: T) -> JsValue {
+ JsValue::from_serde(&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,
+ WasmMaximizeArcs::On => ArcStrategy::FavorLargerArcs,
+ WasmMaximizeArcs::Off => ArcStrategy::FavorCorrectness,
+ }
+}
+
+#[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] = js_points.into_serde().unwrap();
+ WasmBezier(Bezier::from_linear_dvec2(points[0], points[1]))
+ }
+
+ /// Expect js_points to be a list of 3 pairs.
+ pub fn new_quadratic(js_points: &JsValue) -> WasmBezier {
+ let points: [DVec2; 3] = js_points.into_serde().unwrap();
+ WasmBezier(Bezier::from_quadratic_dvec2(points[0], points[1], points[2]))
+ }
+
+ /// Expect js_points to be a list of 4 pairs.
+ pub fn new_cubic(js_points: &JsValue) -> WasmBezier {
+ let points: [DVec2; 4] = js_points.into_serde().unwrap();
+ WasmBezier(Bezier::from_cubic_dvec2(points[0], points[1], points[2], points[3]))
+ }
+
+ pub fn quadratic_through_points(js_points: &JsValue, t: f64) -> WasmBezier {
+ let points: [DVec2; 3] = js_points.into_serde().unwrap();
+ WasmBezier(Bezier::quadratic_through_points(points[0], points[1], points[2], Some(t)))
+ }
+
+ pub fn cubic_through_points(js_points: &JsValue, t: f64, midpoint_separation: f64) -> WasmBezier {
+ let points: [DVec2; 3] = js_points.into_serde().unwrap();
+ WasmBezier(Bezier::cubic_through_points(points[0], points[1], points[2], Some(t), Some(midpoint_separation)))
+ }
+
+ pub fn set_start(&mut self, x: f64, y: f64) {
+ self.0.set_start(DVec2::new(x, y));
+ }
+
+ pub fn set_end(&mut self, x: f64, y: f64) {
+ self.0.set_end(DVec2::new(x, y));
+ }
+
+ pub fn set_handle_start(&mut self, x: f64, y: f64) {
+ self.0.set_handle_start(DVec2::new(x, y));
+ }
+
+ pub fn set_handle_end(&mut self, x: f64, y: f64) {
+ self.0.set_handle_end(DVec2::new(x, y));
+ }
+
+ /// The wrapped return type is `Vec`.
+ pub fn get_points(&self) -> JsValue {
+ let points: Vec = self.0.get_points().map(|point| vec_to_point(&point)).collect();
+ to_js_value(points)
+ }
+
+ pub fn to_svg(&self) -> String {
+ let mut bezier = String::new();
+ self.0.to_svg(
+ &mut bezier,
+ CURVE_ATTRIBUTES.to_string(),
+ ANCHOR_ATTRIBUTES.to_string(),
+ HANDLE_ATTRIBUTES.to_string(),
+ HANDLE_LINE_ATTRIBUTES.to_string(),
+ );
+ format!("{}{}{}", SVG_OPEN_TAG, bezier, SVG_CLOSE_TAG)
+ }
+
+ pub fn length(&self) -> f64 {
+ self.0.length(None)
+ }
+
+ /// The wrapped return type is `Point`.
+ pub fn evaluate(&self, t: f64) -> JsValue {
+ let point: Point = vec_to_point(&self.0.evaluate(t));
+ to_js_value(point)
+ }
+
+ /// The wrapped return type is `Vec`.
+ pub fn compute_lookup_table(&self, steps: usize) -> JsValue {
+ let table_values: Vec = self.0.compute_lookup_table(Some(steps)).iter().map(vec_to_point).collect();
+ to_js_value(table_values)
+ }
+
+ pub fn derivative(&self) -> Option {
+ self.0.derivative().map(WasmBezier)
+ }
+
+ /// The wrapped return type is `Point`.
+ pub fn tangent(&self, t: f64) -> JsValue {
+ let tangent_point: Point = vec_to_point(&self.0.tangent(t));
+ to_js_value(tangent_point)
+ }
+
+ /// The wrapped return type is `Point`.
+ pub fn normal(&self, t: f64) -> JsValue {
+ let normal_point: Point = vec_to_point(&self.0.normal(t));
+ to_js_value(normal_point)
+ }
+
+ pub fn curvature(&self, t: f64) -> f64 {
+ self.0.curvature(t)
+ }
+
+ /// The wrapped return type is `[Vec; 2]`.
+ pub fn split(&self, t: f64) -> JsValue {
+ let bezier_points: [Vec; 2] = self.0.split(t).map(bezier_to_points);
+ to_js_value(bezier_points)
+ }
+
+ pub fn trim(&self, t1: f64, t2: f64) -> WasmBezier {
+ WasmBezier(self.0.trim(t1, t2))
+ }
+
+ pub fn project(&self, x: f64, y: f64) -> f64 {
+ self.0.project(DVec2::new(x, y), ProjectionOptions::default())
+ }
+
+ /// The wrapped return type is `[Vec; 2]`.
+ pub fn local_extrema(&self) -> JsValue {
+ let local_extrema: [Vec; 2] = self.0.local_extrema();
+ to_js_value(local_extrema)
+ }
+
+ /// The wrapped return type is `[Point; 2]`.
+ pub fn bounding_box(&self) -> JsValue {
+ let bbox_points: [Point; 2] = self.0.bounding_box().map(|p| Point { x: p.x, y: p.y });
+ to_js_value(bbox_points)
+ }
+
+ /// The wrapped return type is `Vec`.
+ pub fn inflections(&self) -> JsValue {
+ let inflections: Vec = self.0.inflections();
+ to_js_value(inflections)
+ }
+
+ /// The wrapped return type is `Vec>`.
+ pub fn de_casteljau_points(&self, t: f64) -> JsValue {
+ let points: Vec> = self
+ .0
+ .de_casteljau_points(t)
+ .iter()
+ .map(|level| level.iter().map(|&point| Point { x: point.x, y: point.y }).collect::>())
+ .collect();
+ to_js_value(points)
+ }
+
+ pub fn rotate(&self, angle: f64) -> WasmBezier {
+ WasmBezier(self.0.rotate(angle))
+ }
+
+ fn intersect(&self, curve: &Bezier, error: Option) -> Vec {
+ self.0.intersections(curve, error)
+ }
+
+ pub fn intersect_line_segment(&self, js_points: &JsValue) -> Vec {
+ let points: [DVec2; 2] = js_points.into_serde().unwrap();
+ let line = Bezier::from_linear_dvec2(points[0], points[1]);
+ self.intersect(&line, None)
+ }
+
+ pub fn intersect_quadratic_segment(&self, js_points: &JsValue, error: f64) -> Vec {
+ let points: [DVec2; 3] = js_points.into_serde().unwrap();
+ let quadratic = Bezier::from_quadratic_dvec2(points[0], points[1], points[2]);
+ self.intersect(&quadratic, Some(error))
+ }
+
+ pub fn intersect_cubic_segment(&self, js_points: &JsValue, error: f64) -> Vec {
+ let points: [DVec2; 4] = js_points.into_serde().unwrap();
+ let cubic = Bezier::from_cubic_dvec2(points[0], points[1], points[2], points[3]);
+ self.intersect(&cubic, Some(error))
+ }
+
+ /// The wrapped return type is `Vec<[f64; 2]>`.
+ pub fn intersect_self(&self, error: f64) -> JsValue {
+ let points: Vec<[f64; 2]> = self.0.self_intersections(Some(error));
+ to_js_value(points)
+ }
+
+ pub fn reduce(&self) -> JsValue {
+ let bezier_points: Vec> = self.0.reduce(None).into_iter().map(bezier_to_points).collect();
+ to_js_value(bezier_points)
+ }
+
+ /// The wrapped return type is `Vec>`.
+ pub fn offset(&self, distance: f64) -> JsValue {
+ let bezier_points: Vec> = self.0.offset(distance).into_iter().map(bezier_to_points).collect();
+ to_js_value(bezier_points)
+ }
+
+ /// The wrapped return type is `Vec`.
+ pub fn arcs(&self, error: f64, max_iterations: usize, maximize_arcs: WasmMaximizeArcs) -> JsValue {
+ let strategy = convert_wasm_maximize_arcs(maximize_arcs);
+ let options = ArcsOptions { error, max_iterations, strategy };
+ let circle_sectors: Vec = self
+ .0
+ .arcs(options)
+ .iter()
+ .map(|sector| CircleSector {
+ center: Point {
+ x: sector.center.x,
+ y: sector.center.y,
+ },
+ radius: sector.radius,
+ start_angle: sector.start_angle,
+ end_angle: sector.end_angle,
+ })
+ .collect();
+ to_js_value(circle_sectors)
+ }
+}
diff --git a/website/other/bezier-rs-demos/wasm/src/lib.rs b/website/other/bezier-rs-demos/wasm/src/lib.rs
index 3bae2ee3..0ac7850c 100644
--- a/website/other/bezier-rs-demos/wasm/src/lib.rs
+++ b/website/other/bezier-rs-demos/wasm/src/lib.rs
@@ -1,265 +1,3 @@
+pub mod bezier;
pub mod subpath;
mod svg_drawing;
-
-use bezier_rs::{ArcStrategy, ArcsOptions, Bezier, ProjectionOptions};
-use glam::DVec2;
-use serde::{Deserialize, Serialize};
-use wasm_bindgen::prelude::*;
-
-#[derive(Serialize, Deserialize)]
-struct CircleSector {
- center: Point,
- radius: f64,
- #[serde(rename = "startAngle")]
- start_angle: f64,
- #[serde(rename = "endAngle")]
- end_angle: f64,
-}
-
-#[derive(Serialize, Deserialize)]
-struct Point {
- x: f64,
- y: f64,
-}
-
-#[wasm_bindgen]
-pub enum WasmMaximizeArcs {
- Automatic, // 0
- On, // 1
- Off, // 2
-}
-
-/// Wrapper of the `Bezier` struct to be used in JS.
-#[wasm_bindgen]
-#[derive(Clone)]
-pub struct WasmBezier(Bezier);
-
-impl Drop for WasmBezier {
- fn drop(&mut self) {
- // Is it correct to keep this empty?
- // Consider removing after https://github.com/rustwasm/wasm-bindgen/pull/2984 is merged and released
- }
-}
-
-/// Convert a `DVec2` into a `Point`.
-fn vec_to_point(p: &DVec2) -> Point {
- Point { x: p.x, y: p.y }
-}
-
-/// Convert a bezier to a list of points.
-fn bezier_to_points(bezier: Bezier) -> Vec {
- bezier.get_points().map(|point| Point { x: point.x, y: point.y }).collect()
-}
-
-/// Serialize some data and then convert it to a JsValue.
-fn to_js_value(data: T) -> JsValue {
- JsValue::from_serde(&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,
- WasmMaximizeArcs::On => ArcStrategy::FavorLargerArcs,
- WasmMaximizeArcs::Off => ArcStrategy::FavorCorrectness,
- }
-}
-
-#[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] = js_points.into_serde().unwrap();
- WasmBezier(Bezier::from_linear_dvec2(points[0], points[1]))
- }
-
- /// Expect js_points to be a list of 3 pairs.
- pub fn new_quadratic(js_points: &JsValue) -> WasmBezier {
- let points: [DVec2; 3] = js_points.into_serde().unwrap();
- WasmBezier(Bezier::from_quadratic_dvec2(points[0], points[1], points[2]))
- }
-
- /// Expect js_points to be a list of 4 pairs.
- pub fn new_cubic(js_points: &JsValue) -> WasmBezier {
- let points: [DVec2; 4] = js_points.into_serde().unwrap();
- WasmBezier(Bezier::from_cubic_dvec2(points[0], points[1], points[2], points[3]))
- }
-
- pub fn quadratic_through_points(js_points: &JsValue, t: f64) -> WasmBezier {
- let points: [DVec2; 3] = js_points.into_serde().unwrap();
- WasmBezier(Bezier::quadratic_through_points(points[0], points[1], points[2], Some(t)))
- }
-
- pub fn cubic_through_points(js_points: &JsValue, t: f64, midpoint_separation: f64) -> WasmBezier {
- let points: [DVec2; 3] = js_points.into_serde().unwrap();
- WasmBezier(Bezier::cubic_through_points(points[0], points[1], points[2], Some(t), Some(midpoint_separation)))
- }
-
- pub fn set_start(&mut self, x: f64, y: f64) {
- self.0.set_start(DVec2::new(x, y));
- }
-
- pub fn set_end(&mut self, x: f64, y: f64) {
- self.0.set_end(DVec2::new(x, y));
- }
-
- pub fn set_handle_start(&mut self, x: f64, y: f64) {
- self.0.set_handle_start(DVec2::new(x, y));
- }
-
- pub fn set_handle_end(&mut self, x: f64, y: f64) {
- self.0.set_handle_end(DVec2::new(x, y));
- }
-
- /// The wrapped return type is `Vec`.
- pub fn get_points(&self) -> JsValue {
- let points: Vec = self.0.get_points().map(|point| vec_to_point(&point)).collect();
- to_js_value(points)
- }
-
- pub fn to_svg(&self) -> String {
- self.0.to_svg()
- }
-
- pub fn length(&self) -> f64 {
- self.0.length(None)
- }
-
- /// The wrapped return type is `Point`.
- pub fn evaluate(&self, t: f64) -> JsValue {
- let point: Point = vec_to_point(&self.0.evaluate(t));
- to_js_value(point)
- }
-
- /// The wrapped return type is `Vec`.
- pub fn compute_lookup_table(&self, steps: usize) -> JsValue {
- let table_values: Vec = self.0.compute_lookup_table(Some(steps)).iter().map(vec_to_point).collect();
- to_js_value(table_values)
- }
-
- pub fn derivative(&self) -> Option {
- self.0.derivative().map(WasmBezier)
- }
-
- /// The wrapped return type is `Point`.
- pub fn tangent(&self, t: f64) -> JsValue {
- let tangent_point: Point = vec_to_point(&self.0.tangent(t));
- to_js_value(tangent_point)
- }
-
- /// The wrapped return type is `Point`.
- pub fn normal(&self, t: f64) -> JsValue {
- let normal_point: Point = vec_to_point(&self.0.normal(t));
- to_js_value(normal_point)
- }
-
- pub fn curvature(&self, t: f64) -> f64 {
- self.0.curvature(t)
- }
-
- /// The wrapped return type is `[Vec; 2]`.
- pub fn split(&self, t: f64) -> JsValue {
- let bezier_points: [Vec; 2] = self.0.split(t).map(bezier_to_points);
- to_js_value(bezier_points)
- }
-
- pub fn trim(&self, t1: f64, t2: f64) -> WasmBezier {
- WasmBezier(self.0.trim(t1, t2))
- }
-
- pub fn project(&self, x: f64, y: f64) -> f64 {
- self.0.project(DVec2::new(x, y), ProjectionOptions::default())
- }
-
- /// The wrapped return type is `[Vec; 2]`.
- pub fn local_extrema(&self) -> JsValue {
- let local_extrema: [Vec; 2] = self.0.local_extrema();
- to_js_value(local_extrema)
- }
-
- /// The wrapped return type is `[Point; 2]`.
- pub fn bounding_box(&self) -> JsValue {
- let bbox_points: [Point; 2] = self.0.bounding_box().map(|p| Point { x: p.x, y: p.y });
- to_js_value(bbox_points)
- }
-
- /// The wrapped return type is `Vec`.
- pub fn inflections(&self) -> JsValue {
- let inflections: Vec = self.0.inflections();
- to_js_value(inflections)
- }
-
- /// The wrapped return type is `Vec>`.
- pub fn de_casteljau_points(&self, t: f64) -> JsValue {
- let points: Vec> = self
- .0
- .de_casteljau_points(t)
- .iter()
- .map(|level| level.iter().map(|&point| Point { x: point.x, y: point.y }).collect::>())
- .collect();
- to_js_value(points)
- }
-
- pub fn rotate(&self, angle: f64) -> WasmBezier {
- WasmBezier(self.0.rotate(angle))
- }
-
- fn intersect(&self, curve: &Bezier, error: Option) -> Vec {
- self.0.intersections(curve, error)
- }
-
- pub fn intersect_line_segment(&self, js_points: &JsValue) -> Vec {
- let points: [DVec2; 2] = js_points.into_serde().unwrap();
- let line = Bezier::from_linear_dvec2(points[0], points[1]);
- self.intersect(&line, None)
- }
-
- pub fn intersect_quadratic_segment(&self, js_points: &JsValue, error: f64) -> Vec {
- let points: [DVec2; 3] = js_points.into_serde().unwrap();
- let quadratic = Bezier::from_quadratic_dvec2(points[0], points[1], points[2]);
- self.intersect(&quadratic, Some(error))
- }
-
- pub fn intersect_cubic_segment(&self, js_points: &JsValue, error: f64) -> Vec {
- let points: [DVec2; 4] = js_points.into_serde().unwrap();
- let cubic = Bezier::from_cubic_dvec2(points[0], points[1], points[2], points[3]);
- self.intersect(&cubic, Some(error))
- }
-
- /// The wrapped return type is `Vec<[f64; 2]>`.
- pub fn intersect_self(&self, error: f64) -> JsValue {
- let points: Vec<[f64; 2]> = self.0.self_intersections(Some(error));
- to_js_value(points)
- }
-
- pub fn reduce(&self) -> JsValue {
- let bezier_points: Vec> = self.0.reduce(None).into_iter().map(bezier_to_points).collect();
- to_js_value(bezier_points)
- }
-
- /// The wrapped return type is `Vec>`.
- pub fn offset(&self, distance: f64) -> JsValue {
- let bezier_points: Vec> = self.0.offset(distance).into_iter().map(bezier_to_points).collect();
- to_js_value(bezier_points)
- }
-
- /// The wrapped return type is `Vec`.
- pub fn arcs(&self, error: f64, max_iterations: usize, maximize_arcs: WasmMaximizeArcs) -> JsValue {
- let strategy = convert_wasm_maximize_arcs(maximize_arcs);
- let options = ArcsOptions { error, max_iterations, strategy };
- let circle_sectors: Vec = self
- .0
- .arcs(options)
- .iter()
- .map(|sector| CircleSector {
- center: Point {
- x: sector.center.x,
- y: sector.center.y,
- },
- radius: sector.radius,
- start_angle: sector.start_angle,
- end_angle: sector.end_angle,
- })
- .collect();
- to_js_value(circle_sectors)
- }
-}
diff --git a/website/other/bezier-rs-demos/wasm/src/svg_drawing.rs b/website/other/bezier-rs-demos/wasm/src/svg_drawing.rs
index 5b78bd76..f65b0de0 100644
--- a/website/other/bezier-rs-demos/wasm/src/svg_drawing.rs
+++ b/website/other/bezier-rs-demos/wasm/src/svg_drawing.rs
@@ -5,6 +5,12 @@ pub const SVG_CLOSE_TAG: &str = "";
// Stylistic constants
pub const BLACK: &str = "black";
+// Default attributes
+pub const CURVE_ATTRIBUTES: &str = "stroke=\"black\" stroke-width=\"2\" fill=\"none\"";
+pub const HANDLE_LINE_ATTRIBUTES: &str = "stroke=\"gray\" stroke-width=\"1\" fill=\"none\"";
+pub const ANCHOR_ATTRIBUTES: &str = "r=\"4\" stroke=\"black\" stroke-width=\"2\" fill=\"white\"";
+pub const HANDLE_ATTRIBUTES: &str = "r=\"3\" stroke=\"gray\" stroke-width=\"1.5\" fill=\"white\"";
+
/// Helper function to create an SVG text entitty.
pub fn draw_text(text: String, x_pos: f64, y_pos: f64, fill: &str) -> String {
format!(r#"{text}"#)