mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Bezier-rs: Added curve outline functions (#789)
* Implement offset and reverse Co-authored-by: Rob Nadal <robnadal44@gmail.com> * Initial work on graduated outline Co-authored-by: Rob Nadal <robnadal44@gmail.com> * Handle linear case for graduated scale * Added skewed outline, fixed graduated scale hourglass bug * Removed test code * Update comments * Fix linting issue * Improve comments * Comment fixes Co-authored-by: Hannah Li <hannahli2010@gmail.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
parent
a695584d36
commit
56060b8e5f
@@ -295,6 +295,86 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Outline",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.outline(options.distance),
|
||||
exampleOptions: {
|
||||
[BezierCurveType.Quadratic]: {
|
||||
sliderOptions: [
|
||||
{
|
||||
variable: "distance",
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 1,
|
||||
default: 20,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Graduated Outline",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.graduated_outline(options.start_distance, options.end_distance),
|
||||
exampleOptions: {
|
||||
[BezierCurveType.Quadratic]: {
|
||||
sliderOptions: [
|
||||
{
|
||||
variable: "start_distance",
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 1,
|
||||
default: 30,
|
||||
},
|
||||
{
|
||||
variable: "end_distance",
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 1,
|
||||
default: 30,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Skewed Outline",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string =>
|
||||
bezier.skewed_outline(options.distance1, options.distance2, options.distance3, options.distance4),
|
||||
exampleOptions: {
|
||||
[BezierCurveType.Quadratic]: {
|
||||
sliderOptions: [
|
||||
{
|
||||
variable: "distance1",
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 1,
|
||||
default: 20,
|
||||
},
|
||||
{
|
||||
variable: "distance2",
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 1,
|
||||
default: 10,
|
||||
},
|
||||
{
|
||||
variable: "distance3",
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 1,
|
||||
default: 30,
|
||||
},
|
||||
{
|
||||
variable: "distance4",
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 1,
|
||||
default: 5,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Arcs",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.arcs(options.error, options.max_iterations, options.strategy),
|
||||
|
||||
@@ -571,6 +571,42 @@ impl WasmBezier {
|
||||
wrap_svg_tag(bezier_curves_svg)
|
||||
}
|
||||
|
||||
pub fn outline(&self, distance: f64) -> String {
|
||||
let outline_beziers = self.0.outline(distance);
|
||||
if outline_beziers.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
let outline_svg = draw_beziers(outline_beziers, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED));
|
||||
let bezier_svg = self.get_bezier_path();
|
||||
|
||||
wrap_svg_tag(format!("{bezier_svg}{outline_svg}"))
|
||||
}
|
||||
|
||||
pub fn graduated_outline(&self, start_distance: f64, end_distance: f64) -> String {
|
||||
let outline_beziers = self.0.graduated_outline(start_distance, end_distance);
|
||||
if outline_beziers.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
let outline_svg = draw_beziers(outline_beziers, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED));
|
||||
let bezier_svg = self.get_bezier_path();
|
||||
|
||||
wrap_svg_tag(format!("{bezier_svg}{outline_svg}"))
|
||||
}
|
||||
|
||||
pub fn skewed_outline(&self, distance1: f64, distance2: f64, distance3: f64, distance4: f64) -> String {
|
||||
let outline_beziers = self.0.skewed_outline(distance1, distance2, distance3, distance4);
|
||||
if outline_beziers.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
let outline_svg = draw_beziers(outline_beziers, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED));
|
||||
let bezier_svg = self.get_bezier_path();
|
||||
|
||||
wrap_svg_tag(format!("{bezier_svg}{outline_svg}"))
|
||||
}
|
||||
|
||||
/// The wrapped return type is `Vec<CircleSector>`.
|
||||
pub fn arcs(&self, error: f64, max_iterations: usize, maximize_arcs: WasmMaximizeArcs) -> String {
|
||||
let original_curve_svg = self.get_bezier_path();
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
use bezier_rs::Bezier;
|
||||
use std::fmt::Write;
|
||||
|
||||
// SVG drawing constants
|
||||
pub const SVG_OPEN_TAG: &str = r#"<svg xmlns="http://www.w3.org/2000/svg" width="200px" height="200px">"#;
|
||||
pub const SVG_CLOSE_TAG: &str = "</svg>";
|
||||
@@ -37,6 +40,19 @@ pub fn draw_line(start_x: f64, start_y: f64, end_x: f64, end_y: f64, stroke: &st
|
||||
format!(r#"<line x1="{start_x}" y1="{start_y}" x2="{end_x}" y2="{end_y}" stroke="{stroke}" stroke-width="{stroke_width}"/>"#)
|
||||
}
|
||||
|
||||
/// Helper function to draw a list of beziers.
|
||||
pub fn draw_beziers(beziers: Vec<Bezier>, options: String) -> String {
|
||||
let start_point = beziers.first().unwrap().start();
|
||||
let mut svg = format!("<path d=\"M {} {}", start_point.x, start_point.y);
|
||||
|
||||
beziers.iter().for_each(|bezier| {
|
||||
let _ = write!(svg, " {}", bezier.svg_curve_argument());
|
||||
});
|
||||
|
||||
let _ = write!(svg, " Z\" {}/>", options);
|
||||
svg
|
||||
}
|
||||
|
||||
// Helper function to convert polar to cartesian coordinates
|
||||
fn polar_to_cartesian(center_x: f64, center_y: f64, radius: f64, angle_in_rad: f64) -> [f64; 2] {
|
||||
let x = center_x + radius * angle_in_rad.cos();
|
||||
|
||||
Reference in New Issue
Block a user