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} ;
2
2
use super :: algorithms:: offset_subpath:: offset_subpath;
3
3
use super :: misc:: { CentroidType , point_to_dvec2} ;
4
4
use super :: style:: { Fill , Gradient , GradientStops , Stroke } ;
@@ -11,11 +11,11 @@ use crate::transform::{Footprint, ReferencePoint, Transform, TransformMut};
11
11
use crate :: vector:: PointDomain ;
12
12
use crate :: vector:: style:: { LineCap , LineJoin } ;
13
13
use 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 } ;
15
15
use core:: f64:: consts:: PI ;
16
16
use core:: hash:: { Hash , Hasher } ;
17
17
use glam:: { DAffine2 , DVec2 } ;
18
- use kurbo:: Affine ;
18
+ use kurbo:: { Affine , Shape } ;
19
19
use rand:: { Rng , SeedableRng } ;
20
20
use std:: collections:: hash_map:: DefaultHasher ;
21
21
@@ -1147,144 +1147,43 @@ async fn sample_points(_: impl Ctx, vector_data: VectorDataTable, spacing: f64,
1147
1147
let spacing = spacing. max ( 0.01 ) ;
1148
1148
1149
1149
let vector_data_transform = vector_data. transform ( ) ;
1150
- let vector_data = vector_data. one_instance_ref ( ) . instance ;
1151
1150
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 ( ) ;
1154
1155
1155
1156
// Initialize the result VectorData with the same transformation as the input.
1156
1157
let mut result = VectorDataTable :: default ( ) ;
1157
1158
* result. transform_mut ( ) = vector_data_transform;
1158
1159
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 ;
1183
1162
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 ( ) ) ) ;
1186
1166
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 ( ) ;
1192
1168
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] ;
1200
1171
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;
1206
1174
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 {
1209
1176
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
+ } ;
1276
1178
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 ( ) ) ;
1279
1181
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) ;
1284
1184
}
1285
-
1286
1185
// 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 ( ) ;
1288
1187
result. one_instance_mut ( ) . instance . style . set_stroke_transform ( vector_data_transform) ;
1289
1188
1290
1189
// Return the resulting vector data with newly generated points and segments.
@@ -1320,7 +1219,7 @@ async fn position_on_path(
1320
1219
let t = if progress == bezpath_count { 1. } else { progress. fract ( ) } ;
1321
1220
bezpath. apply_affine ( Affine :: new ( vector_data_transform. to_cols_array ( ) ) ) ;
1322
1221
1323
- point_to_dvec2 ( position_on_bezpath ( bezpath, t, euclidian) )
1222
+ point_to_dvec2 ( position_on_bezpath ( bezpath, t, euclidian, None ) )
1324
1223
} )
1325
1224
}
1326
1225
@@ -1353,10 +1252,10 @@ async fn tangent_on_path(
1353
1252
let t = if progress == bezpath_count { 1. } else { progress. fract ( ) } ;
1354
1253
bezpath. apply_affine ( Affine :: new ( vector_data_transform. to_cols_array ( ) ) ) ;
1355
1254
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 ) ) ;
1357
1256
if tangent == DVec2 :: ZERO {
1358
1257
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 ) ) ;
1360
1259
}
1361
1260
if tangent == DVec2 :: ZERO {
1362
1261
return 0. ;
@@ -1430,8 +1329,11 @@ async fn subpath_segment_lengths(_: impl Ctx, vector_data: VectorDataTable) -> V
1430
1329
let vector_data = vector_data. one_instance_ref ( ) . instance ;
1431
1330
1432
1331
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
+ } )
1435
1337
. collect ( )
1436
1338
}
1437
1339
0 commit comments