Bezier-rs: Convert to_svg used in subpaths to match style used in beziers (#854)
Converted subpath::to_svg to Bezier style
This commit is contained in:
parent
9c87658ae4
commit
00c8fa83c2
|
|
@ -3,9 +3,7 @@
|
||||||
mod bezier;
|
mod bezier;
|
||||||
mod consts;
|
mod consts;
|
||||||
mod subpath;
|
mod subpath;
|
||||||
mod svg;
|
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
pub use bezier::*;
|
pub use bezier::*;
|
||||||
pub use subpath::*;
|
pub use subpath::*;
|
||||||
pub use svg::*;
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::consts::*;
|
use crate::consts::*;
|
||||||
use crate::ToSVGOptions;
|
use std::fmt::Write;
|
||||||
|
|
||||||
/// Functionality relating to core `Subpath` operations, such as constructors and `iter`.
|
/// Functionality relating to core `Subpath` operations, such as constructors and `iter`.
|
||||||
impl Subpath {
|
impl Subpath {
|
||||||
|
|
@ -45,45 +45,59 @@ impl Subpath {
|
||||||
SubpathIter { sub_path: self, index: 0 }
|
SubpathIter { sub_path: self, index: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an SVG representation of the `Subpath`.
|
/// Appends to the `svg` mutable string with an SVG shape representation of the curve.
|
||||||
pub fn to_svg(&self, options: ToSVGOptions) -> String {
|
pub fn curve_to_svg(&self, svg: &mut String, attributes: String) {
|
||||||
if self.is_empty() {
|
|
||||||
return String::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
let curve_start_argument = format!("{SVG_ARG_MOVE}{} {}", self[0].anchor.x, self[0].anchor.y);
|
let curve_start_argument = format!("{SVG_ARG_MOVE}{} {}", self[0].anchor.x, self[0].anchor.y);
|
||||||
let mut curve_arguments: Vec<String> = self.iter().map(|bezier| bezier.svg_curve_argument()).collect();
|
let mut curve_arguments: Vec<String> = self.iter().map(|bezier| bezier.svg_curve_argument()).collect();
|
||||||
if self.closed {
|
if self.closed {
|
||||||
curve_arguments.push(String::from(SVG_ARG_CLOSED));
|
curve_arguments.push(String::from(SVG_ARG_CLOSED));
|
||||||
}
|
}
|
||||||
|
|
||||||
let anchor_arguments = options.formatted_anchor_arguments();
|
let _ = write!(svg, r#"<path d="{} {}" {attributes}/>"#, curve_start_argument, curve_arguments.join(" "));
|
||||||
let anchor_circles = self
|
}
|
||||||
|
|
||||||
|
/// 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<String> = self.iter().filter_map(|bezier| bezier.svg_handle_line_argument()).collect();
|
||||||
|
let _ = write!(svg, r#"<path d="{}" {attributes}/>"#, 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
|
.manipulator_groups
|
||||||
.iter()
|
.iter()
|
||||||
.map(|point| format!(r#"<circle cx="{}" cy="{}" {}/>"#, point.anchor.x, point.anchor.y, anchor_arguments))
|
.map(|point| format!(r#"<circle cx="{}" cy="{}" {attributes}/>"#, point.anchor.x, point.anchor.y))
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
let _ = write!(svg, "{}", anchors.concat());
|
||||||
|
}
|
||||||
|
|
||||||
let handle_point_arguments = options.formatted_handle_point_arguments();
|
/// Appends to the `svg` mutable string with an SVG shape representation of the handles.
|
||||||
let handle_circles: Vec<String> = self
|
pub fn handles_to_svg(&self, svg: &mut String, attributes: String) {
|
||||||
|
let handles = self
|
||||||
.manipulator_groups
|
.manipulator_groups
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|group| [group.in_handle, group.out_handle])
|
.flat_map(|group| [group.in_handle, group.out_handle])
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|handle| format!(r#"<circle cx="{}" cy="{}" {}/>"#, handle.x, handle.y, handle_point_arguments))
|
.map(|handle| format!(r#"<circle cx="{}" cy="{}" {attributes}/>"#, handle.x, handle.y))
|
||||||
.collect();
|
.collect::<Vec<String>>();
|
||||||
|
let _ = write!(svg, "{}", handles.concat());
|
||||||
|
}
|
||||||
|
|
||||||
let handle_pieces: Vec<String> = self.iter().filter_map(|bezier| bezier.svg_handle_line_argument()).collect();
|
/// 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.
|
||||||
format!(
|
pub fn to_svg(&self, svg: &mut String, curve_attributes: String, anchor_attributes: String, handle_attributes: String, handle_line_attributes: String) {
|
||||||
r#"<path d="{} {}" {}/><path d="{}" {}/>{}{}"#,
|
if !curve_attributes.is_empty() {
|
||||||
curve_start_argument,
|
self.curve_to_svg(svg, curve_attributes);
|
||||||
curve_arguments.join(" "),
|
}
|
||||||
options.formatted_curve_arguments(),
|
if !handle_line_attributes.is_empty() {
|
||||||
handle_pieces.join(" "),
|
self.handle_lines_to_svg(svg, handle_line_attributes);
|
||||||
options.formatted_handle_line_arguments(),
|
}
|
||||||
handle_circles.join(""),
|
if !anchor_attributes.is_empty() {
|
||||||
anchor_circles.join(""),
|
self.anchors_to_svg(svg, anchor_attributes);
|
||||||
)
|
}
|
||||||
|
if !handle_attributes.is_empty() {
|
||||||
|
self.handles_to_svg(svg, handle_attributes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -383,15 +383,8 @@ impl WasmBezier {
|
||||||
pub fn rotate(&self, angle: f64, pivot_x: f64, pivot_y: f64) -> String {
|
pub fn rotate(&self, angle: f64, pivot_x: f64, pivot_y: f64) -> String {
|
||||||
let original_bezier_svg = self.get_bezier_path();
|
let original_bezier_svg = self.get_bezier_path();
|
||||||
let rotated_bezier = self.0.rotate_about_point(angle, DVec2::new(pivot_x, pivot_y));
|
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();
|
let mut rotated_bezier_svg = String::new();
|
||||||
rotated_bezier.to_svg(
|
rotated_bezier.to_svg(&mut rotated_bezier_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), String::new(), String::new(), String::new());
|
||||||
&mut rotated_bezier_svg,
|
|
||||||
CURVE_ATTRIBUTES.to_string().replace(BLACK, RED),
|
|
||||||
empty_string.clone(),
|
|
||||||
empty_string.clone(),
|
|
||||||
empty_string,
|
|
||||||
);
|
|
||||||
let pivot = draw_circle(pivot_x, pivot_y, 3., GRAY, 1.5, WHITE);
|
let pivot = draw_circle(pivot_x, pivot_y, 3., GRAY, 1.5, WHITE);
|
||||||
|
|
||||||
// Line between pivot and start point on curve
|
// Line between pivot and start point on curve
|
||||||
|
|
@ -433,15 +426,8 @@ impl WasmBezier {
|
||||||
|
|
||||||
let bezier_curve_svg = self.get_bezier_path();
|
let bezier_curve_svg = self.get_bezier_path();
|
||||||
|
|
||||||
let empty_string = String::new();
|
|
||||||
let mut line_svg = String::new();
|
let mut line_svg = String::new();
|
||||||
line.to_svg(
|
line.to_svg(&mut line_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), String::new(), String::new(), String::new());
|
||||||
&mut line_svg,
|
|
||||||
CURVE_ATTRIBUTES.to_string().replace(BLACK, RED),
|
|
||||||
empty_string.clone(),
|
|
||||||
empty_string.clone(),
|
|
||||||
empty_string,
|
|
||||||
);
|
|
||||||
|
|
||||||
let intersections_svg = self
|
let intersections_svg = self
|
||||||
.intersect(&line, None)
|
.intersect(&line, None)
|
||||||
|
|
@ -460,15 +446,8 @@ impl WasmBezier {
|
||||||
|
|
||||||
let bezier_curve_svg = self.get_bezier_path();
|
let bezier_curve_svg = self.get_bezier_path();
|
||||||
|
|
||||||
let empty_string = String::new();
|
|
||||||
let mut quadratic_svg = String::new();
|
let mut quadratic_svg = String::new();
|
||||||
quadratic.to_svg(
|
quadratic.to_svg(&mut quadratic_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), String::new(), String::new(), String::new());
|
||||||
&mut quadratic_svg,
|
|
||||||
CURVE_ATTRIBUTES.to_string().replace(BLACK, RED),
|
|
||||||
empty_string.clone(),
|
|
||||||
empty_string.clone(),
|
|
||||||
empty_string,
|
|
||||||
);
|
|
||||||
|
|
||||||
let intersections_svg = self
|
let intersections_svg = self
|
||||||
.intersect(&quadratic, Some(error))
|
.intersect(&quadratic, Some(error))
|
||||||
|
|
@ -487,15 +466,8 @@ impl WasmBezier {
|
||||||
|
|
||||||
let bezier_curve_svg = self.get_bezier_path();
|
let bezier_curve_svg = self.get_bezier_path();
|
||||||
|
|
||||||
let empty_string = String::new();
|
|
||||||
let mut cubic_svg = String::new();
|
let mut cubic_svg = String::new();
|
||||||
cubic.to_svg(
|
cubic.to_svg(&mut cubic_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), String::new(), String::new(), String::new());
|
||||||
&mut cubic_svg,
|
|
||||||
CURVE_ATTRIBUTES.to_string().replace(BLACK, RED),
|
|
||||||
empty_string.clone(),
|
|
||||||
empty_string.clone(),
|
|
||||||
empty_string,
|
|
||||||
);
|
|
||||||
|
|
||||||
let intersections_svg = self
|
let intersections_svg = self
|
||||||
.intersect(&cubic, Some(error))
|
.intersect(&cubic, Some(error))
|
||||||
|
|
@ -526,7 +498,6 @@ impl WasmBezier {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reduce(&self) -> String {
|
pub fn reduce(&self) -> String {
|
||||||
let empty_string = String::new();
|
|
||||||
let original_curve_svg = self.get_bezier_path();
|
let original_curve_svg = self.get_bezier_path();
|
||||||
let bezier_curves_svg: String = self
|
let bezier_curves_svg: String = self
|
||||||
.0
|
.0
|
||||||
|
|
@ -538,9 +509,9 @@ impl WasmBezier {
|
||||||
bezier_curve.to_svg(
|
bezier_curve.to_svg(
|
||||||
&mut curve_svg,
|
&mut curve_svg,
|
||||||
CURVE_ATTRIBUTES.to_string().replace(BLACK, &format!("hsl({}, 100%, 50%)", (40 * index))),
|
CURVE_ATTRIBUTES.to_string().replace(BLACK, &format!("hsl({}, 100%, 50%)", (40 * index))),
|
||||||
empty_string.clone(),
|
String::new(),
|
||||||
empty_string.clone(),
|
String::new(),
|
||||||
empty_string.clone(),
|
String::new(),
|
||||||
);
|
);
|
||||||
curve_svg
|
curve_svg
|
||||||
})
|
})
|
||||||
|
|
@ -549,7 +520,6 @@ impl WasmBezier {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset(&self, distance: f64) -> String {
|
pub fn offset(&self, distance: f64) -> String {
|
||||||
let empty_string = String::new();
|
|
||||||
let original_curve_svg = self.get_bezier_path();
|
let original_curve_svg = self.get_bezier_path();
|
||||||
let bezier_curves_svg = self
|
let bezier_curves_svg = self
|
||||||
.0
|
.0
|
||||||
|
|
@ -561,9 +531,9 @@ impl WasmBezier {
|
||||||
bezier_curve.to_svg(
|
bezier_curve.to_svg(
|
||||||
&mut curve_svg,
|
&mut curve_svg,
|
||||||
CURVE_ATTRIBUTES.to_string().replace(BLACK, &format!("hsl({}, 100%, 50%)", (40 * index))),
|
CURVE_ATTRIBUTES.to_string().replace(BLACK, &format!("hsl({}, 100%, 50%)", (40 * index))),
|
||||||
empty_string.clone(),
|
String::new(),
|
||||||
empty_string.clone(),
|
String::new(),
|
||||||
empty_string.clone(),
|
String::new(),
|
||||||
);
|
);
|
||||||
curve_svg
|
curve_svg
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use bezier_rs::{ManipulatorGroup, Subpath, ToSVGOptions};
|
use bezier_rs::{ManipulatorGroup, Subpath};
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
|
@ -37,11 +37,23 @@ impl WasmSubpath {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_svg(&self) -> String {
|
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 {
|
pub fn length(&self) -> String {
|
||||||
let length_text = draw_text(format!("Length: {:.2}", self.0.length(None)), 5., 193., BLACK);
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue