Bezier-rs: Add split for Subpath (#988)

* Add subpath split

* Update comment and colors

* Address comments

* Improve visualization clarity

* Code review

---------

Co-authored-by: Rob Nadal <Robnadal44@gmail.com>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Hannah Li
2023-01-31 03:17:20 -05:00
committed by Keavon Chambers
parent 511a8aa164
commit a328e7d3ef
4 changed files with 305 additions and 0 deletions

View File

@@ -56,6 +56,12 @@ const subpathFeatures = {
[175, 140],
]),
},
Split: {
callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined, computeType: ComputeType): string => subpath.split(options.computeArgument, computeType),
sliderOptions: [{ ...tSliderOptions, variable: "computeArgument" }],
// TODO: Uncomment this after implementing the Euclidean version
// chooseComputeType: true,
},
};
export type SubpathFeatureName = keyof typeof subpathFeatures;

View File

@@ -3,6 +3,7 @@ use crate::svg_drawing::*;
use bezier_rs::{Bezier, ComputeType, ManipulatorGroup, ProjectionOptions, Subpath};
use glam::DVec2;
use std::fmt::Write;
use wasm_bindgen::prelude::*;
/// Wrapper of the `Subpath` struct to be used in JS.
@@ -207,4 +208,64 @@ impl WasmSubpath {
wrap_svg_tag(format!("{subpath_svg}{line_svg}{intersections_svg}"))
}
pub fn split(&self, t: f64, compute_type: String) -> String {
let (main_subpath, optional_subpath) = match compute_type.as_str() {
"Euclidean" => self.0.split(ComputeType::Euclidean(t)),
"Parametric" => self.0.split(ComputeType::Parametric(t)),
_ => panic!("Unexpected ComputeType string: '{}'", compute_type),
};
let mut main_subpath_svg = String::new();
let mut other_subpath_svg = String::new();
if optional_subpath.is_some() {
main_subpath.to_svg(
&mut main_subpath_svg,
CURVE_ATTRIBUTES.to_string().replace(BLACK, ORANGE).replace("stroke-width=\"2\"", "stroke-width=\"8\"") + " opacity=\"0.5\"",
ANCHOR_ATTRIBUTES.to_string().replace(BLACK, ORANGE),
HANDLE_ATTRIBUTES.to_string().replace(GRAY, ORANGE),
HANDLE_LINE_ATTRIBUTES.to_string().replace(GRAY, ORANGE),
);
} else {
main_subpath.iter().enumerate().for_each(|(index, bezier)| {
let hue1 = &format!("hsla({}, 100%, 50%, 0.5)", 40 * index);
let hue2 = &format!("hsla({}, 100%, 50%, 0.5)", 40 * (index + 1));
let gradient_id = &format!("gradient{}", index);
let start = bezier.start();
let end = bezier.end();
let _ = write!(
main_subpath_svg,
r#"<defs><linearGradient id="{}" x1="{}%" y1="{}%" x2="{}%" y2="{}%"><stop offset="0%" stop-color="{}"/><stop offset="100%" stop-color="{}"/></linearGradient></defs>"#,
gradient_id,
start.x / 2.,
start.y / 2.,
end.x / 2.,
end.y / 2.,
hue1,
hue2
);
let stroke = &format!("url(#{})", gradient_id);
bezier.curve_to_svg(
&mut main_subpath_svg,
CURVE_ATTRIBUTES.to_string().replace(BLACK, stroke).replace("stroke-width=\"2\"", "stroke-width=\"8\""),
);
bezier.anchors_to_svg(&mut main_subpath_svg, ANCHOR_ATTRIBUTES.to_string().replace(BLACK, hue1));
bezier.handles_to_svg(&mut main_subpath_svg, HANDLE_ATTRIBUTES.to_string().replace(GRAY, hue1));
bezier.handle_lines_to_svg(&mut main_subpath_svg, HANDLE_LINE_ATTRIBUTES.to_string().replace(GRAY, hue1));
});
}
if let Some(subpath) = optional_subpath {
subpath.to_svg(
&mut other_subpath_svg,
CURVE_ATTRIBUTES.to_string().replace(BLACK, RED).replace("stroke-width=\"2\"", "stroke-width=\"8\"") + " opacity=\"0.5\"",
ANCHOR_ATTRIBUTES.to_string().replace(BLACK, RED),
HANDLE_ATTRIBUTES.to_string().replace(GRAY, RED),
HANDLE_LINE_ATTRIBUTES.to_string().replace(GRAY, RED),
);
}
wrap_svg_tag(format!("{}{}{}", self.to_default_svg(), main_subpath_svg, other_subpath_svg))
}
}