Bezier-rs: Add joins and caps to offsets and outlines (#1083)

* Intial work

* Improve miter and add round join

* Get arcs to go opposite direction

* Add cap and other refactors

* Rename joint to join, fix some bugs

* Fix single point issue

* Clean up

* Fix iframe sizes and update UI

* Address comments and handle single point outline

* Rename variables, fix branches in outline

* Address comments
This commit is contained in:
Hannah Li
2023-03-27 16:25:08 -04:00
committed by GitHub
parent 2291442186
commit 93821f3e3b
18 changed files with 684 additions and 173 deletions
@@ -1,4 +1,6 @@
use crate::svg_drawing::*;
use crate::utils::parse_cap;
use bezier_rs::{ArcStrategy, ArcsOptions, Bezier, Identifier, ProjectionOptions, TValue};
use glam::DVec2;
use serde::{Deserialize, Serialize};
@@ -219,22 +221,25 @@ impl WasmBezier {
}
pub fn curvature(&self, raw_t: f64, t_variant: String) -> String {
let bezier = self.get_bezier_path();
let mut content = self.get_bezier_path();
let t = parse_t_variant(&t_variant, raw_t);
let radius = 1. / self.0.curvature(t);
let normal_point = self.0.normal(t);
let intersection_point = self.0.evaluate(t);
let curvature = self.0.curvature(t);
if curvature > 0. {
let radius = 1. / self.0.curvature(t);
let normal_point = self.0.normal(t);
let intersection_point = self.0.evaluate(t);
let curvature_center = intersection_point + normal_point * radius;
let curvature_center = intersection_point + normal_point * radius;
let content = format!(
"{bezier}{}{}{}{}",
draw_circle(curvature_center, radius.abs(), RED, 1., NONE),
draw_line(intersection_point.x, intersection_point.y, curvature_center.x, curvature_center.y, RED, 1.),
draw_circle(intersection_point, 3., RED, 1., WHITE),
draw_circle(curvature_center, 3., RED, 1., WHITE),
);
content = format!(
"{content}{}{}{}{}",
draw_circle(curvature_center, radius.abs(), RED, 1., NONE),
draw_line(intersection_point.x, intersection_point.y, curvature_center.x, curvature_center.y, RED, 1.),
draw_circle(intersection_point, 3., RED, 1., WHITE),
draw_circle(curvature_center, 3., RED, 1., WHITE),
);
}
wrap_svg_tag(content)
}
@@ -242,19 +247,10 @@ impl WasmBezier {
let t = parse_t_variant(&t_variant, raw_t);
let beziers: [Bezier; 2] = self.0.split(t);
let mut original_bezier_svg = String::new();
self.0.to_svg(
&mut original_bezier_svg,
CURVE_ATTRIBUTES.to_string().replace(BLACK, WHITE),
ANCHOR_ATTRIBUTES.to_string().replace(BLACK, WHITE),
HANDLE_ATTRIBUTES.to_string(),
HANDLE_LINE_ATTRIBUTES.to_string(),
);
let mut bezier_svg_1 = String::new();
beziers[0].to_svg(
&mut bezier_svg_1,
CURVE_ATTRIBUTES.to_string().replace(BLACK, ORANGE),
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),
@@ -263,13 +259,13 @@ impl WasmBezier {
let mut bezier_svg_2 = String::new();
beziers[1].to_svg(
&mut bezier_svg_2,
CURVE_ATTRIBUTES.to_string().replace(BLACK, RED),
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!("{original_bezier_svg}{bezier_svg_1}{bezier_svg_2}"))
wrap_svg_tag(format!("{}{bezier_svg_1}{bezier_svg_2}", self.get_bezier_path()))
}
pub fn trim(&self, raw_t1: f64, raw_t2: f64, t_variant: String) -> String {
@@ -279,7 +275,7 @@ impl WasmBezier {
let mut trimmed_bezier_svg = String::new();
trimmed_bezier.to_svg(
&mut trimmed_bezier_svg,
CURVE_ATTRIBUTES.to_string().replace(BLACK, RED),
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),
@@ -570,8 +566,9 @@ impl WasmBezier {
wrap_svg_tag(bezier_curves_svg)
}
pub fn outline(&self, distance: f64) -> String {
let outline_subpath = self.0.outline::<EmptyId>(distance);
pub fn outline(&self, distance: f64, cap: i32) -> String {
let cap = parse_cap(cap);
let outline_subpath = self.0.outline::<EmptyId>(distance, cap);
if outline_subpath.is_empty() {
return String::new();
}
@@ -583,8 +580,9 @@ impl WasmBezier {
wrap_svg_tag(format!("{bezier_svg}{outline_svg}"))
}
pub fn graduated_outline(&self, start_distance: f64, end_distance: f64) -> String {
let outline_subpath = self.0.graduated_outline::<EmptyId>(start_distance, end_distance);
pub fn graduated_outline(&self, start_distance: f64, end_distance: f64, cap: i32) -> String {
let cap = parse_cap(cap);
let outline_subpath = self.0.graduated_outline::<EmptyId>(start_distance, end_distance, cap);
if outline_subpath.is_empty() {
return String::new();
}
@@ -596,8 +594,9 @@ impl WasmBezier {
wrap_svg_tag(format!("{bezier_svg}{outline_svg}"))
}
pub fn skewed_outline(&self, distance1: f64, distance2: f64, distance3: f64, distance4: f64) -> String {
let outline_subpath = self.0.skewed_outline::<EmptyId>(distance1, distance2, distance3, distance4);
pub fn skewed_outline(&self, distance1: f64, distance2: f64, distance3: f64, distance4: f64, cap: i32) -> String {
let cap = parse_cap(cap);
let outline_subpath = self.0.skewed_outline::<EmptyId>(distance1, distance2, distance3, distance4, cap);
if outline_subpath.is_empty() {
return String::new();
}
@@ -1,3 +1,4 @@
pub mod bezier;
pub mod subpath;
mod svg_drawing;
mod utils;
@@ -1,4 +1,5 @@
use crate::svg_drawing::*;
use crate::utils::{parse_cap, parse_join};
use bezier_rs::{Bezier, ManipulatorGroup, ProjectionOptions, Subpath, SubpathTValue};
@@ -377,8 +378,9 @@ impl WasmSubpath {
wrap_svg_tag(format!("{}{}", self.to_default_svg(), trimmed_subpath_svg))
}
pub fn offset(&self, distance: f64) -> String {
let offset_subpath = self.0.offset(distance, bezier_rs::Joint::Bevel);
pub fn offset(&self, distance: f64, join: i32) -> String {
let join = parse_join(join);
let offset_subpath = self.0.offset(distance, join);
let mut offset_svg = String::new();
offset_subpath.to_svg(&mut offset_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), String::new(), String::new(), String::new());
@@ -386,8 +388,10 @@ impl WasmSubpath {
wrap_svg_tag(format!("{}{offset_svg}", self.to_default_svg()))
}
pub fn outline(&self, distance: f64) -> String {
let (outline_piece1, outline_piece2) = self.0.outline(distance, bezier_rs::Joint::Bevel);
pub fn outline(&self, distance: f64, join: i32, cap: i32) -> String {
let join = parse_join(join);
let cap = parse_cap(cap);
let (outline_piece1, outline_piece2) = self.0.outline(distance, join, cap);
let mut outline_piece1_svg = String::new();
outline_piece1.to_svg(&mut outline_piece1_svg, CURVE_ATTRIBUTES.to_string().replace(BLACK, RED), String::new(), String::new(), String::new());
@@ -0,0 +1,19 @@
use bezier_rs::{Cap, Join};
pub fn parse_join(join: i32) -> Join {
match join {
0 => Join::Bevel,
1 => Join::Miter,
2 => Join::Round,
_ => panic!("Unexpected Join value: '{}'", join),
}
}
pub fn parse_cap(cap: i32) -> Cap {
match cap {
0 => Cap::Butt,
1 => Cap::Round,
2 => Cap::Square,
_ => panic!("Unexpected Cap value: '{}'", cap),
}
}