nl_tokens: Unpack an SVG path string into different curves and lines https : // developer . mozilla . org / en - US / docs / Web / SVG / Attribute / d
pl_tokens: function splitSegments ( polygon ) { if ( typeof polygon !== "string" ) { throw new Error ( "Polygon should be a path string" ) ; } var start = null ; var position = null ; var result = [ ] ; var input = polygon ; function stripWhitespace ( ) { polygon = polygon . trim ( ) ; } function readCharSeq ( n ) { var c = polygon . charCodeAt ( n ) ; while ( c >= 48 && c <= 57 ) { n ++ ; c = polygon . charCodeAt ( n ) ; } return n ; } function readNumber ( ) { stripWhitespace ( ) ; var start = 0 ; var end = 0 ; if ( polygon [ start ] === "," ) { start ++ ; end ++ ; } if ( polygon [ start ] === "-" ) { end ++ ; } end = readCharSeq ( <mask> ) ; if ( polygon [ end ] === "." ) { end ++ ; end = readCharSeq ( end ) ; } var s = polygon . substring ( start , end ) ; if ( s !== "" ) { var num = toFloat ( s ) ; polygon = polygon . substring ( end ) ; if ( polygon . length && polygon [ 0 ] . toLowerCase ( ) === "e" ) { var f = 1 ; var expEnd = 0 ; if ( polygon . length > 1 && polygon [ 1 ] === "-" ) { f = - 1 ; expEnd = readCharSeq ( 2 ) ; } else { expEnd = readCharSeq ( 1 ) ; } var exp = toFloat ( polygon . substring ( 1 , expEnd ) ) ; if ( mathAbs ( exp ) > 0 ) { num *= mathPow ( 10 , exp ) ; } polygon = polygon . substring ( expEnd ) ; } return num ; } else { throw new Error ( "Expected number: " + polygon ) ; } } function readNumbers ( n , fn ) { stripWhitespace ( ) ; var index = 0 ; var c = polygon . charCodeAt ( 0 ) ; while ( ( c >= 48 && c <= 57 ) || c === 44 || c === 45 ) { var numbers = [ ] ; for ( var i = 0 ; i < n ; i ++ ) { numbers . push ( readNumber ( ) ) ; } fn ( numbers , index ) ; stripWhitespace ( ) ; c = polygon . charCodeAt ( 0 ) ; index ++ ; } } function readCoords ( n , fn ) { readNumbers ( n * 2 , function ( numbers , index ) { var coords = [ ] ; for ( var i = 0 ; i < n ; i ++ ) { coords . push ( numbers . splice ( 0 , 2 ) ) ; } fn ( coords , index ) ; } ) ; } function pushType ( itemType , offset ) { return function ( c ) { if ( offset ) { c = c . map ( function ( c ) { return [ x ( c ) + x ( offset ) , y ( c ) + y ( offset ) ] ; } ) ; } c . unshift ( position ) ; result . push ( { type : itemType , coords : c , } ) ; position = c [ c . length - 1 ] ; } ; } function calculateCubicControlPoints ( coords ) { return [ coords [ 0 ] , [ x ( coords [ 0 ] ) + 2.0 / 3.0 * ( x ( coords [ 1 ] ) - x ( coords [ 0 ] ) ) , y ( coords [ 0 ] ) + 2.0 / 3.0 * ( y ( coords [ 1 ] ) - y ( coords [ 0 ] ) ) , ] , [ x ( coords [ 2 ] ) + 2.0 / 3.0 * ( x ( coords [ 1 ] ) - x ( coords [ 2 ] ) ) , y ( coords [ 2 ] ) + 2.0 / 3.0 * ( y ( coords [ 1 ] ) - y ( coords [ 2 ] ) ) , ] , coords [ 2 ] , ] ; } function calculateBezierControlPoint ( ) { var lastBezier = result [ result . length - 1 ] ; var controlPoint = null ; if ( ! lastBezier || lastBezier . type !== bezier3Type ) { controlPoint = position ; } else { // Calculate the mirror point of the last control point var lastPoint = lastBezier . coords [ 2 ] ; var xOffset = x ( position ) - x ( lastPoint ) ; var yOffset = y ( position ) - y ( lastPoint ) ; controlPoint = [ x ( position ) + xOffset , y ( position ) + yOffset ] ; } return controlPoint ; } function handleArcSegment ( relative ) { readNumbers ( 7 , function ( numbers ) { var c2 = coordAdd ( numbers . slice ( 5 , 7 ) , relative ) ; var args = [ position ] . concat ( numbers . slice ( 0 , 5 ) ) . concat ( [ c2 ] ) ; var curve = arcToCurve . apply ( null , args ) ; for ( var i = 0 ; i < curve . length ; i ++ ) { pushType ( bezier3Type ) ( curve [ i ] ) ; } } ) ; } function readSegment ( ) { stripWhitespace ( ) ; if ( polygon === "" ) { return ; } var operator = polygon [ 0 ] ; polygon = polygon . substring ( 1 ) ; var pushLine = pushType ( lineType ) ; var origin = [ 0 , 0 ] ; switch ( operator ) { case "M" : readCoords ( 1 , function ( c , i ) { if ( i === 0 ) { position = c [ 0 ] ; if ( ! start ) { start = position ; } } else { pushType ( lineType ) ( c ) ; } } ) ; break ; case "m" : readCoords ( 1 , function ( c , i ) { if ( i === 0 ) { if ( ! position ) { position = c [ 0 ] ; } else { position = coordAdd ( c , position ) ; } if ( ! start ) { start = position ; } } else { var c0 = c [ 0 ] ; pushType ( lineType ) ( [ coordAdd ( c0 , position ) ] ) ; } } ) ; break ; case "C" : readCoords ( 3 , pushType ( bezier3Type ) ) ; break ; case "c" : readCoords ( 3 , pushType ( bezier3Type , position ) ) ; break ; case "Q" : readCoords ( 2 , function ( coords ) { coords . unshift ( position ) ; coords = calculateCubicControlPoints ( coords ) ; coords . shift ( ) ; pushType ( bezier3Type ) ( coords ) ; } ) ; break ; case "q" : readCoords ( 2 , function ( coords ) { coords = coords . map ( function ( c ) { return coordAdd ( c , position ) ; } ) ; coords . unshift ( position ) ; coords = calculateCubicControlPoints ( coords ) ; coords . shift ( ) ; pushType ( bezier3Type ) ( coords ) ; } ) ; break ; case "S" : readCoords ( 2 , function ( coords ) { var controlPoint = calculateBezierControlPoint ( ) ; coords . unshift ( controlPoint ) ; pushType ( bezier3Type ) ( coords ) ; } ) ; break ; case "s" : readCoords ( 2 , function ( coords ) { var controlPoint = calculateBezierControlPoint ( ) ; coords = coords . map ( function ( c ) { return coordAdd ( c , position ) ; } ) ; coords . unshift ( controlPoint ) ; pushType ( bezier3Type ) ( coords ) ; } ) ; break ; case "A" : handleArcSegment ( origin ) ; break ; case "a" : handleArcSegment ( position ) ; break ; case "L" : readCoords ( 1 , pushType ( lineType ) ) ; break ; case "l" : readCoords ( 1 , function ( c ) { pushLine ( [ [ x ( c [ 0 ] ) + x ( position ) , y ( c [ 0 ] ) + y ( position ) ] ] ) ; } ) ; break ; case "H" : pushType ( lineType ) ( [ [ readNumber ( ) , y ( position ) ] ] ) ; break ; case "h" : pushType ( lineType , position ) ( [ [ readNumber ( ) , 0 ] ] ) ; break ; case "V" : pushType ( lineType ) ( [ [ x ( position ) , readNumber ( ) ] ] ) ; break ; case "v" : pushType ( lineType , position ) ( [ [ 0 , readNumber ( ) ] ] ) ; break ; case "Z" : case "z" : if ( ! coordEqual ( position , start ) ) { pushType ( lineType ) ( [ start ] ) ; } break ; default : throw new Error ( "Unknown operator: " + operator + " for polygon '" + input + "'" ) ; } } while ( polygon . length > 0 ) { readSegment ( ) ; } // Remove zero-length lines for ( var i = 0 ; i < result . length ; i ++ ) { var segment = result [ i ] ; if ( segment . type === lineType && coordEqual ( segment . coords [ 0 ] , segment . coords [ 1 ] ) ) { result . splice ( i , 1 ) ; i -- ; } } return result ; }