mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-07 13:28:11 +08:00
Migrate vector data and tools to use nodes (#1065)
* Add rendering to vector nodes * Add line, shape, rectange and freehand tool * Fix transforms, strokes and fills * Migrate spline tool * Remove blank lines * Fix test * Fix fill in properties * Select layers when filling * Properties panel transform around pivot * Fix select tool outlines * Select tool modifies node graph pivot * Add the pivot assist to the properties * Improve setting non existant fill UX * Cleanup hash function * Path and pen tools * Bug fixes * Disable boolean ops * Fix default handle smoothing on ellipses * Fix test and warnings --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
5759600f95
commit
ac49519fa9
@@ -1,5 +1,5 @@
|
||||
use super::*;
|
||||
use crate::utils::TValue;
|
||||
use crate::utils::{solve_cubic, solve_quadratic, TValue};
|
||||
|
||||
use glam::DMat2;
|
||||
use std::ops::Range;
|
||||
@@ -403,6 +403,105 @@ impl Bezier {
|
||||
let handle2 = other.start - other.non_normalized_tangent(0.) / 3.;
|
||||
Bezier::from_cubic_dvec2(self.end, handle1, handle2, other.start)
|
||||
}
|
||||
|
||||
/// Compute the winding order (number of times crossing an infinate line to the left of the point)
|
||||
///
|
||||
/// Assumes curve is split at the extrema.
|
||||
fn pre_split_winding_number(&self, target_point: DVec2) -> i32 {
|
||||
// Clockwise is -1, anticlockwise is +1 (with +y as up)
|
||||
// Looking only to the left (-x) of the target_point
|
||||
let resulting_sign = if self.end.y > self.start.y {
|
||||
if target_point.y < self.start.y || target_point.y >= self.end.y {
|
||||
return 0;
|
||||
}
|
||||
-1
|
||||
} else if self.end.y < self.start.y {
|
||||
if target_point.y < self.end.y || target_point.y >= self.start.y {
|
||||
return 0;
|
||||
}
|
||||
1
|
||||
} else {
|
||||
return 0;
|
||||
};
|
||||
match &self.handles {
|
||||
BezierHandles::Linear => {
|
||||
if target_point.x < self.start.x.min(self.end.x) {
|
||||
return 0;
|
||||
}
|
||||
if target_point.x >= self.start.x.max(self.end.x) {
|
||||
return resulting_sign;
|
||||
}
|
||||
// line equation ax + by = c
|
||||
let a = self.end.y - self.start.y;
|
||||
let b = self.start.x - self.end.x;
|
||||
let c = a * self.start.x + b * self.start.y;
|
||||
if (a * target_point.x + b * target_point.y - c) * (resulting_sign as f64) <= 0.0 {
|
||||
resulting_sign
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
BezierHandles::Quadratic { handle: p1 } => {
|
||||
if target_point.x < self.start.x.min(self.end.x).min(p1.x) {
|
||||
return 0;
|
||||
}
|
||||
if target_point.x >= self.start.x.max(self.end.x).max(p1.x) {
|
||||
return resulting_sign;
|
||||
}
|
||||
let a = self.end.y - 2.0 * p1.y + self.start.y;
|
||||
let b = 2.0 * (p1.y - self.start.y);
|
||||
let c = self.start.y - target_point.y;
|
||||
|
||||
let discriminant = b * b - 4. * a * c;
|
||||
let two_times_a = 2. * a;
|
||||
for t in solve_quadratic(discriminant, two_times_a, b, c) {
|
||||
if (0.0..=1.0).contains(&t) {
|
||||
let x = self.evaluate(TValue::Parametric(t)).x;
|
||||
if target_point.x >= x {
|
||||
return resulting_sign;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
BezierHandles::Cubic { handle_start: p1, handle_end: p2 } => {
|
||||
if target_point.x < self.start.x.min(self.end.x).min(p1.x).min(p2.x) {
|
||||
return 0;
|
||||
}
|
||||
if target_point.x >= self.start.x.max(self.end.x).max(p1.x).max(p2.x) {
|
||||
return resulting_sign;
|
||||
}
|
||||
let a = self.end.y - 3.0 * p2.y + 3.0 * p1.y - self.start.y;
|
||||
let b = 3.0 * (p2.y - 2.0 * p1.y + self.start.y);
|
||||
let c = 3.0 * (p1.y - self.start.y);
|
||||
let d = self.start.y - target_point.y;
|
||||
for t in solve_cubic(a, b, c, d) {
|
||||
if (0.0..=1.0).contains(&t) {
|
||||
let x = self.evaluate(TValue::Parametric(t)).x;
|
||||
if target_point.x >= x {
|
||||
return resulting_sign;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the winding number contribution of a single segment.
|
||||
///
|
||||
/// Cast a ray to the left and count intersections.
|
||||
pub fn winding(&self, target_point: DVec2) -> i32 {
|
||||
let extrema = self.get_extrema_t_list();
|
||||
extrema
|
||||
.windows(2)
|
||||
.map(|t| self.trim(TValue::Parametric(t[0]), TValue::Parametric(t[1])).pre_split_winding_number(target_point))
|
||||
.sum()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user