diff --git a/node-graph/gcore/src/vector/vector_data.rs b/node-graph/gcore/src/vector/vector_data.rs index aba89db8..1745c146 100644 --- a/node-graph/gcore/src/vector/vector_data.rs +++ b/node-graph/gcore/src/vector/vector_data.rs @@ -7,7 +7,7 @@ use super::style::{PathStyle, Stroke}; use crate::instances::Instances; use crate::{AlphaBlending, Color, GraphicGroupTable}; pub use attributes::*; -use bezier_rs::{BezierHandles, ManipulatorGroup}; +use bezier_rs::ManipulatorGroup; use core::borrow::Borrow; use dyn_any::DynAny; use glam::{DAffine2, DVec2}; @@ -179,66 +179,7 @@ impl VectorData { /// Appends a Kurbo BezPath to the vector data. pub fn append_bezpath(&mut self, bezpath: kurbo::BezPath) { - let mut first_point_index = None; - let mut last_point_index = None; - - let mut first_segment_id = None; - let mut last_segment_id = None; - - let mut point_id = self.point_domain.next_id(); - let mut segment_id = self.segment_domain.next_id(); - - let stroke_id = StrokeId::ZERO; - let fill_id = FillId::ZERO; - - for element in bezpath.elements() { - match *element { - kurbo::PathEl::MoveTo(point) => { - let next_point_index = self.point_domain.ids().len(); - self.point_domain.push(point_id.next_id(), point_to_dvec2(point)); - first_point_index = Some(next_point_index); - last_point_index = Some(next_point_index); - } - kurbo::PathEl::ClosePath => match (first_point_index, last_point_index) { - (Some(first_point_index), Some(last_point_index)) => { - let next_segment_id = segment_id.next_id(); - self.segment_domain.push(next_segment_id, first_point_index, last_point_index, BezierHandles::Linear, stroke_id); - - let next_region_id = self.region_domain.next_id(); - self.region_domain.push(next_region_id, first_segment_id.unwrap()..=next_segment_id, fill_id); - } - _ => { - error!("Empty bezpath cannot be closed.") - } - }, - _ => {} - } - - let mut append_path_element = |handle: BezierHandles, point: kurbo::Point| { - let next_point_index = self.point_domain.ids().len(); - self.point_domain.push(point_id.next_id(), point_to_dvec2(point)); - - let next_segment_id = segment_id.next_id(); - self.segment_domain.push(segment_id.next_id(), last_point_index.unwrap(), next_point_index, handle, stroke_id); - - last_point_index = Some(next_point_index); - first_segment_id = Some(first_segment_id.unwrap_or(next_segment_id)); - last_segment_id = Some(next_segment_id); - }; - - match *element { - kurbo::PathEl::LineTo(point) => append_path_element(BezierHandles::Linear, point), - kurbo::PathEl::QuadTo(handle, point) => append_path_element(BezierHandles::Quadratic { handle: point_to_dvec2(handle) }, point), - kurbo::PathEl::CurveTo(handle_start, handle_end, point) => append_path_element( - BezierHandles::Cubic { - handle_start: point_to_dvec2(handle_start), - handle_end: point_to_dvec2(handle_end), - }, - point, - ), - _ => {} - } - } + AppendBezpath::append_bezpath(self, bezpath); } /// Construct some new vector data from subpaths with an identity transform and black fill. diff --git a/node-graph/gcore/src/vector/vector_data/modification.rs b/node-graph/gcore/src/vector/vector_data/modification.rs index 2df21db0..aae7ed65 100644 --- a/node-graph/gcore/src/vector/vector_data/modification.rs +++ b/node-graph/gcore/src/vector/vector_data/modification.rs @@ -5,6 +5,7 @@ use crate::uuid::generate_uuid; use bezier_rs::BezierHandles; use core::hash::BuildHasher; use dyn_any::DynAny; +use kurbo::{BezPath, PathEl}; use std::collections::{HashMap, HashSet}; /// Represents a procedural change to the [`PointDomain`] in [`VectorData`]. @@ -552,3 +553,95 @@ where let visitor = HashMapVisitor { marker: std::marker::PhantomData }; deserializer.deserialize_seq(visitor) } + +pub struct AppendBezpath<'a> { + first_point_index: Option, + last_point_index: Option, + first_segment_id: Option, + last_segment_id: Option, + next_handle: Option, + point_id: PointId, + segment_id: SegmentId, + vector_data: &'a mut VectorData, +} + +impl<'a> AppendBezpath<'a> { + fn new(vector_data: &'a mut VectorData) -> Self { + Self { + first_point_index: None, + last_point_index: None, + first_segment_id: None, + last_segment_id: None, + next_handle: None, + point_id: vector_data.point_domain.next_id(), + segment_id: vector_data.segment_domain.next_id(), + vector_data, + } + } + + fn append_path_element(&mut self, handle: BezierHandles, point: kurbo::Point, next_element: Option<&PathEl>) { + if let Some(PathEl::ClosePath) = next_element { + self.next_handle = Some(handle); + } else { + let next_point_index = self.vector_data.point_domain.ids().len(); + self.vector_data.point_domain.push(self.point_id.next_id(), point_to_dvec2(point)); + + let next_segment_id = self.segment_id.next_id(); + self.vector_data + .segment_domain + .push(self.segment_id.next_id(), self.last_point_index.unwrap(), next_point_index, handle, StrokeId::ZERO); + + self.last_point_index = Some(next_point_index); + self.first_segment_id = Some(self.first_segment_id.unwrap_or(next_segment_id)); + self.last_segment_id = Some(next_segment_id); + } + } + + pub fn append_bezpath(vector_data: &'a mut VectorData, bezpath: BezPath) { + let mut this = Self::new(vector_data); + + let stroke_id = StrokeId::ZERO; + let fill_id = FillId::ZERO; + + for i in 0..bezpath.elements().len() { + let current_element = bezpath.elements()[i]; + let next_element = bezpath.elements().get(i + 1); + + match current_element { + kurbo::PathEl::MoveTo(point) => { + let next_point_index = this.vector_data.point_domain.ids().len(); + this.vector_data.point_domain.push(this.point_id.next_id(), point_to_dvec2(point)); + this.first_point_index = Some(next_point_index); + this.last_point_index = Some(next_point_index); + } + kurbo::PathEl::ClosePath => match (this.first_point_index, this.last_point_index) { + (Some(first_point_index), Some(last_point_index)) => { + let next_segment_id = this.segment_id.next_id(); + this.vector_data + .segment_domain + .push(next_segment_id, last_point_index, first_point_index, this.next_handle.unwrap_or(BezierHandles::Linear), stroke_id); + + let next_region_id = this.vector_data.region_domain.next_id(); + // In case there is only one anchor point. + let first_segment_id = this.first_segment_id.unwrap_or(next_segment_id); + + this.vector_data.region_domain.push(next_region_id, first_segment_id..=next_segment_id, fill_id); + } + _ => { + error!("Empty bezpath cannot be closed.") + } + }, + kurbo::PathEl::LineTo(point) => this.append_path_element(BezierHandles::Linear, point, next_element), + kurbo::PathEl::QuadTo(handle, point) => this.append_path_element(BezierHandles::Quadratic { handle: point_to_dvec2(handle) }, point, next_element), + kurbo::PathEl::CurveTo(handle_start, handle_end, point) => this.append_path_element( + BezierHandles::Cubic { + handle_start: point_to_dvec2(handle_start), + handle_end: point_to_dvec2(handle_end), + }, + point, + next_element, + ), + } + } + } +}