Skip to content

Commit 4131e1e

Browse files
committed
Merge branch 'master' into 2558-free-floating-anchors-overlays
2 parents 1e3e218 + 269c572 commit 4131e1e

File tree

4 files changed

+248
-131
lines changed

4 files changed

+248
-131
lines changed

node-graph/gcore/src/vector/algorithms/bezpath_algorithms.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
use super::poisson_disk::poisson_disk_sample;
22
use crate::vector::misc::dvec2_to_point;
33
use glam::DVec2;
4-
use kurbo::{BezPath, Line, ParamCurve, ParamCurveDeriv, PathSeg, Point, Rect, Shape};
5-
6-
/// Accuracy to find the position on [kurbo::Bezpath].
7-
const POSITION_ACCURACY: f64 = 1e-5;
8-
/// Accuracy to find the length of the [kurbo::PathSeg].
9-
pub const PERIMETER_ACCURACY: f64 = 1e-5;
4+
use kurbo::{BezPath, DEFAULT_ACCURACY, Line, ParamCurve, ParamCurveDeriv, PathSeg, Point, Rect, Shape};
105

116
pub fn position_on_bezpath(bezpath: &BezPath, t: f64, euclidian: bool, segments_length: Option<&[f64]>) -> Point {
127
let (segment_index, t) = t_value_to_parametric(bezpath, t, euclidian, segments_length);
@@ -79,7 +74,7 @@ pub fn sample_points_on_bezpath(bezpath: BezPath, spacing: f64, start_offset: f6
7974
let t = (next_length / next_segment_length).clamp(0., 1.);
8075

8176
let segment = bezpath.get_seg(next_segment_index + 1).unwrap();
82-
let t = eval_pathseg_euclidean(segment, t, POSITION_ACCURACY);
77+
let t = eval_pathseg_euclidean(segment, t, DEFAULT_ACCURACY);
8378
let point = segment.eval(t);
8479

8580
if sample_bezpath.elements().is_empty() {
@@ -96,7 +91,7 @@ pub fn t_value_to_parametric(bezpath: &BezPath, t: f64, euclidian: bool, segment
9691
if euclidian {
9792
let (segment_index, t) = bezpath_t_value_to_parametric(bezpath, BezPathTValue::GlobalEuclidean(t), segments_length);
9893
let segment = bezpath.get_seg(segment_index + 1).unwrap();
99-
return (segment_index, eval_pathseg_euclidean(segment, t, POSITION_ACCURACY));
94+
return (segment_index, eval_pathseg_euclidean(segment, t, DEFAULT_ACCURACY));
10095
}
10196
bezpath_t_value_to_parametric(bezpath, BezPathTValue::GlobalParametric(t), segments_length)
10297
}
@@ -164,7 +159,7 @@ fn bezpath_t_value_to_parametric(bezpath: &kurbo::BezPath, t: BezPathTValue, pre
164159
let segments_length = if let Some(segments_length) = precomputed_segments_length {
165160
segments_length
166161
} else {
167-
computed_segments_length = bezpath.segments().map(|segment| segment.perimeter(PERIMETER_ACCURACY)).collect::<Vec<f64>>();
162+
computed_segments_length = bezpath.segments().map(|segment| segment.perimeter(DEFAULT_ACCURACY)).collect::<Vec<f64>>();
168163
computed_segments_length.as_slice()
169164
};
170165

node-graph/gcore/src/vector/vector_data.rs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use core::borrow::Borrow;
1313
use dyn_any::DynAny;
1414
use glam::{DAffine2, DVec2};
1515
pub use indexed::VectorDataIndex;
16-
use kurbo::{Affine, Shape};
16+
use kurbo::{Affine, Rect, Shape};
1717
pub use modification::*;
1818
use std::collections::HashMap;
1919

@@ -219,6 +219,11 @@ impl VectorData {
219219
vector_data
220220
}
221221

222+
/// Compute the bounding boxes of the bezpaths without any transform
223+
pub fn bounding_box_rect(&self) -> Option<Rect> {
224+
self.bounding_box_with_transform_rect(DAffine2::IDENTITY)
225+
}
226+
222227
pub fn close_subpaths(&mut self) {
223228
let segments_to_add: Vec<_> = self
224229
.stroke_bezier_paths()
@@ -238,27 +243,25 @@ impl VectorData {
238243

239244
/// Compute the bounding boxes of the subpaths without any transform
240245
pub fn bounding_box(&self) -> Option<[DVec2; 2]> {
241-
self.bounding_box_with_transform(DAffine2::IDENTITY)
246+
self.bounding_box_with_transform_rect(DAffine2::IDENTITY)
247+
.map(|rect| [DVec2::new(rect.x0, rect.y0), DVec2::new(rect.x1, rect.y1)])
242248
}
243249

244250
/// Compute the bounding boxes of the subpaths with the specified transform
245251
pub fn bounding_box_with_transform(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
246-
let combine = |[a_min, a_max]: [DVec2; 2], [b_min, b_max]: [DVec2; 2]| [a_min.min(b_min), a_max.max(b_max)];
247-
248-
let anchor_bounds = self
249-
.point_domain
250-
.positions()
251-
.iter()
252-
.map(|&point| transform.transform_point2(point))
253-
.map(|point| [point, point])
254-
.reduce(combine);
255-
256-
let segment_bounds = self
257-
.segment_bezier_iter()
258-
.map(|(_, bezier, _, _)| bezier.apply_transformation(|point| transform.transform_point2(point)).bounding_box())
259-
.reduce(combine);
252+
self.bounding_box_with_transform_rect(transform)
253+
.map(|rect| [DVec2::new(rect.x0, rect.y0), DVec2::new(rect.x1, rect.y1)])
254+
}
260255

261-
anchor_bounds.iter().chain(segment_bounds.iter()).copied().reduce(combine)
256+
/// Compute the bounding boxes of the bezpaths with the specified transform
257+
pub fn bounding_box_with_transform_rect(&self, transform: DAffine2) -> Option<Rect> {
258+
let combine = |r1: Rect, r2: Rect| r1.union(r2);
259+
self.stroke_bezpath_iter()
260+
.map(|mut bezpath| {
261+
bezpath.apply_affine(Affine::new(transform.to_cols_array()));
262+
bezpath.bounding_box()
263+
})
264+
.reduce(combine)
262265
}
263266

264267
/// Calculate the corners of the bounding box but with a nonzero size.

node-graph/gcore/src/vector/vector_data/modification.rs

Lines changed: 94 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::uuid::generate_uuid;
55
use bezier_rs::BezierHandles;
66
use core::hash::BuildHasher;
77
use dyn_any::DynAny;
8-
use kurbo::{BezPath, PathEl};
8+
use kurbo::{BezPath, PathEl, Point};
99
use std::collections::{HashMap, HashSet};
1010

1111
/// Represents a procedural change to the [`PointDomain`] in [`VectorData`].
@@ -556,11 +556,12 @@ where
556556
}
557557

558558
pub struct AppendBezpath<'a> {
559+
first_point: Option<Point>,
560+
last_point: Option<Point>,
559561
first_point_index: Option<usize>,
560562
last_point_index: Option<usize>,
561563
first_segment_id: Option<SegmentId>,
562564
last_segment_id: Option<SegmentId>,
563-
next_handle: Option<BezierHandles>,
564565
point_id: PointId,
565566
segment_id: SegmentId,
566567
vector_data: &'a mut VectorData,
@@ -569,79 +570,118 @@ pub struct AppendBezpath<'a> {
569570
impl<'a> AppendBezpath<'a> {
570571
fn new(vector_data: &'a mut VectorData) -> Self {
571572
Self {
573+
first_point: None,
574+
last_point: None,
572575
first_point_index: None,
573576
last_point_index: None,
574577
first_segment_id: None,
575578
last_segment_id: None,
576-
next_handle: None,
577579
point_id: vector_data.point_domain.next_id(),
578580
segment_id: vector_data.segment_domain.next_id(),
579581
vector_data,
580582
}
581583
}
582584

583-
fn append_path_element(&mut self, handle: BezierHandles, point: kurbo::Point, next_element: Option<&PathEl>) {
584-
if let Some(PathEl::ClosePath) = next_element {
585-
self.next_handle = Some(handle);
585+
fn append_segment_and_close_path(&mut self, point: Point, handle: BezierHandles) {
586+
let handle = if self.first_point.unwrap() != point {
587+
// If the first point is not the same as the last point of the path then we append the segment
588+
// with given handle and point and then close the path with linear handle.
589+
self.append_segment(point, handle);
590+
BezierHandles::Linear
586591
} else {
587-
let next_point_index = self.vector_data.point_domain.ids().len();
588-
self.vector_data.point_domain.push(self.point_id.next_id(), point_to_dvec2(point));
592+
// if the endpoints are the same then we close the path with given handle.
593+
handle
594+
};
595+
596+
// Create a new segment.
597+
let next_segment_id = self.segment_id.next_id();
598+
self.vector_data
599+
.segment_domain
600+
.push(next_segment_id, self.last_point_index.unwrap(), self.first_point_index.unwrap(), handle, StrokeId::ZERO);
601+
602+
// Create a new region.
603+
let next_region_id = self.vector_data.region_domain.next_id();
604+
let first_segment_id = self.first_segment_id.unwrap_or(next_segment_id);
605+
let last_segment_id = next_segment_id;
606+
607+
self.vector_data.region_domain.push(next_region_id, first_segment_id..=last_segment_id, FillId::ZERO);
608+
}
589609

590-
let next_segment_id = self.segment_id.next_id();
591-
self.vector_data
592-
.segment_domain
593-
.push(self.segment_id.next_id(), self.last_point_index.unwrap(), next_point_index, handle, StrokeId::ZERO);
610+
fn append_segment(&mut self, end_point: Point, handle: BezierHandles) {
611+
// Append the point.
612+
let next_point_index = self.vector_data.point_domain.ids().len();
613+
let next_point_id = self.point_id.next_id();
594614

595-
self.last_point_index = Some(next_point_index);
596-
self.first_segment_id = Some(self.first_segment_id.unwrap_or(next_segment_id));
597-
self.last_segment_id = Some(next_segment_id);
598-
}
615+
self.vector_data.point_domain.push(next_point_id, point_to_dvec2(end_point));
616+
617+
// Append the segment.
618+
let next_segment_id = self.segment_id.next_id();
619+
self.vector_data
620+
.segment_domain
621+
.push(next_segment_id, self.last_point_index.unwrap(), next_point_index, handle, StrokeId::ZERO);
622+
623+
// Update the states.
624+
self.last_point = Some(end_point);
625+
self.last_point_index = Some(next_point_index);
626+
627+
self.first_segment_id = Some(self.first_segment_id.unwrap_or(next_segment_id));
628+
self.last_segment_id = Some(next_segment_id);
599629
}
600630

601-
pub fn append_bezpath(vector_data: &'a mut VectorData, bezpath: BezPath) {
602-
let mut this = Self::new(vector_data);
631+
fn append_first_point(&mut self, point: Point) {
632+
self.first_point = Some(point);
633+
self.last_point = Some(point);
603634

604-
let stroke_id = StrokeId::ZERO;
605-
let fill_id = FillId::ZERO;
635+
// Append the first point.
636+
let next_point_index = self.vector_data.point_domain.ids().len();
637+
self.vector_data.point_domain.push(self.point_id.next_id(), point_to_dvec2(point));
606638

607-
for i in 0..bezpath.elements().len() {
608-
let current_element = bezpath.elements()[i];
609-
let next_element = bezpath.elements().get(i + 1);
639+
// Update the state.
640+
self.first_point_index = Some(next_point_index);
641+
self.last_point_index = Some(next_point_index);
642+
}
610643

611-
match current_element {
612-
kurbo::PathEl::MoveTo(point) => {
613-
let next_point_index = this.vector_data.point_domain.ids().len();
614-
this.vector_data.point_domain.push(this.point_id.next_id(), point_to_dvec2(point));
615-
this.first_point_index = Some(next_point_index);
616-
this.last_point_index = Some(next_point_index);
644+
pub fn append_bezpath(vector_data: &'a mut VectorData, bezpath: BezPath) {
645+
let mut this = Self::new(vector_data);
646+
let mut elements = bezpath.elements().iter().peekable();
647+
648+
while let Some(element) = elements.next() {
649+
let close_path = elements.peek().is_some_and(|elm| **elm == PathEl::ClosePath);
650+
651+
match *element {
652+
PathEl::MoveTo(point) => this.append_first_point(point),
653+
PathEl::LineTo(point) => {
654+
let handle = BezierHandles::Linear;
655+
if close_path {
656+
this.append_segment_and_close_path(point, handle);
657+
} else {
658+
this.append_segment(point, handle);
659+
}
617660
}
618-
kurbo::PathEl::ClosePath => match (this.first_point_index, this.last_point_index) {
619-
(Some(first_point_index), Some(last_point_index)) => {
620-
let next_segment_id = this.segment_id.next_id();
621-
this.vector_data
622-
.segment_domain
623-
.push(next_segment_id, last_point_index, first_point_index, this.next_handle.unwrap_or(BezierHandles::Linear), stroke_id);
624-
625-
let next_region_id = this.vector_data.region_domain.next_id();
626-
// In case there is only one anchor point.
627-
let first_segment_id = this.first_segment_id.unwrap_or(next_segment_id);
628-
629-
this.vector_data.region_domain.push(next_region_id, first_segment_id..=next_segment_id, fill_id);
661+
PathEl::QuadTo(point, point1) => {
662+
let handle = BezierHandles::Quadratic { handle: point_to_dvec2(point) };
663+
if close_path {
664+
this.append_segment_and_close_path(point1, handle);
665+
} else {
666+
this.append_segment(point1, handle);
630667
}
631-
_ => {
632-
error!("Empty bezpath cannot be closed.")
668+
}
669+
PathEl::CurveTo(point, point1, point2) => {
670+
let handle = BezierHandles::Cubic {
671+
handle_start: point_to_dvec2(point),
672+
handle_end: point_to_dvec2(point1),
673+
};
674+
675+
if close_path {
676+
this.append_segment_and_close_path(point2, handle);
677+
} else {
678+
this.append_segment(point2, handle);
633679
}
634-
},
635-
kurbo::PathEl::LineTo(point) => this.append_path_element(BezierHandles::Linear, point, next_element),
636-
kurbo::PathEl::QuadTo(handle, point) => this.append_path_element(BezierHandles::Quadratic { handle: point_to_dvec2(handle) }, point, next_element),
637-
kurbo::PathEl::CurveTo(handle_start, handle_end, point) => this.append_path_element(
638-
BezierHandles::Cubic {
639-
handle_start: point_to_dvec2(handle_start),
640-
handle_end: point_to_dvec2(handle_end),
641-
},
642-
point,
643-
next_element,
644-
),
680+
}
681+
PathEl::ClosePath => {
682+
// Already handled using `append_segment_and_close_path()`;
683+
break;
684+
}
645685
}
646686
}
647687
}

0 commit comments

Comments
 (0)