@@ -6,6 +6,7 @@ use crate::math::lerp;
6
6
use crate :: path_segment:: PathSegment ;
7
7
8
8
use glam:: DVec2 ;
9
+ use roots:: { find_roots_cubic, Roots } ;
9
10
10
11
#[ derive( Clone ) ]
11
12
struct IntersectionSegment {
@@ -88,32 +89,41 @@ pub fn segments_equal(seg0: &PathSegment, seg1: &PathSegment, point_epsilon: f64
88
89
}
89
90
90
91
pub fn path_segment_intersection ( seg0 : & PathSegment , seg1 : & PathSegment , endpoints : bool , eps : & Epsilons ) -> Vec < [ f64 ; 2 ] > {
91
- if let ( PathSegment :: Line ( start0, end0) , PathSegment :: Line ( start1, end1) ) = ( seg0, seg1) {
92
- if let Some ( st) = line_segment_intersection ( [ * start0, * end0] , [ * start1, * end1] , eps. param ) {
93
- if !endpoints && ( st. 0 < eps. param || st. 0 > 1. - eps. param ) && ( st. 1 < eps. param || st. 1 > 1. - eps. param ) {
94
- return vec ! [ ] ;
92
+ match ( seg0, seg1) {
93
+ ( PathSegment :: Line ( start0, end0) , PathSegment :: Line ( start1, end1) ) => {
94
+ if let Some ( st) = line_segment_intersection ( [ * start0, * end0] , [ * start1, * end1] , eps. param ) {
95
+ if !endpoints && ( st. 0 < eps. param || st. 0 > 1. - eps. param ) && ( st. 1 < eps. param || st. 1 > 1. - eps. param ) {
96
+ return vec ! [ ] ;
97
+ }
98
+ return vec ! [ st. into( ) ] ;
95
99
}
96
- return vec ! [ st. into( ) ] ;
97
100
}
98
- }
99
- if let ( PathSegment :: Cubic ( s1, c11, c21, e1) , PathSegment :: Cubic ( s2, c12, c22, e2) ) = ( seg0, seg1) {
100
- let path1 = lyon_geom:: CubicBezierSegment {
101
- from : ( s1. x , s1. y ) . into ( ) ,
102
- ctrl1 : ( c11. x , c11. y ) . into ( ) ,
103
- ctrl2 : ( c21. x , c21. y ) . into ( ) ,
104
- to : ( e1. x , e1. y ) . into ( ) ,
105
- } ;
106
- let path2 = lyon_geom:: CubicBezierSegment {
107
- from : ( s2. x , s2. y ) . into ( ) ,
108
- ctrl1 : ( c12. x , c12. y ) . into ( ) ,
109
- ctrl2 : ( c22. x , c22. y ) . into ( ) ,
110
- to : ( e2. x , e2. y ) . into ( ) ,
111
- } ;
112
-
113
- let intersections = path1. cubic_intersections_t ( & path2) ;
114
- let intersections: Vec < _ > = intersections. into_iter ( ) . map ( |( s, t) | [ s, t] ) . collect ( ) ;
115
- return intersections;
116
- }
101
+ ( PathSegment :: Cubic ( s1, c11, c21, e1) , PathSegment :: Cubic ( s2, c12, c22, e2) ) => {
102
+ let path1 = lyon_geom:: CubicBezierSegment {
103
+ from : ( s1. x , s1. y ) . into ( ) ,
104
+ ctrl1 : ( c11. x , c11. y ) . into ( ) ,
105
+ ctrl2 : ( c21. x , c21. y ) . into ( ) ,
106
+ to : ( e1. x , e1. y ) . into ( ) ,
107
+ } ;
108
+ let path2 = lyon_geom:: CubicBezierSegment {
109
+ from : ( s2. x , s2. y ) . into ( ) ,
110
+ ctrl1 : ( c12. x , c12. y ) . into ( ) ,
111
+ ctrl2 : ( c22. x , c22. y ) . into ( ) ,
112
+ to : ( e2. x , e2. y ) . into ( ) ,
113
+ } ;
114
+
115
+ let intersections = path1. cubic_intersections_t ( & path2) ;
116
+ let intersections: Vec < _ > = intersections. into_iter ( ) . map ( |( s, t) | [ s, t] ) . collect ( ) ;
117
+ return intersections;
118
+ }
119
+ ( PathSegment :: Cubic ( _, _, _, _) , PathSegment :: Line ( _, _) ) => {
120
+ return cubic_line_intersection ( seg0, seg1, eps) ;
121
+ }
122
+ ( PathSegment :: Line ( _, _) , PathSegment :: Cubic ( _, _, _, _) ) => {
123
+ return cubic_line_intersection ( seg1, seg0, eps) . into_iter ( ) . map ( |[ t1, t0] | [ t0, t1] ) . collect ( ) ;
124
+ }
125
+ _ => ( ) ,
126
+ } ;
117
127
118
128
// https://math.stackexchange.com/questions/20321/how-can-i-tell-when-two-cubic-b%C3%A9zier-curves-intersect
119
129
@@ -237,6 +247,79 @@ fn calculate_overlap_intersections(seg0: &PathSegment, seg1: &PathSegment, eps:
237
247
intersections
238
248
}
239
249
250
+ fn cubic_line_intersection ( cubic : & PathSegment , line : & PathSegment , eps : & Epsilons ) -> Vec < [ f64 ; 2 ] > {
251
+ let ( p0, p1, p2, p3) = match cubic {
252
+ PathSegment :: Cubic ( p0, p1, p2, p3) => ( p0, p1, p2, p3) ,
253
+ _ => unreachable ! ( "Expected a cubic Bézier curve" ) ,
254
+ } ;
255
+ let ( line_start, line_end) = match line {
256
+ PathSegment :: Line ( start, end) => ( start, end) ,
257
+ _ => unreachable ! ( "Expected a line segment" ) ,
258
+ } ;
259
+
260
+ // Transform the cubic curve to the line's coordinate system
261
+ let line_dir = * line_end - * line_start;
262
+ let line_len = line_dir. length ( ) ;
263
+ let line_norm = line_dir / line_len;
264
+ let perp = DVec2 :: new ( -line_norm. y , line_norm. x ) ;
265
+
266
+ let transform = |p : & DVec2 | -> DVec2 {
267
+ let v = * p - * line_start;
268
+ DVec2 :: new ( v. dot ( line_norm) , v. dot ( perp) )
269
+ } ;
270
+
271
+ let q0 = transform ( p0) ;
272
+ let q1 = transform ( p1) ;
273
+ let q2 = transform ( p2) ;
274
+ let q3 = transform ( p3) ;
275
+
276
+ // Now we're looking for roots of the cubic equation in y
277
+ let a = -q0. y + 3.0 * q1. y - 3.0 * q2. y + q3. y ;
278
+ let b = 3.0 * q0. y - 6.0 * q1. y + 3.0 * q2. y ;
279
+ let c = -3.0 * q0. y + 3.0 * q1. y ;
280
+ let d = q0. y ;
281
+
282
+ let roots = find_roots_cubic ( a, b, c, d) ;
283
+
284
+ let mut intersections = Vec :: new ( ) ;
285
+
286
+ let mut process_root = |t : f64 | {
287
+ if ( 0.0 ..=1.0 ) . contains ( & t) {
288
+ let x = ( ( 1.0 - t) . powi ( 3 ) * q0. x + 3.0 * t * ( 1.0 - t) . powi ( 2 ) * q1. x + 3.0 * t. powi ( 2 ) * ( 1.0 - t) * q2. x + t. powi ( 3 ) * q3. x ) / line_len;
289
+ if ( 0.0 ..=1.0 ) . contains ( & x) {
290
+ intersections. push ( [ t, x] ) ;
291
+ }
292
+ }
293
+ } ;
294
+
295
+ match roots {
296
+ Roots :: Three ( roots) => {
297
+ for & t in roots. iter ( ) {
298
+ process_root ( t) ;
299
+ }
300
+ }
301
+ Roots :: Two ( roots) => {
302
+ for & t in roots. iter ( ) {
303
+ process_root ( t) ;
304
+ }
305
+ }
306
+ Roots :: One ( roots) => {
307
+ for & t in roots. iter ( ) {
308
+ process_root ( t) ;
309
+ }
310
+ }
311
+ _ => { }
312
+ }
313
+
314
+ // Sort intersections by the cubic's t parameter
315
+ intersections. sort_by ( |a, b| a[ 0 ] . partial_cmp ( & b[ 0 ] ) . unwrap ( ) ) ;
316
+
317
+ // Remove duplicates within epsilon
318
+ intersections. dedup_by ( |a, b| ( a[ 0 ] - b[ 0 ] ) . abs ( ) < eps. param ) ;
319
+
320
+ intersections
321
+ }
322
+
240
323
fn find_point_on_segment ( seg : & PathSegment , point : DVec2 , eps : & Epsilons ) -> Option < f64 > {
241
324
let start = 0. ;
242
325
let end = 1. ;
0 commit comments