@@ -8,6 +8,9 @@ const CUBIC_REGULARIZATION_ACCURACY: f64 = 0.5;
88const CUBIC_TO_BEZPATH_ACCURACY : f64 = 1e-3 ;
99/// Constant used to determine if `f64`s are equivalent.
1010pub const MAX_ABSOLUTE_DIFFERENCE : f64 = 1e-7 ;
11+ /// Squared version to avoid sqrt in distance checks.
12+ const MAX_ABSOLUTE_DIFFERENCE_SQUARED : f64 = MAX_ABSOLUTE_DIFFERENCE * MAX_ABSOLUTE_DIFFERENCE ;
13+ const MAX_FITTED_SEGMENTS : usize = 10000 ;
1114
1215/// Reduces the segments of the bezpath into simple subcurves, then offset each subcurve a set `distance` away.
1316/// The intersections of segments of the subpath are joined using the method specified by the `join` argument.
@@ -19,15 +22,31 @@ pub fn offset_bezpath(bezpath: &BezPath, distance: f64, join: Join, miter_limit:
1922 }
2023
2124 let mut bezpaths = bezpath
22- . segments ( )
23- . map ( |bezier| bezier. to_cubic ( ) )
24- . map ( |cubic_bez| {
25- let cubic_offset = kurbo:: offset:: CubicOffset :: new_regularized ( cubic_bez, distance, CUBIC_REGULARIZATION_ACCURACY ) ;
26-
27- kurbo:: fit_to_bezpath ( & cubic_offset, CUBIC_TO_BEZPATH_ACCURACY )
28- } )
29- . filter ( |bezpath| bezpath. get_seg ( 1 ) . is_some ( ) ) // In some cases the reduced and scaled bézier is marked by is_point (so the subpath is empty).
30- . collect :: < Vec < BezPath > > ( ) ;
25+ . segments ( )
26+ . map ( |bezier| bezier. to_cubic ( ) )
27+ . filter_map ( |cubic_bez| {
28+ // Skip degenerate curves where all control points are at the same location.
29+ // Offsetting a point is undefined and causes infinite recursion in fit_to_bezpath.
30+ let start = cubic_bez. p0 ;
31+ let is_degenerate = start. distance_squared ( cubic_bez. p1 ) < MAX_ABSOLUTE_DIFFERENCE_SQUARED
32+ && start. distance_squared ( cubic_bez. p2 ) < MAX_ABSOLUTE_DIFFERENCE_SQUARED
33+ && start. distance_squared ( cubic_bez. p3 ) < MAX_ABSOLUTE_DIFFERENCE_SQUARED ;
34+
35+ if is_degenerate {
36+ return None ;
37+ }
38+
39+ let cubic_offset = kurbo:: offset:: CubicOffset :: new_regularized ( cubic_bez, distance, CUBIC_REGULARIZATION_ACCURACY ) ;
40+
41+ let fitted = kurbo:: fit_to_bezpath ( & cubic_offset, CUBIC_TO_BEZPATH_ACCURACY ) ;
42+
43+ if fitted. segments ( ) . count ( ) > MAX_FITTED_SEGMENTS {
44+ None
45+ } else {
46+ fitted. get_seg ( 1 ) . is_some ( ) . then_some ( fitted)
47+ }
48+ } )
49+ . collect :: < Vec < BezPath > > ( ) ;
3150
3251 // Clip or join consecutive Subpaths
3352 for i in 0 ..bezpaths. len ( ) - 1 {
0 commit comments