diff --git a/libraries/bezier-rs/src/lib.rs b/libraries/bezier-rs/src/lib.rs index 0c2482af..3b0d26b2 100644 --- a/libraries/bezier-rs/src/lib.rs +++ b/libraries/bezier-rs/src/lib.rs @@ -3,9 +3,7 @@ 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 d466d1ac..d791ec63 100644 --- a/libraries/bezier-rs/src/subpath/core.rs +++ b/libraries/bezier-rs/src/subpath/core.rs @@ -1,6 +1,6 @@ use super::*; use crate::consts::*; -use crate::ToSVGOptions; +use std::fmt::Write; /// Functionality relating to core `Subpath` operations, such as constructors and `iter`. impl Subpath { @@ -45,45 +45,59 @@ impl Subpath { SubpathIter { sub_path: self, index: 0 } } - /// Returns an SVG representation of the `Subpath`. - pub fn to_svg(&self, options: ToSVGOptions) -> String { - if self.is_empty() { - return String::new(); - } - + /// 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 curve_start_argument = format!("{SVG_ARG_MOVE}{} {}", self[0].anchor.x, self[0].anchor.y); let mut curve_arguments: Vec = self.iter().map(|bezier| bezier.svg_curve_argument()).collect(); if self.closed { curve_arguments.push(String::from(SVG_ARG_CLOSED)); } - let anchor_arguments = options.formatted_anchor_arguments(); - let anchor_circles = self + let _ = write!(svg, r#""#, curve_start_argument, curve_arguments.join(" ")); + } + + /// 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 handle_lines: Vec = self.iter().filter_map(|bezier| bezier.svg_handle_line_argument()).collect(); + let _ = write!(svg, r#""#, handle_lines.join(" ")); + } + + /// 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 anchors = self .manipulator_groups .iter() - .map(|point| format!(r#""#, point.anchor.x, point.anchor.y, anchor_arguments)) + .map(|point| format!(r#""#, point.anchor.x, point.anchor.y)) .collect::>(); + let _ = write!(svg, "{}", anchors.concat()); + } - let handle_point_arguments = options.formatted_handle_point_arguments(); - let handle_circles: Vec = self + /// 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) { + let handles = self .manipulator_groups .iter() .flat_map(|group| [group.in_handle, group.out_handle]) .flatten() - .map(|handle| format!(r#""#, handle.x, handle.y, handle_point_arguments)) - .collect(); + .map(|handle| format!(r#""#, handle.x, handle.y)) + .collect::>(); + let _ = write!(svg, "{}", handles.concat()); + } - let handle_pieces: Vec = self.iter().filter_map(|bezier| bezier.svg_handle_line_argument()).collect(); - - format!( - r#"{}{}"#, - curve_start_argument, - curve_arguments.join(" "), - options.formatted_curve_arguments(), - handle_pieces.join(" "), - options.formatted_handle_line_arguments(), - handle_circles.join(""), - anchor_circles.join(""), - ) + /// Returns an SVG representation of the `Subpath`. + /// 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) { + if !curve_attributes.is_empty() { + self.curve_to_svg(svg, curve_attributes); + } + if !handle_line_attributes.is_empty() { + self.handle_lines_to_svg(svg, handle_line_attributes); + } + if !anchor_attributes.is_empty() { + self.anchors_to_svg(svg, anchor_attributes); + } + if !handle_attributes.is_empty() { + self.handles_to_svg(svg, handle_attributes); + } } } diff --git a/libraries/bezier-rs/src/svg.rs b/libraries/bezier-rs/src/svg.rs deleted file mode 100644 index fb175083..00000000 --- a/libraries/bezier-rs/src/svg.rs +++ /dev/null @@ -1,74 +0,0 @@ -/// 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 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 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 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 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/wasm/src/bezier.rs b/website/other/bezier-rs-demos/wasm/src/bezier.rs index e0c76c79..2d4421e3 100644 --- a/website/other/bezier-rs-demos/wasm/src/bezier.rs +++ b/website/other/bezier-rs-demos/wasm/src/bezier.rs @@ -383,15 +383,8 @@ impl WasmBezier { pub fn rotate(&self, angle: f64, pivot_x: f64, pivot_y: f64) -> String { let original_bezier_svg = self.get_bezier_path(); let rotated_bezier = self.0.rotate_about_point(angle, DVec2::new(pivot_x, pivot_y)); - let empty_string = String::new(); let mut rotated_bezier_svg = String::new(); - rotated_bezier.to_svg( - &mut rotated_bezier_svg, - CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), - empty_string.clone(), - empty_string.clone(), - empty_string, - ); + rotated_bezier.to_svg(&mut rotated_bezier_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), String::new(), String::new(), String::new()); let pivot = draw_circle(pivot_x, pivot_y, 3., GRAY, 1.5, WHITE); // Line between pivot and start point on curve @@ -433,15 +426,8 @@ impl WasmBezier { let bezier_curve_svg = self.get_bezier_path(); - let empty_string = String::new(); let mut line_svg = String::new(); - line.to_svg( - &mut line_svg, - CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), - empty_string.clone(), - empty_string.clone(), - empty_string, - ); + line.to_svg(&mut line_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), String::new(), String::new(), String::new()); let intersections_svg = self .intersect(&line, None) @@ -460,15 +446,8 @@ impl WasmBezier { let bezier_curve_svg = self.get_bezier_path(); - let empty_string = String::new(); let mut quadratic_svg = String::new(); - quadratic.to_svg( - &mut quadratic_svg, - CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), - empty_string.clone(), - empty_string.clone(), - empty_string, - ); + quadratic.to_svg(&mut quadratic_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), String::new(), String::new(), String::new()); let intersections_svg = self .intersect(&quadratic, Some(error)) @@ -487,15 +466,8 @@ impl WasmBezier { let bezier_curve_svg = self.get_bezier_path(); - let empty_string = String::new(); let mut cubic_svg = String::new(); - cubic.to_svg( - &mut cubic_svg, - CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), - empty_string.clone(), - empty_string.clone(), - empty_string, - ); + cubic.to_svg(&mut cubic_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), String::new(), String::new(), String::new()); let intersections_svg = self .intersect(&cubic, Some(error)) @@ -526,7 +498,6 @@ impl WasmBezier { } pub fn reduce(&self) -> String { - let empty_string = String::new(); let original_curve_svg = self.get_bezier_path(); let bezier_curves_svg: String = self .0 @@ -538,9 +509,9 @@ impl WasmBezier { bezier_curve.to_svg( &mut curve_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, &format!("hsl({}, 100%, 50%)", (40 * index))), - empty_string.clone(), - empty_string.clone(), - empty_string.clone(), + String::new(), + String::new(), + String::new(), ); curve_svg }) @@ -549,7 +520,6 @@ impl WasmBezier { } pub fn offset(&self, distance: f64) -> String { - let empty_string = String::new(); let original_curve_svg = self.get_bezier_path(); let bezier_curves_svg = self .0 @@ -561,9 +531,9 @@ impl WasmBezier { bezier_curve.to_svg( &mut curve_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, &format!("hsl({}, 100%, 50%)", (40 * index))), - empty_string.clone(), - empty_string.clone(), - empty_string.clone(), + String::new(), + String::new(), + String::new(), ); curve_svg }) diff --git a/website/other/bezier-rs-demos/wasm/src/subpath.rs b/website/other/bezier-rs-demos/wasm/src/subpath.rs index 06670b30..827254bb 100644 --- a/website/other/bezier-rs-demos/wasm/src/subpath.rs +++ b/website/other/bezier-rs-demos/wasm/src/subpath.rs @@ -1,4 +1,4 @@ -use bezier_rs::{ManipulatorGroup, Subpath, ToSVGOptions}; +use bezier_rs::{ManipulatorGroup, Subpath}; use glam::DVec2; use wasm_bindgen::prelude::*; @@ -37,11 +37,23 @@ impl WasmSubpath { } pub fn to_svg(&self) -> String { - format!("{}{}{}", SVG_OPEN_TAG, self.0.to_svg(ToSVGOptions::default()), SVG_CLOSE_TAG) + format!("{}{}{}", SVG_OPEN_TAG, self.to_default_svg(), SVG_CLOSE_TAG) + } + + fn to_default_svg(&self) -> String { + let mut subpath_svg = String::new(); + self.0.to_svg( + &mut subpath_svg, + CURVE_ATTRIBUTES.to_string(), + ANCHOR_ATTRIBUTES.to_string(), + HANDLE_ATTRIBUTES.to_string(), + HANDLE_LINE_ATTRIBUTES.to_string(), + ); + subpath_svg } pub fn length(&self) -> String { let length_text = draw_text(format!("Length: {:.2}", self.0.length(None)), 5., 193., BLACK); - format!("{}{}{}{}", SVG_OPEN_TAG, self.0.to_svg(ToSVGOptions::default()), length_text, SVG_CLOSE_TAG) + format!("{}{}{}{}", SVG_OPEN_TAG, self.to_default_svg(), length_text, SVG_CLOSE_TAG) } }