Skip to content

Refactor the 'Bevel' and 'Area' nodes to use Kurbo instead of Bezier-rs #2681

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 28 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
102b7ee
feat: update the bevel algorithm for same bevel length
MohdMohsin97 Feb 15, 2025
2900271
Merge branch 'master' into bevel-algo
Keavon Feb 20, 2025
367a3ca
Fix: Resolves issue in bevel algorithm
MohdMohsin97 Feb 20, 2025
3fe1c3b
Merge branch 'bevel-algo' of https://github.com/MohdMohsin97/Graphite…
MohdMohsin97 Feb 20, 2025
8c2de3b
Merge branch 'GraphiteEditor:master' into bevel-algo
MohdMohsin97 Feb 20, 2025
586fea7
fix bevel algorithm
MohdMohsin97 Mar 3, 2025
1e9150a
Merge branch 'bevel-algo' of https://github.com/MohdMohsin97/Graphite…
MohdMohsin97 Mar 3, 2025
220d629
Feat : update the bevel algorithm for bezier curve
MohdMohsin97 Mar 8, 2025
8001050
Feat: Add support for rounded and inward-rounded Bevel options
MohdMohsin97 Mar 9, 2025
f716b8b
fix bevel algo for curves
MohdMohsin97 Mar 15, 2025
786f223
Merge branch 'master' into bevel-algo
MohdMohsin97 Mar 30, 2025
5a46cb5
Merge branch 'GraphiteEditor:master' into bevel-algo
MohdMohsin97 Apr 26, 2025
dd09ae9
Nits
MohdMohsin97 Apr 27, 2025
cd749cf
Merge branch 'GraphiteEditor:master' into bevel-algo
MohdMohsin97 May 1, 2025
a4d022e
Merge branch 'master' into bevel-algo
Keavon May 1, 2025
f2f4ced
refactor area node
indierusty May 27, 2025
c7999d6
Merge branch 'GraphiteEditor:master' into bevel-algo
MohdMohsin97 May 28, 2025
5680ab6
Merge branch 'master' into ir/area
indierusty May 31, 2025
63e15cd
refactor close path node
indierusty May 31, 2025
a4ea745
small refactor of 'check_point_inside_shape' method
indierusty May 31, 2025
8739b5c
copy is_linear function from bezier-rs lib
indierusty May 31, 2025
e011983
refactor bevel node implementation
indierusty May 31, 2025
fcc5d32
cleanup
indierusty May 31, 2025
1595265
Merge branch 'master' into bevel-algo
indierusty Jun 1, 2025
f8f85a6
Merge branch 'ir/area' into bevel
indierusty Jun 2, 2025
dd32fe0
refactor bevel implementation
indierusty Jun 2, 2025
bc9075f
fix transformation
indierusty Jun 2, 2025
2a7e196
cleanup
indierusty Jun 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion node-graph/gcore/src/vector/algorithms/bezpath_algorithms.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use super::poisson_disk::poisson_disk_sample;
use crate::vector::misc::dvec2_to_point;
use glam::DVec2;
use kurbo::{BezPath, Line, ParamCurve, ParamCurveDeriv, PathSeg, Point, Rect, Shape};
use kurbo::{BezPath, CubicBez, Line, ParamCurve, ParamCurveDeriv, PathSeg, Point, QuadBez, Rect, Shape};

/// Accuracy to find the position on [kurbo::Bezpath].
const POSITION_ACCURACY: f64 = 1e-5;
/// Accuracy to find the length of the [kurbo::PathSeg].
pub const PERIMETER_ACCURACY: f64 = 1e-5;
/// Constant used to determine if `f64`s are equivalent.
pub const MAX_ABSOLUTE_DIFFERENCE: f64 = 1e-3;

pub fn position_on_bezpath(bezpath: &BezPath, t: f64, euclidian: bool, segments_length: Option<&[f64]>) -> Point {
let (segment_index, t) = t_value_to_parametric(bezpath, t, euclidian, segments_length);
Expand Down Expand Up @@ -241,3 +243,16 @@ pub fn poisson_disk_points(bezpath_index: usize, bezpaths: &[(BezPath, Rect)], s

poisson_disk_sample(offset, width, height, separation_disk_diameter, point_in_shape_checker, line_intersect_shape_checker, rng)
}

/// Returns true if the Bezier curve is equivalent to a line.
///
/// **NOTE**: This is different from simply checking if the segment is [`PathSeg::Line`] or [`PathSeg::Quad`] or [`PathSeg::Cubic`]. Bezier curve can also be a line if the control points are colinear to the start and end points. Therefore if the handles exceed the start and end point, it will still be considered as a line.
pub fn is_linear(segment: &PathSeg) -> bool {
let is_colinear = |a: Point, b: Point, c: Point| -> bool { ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)).abs() < MAX_ABSOLUTE_DIFFERENCE };

match *segment {
PathSeg::Line(_) => true,
PathSeg::Quad(QuadBez { p0, p1, p2 }) => is_colinear(p0, p1, p2),
PathSeg::Cubic(CubicBez { p0, p1, p2, p3 }) => is_colinear(p0, p1, p3) && is_colinear(p0, p2, p3),
}
}
33 changes: 32 additions & 1 deletion node-graph/gcore/src/vector/misc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use bezier_rs::BezierHandles;
use dyn_any::DynAny;
use glam::DVec2;
use kurbo::Point;
use kurbo::{CubicBez, Line, PathSeg, Point, QuadBez};

/// Represents different ways of calculating the centroid.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash, DynAny, specta::Type, node_macro::ChoiceType)]
Expand Down Expand Up @@ -101,3 +102,33 @@ pub fn point_to_dvec2(point: Point) -> DVec2 {
pub fn dvec2_to_point(value: DVec2) -> Point {
Point { x: value.x, y: value.y }
}

pub fn segment_to_handles(segment: &PathSeg) -> BezierHandles {
match *segment {
PathSeg::Line(_) => BezierHandles::Linear,
PathSeg::Quad(QuadBez { p0: _, p1, p2: _ }) => BezierHandles::Quadratic { handle: point_to_dvec2(p1) },
PathSeg::Cubic(CubicBez { p0: _, p1, p2, p3: _ }) => BezierHandles::Cubic {
handle_start: point_to_dvec2(p1),
handle_end: point_to_dvec2(p2),
},
}
}

pub fn handles_to_segment(start: DVec2, handles: BezierHandles, end: DVec2) -> PathSeg {
match handles {
bezier_rs::BezierHandles::Linear => PathSeg::Line(Line::new(dvec2_to_point(start), dvec2_to_point(end))),
bezier_rs::BezierHandles::Quadratic { handle } => {
let p0 = dvec2_to_point(start);
let p1 = dvec2_to_point(handle);
let p2 = dvec2_to_point(end);
PathSeg::Quad(QuadBez::new(p0, p1, p2))
}
bezier_rs::BezierHandles::Cubic { handle_start, handle_end } => {
let p0 = dvec2_to_point(start);
let p1 = dvec2_to_point(handle_start);
let p2 = dvec2_to_point(handle_end);
let p3 = dvec2_to_point(end);
PathSeg::Cubic(CubicBez::new(p0, p1, p2, p3))
}
}
}
26 changes: 8 additions & 18 deletions node-graph/gcore/src/vector/vector_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,10 @@ impl VectorData {

pub fn close_subpaths(&mut self) {
let segments_to_add: Vec<_> = self
.stroke_bezier_paths()
.filter(|subpath| !subpath.closed)
.filter_map(|subpath| {
let (first, last) = subpath.manipulator_groups().first().zip(subpath.manipulator_groups().last())?;
.build_stroke_path_iter()
.filter(|(_, closed)| !closed)
.filter_map(|(manipulator_groups, _)| {
let (first, last) = manipulator_groups.first().zip(manipulator_groups.last())?;
let (start, end) = self.point_domain.resolve_id(first.id).zip(self.point_domain.resolve_id(last.id))?;
Some((start, end))
})
Expand Down Expand Up @@ -335,7 +335,7 @@ impl VectorData {
}

pub fn check_point_inside_shape(&self, vector_data_transform: DAffine2, point: DVec2) -> bool {
let bez_paths: Vec<_> = self
let number = self
.stroke_bezpath_iter()
.map(|mut bezpath| {
// TODO: apply transform to points instead of modifying the paths
Expand All @@ -344,19 +344,9 @@ impl VectorData {
let bbox = bezpath.bounding_box();
(bezpath, bbox)
})
.collect();

// Check against all paths the point is contained in to compute the correct winding number
let mut number = 0;

for (shape, bbox) in bez_paths {
if bbox.x0 > point.x || bbox.y0 > point.y || bbox.x1 < point.x || bbox.y1 < point.y {
continue;
}

let winding = shape.winding(dvec2_to_point(point));
number += winding;
}
.filter(|(_, bbox)| bbox.contains(dvec2_to_point(point)))
.map(|(bezpath, _)| bezpath.winding(dvec2_to_point(point)))
.sum::<i32>();

// Non-zero fill rule
number != 0
Expand Down
2 changes: 1 addition & 1 deletion node-graph/gcore/src/vector/vector_data/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,7 @@ impl VectorData {
})
}

fn build_stroke_path_iter(&self) -> StrokePathIter {
pub fn build_stroke_path_iter(&self) -> StrokePathIter {
let mut points = vec![StrokePathIterPointMetadata::default(); self.point_domain.ids().len()];
for (segment_index, (&start, &end)) in self.segment_domain.start_point.iter().zip(&self.segment_domain.end_point).enumerate() {
points[start].set(StrokePathIterPointSegmentMetadata::new(segment_index, false));
Expand Down
Loading
Loading