1- use super :: algorithms:: bezpath_algorithms:: { position_on_bezpath, tangent_on_bezpath} ;
1+ use super :: algorithms:: bezpath_algorithms:: { PERIMETER_ACCURACY , position_on_bezpath, sample_points_on_bezpath , tangent_on_bezpath} ;
22use super :: algorithms:: offset_subpath:: offset_subpath;
33use super :: misc:: { CentroidType , point_to_dvec2} ;
44use super :: style:: { Fill , Gradient , GradientStops , Stroke } ;
@@ -11,11 +11,11 @@ use crate::transform::{Footprint, ReferencePoint, Transform, TransformMut};
1111use crate :: vector:: PointDomain ;
1212use crate :: vector:: style:: { LineCap , LineJoin } ;
1313use crate :: { CloneVarArgs , Color , Context , Ctx , ExtractAll , GraphicElement , GraphicGroupTable , OwnedContextImpl } ;
14- use bezier_rs:: { Join , ManipulatorGroup , Subpath , SubpathTValue , TValue } ;
14+ use bezier_rs:: { Join , ManipulatorGroup , Subpath , SubpathTValue } ;
1515use core:: f64:: consts:: PI ;
1616use core:: hash:: { Hash , Hasher } ;
1717use glam:: { DAffine2 , DVec2 } ;
18- use kurbo:: Affine ;
18+ use kurbo:: { Affine , Shape } ;
1919use rand:: { Rng , SeedableRng } ;
2020use std:: collections:: hash_map:: DefaultHasher ;
2121
@@ -1147,144 +1147,43 @@ async fn sample_points(_: impl Ctx, vector_data: VectorDataTable, spacing: f64,
11471147 let spacing = spacing. max ( 0.01 ) ;
11481148
11491149 let vector_data_transform = vector_data. transform ( ) ;
1150- let vector_data = vector_data. one_instance_ref ( ) . instance ;
11511150
1152- // Create an iterator over the bezier segments with enumeration and peeking capability.
1153- let mut bezier = vector_data. segment_bezier_iter ( ) . enumerate ( ) . peekable ( ) ;
1151+ // Using `stroke_bezpath_iter` so that the `subpath_segment_lengths` is aligned to the segments of each bezpath.
1152+ // So we can index into `subpath_segment_lengths` to get the length of the segments.
1153+ // NOTE: `subpath_segment_lengths` has precalulated lengths with transformation applied.
1154+ let bezpaths = vector_data. one_instance_ref ( ) . instance . stroke_bezpath_iter ( ) ;
11541155
11551156 // Initialize the result VectorData with the same transformation as the input.
11561157 let mut result = VectorDataTable :: default ( ) ;
11571158 * result. transform_mut ( ) = vector_data_transform;
11581159
1159- // Iterate over each segment in the bezier iterator.
1160- while let Some ( ( index, ( segment_id, _, start_point_index, mut last_end) ) ) = bezier. next ( ) {
1161- // Record the start point index of the subpath.
1162- let subpath_start_point_index = start_point_index;
1163-
1164- // Collect connected segments that form a continuous path.
1165- let mut lengths = vec ! [ ( segment_id, subpath_segment_lengths. get( index) . copied( ) . unwrap_or_default( ) ) ] ;
1166-
1167- // Continue collecting segments as long as they are connected end-to-start.
1168- while let Some ( & seg) = bezier. peek ( ) {
1169- let ( _, ( _, _, ref start, _) ) = seg;
1170- if * start == last_end {
1171- // Consume the next element since it continues the path.
1172- let ( index, ( next_segment_id, _, _, end) ) = bezier. next ( ) . unwrap ( ) ;
1173- last_end = end;
1174- lengths. push ( ( next_segment_id, subpath_segment_lengths. get ( index) . copied ( ) . unwrap_or_default ( ) ) ) ;
1175- } else {
1176- // The next segment does not continue the path.
1177- break ;
1178- }
1179- }
1180-
1181- // Determine if the subpath is closed.
1182- let subpath_is_closed = last_end == subpath_start_point_index;
1160+ // Keeps track of the index of the first segment of the next bezpath in order to get lengths of all segments.
1161+ let mut next_segment_index = 0 ;
11831162
1184- // Calculate the total length of the collected segments.
1185- let total_length: f64 = lengths. iter ( ) . map ( |( _, len) | * len) . sum ( ) ;
1163+ for mut bezpath in bezpaths {
1164+ // Apply the tranformation to the current bezpath to calculate points after transformation.
1165+ bezpath. apply_affine ( Affine :: new ( vector_data_transform. to_cols_array ( ) ) ) ;
11861166
1187- // Adjust the usable length by subtracting start and stop offsets.
1188- let mut used_length = total_length - start_offset - stop_offset;
1189- if used_length <= 0. {
1190- continue ;
1191- }
1167+ let segment_count = bezpath. segments ( ) . count ( ) ;
11921168
1193- // Determine the number of points to generate along the path.
1194- let count = if adaptive_spacing {
1195- // Calculate point count to evenly distribute points while covering the entire path.
1196- // With adaptive spacing, we widen or narrow the points as necessary to ensure the last point is always at the end of the path.
1197- ( used_length / spacing) . round ( )
1198- } else {
1199- // Calculate point count based on exact spacing, which may not cover the entire path.
1169+ // For the current bezpath we get its segment's length by calculating the start index and end index.
1170+ let current_bezpath_segments_length = & subpath_segment_lengths[ next_segment_index..next_segment_index + segment_count] ;
12001171
1201- // Without adaptive spacing, we just evenly space the points at the exact specified spacing, usually falling short before the end of the path.
1202- let c = ( used_length / spacing + f64:: EPSILON ) . floor ( ) ;
1203- used_length -= used_length % spacing;
1204- c
1205- } ;
1172+ // Increment the segment index by the number of segments in the current bezpath to calculate the next bezpath segment's length.
1173+ next_segment_index += segment_count;
12061174
1207- // Skip if there are no points to generate.
1208- if count < 1. {
1175+ let Some ( mut sample_bezpath) = sample_points_on_bezpath ( bezpath, spacing, start_offset, stop_offset, adaptive_spacing, current_bezpath_segments_length) else {
12091176 continue ;
1210- }
1211-
1212- // Initialize a vector to store indices of generated points.
1213- let mut point_indices = Vec :: new ( ) ;
1214-
1215- // Generate points along the path based on calculated intervals.
1216- let max_c = if subpath_is_closed { count as usize - 1 } else { count as usize } ;
1217- for c in 0 ..=max_c {
1218- let fraction = c as f64 / count;
1219- let total_distance = fraction * used_length + start_offset;
1220-
1221- // Find the segment corresponding to the current total_distance.
1222- let ( mut current_segment_id, mut length) = lengths[ 0 ] ;
1223- let mut total_length_before = 0. ;
1224- for & ( next_segment_id, next_length) in lengths. iter ( ) . skip ( 1 ) {
1225- if total_length_before + length > total_distance {
1226- break ;
1227- }
1228-
1229- total_length_before += length;
1230- current_segment_id = next_segment_id;
1231- length = next_length;
1232- }
1233-
1234- // Retrieve the segment and apply transformation.
1235- let Some ( segment) = vector_data. segment_from_id ( current_segment_id) else { continue } ;
1236- let segment = segment. apply_transformation ( |point| vector_data_transform. transform_point2 ( point) ) ;
1237-
1238- // Calculate the position on the segment.
1239- let parametric_t = segment. euclidean_to_parametric_with_total_length ( ( total_distance - total_length_before) / length, 0.001 , length) ;
1240- let point = segment. evaluate ( TValue :: Parametric ( parametric_t) ) ;
1241-
1242- // Generate a new PointId and add the point to result.point_domain.
1243- let point_id = PointId :: generate ( ) ;
1244- result. one_instance_mut ( ) . instance . point_domain . push ( point_id, vector_data_transform. inverse ( ) . transform_point2 ( point) ) ;
1245-
1246- // Store the index of the point.
1247- let point_index = result. one_instance_mut ( ) . instance . point_domain . ids ( ) . len ( ) - 1 ;
1248- point_indices. push ( point_index) ;
1249- }
1250-
1251- // After generating points, create segments between consecutive points.
1252- for window in point_indices. windows ( 2 ) {
1253- if let [ start_index, end_index] = * window {
1254- // Generate a new SegmentId.
1255- let segment_id = SegmentId :: generate ( ) ;
1256-
1257- // Use BezierHandles::Linear for linear segments.
1258- let handles = bezier_rs:: BezierHandles :: Linear ;
1259-
1260- // Generate a new StrokeId.
1261- let stroke_id = StrokeId :: generate ( ) ;
1262-
1263- // Add the segment to result.segment_domain.
1264- result. one_instance_mut ( ) . instance . segment_domain . push ( segment_id, start_index, end_index, handles, stroke_id) ;
1265- }
1266- }
1267-
1268- // If the subpath is closed, add a closing segment connecting the last point to the first point.
1269- if subpath_is_closed {
1270- if let ( Some ( & first_index) , Some ( & last_index) ) = ( point_indices. first ( ) , point_indices. last ( ) ) {
1271- // Generate a new SegmentId.
1272- let segment_id = SegmentId :: generate ( ) ;
1273-
1274- // Use BezierHandles::Linear for linear segments.
1275- let handles = bezier_rs:: BezierHandles :: Linear ;
1177+ } ;
12761178
1277- // Generate a new StrokeId .
1278- let stroke_id = StrokeId :: generate ( ) ;
1179+ // Reverse the transformation applied to the bezpath as the `result` already has the transformation set .
1180+ sample_bezpath . apply_affine ( Affine :: new ( vector_data_transform . to_cols_array ( ) ) . inverse ( ) ) ;
12791181
1280- // Add the closing segment to result.segment_domain.
1281- result. one_instance_mut ( ) . instance . segment_domain . push ( segment_id, last_index, first_index, handles, stroke_id) ;
1282- }
1283- }
1182+ // Append the bezpath (subpath) that connects generated points by lines.
1183+ result. one_instance_mut ( ) . instance . append_bezpath ( sample_bezpath) ;
12841184 }
1285-
12861185 // Transfer the style from the input vector data to the result.
1287- result. one_instance_mut ( ) . instance . style = vector_data. style . clone ( ) ;
1186+ result. one_instance_mut ( ) . instance . style = vector_data. one_instance_ref ( ) . instance . style . clone ( ) ;
12881187 result. one_instance_mut ( ) . instance . style . set_stroke_transform ( vector_data_transform) ;
12891188
12901189 // Return the resulting vector data with newly generated points and segments.
@@ -1320,7 +1219,7 @@ async fn position_on_path(
13201219 let t = if progress == bezpath_count { 1. } else { progress. fract ( ) } ;
13211220 bezpath. apply_affine ( Affine :: new ( vector_data_transform. to_cols_array ( ) ) ) ;
13221221
1323- point_to_dvec2 ( position_on_bezpath ( bezpath, t, euclidian) )
1222+ point_to_dvec2 ( position_on_bezpath ( bezpath, t, euclidian, None ) )
13241223 } )
13251224}
13261225
@@ -1353,10 +1252,10 @@ async fn tangent_on_path(
13531252 let t = if progress == bezpath_count { 1. } else { progress. fract ( ) } ;
13541253 bezpath. apply_affine ( Affine :: new ( vector_data_transform. to_cols_array ( ) ) ) ;
13551254
1356- let mut tangent = point_to_dvec2 ( tangent_on_bezpath ( bezpath, t, euclidian) ) ;
1255+ let mut tangent = point_to_dvec2 ( tangent_on_bezpath ( bezpath, t, euclidian, None ) ) ;
13571256 if tangent == DVec2 :: ZERO {
13581257 let t = t + if t > 0.5 { -0.001 } else { 0.001 } ;
1359- tangent = point_to_dvec2 ( tangent_on_bezpath ( bezpath, t, euclidian) ) ;
1258+ tangent = point_to_dvec2 ( tangent_on_bezpath ( bezpath, t, euclidian, None ) ) ;
13601259 }
13611260 if tangent == DVec2 :: ZERO {
13621261 return 0. ;
@@ -1430,8 +1329,11 @@ async fn subpath_segment_lengths(_: impl Ctx, vector_data: VectorDataTable) -> V
14301329 let vector_data = vector_data. one_instance_ref ( ) . instance ;
14311330
14321331 vector_data
1433- . segment_bezier_iter ( )
1434- . map ( |( _id, bezier, _, _) | bezier. apply_transformation ( |point| vector_data_transform. transform_point2 ( point) ) . length ( None ) )
1332+ . stroke_bezpath_iter ( )
1333+ . flat_map ( |mut bezpath| {
1334+ bezpath. apply_affine ( Affine :: new ( vector_data_transform. to_cols_array ( ) ) ) ;
1335+ bezpath. segments ( ) . map ( |segment| segment. perimeter ( PERIMETER_ACCURACY ) ) . collect :: < Vec < f64 > > ( )
1336+ } )
14351337 . collect ( )
14361338}
14371339
0 commit comments