diff --git a/node-graph/libraries/core-types/src/math/polynomial.rs b/node-graph/libraries/core-types/src/math/polynomial.rs index 392fee51e4..4ca1807b48 100644 --- a/node-graph/libraries/core-types/src/math/polynomial.rs +++ b/node-graph/libraries/core-types/src/math/polynomial.rs @@ -1,10 +1,9 @@ use kurbo::PathSeg; -use std::fmt::{self, Display, Formatter}; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use std::ops::{Mul, MulAssign}; /// A struct that represents a polynomial with a maximum degree of `N-1`. /// -/// It provides basic mathematical operations for polynomials like addition, multiplication, differentiation, integration, etc. +/// It provides basic mathematical operations for polynomials like multiplication, differentiation, integration, etc. #[derive(Copy, Clone, Debug, PartialEq)] pub struct Polynomial { coefficients: [f64; N], @@ -18,18 +17,6 @@ impl Polynomial { Polynomial { coefficients } } - /// Create a polynomial where all its coefficients are zero. - pub fn zero() -> Polynomial { - Polynomial { coefficients: [0.; N] } - } - - /// Return an immutable reference to the coefficients. - /// - /// The coefficient for nth degree is at the nth index in array. Therefore the order of coefficients are reversed than the usual order for writing polynomials mathematically. - pub fn coefficients(&self) -> &[f64; N] { - &self.coefficients - } - /// Return a mutable reference to the coefficients. /// /// The coefficient for nth degree is at the nth index in array. Therefore the order of coefficients are reversed than the usual order for writing polynomials mathematically. @@ -83,98 +70,6 @@ impl Polynomial { ans.derivative_mut(); ans } - - /// Computes the antiderivative at `C = 0`. - /// - /// Returns `None` if the polynomial is not big enough to accommodate the extra degree. - pub fn antiderivative(&self) -> Option> { - let mut ans = *self; - ans.antiderivative_mut()?; - Some(ans) - } -} - -impl Default for Polynomial { - fn default() -> Self { - Self::zero() - } -} - -impl Display for Polynomial { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut first = true; - for (index, coefficient) in self.coefficients.iter().enumerate().rev().filter(|&(_, &coefficient)| coefficient != 0.) { - if first { - first = false; - } else { - f.write_str(" + ")? - } - - coefficient.fmt(f)?; - if index == 0 { - continue; - } - f.write_str("x")?; - if index == 1 { - continue; - } - f.write_str("^")?; - index.fmt(f)?; - } - - Ok(()) - } -} - -impl AddAssign<&Polynomial> for Polynomial { - fn add_assign(&mut self, rhs: &Polynomial) { - self.coefficients.iter_mut().zip(rhs.coefficients.iter()).for_each(|(a, b)| *a += b); - } -} - -impl Add for &Polynomial { - type Output = Polynomial; - - fn add(self, other: &Polynomial) -> Polynomial { - let mut output = *self; - output += other; - output - } -} - -impl Neg for &Polynomial { - type Output = Polynomial; - - fn neg(self) -> Polynomial { - let mut output = *self; - output.coefficients.iter_mut().for_each(|x| *x = -*x); - output - } -} - -impl Neg for Polynomial { - type Output = Polynomial; - - fn neg(mut self) -> Polynomial { - self.coefficients.iter_mut().for_each(|x| *x = -*x); - self - } -} - -impl SubAssign<&Polynomial> for Polynomial { - fn sub_assign(&mut self, rhs: &Polynomial) { - self.coefficients.iter_mut().zip(rhs.coefficients.iter()).for_each(|(a, b)| *a -= b); - } -} - -impl Sub for &Polynomial { - type Output = Polynomial; - - fn sub(self, other: &Polynomial) -> Polynomial { - let mut output = *self; - output -= other; - output - } } impl MulAssign<&Polynomial> for Polynomial { @@ -248,18 +143,6 @@ mod test { assert_eq!(p2.as_size::<2>(), None); } - #[test] - fn addition_and_subtaction() { - let p1 = Polynomial::new([1., 2., 3.]); - let p2 = Polynomial::new([4., 5., 6.]); - - let addition = Polynomial::new([5., 7., 9.]); - let subtraction = Polynomial::new([-3., -3., -3.]); - - assert_eq!(&p1 + &p2, addition); - assert_eq!(&p1 - &p2, subtraction); - } - #[test] fn multiplication() { let p1 = Polynomial::new([1., 2., 3.]).as_size().unwrap(); @@ -278,15 +161,10 @@ mod test { assert_eq!(p.derivative(), p_deriv); p.coefficients_mut()[0] = 0.; - assert_eq!(p_deriv.antiderivative().unwrap(), p); + let mut antiderivative = p_deriv; + assert_eq!(antiderivative.antiderivative_mut(), Some(())); + assert_eq!(antiderivative, p); - assert_eq!(p.antiderivative(), None); - } - - #[test] - fn display() { - let p = Polynomial::new([1., 2., 0., 3.]); - - assert_eq!(format!("{p:.2}"), "3.00x^3 + 2.00x + 1.00"); + assert_eq!(p.antiderivative_mut(), None); } } diff --git a/node-graph/libraries/vector-types/src/vector/algorithms/bezpath_algorithms.rs b/node-graph/libraries/vector-types/src/vector/algorithms/bezpath_algorithms.rs index 5185fe6993..bd067a0443 100644 --- a/node-graph/libraries/vector-types/src/vector/algorithms/bezpath_algorithms.rs +++ b/node-graph/libraries/vector-types/src/vector/algorithms/bezpath_algorithms.rs @@ -1,15 +1,13 @@ +use super::consts::MAX_ABSOLUTE_DIFFERENCE; use super::intersection::{bezpath_intersections, filtered_all_segment_intersections, pathseg_self_intersections}; use super::poisson_disk::poisson_disk_sample; use super::util::pathseg_tangent; use crate::vector::misc::{PointSpacingType, dvec2_to_point, point_to_dvec2}; use core_types::math::polynomial::pathseg_to_parametric_polynomial; use glam::{DMat2, DVec2}; -use kurbo::{BezPath, CubicBez, DEFAULT_ACCURACY, Line, ParamCurve, ParamCurveArclen, ParamCurveDeriv, PathEl, PathSeg, Point, QuadBez, Rect, Shape, Vec2}; +use kurbo::{BezPath, CubicBez, DEFAULT_ACCURACY, Line, ParamCurve, ParamCurveArclen, PathEl, PathSeg, Point, QuadBez, Rect, Shape, Vec2}; use std::f64::consts::{FRAC_PI_2, PI}; -/// Default threshold for comparing floating point values in intersection and centroid math. -const MAX_ABSOLUTE_DIFFERENCE: f64 = 1e-3; - /// Splits the [`BezPath`] at segment index at `t` value which lie in the range of [0, 1]. /// Returns [`None`] if the given [`BezPath`] has no segments or `t` is within f64::EPSILON of 0 or 1. fn split_bezpath_at_segment(bezpath: &BezPath, segment_index: usize, t: f64) -> Option<(BezPath, BezPath)> { @@ -79,11 +77,7 @@ pub fn tangent_on_bezpath(bezpath: &BezPath, t_value: TValue, segments_length: O let (segment_index, t) = eval_bezpath(bezpath, t_value, segments_length); let segment = bezpath.get_seg(segment_index + 1).unwrap(); - match segment { - PathSeg::Line(line) => line.deriv().eval(t), - PathSeg::Quad(quad_bez) => quad_bez.deriv().eval(t), - PathSeg::Cubic(cubic_bez) => cubic_bez.deriv().eval(t), - } + dvec2_to_point(pathseg_tangent(segment, t)) } /// Computes sample locations along a bezpath, returning parametric `(segment_index, t)` pairs and whether the path was closed. @@ -254,7 +248,7 @@ pub(crate) fn pathseg_length_centroid_and_length(segment: PathSeg, accuracy: Opt let QuadBez { p0, p1, p2 } = quad_bez; // Use Casteljau subdivision, noting that the length is more than the straight line distance from start to end but less than the straight line distance through the handles fn recurse(a0: Vec2, a1: Vec2, a2: Vec2, accuracy: f64, level: u8) -> (f64, Vec2) { - let lower = (a2 - a1).length(); + let lower = (a2 - a0).length(); let upper = (a1 - a0).length() + (a2 - a1).length(); if upper - lower <= 2. * accuracy || level >= 8 { let length = (lower + upper) / 2.; diff --git a/node-graph/libraries/vector-types/src/vector/algorithms/consts.rs b/node-graph/libraries/vector-types/src/vector/algorithms/consts.rs new file mode 100644 index 0000000000..46eec7326b --- /dev/null +++ b/node-graph/libraries/vector-types/src/vector/algorithms/consts.rs @@ -0,0 +1,8 @@ +/// Minimum allowable separation between adjacent `t` values when calculating curve intersections +pub(crate) const MIN_SEPARATION_VALUE: f64 = 5. * 1e-3; + +/// Threshold for comparing floating point values in intersection and centroid math. +pub(crate) const MAX_ABSOLUTE_DIFFERENCE: f64 = 1e-3; + +/// Maximum distance at which two points are treated as one and the same point. +pub(crate) const MAX_COINCIDENT_POINT_DISTANCE: f64 = 1e-7; diff --git a/node-graph/libraries/vector-types/src/vector/algorithms/contants.rs b/node-graph/libraries/vector-types/src/vector/algorithms/contants.rs deleted file mode 100644 index 3533369988..0000000000 --- a/node-graph/libraries/vector-types/src/vector/algorithms/contants.rs +++ /dev/null @@ -1,6 +0,0 @@ -/// Minimum allowable separation between adjacent `t` values when calculating curve intersections -pub(crate) const MIN_SEPARATION_VALUE: f64 = 5. * 1e-3; - -/// Constant used to determine if `f64`s are equivalent. -#[cfg(test)] -pub(crate) const MAX_ABSOLUTE_DIFFERENCE: f64 = 1e-3; diff --git a/node-graph/libraries/vector-types/src/vector/algorithms/intersection.rs b/node-graph/libraries/vector-types/src/vector/algorithms/intersection.rs index b4a1c8c779..fa351888fc 100644 --- a/node-graph/libraries/vector-types/src/vector/algorithms/intersection.rs +++ b/node-graph/libraries/vector-types/src/vector/algorithms/intersection.rs @@ -1,4 +1,4 @@ -use super::contants::MIN_SEPARATION_VALUE; +use super::consts::MIN_SEPARATION_VALUE; use kurbo::{BezPath, DEFAULT_ACCURACY, ParamCurve, PathSeg, Shape}; use lyon_geom::{CubicBezierSegment, Point}; @@ -260,7 +260,7 @@ pub(crate) fn pathseg_self_intersections(segment: PathSeg, accuracy: Option mod tests { use super::{bezpath_and_segment_intersections, filtered_segment_intersections}; use crate::vector::algorithms::{ - contants::MAX_ABSOLUTE_DIFFERENCE, + consts::MAX_ABSOLUTE_DIFFERENCE, util::{compare_points, compare_vec_of_points, dvec2_compare}, }; diff --git a/node-graph/libraries/vector-types/src/vector/algorithms/mod.rs b/node-graph/libraries/vector-types/src/vector/algorithms/mod.rs index 038b7ed732..3d45506dc9 100644 --- a/node-graph/libraries/vector-types/src/vector/algorithms/mod.rs +++ b/node-graph/libraries/vector-types/src/vector/algorithms/mod.rs @@ -1,8 +1,8 @@ pub mod bezpath_algorithms; -mod contants; +pub(crate) mod consts; pub mod intersection; pub mod merge_by_distance; -pub mod offset_subpath; +pub mod offset_bezpath; mod poisson_disk; pub mod shapes; pub mod spline; diff --git a/node-graph/libraries/vector-types/src/vector/algorithms/offset_subpath.rs b/node-graph/libraries/vector-types/src/vector/algorithms/offset_bezpath.rs similarity index 91% rename from node-graph/libraries/vector-types/src/vector/algorithms/offset_subpath.rs rename to node-graph/libraries/vector-types/src/vector/algorithms/offset_bezpath.rs index 34b419e99e..112df67a7c 100644 --- a/node-graph/libraries/vector-types/src/vector/algorithms/offset_subpath.rs +++ b/node-graph/libraries/vector-types/src/vector/algorithms/offset_bezpath.rs @@ -1,13 +1,12 @@ use super::bezpath_algorithms::{clip_simple_bezpaths, miter_line_join, round_line_join}; +use super::consts::MAX_COINCIDENT_POINT_DISTANCE; use crate::vector::misc::point_to_dvec2; use kurbo::{BezPath, Join, ParamCurve, PathEl, PathSeg}; /// Value to control smoothness and mathematical accuracy to offset a cubic Bezier. const CUBIC_REGULARIZATION_ACCURACY: f64 = 0.5; -/// Constant used to determine if `f64`s are equivalent. -pub const MAX_ABSOLUTE_DIFFERENCE: f64 = 1e-7; /// Squared version to avoid sqrt in distance checks. -const MAX_ABSOLUTE_DIFFERENCE_SQUARED: f64 = MAX_ABSOLUTE_DIFFERENCE * MAX_ABSOLUTE_DIFFERENCE; +const MAX_COINCIDENT_POINT_DISTANCE_SQUARED: f64 = MAX_COINCIDENT_POINT_DISTANCE * MAX_COINCIDENT_POINT_DISTANCE; const MAX_FITTED_SEGMENTS: usize = 10000; /// Reduces the segments of the bezpath into simple subcurves, then offset each subcurve a set `distance` away. @@ -26,9 +25,9 @@ pub fn offset_bezpath(bezpath: &BezPath, distance: f64, join: Join, miter_limit: // Skip degenerate curves where all control points are at the same location. // Offsetting a point is undefined and causes infinite recursion in fit_to_bezpath. let start = cubic_bez.p0; - let is_degenerate = start.distance_squared(cubic_bez.p1) < MAX_ABSOLUTE_DIFFERENCE_SQUARED - && start.distance_squared(cubic_bez.p2) < MAX_ABSOLUTE_DIFFERENCE_SQUARED - && start.distance_squared(cubic_bez.p3) < MAX_ABSOLUTE_DIFFERENCE_SQUARED; + let is_degenerate = start.distance_squared(cubic_bez.p1) < MAX_COINCIDENT_POINT_DISTANCE_SQUARED + && start.distance_squared(cubic_bez.p2) < MAX_COINCIDENT_POINT_DISTANCE_SQUARED + && start.distance_squared(cubic_bez.p3) < MAX_COINCIDENT_POINT_DISTANCE_SQUARED; if is_degenerate { return None; @@ -59,7 +58,7 @@ pub fn offset_bezpath(bezpath: &BezPath, distance: f64, join: Join, miter_limit: let first_segment_start = point_to_dvec2(bezpath2.segments().next().unwrap().start()); // If the anchors are approximately equal, there is no need to clip / join the segments - if last_segment_end.abs_diff_eq(first_segment_start, MAX_ABSOLUTE_DIFFERENCE) { + if last_segment_end.abs_diff_eq(first_segment_start, MAX_COINCIDENT_POINT_DISTANCE) { continue; } diff --git a/node-graph/libraries/vector-types/src/vector/algorithms/shapes.rs b/node-graph/libraries/vector-types/src/vector/algorithms/shapes.rs index 8478e66612..33c6265cee 100644 --- a/node-graph/libraries/vector-types/src/vector/algorithms/shapes.rs +++ b/node-graph/libraries/vector-types/src/vector/algorithms/shapes.rs @@ -2,7 +2,7 @@ //! //! Anchor order and winding direction are load-bearing, since fills rely on every generator agreeing. -use crate::vector::misc::{ArcType, SpiralType, dvec2_to_point}; +use crate::vector::misc::{ArcType, SpiralType, bezpath_from_anchors_and_handles}; use glam::DVec2; use kurbo::BezPath; use std::f64::consts::TAU; @@ -28,31 +28,8 @@ impl Anchor { } } -/// Stitches anchors into a path, emitting a cubic when both facing handles exist, a quadratic when only one does, and a line otherwise. fn bezpath_from_anchors(anchors: &[Anchor], closed: bool) -> BezPath { - let mut bezpath = BezPath::new(); - - let Some(first) = anchors.first() else { return bezpath }; - bezpath.move_to(dvec2_to_point(first.position)); - let mut out_handle = first.out_handle; - - let connect_to = |bezpath: &mut BezPath, out_handle: Option, anchor: &Anchor| match (out_handle, anchor.in_handle) { - (Some(handle_start), Some(handle_end)) => bezpath.curve_to(dvec2_to_point(handle_start), dvec2_to_point(handle_end), dvec2_to_point(anchor.position)), - (None, None) => bezpath.line_to(dvec2_to_point(anchor.position)), - (None, Some(handle)) | (Some(handle), None) => bezpath.quad_to(dvec2_to_point(handle), dvec2_to_point(anchor.position)), - }; - - for anchor in anchors.iter().skip(1) { - connect_to(&mut bezpath, out_handle, anchor); - out_handle = anchor.out_handle; - } - - if closed { - connect_to(&mut bezpath, out_handle, first); - bezpath.close_path(); - } - - bezpath + bezpath_from_anchors_and_handles(anchors.iter().map(|anchor| (anchor.position, anchor.in_handle, anchor.out_handle)), closed) } /// Stitches a sequence of sharp (handleless) anchors into a polyline, or a closed polygon. diff --git a/node-graph/libraries/vector-types/src/vector/algorithms/util.rs b/node-graph/libraries/vector-types/src/vector/algorithms/util.rs index 2ec1a67c27..662f47e2c5 100644 --- a/node-graph/libraries/vector-types/src/vector/algorithms/util.rs +++ b/node-graph/libraries/vector-types/src/vector/algorithms/util.rs @@ -18,7 +18,7 @@ pub fn pathseg_tangent(segment: PathSeg, t: f64) -> DVec2 { #[cfg(test)] pub(crate) fn compare_points(p1: kurbo::Point, p2: kurbo::Point) -> bool { let (p1, p2) = (crate::vector::misc::point_to_dvec2(p1), crate::vector::misc::point_to_dvec2(p2)); - p1.abs_diff_eq(p2, super::contants::MAX_ABSOLUTE_DIFFERENCE) + p1.abs_diff_eq(p2, super::consts::MAX_ABSOLUTE_DIFFERENCE) } /// Compare vectors of points by allowing some maximum absolute difference to account for floating point errors diff --git a/node-graph/libraries/vector-types/src/vector/misc.rs b/node-graph/libraries/vector-types/src/vector/misc.rs index 065ffb086b..b889bf87f0 100644 --- a/node-graph/libraries/vector-types/src/vector/misc.rs +++ b/node-graph/libraries/vector-types/src/vector/misc.rs @@ -1,5 +1,5 @@ use super::PointId; -use super::algorithms::offset_subpath::MAX_ABSOLUTE_DIFFERENCE; +use super::algorithms::consts::MAX_COINCIDENT_POINT_DISTANCE; use crate::vector::{SegmentId, Vector}; use core_types::list::{Item, List}; use dyn_any::DynAny; @@ -227,36 +227,42 @@ pub fn handles_to_segment(start: DVec2, handles: BezierHandles, end: DVec2) -> P } } -pub fn bezpath_from_manipulator_groups(manipulator_groups: &[ManipulatorGroup], closed: bool) -> BezPath { - let mut bezpath = kurbo::BezPath::new(); - let mut out_handle; +/// Stitches anchors into a path, emitting a cubic when both facing handles exist, a quadratic when only one does, and a line otherwise. +/// +/// Each item is an anchor position paired with its incoming and outgoing handle positions, in absolute coordinates. +pub fn bezpath_from_anchors_and_handles(anchors: impl IntoIterator, Option)>, closed: bool) -> BezPath { + let mut bezpath = BezPath::new(); + let mut anchors = anchors.into_iter(); - let Some(first) = manipulator_groups.first() else { return bezpath }; - bezpath.move_to(dvec2_to_point(first.anchor)); - out_handle = first.out_handle; + let Some((first_anchor, first_in_handle, first_out_handle)) = anchors.next() else { + return bezpath; + }; + bezpath.move_to(dvec2_to_point(first_anchor)); + let mut out_handle = first_out_handle; - for manipulator in manipulator_groups.iter().skip(1) { - match (out_handle, manipulator.in_handle) { - (Some(handle_start), Some(handle_end)) => bezpath.curve_to(dvec2_to_point(handle_start), dvec2_to_point(handle_end), dvec2_to_point(manipulator.anchor)), - (None, None) => bezpath.line_to(dvec2_to_point(manipulator.anchor)), - (None, Some(handle)) => bezpath.quad_to(dvec2_to_point(handle), dvec2_to_point(manipulator.anchor)), - (Some(handle), None) => bezpath.quad_to(dvec2_to_point(handle), dvec2_to_point(manipulator.anchor)), - } - out_handle = manipulator.out_handle; + let connect_to = |bezpath: &mut BezPath, out_handle: Option, anchor: DVec2, in_handle: Option| match (out_handle, in_handle) { + (Some(handle_start), Some(handle_end)) => bezpath.curve_to(dvec2_to_point(handle_start), dvec2_to_point(handle_end), dvec2_to_point(anchor)), + (None, None) => bezpath.line_to(dvec2_to_point(anchor)), + (None, Some(handle)) | (Some(handle), None) => bezpath.quad_to(dvec2_to_point(handle), dvec2_to_point(anchor)), + }; + + for (anchor, in_handle, anchor_out_handle) in anchors { + connect_to(&mut bezpath, out_handle, anchor, in_handle); + out_handle = anchor_out_handle; } if closed { - match (out_handle, first.in_handle) { - (Some(handle_start), Some(handle_end)) => bezpath.curve_to(dvec2_to_point(handle_start), dvec2_to_point(handle_end), dvec2_to_point(first.anchor)), - (None, None) => bezpath.line_to(dvec2_to_point(first.anchor)), - (None, Some(handle)) => bezpath.quad_to(dvec2_to_point(handle), dvec2_to_point(first.anchor)), - (Some(handle), None) => bezpath.quad_to(dvec2_to_point(handle), dvec2_to_point(first.anchor)), - } + connect_to(&mut bezpath, out_handle, first_anchor, first_in_handle); bezpath.close_path(); } + bezpath } +pub fn bezpath_from_manipulator_groups(manipulator_groups: &[ManipulatorGroup], closed: bool) -> BezPath { + bezpath_from_anchors_and_handles(manipulator_groups.iter().map(|group| (group.anchor, group.in_handle, group.out_handle)), closed) +} + pub fn bezpath_to_manipulator_groups(bezpath: &BezPath) -> (Vec, bool) { let mut manipulator_groups = Vec::::new(); let mut is_closed = false; @@ -293,7 +299,7 @@ pub fn bezpath_to_manipulator_groups(bezpath: &BezPath) -> (Vec 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 }; + 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_COINCIDENT_POINT_DISTANCE }; match segment { PathSeg::Line(_) => true, diff --git a/node-graph/libraries/vector-types/src/vector/vector_attributes.rs b/node-graph/libraries/vector-types/src/vector/vector_attributes.rs index 41acb33bb2..c450fb56a4 100644 --- a/node-graph/libraries/vector-types/src/vector/vector_attributes.rs +++ b/node-graph/libraries/vector-types/src/vector/vector_attributes.rs @@ -1043,35 +1043,8 @@ impl Vector { /// Construct a [`kurbo::BezPath`] curve for stroke. pub fn stroke_bezpath_iter(&self) -> impl Iterator { - self.build_stroke_path_iter().map(|(manipulators_list, closed)| { - let mut bezpath = kurbo::BezPath::new(); - let mut out_handle; - - let Some(first) = manipulators_list.first() else { return bezpath }; - bezpath.move_to(dvec2_to_point(first.anchor)); - out_handle = first.out_handle; - - for manipulator in manipulators_list.iter().skip(1) { - match (out_handle, manipulator.in_handle) { - (Some(handle_start), Some(handle_end)) => bezpath.curve_to(dvec2_to_point(handle_start), dvec2_to_point(handle_end), dvec2_to_point(manipulator.anchor)), - (None, None) => bezpath.line_to(dvec2_to_point(manipulator.anchor)), - (None, Some(handle)) => bezpath.quad_to(dvec2_to_point(handle), dvec2_to_point(manipulator.anchor)), - (Some(handle), None) => bezpath.quad_to(dvec2_to_point(handle), dvec2_to_point(manipulator.anchor)), - } - out_handle = manipulator.out_handle; - } - - if closed { - match (out_handle, first.in_handle) { - (Some(handle_start), Some(handle_end)) => bezpath.curve_to(dvec2_to_point(handle_start), dvec2_to_point(handle_end), dvec2_to_point(first.anchor)), - (None, None) => bezpath.line_to(dvec2_to_point(first.anchor)), - (None, Some(handle)) => bezpath.quad_to(dvec2_to_point(handle), dvec2_to_point(first.anchor)), - (Some(handle), None) => bezpath.quad_to(dvec2_to_point(handle), dvec2_to_point(first.anchor)), - } - bezpath.close_path(); - } - bezpath - }) + self.build_stroke_path_iter() + .map(|(manipulators_list, closed)| crate::vector::misc::bezpath_from_manipulator_groups(&manipulators_list, closed)) } pub fn transform(&mut self, transform: DAffine2) { diff --git a/node-graph/nodes/vector/src/vector_nodes.rs b/node-graph/nodes/vector/src/vector_nodes.rs index 82d94db40c..1a09a0882c 100644 --- a/node-graph/nodes/vector/src/vector_nodes.rs +++ b/node-graph/nodes/vector/src/vector_nodes.rs @@ -27,7 +27,7 @@ use vector_types::vector::algorithms::bezpath_algorithms::{ self, TValue, bezpath_area_centroid_and_area, bezpath_length_centroid_and_length, eval_pathseg_euclidean, evaluate_bezpath, split_bezpath, tangent_on_bezpath, }; use vector_types::vector::algorithms::merge_by_distance::MergeByDistanceExt; -use vector_types::vector::algorithms::offset_subpath::offset_bezpath; +use vector_types::vector::algorithms::offset_bezpath::offset_bezpath; use vector_types::vector::algorithms::spline::{solve_spline_first_handle_closed, solve_spline_first_handle_open}; use vector_types::vector::misc::{ BezierHandles, CentroidType, ExtrudeJoiningAlgorithm, HandleId, InterpolationDistribution, ManipulatorGroup, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns,