mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-10 02:30:57 +08:00
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:
committed by
Keavon Chambers
parent
7e124c8035
commit
c0576ab4e0
@@ -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),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user