2016-09-13 12:40:39 -04:00
! function ( e ) { if ( "object" == typeof exports && "undefined" != typeof module ) module . exports = e ( ) ; else if ( "function" == typeof define && define . amd ) define ( [ ] , e ) ; else { var f ; "undefined" != typeof window ? f = window : "undefined" != typeof global ? f = global : "undefined" != typeof self && ( f = self ) , f . decomp = e ( ) } } ( function ( ) { var define , module , exports ; return ( function e ( t , n , r ) { function s ( o , u ) { if ( ! n [ o ] ) { if ( ! t [ o ] ) { var a = typeof require == "function" && require ; if ( ! u && a ) return a ( o , ! 0 ) ; if ( i ) return i ( o , ! 0 ) ; throw new Error ( "Cannot find module '" + o + "'" ) } var f = n [ o ] = { exports : { } } ; t [ o ] [ 0 ] . call ( f . exports , function ( e ) { var n = t [ o ] [ 1 ] [ e ] ; return s ( n ? n : e ) } , f , f . exports , e , t , n , r ) } return n [ o ] . exports } var i = typeof require == "function" && require ; for ( var o = 0 ; o < r . length ; o ++ ) s ( r [ o ] ) ; return s } ) ( { 1 : [ function ( _dereq _ , module , exports ) {
module . exports = {
decomp : polygonDecomp ,
quickDecomp : polygonQuickDecomp ,
isSimple : polygonIsSimple ,
removeCollinearPoints : polygonRemoveCollinearPoints ,
2021-01-12 18:36:04 -05:00
removeDuplicatePoints : polygonRemoveDuplicatePoints ,
2016-09-13 12:40:39 -04:00
makeCCW : polygonMakeCCW
} ;
2015-02-01 17:55:24 -05:00
/ * *
* Compute the intersection between two lines .
* @ static
* @ method lineInt
* @ param { Array } l1 Line vector 1
* @ param { Array } l2 Line vector 2
* @ param { Number } precision Precision to use when checking if the lines are parallel
* @ return { Array } The intersection point .
* /
2016-09-13 12:40:39 -04:00
function lineInt ( l1 , l2 , precision ) {
2015-02-01 17:55:24 -05:00
precision = precision || 0 ;
var i = [ 0 , 0 ] ; // point
var a1 , b1 , c1 , a2 , b2 , c2 , det ; // scalars
a1 = l1 [ 1 ] [ 1 ] - l1 [ 0 ] [ 1 ] ;
b1 = l1 [ 0 ] [ 0 ] - l1 [ 1 ] [ 0 ] ;
c1 = a1 * l1 [ 0 ] [ 0 ] + b1 * l1 [ 0 ] [ 1 ] ;
a2 = l2 [ 1 ] [ 1 ] - l2 [ 0 ] [ 1 ] ;
b2 = l2 [ 0 ] [ 0 ] - l2 [ 1 ] [ 0 ] ;
c2 = a2 * l2 [ 0 ] [ 0 ] + b2 * l2 [ 0 ] [ 1 ] ;
det = a1 * b2 - a2 * b1 ;
2016-09-13 12:40:39 -04:00
if ( ! scalar _eq ( det , 0 , precision ) ) { // lines are not parallel
2015-02-01 17:55:24 -05:00
i [ 0 ] = ( b2 * c1 - b1 * c2 ) / det ;
i [ 1 ] = ( a1 * c2 - a2 * c1 ) / det ;
}
return i ;
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
/ * *
* Checks if two line segments intersects .
* @ method segmentsIntersect
* @ param { Array } p1 The start vertex of the first line segment .
* @ param { Array } p2 The end vertex of the first line segment .
* @ param { Array } q1 The start vertex of the second line segment .
* @ param { Array } q2 The end vertex of the second line segment .
* @ return { Boolean } True if the two line segments intersect
* /
2016-09-13 12:40:39 -04:00
function lineSegmentsIntersect ( p1 , p2 , q1 , q2 ) {
var dx = p2 [ 0 ] - p1 [ 0 ] ;
var dy = p2 [ 1 ] - p1 [ 1 ] ;
var da = q2 [ 0 ] - q1 [ 0 ] ;
var db = q2 [ 1 ] - q1 [ 1 ] ;
2015-02-01 17:55:24 -05:00
2016-09-13 12:40:39 -04:00
// segments are parallel
if ( ( da * dy - db * dx ) === 0 ) {
return false ;
}
2015-02-01 17:55:24 -05:00
2016-09-13 12:40:39 -04:00
var s = ( dx * ( q1 [ 1 ] - p1 [ 1 ] ) + dy * ( p1 [ 0 ] - q1 [ 0 ] ) ) / ( da * dy - db * dx ) ;
var t = ( da * ( p1 [ 1 ] - q1 [ 1 ] ) + db * ( q1 [ 0 ] - p1 [ 0 ] ) ) / ( db * dx - da * dy ) ;
2015-02-01 17:55:24 -05:00
2016-09-13 12:40:39 -04:00
return ( s >= 0 && s <= 1 && t >= 0 && t <= 1 ) ;
}
2015-02-01 17:55:24 -05:00
/ * *
* Get the area of a triangle spanned by the three given points . Note that the area will be negative if the points are not given in counter - clockwise order .
* @ static
* @ method area
* @ param { Array } a
* @ param { Array } b
* @ param { Array } c
* @ return { Number }
* /
2016-09-13 12:40:39 -04:00
function triangleArea ( a , b , c ) {
2015-02-01 17:55:24 -05:00
return ( ( ( b [ 0 ] - a [ 0 ] ) * ( c [ 1 ] - a [ 1 ] ) ) - ( ( c [ 0 ] - a [ 0 ] ) * ( b [ 1 ] - a [ 1 ] ) ) ) ;
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
2016-09-13 12:40:39 -04:00
function isLeft ( a , b , c ) {
return triangleArea ( a , b , c ) > 0 ;
}
2015-02-01 17:55:24 -05:00
2016-09-13 12:40:39 -04:00
function isLeftOn ( a , b , c ) {
return triangleArea ( a , b , c ) >= 0 ;
}
2015-02-01 17:55:24 -05:00
2016-09-13 12:40:39 -04:00
function isRight ( a , b , c ) {
return triangleArea ( a , b , c ) < 0 ;
}
2015-02-01 17:55:24 -05:00
2016-09-13 12:40:39 -04:00
function isRightOn ( a , b , c ) {
return triangleArea ( a , b , c ) <= 0 ;
}
2015-02-01 17:55:24 -05:00
var tmpPoint1 = [ ] ,
tmpPoint2 = [ ] ;
/ * *
* Check if three points are collinear
* @ method collinear
* @ param { Array } a
* @ param { Array } b
* @ param { Array } c
* @ param { Number } [ thresholdAngle = 0 ] Threshold angle to use when comparing the vectors . The function will return true if the angle between the resulting vectors is less than this value . Use zero for max precision .
* @ return { Boolean }
* /
2016-09-13 12:40:39 -04:00
function collinear ( a , b , c , thresholdAngle ) {
if ( ! thresholdAngle ) {
return triangleArea ( a , b , c ) === 0 ;
} else {
2015-02-01 17:55:24 -05:00
var ab = tmpPoint1 ,
bc = tmpPoint2 ;
ab [ 0 ] = b [ 0 ] - a [ 0 ] ;
ab [ 1 ] = b [ 1 ] - a [ 1 ] ;
bc [ 0 ] = c [ 0 ] - b [ 0 ] ;
bc [ 1 ] = c [ 1 ] - b [ 1 ] ;
var dot = ab [ 0 ] * bc [ 0 ] + ab [ 1 ] * bc [ 1 ] ,
magA = Math . sqrt ( ab [ 0 ] * ab [ 0 ] + ab [ 1 ] * ab [ 1 ] ) ,
magB = Math . sqrt ( bc [ 0 ] * bc [ 0 ] + bc [ 1 ] * bc [ 1 ] ) ,
angle = Math . acos ( dot / ( magA * magB ) ) ;
return angle < thresholdAngle ;
}
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
2016-09-13 12:40:39 -04:00
function sqdist ( a , b ) {
2015-02-01 17:55:24 -05:00
var dx = b [ 0 ] - a [ 0 ] ;
var dy = b [ 1 ] - a [ 1 ] ;
return dx * dx + dy * dy ;
}
/ * *
* Get a vertex at position i . It does not matter if i is out of bounds , this function will just cycle .
* @ method at
* @ param { Number } i
* @ return { Array }
* /
2016-09-13 12:40:39 -04:00
function polygonAt ( polygon , i ) {
var s = polygon . length ;
return polygon [ i < 0 ? i % s + s : i % s ] ;
}
2015-02-01 17:55:24 -05:00
/ * *
* Clear the polygon data
* @ method clear
* @ return { Array }
* /
2016-09-13 12:40:39 -04:00
function polygonClear ( polygon ) {
polygon . length = 0 ;
}
2015-02-01 17:55:24 -05:00
/ * *
* Append points "from" to "to" - 1 from an other polygon "poly" onto this one .
* @ method append
* @ param { Polygon } poly The polygon to get points from .
* @ param { Number } from The vertex index in "poly" .
* @ param { Number } to The end vertex index in "poly" . Note that this vertex is NOT included when appending .
* @ return { Array }
* /
2016-09-13 12:40:39 -04:00
function polygonAppend ( polygon , poly , from , to ) {
2015-02-01 17:55:24 -05:00
for ( var i = from ; i < to ; i ++ ) {
2016-09-13 12:40:39 -04:00
polygon . push ( poly [ i ] ) ;
2015-02-01 17:55:24 -05:00
}
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
/ * *
* Make sure that the polygon vertices are ordered counter - clockwise .
* @ method makeCCW
* /
2016-09-13 12:40:39 -04:00
function polygonMakeCCW ( polygon ) {
2015-02-01 17:55:24 -05:00
var br = 0 ,
2016-09-13 12:40:39 -04:00
v = polygon ;
2015-02-01 17:55:24 -05:00
// find bottom right point
2016-09-13 12:40:39 -04:00
for ( var i = 1 ; i < polygon . length ; ++ i ) {
if ( v [ i ] [ 1 ] < v [ br ] [ 1 ] || ( v [ i ] [ 1 ] === v [ br ] [ 1 ] && v [ i ] [ 0 ] > v [ br ] [ 0 ] ) ) {
2015-02-01 17:55:24 -05:00
br = i ;
}
}
// reverse poly if clockwise
2016-09-13 12:40:39 -04:00
if ( ! isLeft ( polygonAt ( polygon , br - 1 ) , polygonAt ( polygon , br ) , polygonAt ( polygon , br + 1 ) ) ) {
polygonReverse ( polygon ) ;
2021-01-12 18:36:04 -05:00
return true ;
} else {
return false ;
2015-02-01 17:55:24 -05:00
}
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
/ * *
* Reverse the vertices in the polygon
* @ method reverse
* /
2016-09-13 12:40:39 -04:00
function polygonReverse ( polygon ) {
2015-02-01 17:55:24 -05:00
var tmp = [ ] ;
2016-09-13 12:40:39 -04:00
var N = polygon . length ;
for ( var i = 0 ; i !== N ; i ++ ) {
tmp . push ( polygon . pop ( ) ) ;
2015-02-01 17:55:24 -05:00
}
2016-09-13 12:40:39 -04:00
for ( var i = 0 ; i !== N ; i ++ ) {
polygon [ i ] = tmp [ i ] ;
}
}
2015-02-01 17:55:24 -05:00
/ * *
* Check if a point in the polygon is a reflex point
* @ method isReflex
* @ param { Number } i
* @ return { Boolean }
* /
2016-09-13 12:40:39 -04:00
function polygonIsReflex ( polygon , i ) {
return isRight ( polygonAt ( polygon , i - 1 ) , polygonAt ( polygon , i ) , polygonAt ( polygon , i + 1 ) ) ;
}
2015-02-01 17:55:24 -05:00
var tmpLine1 = [ ] ,
tmpLine2 = [ ] ;
/ * *
* Check if two vertices in the polygon can see each other
* @ method canSee
* @ param { Number } a Vertex index 1
* @ param { Number } b Vertex index 2
* @ return { Boolean }
* /
2016-09-13 12:40:39 -04:00
function polygonCanSee ( polygon , a , b ) {
2015-02-01 17:55:24 -05:00
var p , dist , l1 = tmpLine1 , l2 = tmpLine2 ;
2016-09-13 12:40:39 -04:00
if ( isLeftOn ( polygonAt ( polygon , a + 1 ) , polygonAt ( polygon , a ) , polygonAt ( polygon , b ) ) && isRightOn ( polygonAt ( polygon , a - 1 ) , polygonAt ( polygon , a ) , polygonAt ( polygon , b ) ) ) {
2015-02-01 17:55:24 -05:00
return false ;
}
2016-09-13 12:40:39 -04:00
dist = sqdist ( polygonAt ( polygon , a ) , polygonAt ( polygon , b ) ) ;
for ( var i = 0 ; i !== polygon . length ; ++ i ) { // for each edge
if ( ( i + 1 ) % polygon . length === a || i === a ) { // ignore incident edges
2015-02-01 17:55:24 -05:00
continue ;
2016-09-13 12:40:39 -04:00
}
if ( isLeftOn ( polygonAt ( polygon , a ) , polygonAt ( polygon , b ) , polygonAt ( polygon , i + 1 ) ) && isRightOn ( polygonAt ( polygon , a ) , polygonAt ( polygon , b ) , polygonAt ( polygon , i ) ) ) { // if diag intersects an edge
l1 [ 0 ] = polygonAt ( polygon , a ) ;
l1 [ 1 ] = polygonAt ( polygon , b ) ;
l2 [ 0 ] = polygonAt ( polygon , i ) ;
l2 [ 1 ] = polygonAt ( polygon , i + 1 ) ;
p = lineInt ( l1 , l2 ) ;
if ( sqdist ( polygonAt ( polygon , a ) , p ) < dist ) { // if edge is blocking visibility to b
2015-02-01 17:55:24 -05:00
return false ;
}
}
}
return true ;
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
2021-01-12 18:36:04 -05:00
/ * *
* Check if two vertices in the polygon can see each other
* @ method canSee2
* @ param { Number } a Vertex index 1
* @ param { Number } b Vertex index 2
* @ return { Boolean }
* /
function polygonCanSee2 ( polygon , a , b ) {
// for each edge
for ( var i = 0 ; i !== polygon . length ; ++ i ) {
// ignore incident edges
if ( i === a || i === b || ( i + 1 ) % polygon . length === a || ( i + 1 ) % polygon . length === b ) {
continue ;
}
if ( lineSegmentsIntersect ( polygonAt ( polygon , a ) , polygonAt ( polygon , b ) , polygonAt ( polygon , i ) , polygonAt ( polygon , i + 1 ) ) ) {
return false ;
}
}
return true ;
}
2015-02-01 17:55:24 -05:00
/ * *
* Copy the polygon from vertex i to vertex j .
* @ method copy
* @ param { Number } i
* @ param { Number } j
* @ param { Polygon } [ targetPoly ] Optional target polygon to save in .
* @ return { Polygon } The resulting copy .
* /
2016-09-13 12:40:39 -04:00
function polygonCopy ( polygon , i , j , targetPoly ) {
var p = targetPoly || [ ] ;
polygonClear ( p ) ;
2015-02-01 17:55:24 -05:00
if ( i < j ) {
// Insert all vertices from i to j
2016-09-13 12:40:39 -04:00
for ( var k = i ; k <= j ; k ++ ) {
p . push ( polygon [ k ] ) ;
}
2015-02-01 17:55:24 -05:00
} else {
// Insert vertices 0 to j
2016-09-13 12:40:39 -04:00
for ( var k = 0 ; k <= j ; k ++ ) {
p . push ( polygon [ k ] ) ;
}
2015-02-01 17:55:24 -05:00
// Insert vertices i to end
2016-09-13 12:40:39 -04:00
for ( var k = i ; k < polygon . length ; k ++ ) {
p . push ( polygon [ k ] ) ;
}
2015-02-01 17:55:24 -05:00
}
return p ;
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
/ * *
* Decomposes the polygon into convex pieces . Returns a list of edges [ [ p1 , p2 ] , [ p2 , p3 ] , ... ] that cuts the polygon .
* Note that this algorithm has complexity O ( N ^ 4 ) and will be very slow for polygons with many vertices .
* @ method getCutEdges
* @ return { Array }
* /
2016-09-13 12:40:39 -04:00
function polygonGetCutEdges ( polygon ) {
var min = [ ] , tmp1 = [ ] , tmp2 = [ ] , tmpPoly = [ ] ;
2015-02-01 17:55:24 -05:00
var nDiags = Number . MAX _VALUE ;
2016-09-13 12:40:39 -04:00
for ( var i = 0 ; i < polygon . length ; ++ i ) {
if ( polygonIsReflex ( polygon , i ) ) {
for ( var j = 0 ; j < polygon . length ; ++ j ) {
if ( polygonCanSee ( polygon , i , j ) ) {
tmp1 = polygonGetCutEdges ( polygonCopy ( polygon , i , j , tmpPoly ) ) ;
tmp2 = polygonGetCutEdges ( polygonCopy ( polygon , j , i , tmpPoly ) ) ;
2015-02-01 17:55:24 -05:00
2016-09-13 12:40:39 -04:00
for ( var k = 0 ; k < tmp2 . length ; k ++ ) {
2015-02-01 17:55:24 -05:00
tmp1 . push ( tmp2 [ k ] ) ;
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
if ( tmp1 . length < nDiags ) {
min = tmp1 ;
nDiags = tmp1 . length ;
2016-09-13 12:40:39 -04:00
min . push ( [ polygonAt ( polygon , i ) , polygonAt ( polygon , j ) ] ) ;
2015-02-01 17:55:24 -05:00
}
}
}
}
}
return min ;
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
/ * *
* Decomposes the polygon into one or more convex sub - Polygons .
* @ method decomp
* @ return { Array } An array or Polygon objects .
* /
2016-09-13 12:40:39 -04:00
function polygonDecomp ( polygon ) {
var edges = polygonGetCutEdges ( polygon ) ;
if ( edges . length > 0 ) {
return polygonSlice ( polygon , edges ) ;
} else {
return [ polygon ] ;
}
}
2015-02-01 17:55:24 -05:00
/ * *
* Slices the polygon given one or more cut edges . If given one , this function will return two polygons ( false on failure ) . If many , an array of polygons .
* @ method slice
* @ param { Array } cutEdges A list of edges , as returned by . getCutEdges ( )
* @ return { Array }
* /
2016-09-13 12:40:39 -04:00
function polygonSlice ( polygon , cutEdges ) {
if ( cutEdges . length === 0 ) {
return [ polygon ] ;
}
if ( cutEdges instanceof Array && cutEdges . length && cutEdges [ 0 ] instanceof Array && cutEdges [ 0 ] . length === 2 && cutEdges [ 0 ] [ 0 ] instanceof Array ) {
2015-02-01 17:55:24 -05:00
2016-09-13 12:40:39 -04:00
var polys = [ polygon ] ;
2015-02-01 17:55:24 -05:00
for ( var i = 0 ; i < cutEdges . length ; i ++ ) {
var cutEdge = cutEdges [ i ] ;
// Cut all polys
for ( var j = 0 ; j < polys . length ; j ++ ) {
var poly = polys [ j ] ;
2016-09-13 12:40:39 -04:00
var result = polygonSlice ( poly , cutEdge ) ;
2015-02-01 17:55:24 -05:00
if ( result ) {
// Found poly! Cut and quit
polys . splice ( j , 1 ) ;
polys . push ( result [ 0 ] , result [ 1 ] ) ;
break ;
}
}
}
return polys ;
} else {
// Was given one edge
var cutEdge = cutEdges ;
2016-09-13 12:40:39 -04:00
var i = polygon . indexOf ( cutEdge [ 0 ] ) ;
var j = polygon . indexOf ( cutEdge [ 1 ] ) ;
2015-02-01 17:55:24 -05:00
2016-09-13 12:40:39 -04:00
if ( i !== - 1 && j !== - 1 ) {
return [ polygonCopy ( polygon , i , j ) ,
polygonCopy ( polygon , j , i ) ] ;
2015-02-01 17:55:24 -05:00
} else {
return false ;
}
}
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
/ * *
* Checks that the line segments of this polygon do not intersect each other .
* @ method isSimple
* @ param { Array } path An array of vertices e . g . [ [ 0 , 0 ] , [ 0 , 1 ] , ... ]
* @ return { Boolean }
* @ todo Should it check all segments with all others ?
* /
2016-09-13 12:40:39 -04:00
function polygonIsSimple ( polygon ) {
var path = polygon , i ;
2015-02-01 17:55:24 -05:00
// Check
2016-09-13 12:40:39 -04:00
for ( i = 0 ; i < path . length - 1 ; i ++ ) {
2015-02-01 17:55:24 -05:00
for ( var j = 0 ; j < i - 1 ; j ++ ) {
2016-09-13 12:40:39 -04:00
if ( lineSegmentsIntersect ( path [ i ] , path [ i + 1 ] , path [ j ] , path [ j + 1 ] ) ) {
2015-02-01 17:55:24 -05:00
return false ;
}
}
}
// Check the segment between the last and the first point to all others
2016-09-13 12:40:39 -04:00
for ( i = 1 ; i < path . length - 2 ; i ++ ) {
if ( lineSegmentsIntersect ( path [ 0 ] , path [ path . length - 1 ] , path [ i ] , path [ i + 1 ] ) ) {
2015-02-01 17:55:24 -05:00
return false ;
}
}
return true ;
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
function getIntersectionPoint ( p1 , p2 , q1 , q2 , delta ) {
2016-09-13 12:40:39 -04:00
delta = delta || 0 ;
var a1 = p2 [ 1 ] - p1 [ 1 ] ;
var b1 = p1 [ 0 ] - p2 [ 0 ] ;
var c1 = ( a1 * p1 [ 0 ] ) + ( b1 * p1 [ 1 ] ) ;
var a2 = q2 [ 1 ] - q1 [ 1 ] ;
var b2 = q1 [ 0 ] - q2 [ 0 ] ;
var c2 = ( a2 * q1 [ 0 ] ) + ( b2 * q1 [ 1 ] ) ;
var det = ( a1 * b2 ) - ( a2 * b1 ) ;
if ( ! scalar _eq ( det , 0 , delta ) ) {
return [ ( ( b2 * c1 ) - ( b1 * c2 ) ) / det , ( ( a1 * c2 ) - ( a2 * c1 ) ) / det ] ;
} else {
return [ 0 , 0 ] ;
}
2015-02-01 17:55:24 -05:00
}
/ * *
* Quickly decompose the Polygon into convex sub - polygons .
* @ method quickDecomp
* @ param { Array } result
* @ param { Array } [ reflexVertices ]
* @ param { Array } [ steinerPoints ]
* @ param { Number } [ delta ]
* @ param { Number } [ maxlevel ]
* @ param { Number } [ level ]
* @ return { Array }
* /
2016-09-13 12:40:39 -04:00
function polygonQuickDecomp ( polygon , result , reflexVertices , steinerPoints , delta , maxlevel , level ) {
2015-02-01 17:55:24 -05:00
maxlevel = maxlevel || 100 ;
level = level || 0 ;
delta = delta || 25 ;
2016-09-13 12:40:39 -04:00
result = typeof ( result ) !== "undefined" ? result : [ ] ;
2015-02-01 17:55:24 -05:00
reflexVertices = reflexVertices || [ ] ;
steinerPoints = steinerPoints || [ ] ;
var upperInt = [ 0 , 0 ] , lowerInt = [ 0 , 0 ] , p = [ 0 , 0 ] ; // Points
var upperDist = 0 , lowerDist = 0 , d = 0 , closestDist = 0 ; // scalars
var upperIndex = 0 , lowerIndex = 0 , closestIndex = 0 ; // Integers
2016-09-13 12:40:39 -04:00
var lowerPoly = [ ] , upperPoly = [ ] ; // polygons
var poly = polygon ,
v = polygon ;
2015-02-01 17:55:24 -05:00
2016-09-13 12:40:39 -04:00
if ( v . length < 3 ) {
return result ;
}
2015-02-01 17:55:24 -05:00
level ++ ;
if ( level > maxlevel ) {
console . warn ( "quickDecomp: max level (" + maxlevel + ") reached." ) ;
return result ;
}
2016-09-13 12:40:39 -04:00
for ( var i = 0 ; i < polygon . length ; ++ i ) {
if ( polygonIsReflex ( poly , i ) ) {
reflexVertices . push ( poly [ i ] ) ;
2015-02-01 17:55:24 -05:00
upperDist = lowerDist = Number . MAX _VALUE ;
2016-09-13 12:40:39 -04:00
for ( var j = 0 ; j < polygon . length ; ++ j ) {
if ( isLeft ( polygonAt ( poly , i - 1 ) , polygonAt ( poly , i ) , polygonAt ( poly , j ) ) && isRightOn ( polygonAt ( poly , i - 1 ) , polygonAt ( poly , i ) , polygonAt ( poly , j - 1 ) ) ) { // if line intersects with an edge
p = getIntersectionPoint ( polygonAt ( poly , i - 1 ) , polygonAt ( poly , i ) , polygonAt ( poly , j ) , polygonAt ( poly , j - 1 ) ) ; // find the point of intersection
if ( isRight ( polygonAt ( poly , i + 1 ) , polygonAt ( poly , i ) , p ) ) { // make sure it's inside the poly
d = sqdist ( poly [ i ] , p ) ;
2015-02-01 17:55:24 -05:00
if ( d < lowerDist ) { // keep only the closest intersection
lowerDist = d ;
lowerInt = p ;
lowerIndex = j ;
}
}
}
2016-09-13 12:40:39 -04:00
if ( isLeft ( polygonAt ( poly , i + 1 ) , polygonAt ( poly , i ) , polygonAt ( poly , j + 1 ) ) && isRightOn ( polygonAt ( poly , i + 1 ) , polygonAt ( poly , i ) , polygonAt ( poly , j ) ) ) {
p = getIntersectionPoint ( polygonAt ( poly , i + 1 ) , polygonAt ( poly , i ) , polygonAt ( poly , j ) , polygonAt ( poly , j + 1 ) ) ;
if ( isLeft ( polygonAt ( poly , i - 1 ) , polygonAt ( poly , i ) , p ) ) {
d = sqdist ( poly [ i ] , p ) ;
2015-02-01 17:55:24 -05:00
if ( d < upperDist ) {
upperDist = d ;
upperInt = p ;
upperIndex = j ;
}
}
}
}
// if there are no vertices to connect to, choose a point in the middle
2016-09-13 12:40:39 -04:00
if ( lowerIndex === ( upperIndex + 1 ) % polygon . length ) {
//console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+polygon.length+")");
2015-02-01 17:55:24 -05:00
p [ 0 ] = ( lowerInt [ 0 ] + upperInt [ 0 ] ) / 2 ;
p [ 1 ] = ( lowerInt [ 1 ] + upperInt [ 1 ] ) / 2 ;
steinerPoints . push ( p ) ;
if ( i < upperIndex ) {
//lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.begin() + upperIndex + 1);
2016-09-13 12:40:39 -04:00
polygonAppend ( lowerPoly , poly , i , upperIndex + 1 ) ;
lowerPoly . push ( p ) ;
upperPoly . push ( p ) ;
if ( lowerIndex !== 0 ) {
2015-02-01 17:55:24 -05:00
//upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.end());
2016-09-13 12:40:39 -04:00
polygonAppend ( upperPoly , poly , lowerIndex , poly . length ) ;
2015-02-01 17:55:24 -05:00
}
//upperPoly.insert(upperPoly.end(), poly.begin(), poly.begin() + i + 1);
2016-09-13 12:40:39 -04:00
polygonAppend ( upperPoly , poly , 0 , i + 1 ) ;
2015-02-01 17:55:24 -05:00
} else {
2016-09-13 12:40:39 -04:00
if ( i !== 0 ) {
2015-02-01 17:55:24 -05:00
//lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.end());
2016-09-13 12:40:39 -04:00
polygonAppend ( lowerPoly , poly , i , poly . length ) ;
2015-02-01 17:55:24 -05:00
}
//lowerPoly.insert(lowerPoly.end(), poly.begin(), poly.begin() + upperIndex + 1);
2016-09-13 12:40:39 -04:00
polygonAppend ( lowerPoly , poly , 0 , upperIndex + 1 ) ;
lowerPoly . push ( p ) ;
upperPoly . push ( p ) ;
2015-02-01 17:55:24 -05:00
//upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1);
2016-09-13 12:40:39 -04:00
polygonAppend ( upperPoly , poly , lowerIndex , i + 1 ) ;
2015-02-01 17:55:24 -05:00
}
} else {
// connect to the closest point within the triangle
2016-09-13 12:40:39 -04:00
//console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+polygon.length+")\n");
2015-02-01 17:55:24 -05:00
if ( lowerIndex > upperIndex ) {
2016-09-13 12:40:39 -04:00
upperIndex += polygon . length ;
2015-02-01 17:55:24 -05:00
}
closestDist = Number . MAX _VALUE ;
if ( upperIndex < lowerIndex ) {
return result ;
}
for ( var j = lowerIndex ; j <= upperIndex ; ++ j ) {
2021-01-12 18:36:04 -05:00
if (
isLeftOn ( polygonAt ( poly , i - 1 ) , polygonAt ( poly , i ) , polygonAt ( poly , j ) ) &&
isRightOn ( polygonAt ( poly , i + 1 ) , polygonAt ( poly , i ) , polygonAt ( poly , j ) )
) {
2016-09-13 12:40:39 -04:00
d = sqdist ( polygonAt ( poly , i ) , polygonAt ( poly , j ) ) ;
2021-01-12 18:36:04 -05:00
if ( d < closestDist && polygonCanSee2 ( poly , i , j ) ) {
2015-02-01 17:55:24 -05:00
closestDist = d ;
2016-09-13 12:40:39 -04:00
closestIndex = j % polygon . length ;
2015-02-01 17:55:24 -05:00
}
}
}
if ( i < closestIndex ) {
2016-09-13 12:40:39 -04:00
polygonAppend ( lowerPoly , poly , i , closestIndex + 1 ) ;
if ( closestIndex !== 0 ) {
polygonAppend ( upperPoly , poly , closestIndex , v . length ) ;
2015-02-01 17:55:24 -05:00
}
2016-09-13 12:40:39 -04:00
polygonAppend ( upperPoly , poly , 0 , i + 1 ) ;
2015-02-01 17:55:24 -05:00
} else {
2016-09-13 12:40:39 -04:00
if ( i !== 0 ) {
polygonAppend ( lowerPoly , poly , i , v . length ) ;
2015-02-01 17:55:24 -05:00
}
2016-09-13 12:40:39 -04:00
polygonAppend ( lowerPoly , poly , 0 , closestIndex + 1 ) ;
polygonAppend ( upperPoly , poly , closestIndex , i + 1 ) ;
2015-02-01 17:55:24 -05:00
}
}
// solve smallest poly first
2016-09-13 12:40:39 -04:00
if ( lowerPoly . length < upperPoly . length ) {
polygonQuickDecomp ( lowerPoly , result , reflexVertices , steinerPoints , delta , maxlevel , level ) ;
polygonQuickDecomp ( upperPoly , result , reflexVertices , steinerPoints , delta , maxlevel , level ) ;
2015-02-01 17:55:24 -05:00
} else {
2016-09-13 12:40:39 -04:00
polygonQuickDecomp ( upperPoly , result , reflexVertices , steinerPoints , delta , maxlevel , level ) ;
polygonQuickDecomp ( lowerPoly , result , reflexVertices , steinerPoints , delta , maxlevel , level ) ;
2015-02-01 17:55:24 -05:00
}
return result ;
}
}
2016-09-13 12:40:39 -04:00
result . push ( polygon ) ;
2015-02-01 17:55:24 -05:00
return result ;
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
/ * *
* Remove collinear points in the polygon .
* @ method removeCollinearPoints
* @ param { Number } [ precision ] The threshold angle to use when determining whether two edges are collinear . Use zero for finest precision .
* @ return { Number } The number of points removed
* /
2016-09-13 12:40:39 -04:00
function polygonRemoveCollinearPoints ( polygon , precision ) {
2015-02-01 17:55:24 -05:00
var num = 0 ;
2016-09-13 12:40:39 -04:00
for ( var i = polygon . length - 1 ; polygon . length > 3 && i >= 0 ; -- i ) {
if ( collinear ( polygonAt ( polygon , i - 1 ) , polygonAt ( polygon , i ) , polygonAt ( polygon , i + 1 ) , precision ) ) {
2015-02-01 17:55:24 -05:00
// Remove the middle point
2016-09-13 12:40:39 -04:00
polygon . splice ( i % polygon . length , 1 ) ;
2015-02-01 17:55:24 -05:00
num ++ ;
}
}
return num ;
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
2021-01-12 18:36:04 -05:00
/ * *
* Remove duplicate points in the polygon .
* @ method removeDuplicatePoints
* @ param { Number } [ precision ] The threshold to use when determining whether two points are the same . Use zero for best precision .
* /
function polygonRemoveDuplicatePoints ( polygon , precision ) {
for ( var i = polygon . length - 1 ; i >= 1 ; -- i ) {
var pi = polygon [ i ] ;
for ( var j = i - 1 ; j >= 0 ; -- j ) {
if ( points _eq ( pi , polygon [ j ] , precision ) ) {
polygon . splice ( i , 1 ) ;
continue ;
}
}
}
}
2015-02-01 17:55:24 -05:00
/ * *
* Check if two scalars are equal
* @ static
* @ method eq
* @ param { Number } a
* @ param { Number } b
* @ param { Number } [ precision ]
* @ return { Boolean }
* /
2016-09-13 12:40:39 -04:00
function scalar _eq ( a , b , precision ) {
2015-02-01 17:55:24 -05:00
precision = precision || 0 ;
2021-01-12 18:36:04 -05:00
return Math . abs ( a - b ) <= precision ;
}
/ * *
* Check if two points are equal
* @ static
* @ method points _eq
* @ param { Array } a
* @ param { Array } b
* @ param { Number } [ precision ]
* @ return { Boolean }
* /
function points _eq ( a , b , precision ) {
return scalar _eq ( a [ 0 ] , b [ 0 ] , precision ) && scalar _eq ( a [ 1 ] , b [ 1 ] , precision ) ;
2016-09-13 12:40:39 -04:00
}
2015-02-01 17:55:24 -05:00
2016-09-13 12:40:39 -04:00
} , { } ] } , { } , [ 1 ] )
( 1 )
2021-01-12 18:36:04 -05:00
} ) ;