@@ -689,15 +689,19 @@ statics: {
689689 } ,
690690
691691 _getWinding : function ( v , x , y , roots1 , roots2 ) {
692- var tolerance = /*#=*/ Numerical . TOLERANCE ,
693- abs = Math . abs ;
694-
695692 // Implementation of the crossing number algorithm:
696693 // http://en.wikipedia.org/wiki/Point_in_polygon
697694 // Solve the y-axis cubic polynomial for y and count all solutions
698695 // to the right of x as crossings.
699- if ( Curve . isLinear ( v ) ) {
700- // Special case for handling lines.
696+ var tolerance = /*#=*/ Numerical . TOLERANCE ,
697+ abs = Math . abs ;
698+
699+ // Looks at the curve's start and end y coordinates to determine
700+ // orientation. This only makes sense for curves with clear orientation,
701+ // which is why we need to split them at y extrema, see below.
702+ // Returns 0 if the curve is outside the boundaries and is not to be
703+ // considered.
704+ function getOrientation ( v ) {
701705 var y0 = v [ 1 ] ,
702706 y1 = v [ 7 ] ,
703707 dir = 1 ;
@@ -708,7 +712,15 @@ statics: {
708712 dir = - 1 ;
709713 }
710714 if ( y < y0 || y > y1 )
711- return 0 ;
715+ dir = 0 ;
716+ return dir ;
717+ }
718+
719+ if ( Curve . isLinear ( v ) ) {
720+ // Special simplified case for handling lines.
721+ var dir = getOrientation ( v ) ;
722+ if ( ! dir )
723+ return 0 ;
712724 var cross = ( v [ 6 ] - v [ 0 ] ) * ( y - v [ 1 ] ) - ( v [ 7 ] - v [ 1 ] ) * ( x - v [ 0 ] ) ;
713725 return ( cross < - tolerance ? - 1 : 1 ) == dir ? 0 : dir ;
714726 }
@@ -719,18 +731,18 @@ statics: {
719731 y1 = v [ 3 ] ,
720732 y2 = v [ 5 ] ,
721733 y3 = v [ 7 ] ;
722- // Split the curve at Y extremas, to get mono bezier curves
734+ // Split the curve at y extrema, to get bezier curves with clear
735+ // orientation: Calculate the derivative and find its roots.
723736 var a = 3 * ( y1 - y2 ) - y0 + y3 ,
724737 b = 2 * ( y0 + y2 ) - 4 * y1 ,
725- c = y1 - y0 ,
726- // Keep then range to 0 .. 1 (excluding) in the search for y extrema
727- count = Numerical . solveQuadratic ( a , b , c , roots1 , tolerance ,
728- 1 - tolerance ) ;
729-
730- var winding = 0 ,
731- left ,
732- right = v ;
733- var t1 = roots1 [ 0 ] ;
738+ c = y1 - y0 ;
739+ // Keep then range to 0 .. 1 (excluding) in the search for y extrema
740+ var count = Numerical . solveQuadratic ( a , b , c , roots1 , tolerance ,
741+ 1 - tolerance ) ,
742+ left , // The part of the curve that's chopped off.
743+ right = v , // The part that's left to be chopped.
744+ t1 = roots1 [ 0 ] , // The first root
745+ winding = 0 ;
734746 for ( var i = 0 ; i <= count ; i ++ ) {
735747 if ( i === count ) {
736748 left = right ;
@@ -749,36 +761,32 @@ statics: {
749761 left [ 3 ] = left [ 1 ] ; // curve2.handle1.y = curve2.point1.y;
750762 if ( i < count )
751763 left [ 5 ] = right [ 1 ] ; // curve1.handle2.y = curve2.point1.y;
752- var dir = 1 ;
753- if ( left [ 1 ] > left [ 7 ] ) {
754- left = [
755- left [ 6 ] , left [ 7 ] ,
756- left [ 4 ] , left [ 5 ] ,
757- left [ 2 ] , left [ 3 ] ,
758- left [ 0 ] , left [ 1 ]
759- ] ;
760- dir = - 1 ;
761- }
762- if ( y < left [ 1 ] || y > left [ 7 ] )
764+ var dir = getOrientation ( left ) ;
765+ if ( ! dir )
763766 continue ;
764767 // Adjust start and end range depending on if curve was flipped.
765768 // In normal orientation we exclude the end point since it's also
766769 // the start point of the next curve. If flipped, we have to exclude
767770 // the end point instead.
768- var min = - tolerance * dir ,
769- t2 ,
771+ var t2 ,
770772 px ;
771- if ( Curve . solveCubic ( left , 1 , y , roots2 , min , 1 + min ) === 1 ) {
773+ // Since we've split at y extrema, there can only be 0, 1, or
774+ // infinite solutions now.
775+ if ( Curve . solveCubic ( left , 1 , y , roots2 , - tolerance , 1 + - tolerance )
776+ === 1 ) {
772777 t2 = roots2 [ 0 ] ;
773778 px = Curve . evaluate ( left , t2 , 0 ) . x ;
774779 } else {
775780 var mid = ( left [ 1 ] + left [ 7 ] ) / 2 ;
776- px = y < mid ? left [ 0 ] : left [ 6 ] ;
777- t2 = y < mid ? 0 : 1 ;
778- // Filter out end points based on direction.
779- if ( dir < 0 && abs ( t2 ) < tolerance && y == left [ 1 ] ||
780- dir > 0 && abs ( t2 - 1 ) < tolerance && y == left [ 7 ] )
781+ // Pick t2 based on the direction of the curve. If y < mid,
782+ // choose the beginning (which is the end of a curve with
783+ // negative orientation, as we're not actually flipping curves).
784+ t2 = y < mid && dir > 0 ? 0 : 1 ;
785+ // Filter out the end point, as it'll be the start point of the
786+ // next curve.
787+ if ( t2 === 1 && y == left [ 7 ] )
781788 continue ;
789+ px = t2 === 0 ? left [ 0 ] : left [ 6 ] ;
782790 }
783791 // See if we're touching a horizontal stationary point by looking at
784792 // the tanget's y coordinate.
@@ -797,8 +805,11 @@ statics: {
797805 || abs ( t2 - 1 ) < tolerance && x != left [ 6 ] ) )
798806 continue ;
799807 // If this is a horizontal stationary point, and we're at the
800- // end of the curve, flip the orientation of dir.
801- winding += flat && abs ( t2 - 1 ) < tolerance ? - dir : dir ;
808+ // end of the curve (or at the beginning of a curve with
809+ // negative direction, as we're not actually flipping them),
810+ // flip dir, as the curve is about to change orientation.
811+ winding += flat && abs ( t2 - ( dir > 0 ? 1 : 0 ) ) < tolerance
812+ ? - dir : dir ;
802813 }
803814 }
804815 return winding ;
0 commit comments