11use super :: misc:: CentroidType ;
22use super :: style:: { Fill , Gradient , GradientStops , Stroke } ;
3- use super :: { PointId , SegmentId , StrokeId , VectorData } ;
3+ use super :: { PointId , SegmentDomain , SegmentId , StrokeId , VectorData } ;
44use crate :: registry:: types:: { Angle , Fraction , IntegerCount , Length , SeedValue } ;
55use crate :: renderer:: GraphicElementRendered ;
66use crate :: transform:: { Footprint , Transform , TransformMut } ;
@@ -11,7 +11,6 @@ use crate::{Color, GraphicElement, GraphicGroup};
1111use bezier_rs:: { Cap , Join , Subpath , SubpathTValue , TValue } ;
1212use glam:: { DAffine2 , DVec2 } ;
1313use rand:: { Rng , SeedableRng } ;
14- use std:: collections:: { BTreeMap , BTreeSet , VecDeque } ;
1514
1615/// Implemented for types that can be converted to an iterator of vector data.
1716/// Used for the fill and stroke node so they can be used on VectorData or GraphicGroup
@@ -857,120 +856,35 @@ async fn splines_from_points<F: 'n + Send>(
857856 return vector_data;
858857 }
859858
860- // Extract points and take ownership of the segment domain for processing.
861- let points = & vector_data. point_domain ;
862- let segments = std:: mem:: take ( & mut vector_data. segment_domain ) ;
863-
864- // Map segment IDs to their indices using BTreeMap for deterministic ordering.
865- let segment_id_to_index = segments. ids ( ) . iter ( ) . copied ( ) . enumerate ( ) . map ( |( i, id) | ( id, i) ) . collect :: < BTreeMap < _ , _ > > ( ) ;
866-
867- // Iterate over all segments to generate splines.
868- let mut visited_segments = BTreeSet :: new ( ) ;
869- for ( segment_index, & segment_id) in segments. ids ( ) . iter ( ) . enumerate ( ) {
870- // Skip segments that have already been visited.
871- if visited_segments. contains ( & segment_id) {
872- continue ;
873- }
874-
875- let mut current_subpath_segments = Vec :: new ( ) ;
876- let mut queue = VecDeque :: new ( ) ;
877- queue. push_back ( segment_index) ;
878-
879- // Traverse the connected segments to form a subpath.
880- while let Some ( segment_index) = queue. pop_front ( ) {
881- // Skip segments that have already been visited, otherwise add them to the visited set and the current subpath.
882- let seg_id = segments. ids ( ) [ segment_index] ;
883- if visited_segments. contains ( & seg_id) {
884- continue ;
885- }
886- visited_segments. insert ( seg_id) ;
887- current_subpath_segments. push ( segment_index) ;
888-
889- // Get the start and end points of the segment.
890- let start_point_index = segments. start_point ( ) [ segment_index] ;
891- let end_point_index = segments. end_point ( ) [ segment_index] ;
892-
893- // For both start and end points, find and enqueue connected segments.
894- for point_index in [ start_point_index, end_point_index] {
895- let mut connected_seg_ids = segments. start_connected ( point_index) . chain ( segments. end_connected ( point_index) ) . collect :: < Vec < _ > > ( ) ;
896- connected_seg_ids. sort_unstable ( ) ; // Ensure deterministic order
897- for connected_seg_id in connected_seg_ids {
898- let connected_seg_index = * segment_id_to_index. get ( & connected_seg_id) . unwrap_or ( & usize:: MAX ) ;
899- if connected_seg_index != usize:: MAX && !visited_segments. contains ( & connected_seg_id) {
900- queue. push_back ( connected_seg_index) ;
901- }
902- }
903- }
904- }
905-
906- // Build a mapping from each point to its connected points using BTreeMap for deterministic ordering.
907- let mut point_connections: BTreeMap < usize , Vec < usize > > = BTreeMap :: new ( ) ;
908- for & seg_index in & current_subpath_segments {
909- let start = segments. start_point ( ) [ seg_index] ;
910- let end = segments. end_point ( ) [ seg_index] ;
911- point_connections. entry ( start) . or_default ( ) . push ( end) ;
912- point_connections. entry ( end) . or_default ( ) . push ( start) ;
913- }
914-
915- // Sort connected points for deterministic traversal.
916- for neighbors in point_connections. values_mut ( ) {
917- neighbors. sort_unstable ( ) ;
918- }
919-
920- // Identify endpoints.
921- let endpoints = point_connections
922- . iter ( )
923- . filter ( |( _, neighbors) | neighbors. len ( ) == 1 )
924- . map ( |( & point_index, _) | point_index)
925- . collect :: < Vec < _ > > ( ) ;
926-
927- let mut ordered_point_indices = Vec :: new ( ) ;
928-
929- // Start with the first endpoint or the first point if there are no endpoints because it's a closed subpath.
930- let start_point_index = endpoints. first ( ) . copied ( ) . unwrap_or_else ( || * point_connections. keys ( ) . next ( ) . unwrap ( ) ) ;
931-
932- // Traverse points to order them into a path.
933- let mut visited_points = BTreeSet :: new ( ) ;
934- let mut current_point = start_point_index;
935- loop {
936- ordered_point_indices. push ( current_point) ;
937- visited_points. insert ( current_point) ;
938-
939- let Some ( neighbors) = point_connections. get ( & current_point) else { break } ;
940- let next_point = neighbors. iter ( ) . find ( |& pt| !visited_points. contains ( pt) ) ;
941- let Some ( & next_point) = next_point else { break } ;
942- current_point = next_point;
943- }
944-
945- // If it's a closed subpath, close the spline loop by adding the start point at the end.
946- let closed = endpoints. is_empty ( ) ;
947- if closed {
948- ordered_point_indices. push ( start_point_index) ;
949- }
950-
951- // Collect the positions of the ordered points.
952- let positions = ordered_point_indices. iter ( ) . map ( |& index| points. positions ( ) [ index] ) . collect :: < Vec < _ > > ( ) ;
859+ let mut segment_domain = SegmentDomain :: default ( ) ;
860+ for subpath in vector_data. stroke_bezier_paths ( ) {
861+ let positions = subpath. manipulator_groups ( ) . iter ( ) . map ( |group| group. anchor ) . collect :: < Vec < _ > > ( ) ;
862+ let closed = subpath. closed ( ) ;
953863
954864 // Compute control point handles for Bezier spline.
955- // TODO: Make this support wrapping around between start and end points for closed subpaths.
956- let first_handles = bezier_rs:: solve_spline_first_handle ( & positions) ;
865+ let first_handles = if closed {
866+ bezier_rs:: solve_spline_first_handle_closed ( & positions)
867+ } else {
868+ bezier_rs:: solve_spline_first_handle_open ( & positions)
869+ } ;
957870
958871 let stroke_id = StrokeId :: ZERO ;
959872
960873 // Create segments with computed Bezier handles and add them to vector data.
961- for i in 0 ..( positions. len ( ) - 1 ) {
874+ for i in 0 ..( positions. len ( ) - if closed { 0 } else { 1 } ) {
962875 let next_index = ( i + 1 ) % positions. len ( ) ;
963876
964- let start_index = ordered_point_indices [ i] ;
965- let end_index = ordered_point_indices [ next_index] ;
877+ let start_index = vector_data . point_domain . resolve_id ( subpath . manipulator_groups ( ) [ i] . id ) . unwrap ( ) ;
878+ let end_index = vector_data . point_domain . resolve_id ( subpath . manipulator_groups ( ) [ next_index] . id ) . unwrap ( ) ;
966879
967880 let handle_start = first_handles[ i] ;
968881 let handle_end = positions[ next_index] * 2. - first_handles[ next_index] ;
969882 let handles = bezier_rs:: BezierHandles :: Cubic { handle_start, handle_end } ;
970883
971- vector_data . segment_domain . push ( SegmentId :: generate ( ) , start_index, end_index, handles, stroke_id) ;
884+ segment_domain. push ( SegmentId :: generate ( ) , start_index, end_index, handles, stroke_id) ;
972885 }
973886 }
887+ vector_data. segment_domain = segment_domain;
974888
975889 vector_data
976890}
0 commit comments