Updated earcut from 2.1.1 to 2.1.4.

This commit is contained in:
Richard Davey 2018-12-10 16:35:11 +00:00
parent 24ffe623cd
commit 27c1426856
2 changed files with 65 additions and 58 deletions

View file

@ -184,6 +184,7 @@ one set of bindings ever created, which makes things a lot cleaner.
* The Animation Component `restart` method has had is sole `key` argument removed. Previously, you had to pass in the key of the animation you wished to reverse, but now you can just call the method directly, and as long as there is an animation playing, it will automatically start playing in reverse, without the nee for a key (the way it should have been originally) * The Animation Component `restart` method has had is sole `key` argument removed. Previously, you had to pass in the key of the animation you wished to reverse, but now you can just call the method directly, and as long as there is an animation playing, it will automatically start playing in reverse, without the nee for a key (the way it should have been originally)
* `Animation.play` and `playReverse` will now accept either a string-based key of the animation to play (like before), or you can pass in an Animation instance, and it will play that animation. * `Animation.play` and `playReverse` will now accept either a string-based key of the animation to play (like before), or you can pass in an Animation instance, and it will play that animation.
* `CanvasTexture.clear` now has 4 new optional arguments: `x, y, width, height` which allow you to define the region of the texture to be cleared. If not provided it will clear the whole texture, which is the same behavior as before. * `CanvasTexture.clear` now has 4 new optional arguments: `x, y, width, height` which allow you to define the region of the texture to be cleared. If not provided it will clear the whole texture, which is the same behavior as before.
* EarCut, the polygon triangulation library used by the Graphics and WebGL classes, has been upgraded from 2.1.1 to 2.1.4. 2.1.2 fixed a few race conditions where bad input would cause an error. 2.1.3 improved performance for bigger inputs (5-12%) and 2.1.4 fixed a race condition that could lead to a freeze on degenerate input.
### Bug Fixes ### Bug Fixes

View file

@ -4,37 +4,30 @@
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/ */
// 2.1.1 (Mar 17, 2016) // Earcut 2.1.4 (December 4th 2018)
/* /*
ISC License * ISC License
*
Copyright (c) 2016, Mapbox * Copyright (c) 2016, Mapbox
*
Permission to use, copy, modify, and/or distribute this software for any purpose * Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice * with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies. * and this permission notice appear in all copies.
*
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE. * THIS SOFTWARE.
*/ */
'use strict'; 'use strict';
module.exports = earcut; module.exports = earcut;
/*
vertices is a flat array of vertice coordinates like [x0,y0, x1,y1, x2,y2, ...].
holes is an array of hole indices if any (e.g. [5, 8] for a 12-vertice input would mean one hole with vertices 57 and another with 811).
dimensions is the number of coordinates per vertice in the input array (2 by default).
Each group of three vertice indices in the resulting array forms a triangle.
*/
function earcut(data, holeIndices, dim) { function earcut(data, holeIndices, dim) {
dim = dim || 2; dim = dim || 2;
@ -44,9 +37,9 @@ function earcut(data, holeIndices, dim) {
outerNode = linkedList(data, 0, outerLen, dim, true), outerNode = linkedList(data, 0, outerLen, dim, true),
triangles = []; triangles = [];
if (!outerNode) return triangles; if (!outerNode || outerNode.next === outerNode.prev) return triangles;
var minX, minY, maxX, maxY, x, y, size; var minX, minY, maxX, maxY, x, y, invSize;
if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
@ -64,11 +57,12 @@ function earcut(data, holeIndices, dim) {
if (y > maxY) maxY = y; if (y > maxY) maxY = y;
} }
// minX, minY and size are later used to transform coords into integers for z-order calculation // minX, minY and invSize are later used to transform coords into integers for z-order calculation
size = Math.max(maxX - minX, maxY - minY); invSize = Math.max(maxX - minX, maxY - minY);
invSize = invSize !== 0 ? 1 / invSize : 0;
} }
earcutLinked(outerNode, triangles, dim, minX, minY, size); earcutLinked(outerNode, triangles, dim, minX, minY, invSize);
return triangles; return triangles;
} }
@ -104,7 +98,7 @@ function filterPoints(start, end) {
if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
removeNode(p); removeNode(p);
p = end = p.prev; p = end = p.prev;
if (p === p.next) return null; if (p === p.next) break;
again = true; again = true;
} else { } else {
@ -116,11 +110,11 @@ function filterPoints(start, end) {
} }
// main ear slicing loop which triangulates a polygon (given as a linked list) // main ear slicing loop which triangulates a polygon (given as a linked list)
function earcutLinked(ear, triangles, dim, minX, minY, size, pass) { function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
if (!ear) return; if (!ear) return;
// interlink polygon nodes in z-order // interlink polygon nodes in z-order
if (!pass && size) indexCurve(ear, minX, minY, size); if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
var stop = ear, var stop = ear,
prev, next; prev, next;
@ -130,7 +124,7 @@ function earcutLinked(ear, triangles, dim, minX, minY, size, pass) {
prev = ear.prev; prev = ear.prev;
next = ear.next; next = ear.next;
if (size ? isEarHashed(ear, minX, minY, size) : isEar(ear)) { if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
// cut off the triangle // cut off the triangle
triangles.push(prev.i / dim); triangles.push(prev.i / dim);
triangles.push(ear.i / dim); triangles.push(ear.i / dim);
@ -138,7 +132,7 @@ function earcutLinked(ear, triangles, dim, minX, minY, size, pass) {
removeNode(ear); removeNode(ear);
// skipping the next vertice leads to less sliver triangles // skipping the next vertex leads to less sliver triangles
ear = next.next; ear = next.next;
stop = next.next; stop = next.next;
@ -151,16 +145,16 @@ function earcutLinked(ear, triangles, dim, minX, minY, size, pass) {
if (ear === stop) { if (ear === stop) {
// try filtering points and slicing again // try filtering points and slicing again
if (!pass) { if (!pass) {
earcutLinked(filterPoints(ear), triangles, dim, minX, minY, size, 1); earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
// if this didn't work, try curing all small self-intersections locally // if this didn't work, try curing all small self-intersections locally
} else if (pass === 1) { } else if (pass === 1) {
ear = cureLocalIntersections(ear, triangles, dim); ear = cureLocalIntersections(ear, triangles, dim);
earcutLinked(ear, triangles, dim, minX, minY, size, 2); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
// as a last resort, try splitting the remaining polygon into two // as a last resort, try splitting the remaining polygon into two
} else if (pass === 2) { } else if (pass === 2) {
splitEarcut(ear, triangles, dim, minX, minY, size); splitEarcut(ear, triangles, dim, minX, minY, invSize);
} }
break; break;
@ -188,7 +182,7 @@ function isEar(ear) {
return true; return true;
} }
function isEarHashed(ear, minX, minY, size) { function isEarHashed(ear, minX, minY, invSize) {
var a = ear.prev, var a = ear.prev,
b = ear, b = ear,
c = ear.next; c = ear.next;
@ -202,22 +196,26 @@ function isEarHashed(ear, minX, minY, size) {
maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y); maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y);
// z-order range for the current triangle bbox; // z-order range for the current triangle bbox;
var minZ = zOrder(minTX, minTY, minX, minY, size), var minZ = zOrder(minTX, minTY, minX, minY, invSize),
maxZ = zOrder(maxTX, maxTY, minX, minY, size); maxZ = zOrder(maxTX, maxTY, minX, minY, invSize);
// first look for points inside the triangle in increasing z-order var p = ear.prevZ,
var p = ear.nextZ; n = ear.nextZ;
while (p && p.z <= maxZ) { // look for points inside the triangle in both directions
while (p && p.z >= minZ && n && n.z <= maxZ) {
if (p !== ear.prev && p !== ear.next && if (p !== ear.prev && p !== ear.next &&
pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
area(p.prev, p, p.next) >= 0) return false; area(p.prev, p, p.next) >= 0) return false;
p = p.nextZ; p = p.prevZ;
if (n !== ear.prev && n !== ear.next &&
pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) &&
area(n.prev, n, n.next) >= 0) return false;
n = n.nextZ;
} }
// then look for points in decreasing z-order // look for remaining points in decreasing z-order
p = ear.prevZ;
while (p && p.z >= minZ) { while (p && p.z >= minZ) {
if (p !== ear.prev && p !== ear.next && if (p !== ear.prev && p !== ear.next &&
pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
@ -225,6 +223,14 @@ function isEarHashed(ear, minX, minY, size) {
p = p.prevZ; p = p.prevZ;
} }
// look for remaining points in increasing z-order
while (n && n.z <= maxZ) {
if (n !== ear.prev && n !== ear.next &&
pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) &&
area(n.prev, n, n.next) >= 0) return false;
n = n.nextZ;
}
return true; return true;
} }
@ -254,7 +260,7 @@ function cureLocalIntersections(start, triangles, dim) {
} }
// try splitting polygon into two and triangulate them independently // try splitting polygon into two and triangulate them independently
function splitEarcut(start, triangles, dim, minX, minY, size) { function splitEarcut(start, triangles, dim, minX, minY, invSize) {
// look for a valid diagonal that divides the polygon into two // look for a valid diagonal that divides the polygon into two
var a = start; var a = start;
do { do {
@ -269,8 +275,8 @@ function splitEarcut(start, triangles, dim, minX, minY, size) {
c = filterPoints(c, c.next); c = filterPoints(c, c.next);
// run earcut on each half // run earcut on each half
earcutLinked(a, triangles, dim, minX, minY, size); earcutLinked(a, triangles, dim, minX, minY, invSize);
earcutLinked(c, triangles, dim, minX, minY, size); earcutLinked(c, triangles, dim, minX, minY, invSize);
return; return;
} }
b = b.next; b = b.next;
@ -376,10 +382,10 @@ function findHoleBridge(hole, outerNode) {
} }
// interlink polygon nodes in z-order // interlink polygon nodes in z-order
function indexCurve(start, minX, minY, size) { function indexCurve(start, minX, minY, invSize) {
var p = start; var p = start;
do { do {
if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, size); if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize);
p.prevZ = p.prev; p.prevZ = p.prev;
p.nextZ = p.next; p.nextZ = p.next;
p = p.next; p = p.next;
@ -444,11 +450,11 @@ function sortLinked(list) {
return list; return list;
} }
// z-order of a point given coords and size of the data bounding box // z-order of a point given coords and inverse of the longer side of data bbox
function zOrder(x, y, minX, minY, size) { function zOrder(x, y, minX, minY, invSize) {
// coords are transformed into non-negative 15-bit integer range // coords are transformed into non-negative 15-bit integer range
x = 32767 * (x - minX) / size; x = 32767 * (x - minX) * invSize;
y = 32767 * (y - minY) / size; y = 32767 * (y - minY) * invSize;
x = (x | (x << 8)) & 0x00FF00FF; x = (x | (x << 8)) & 0x00FF00FF;
x = (x | (x << 4)) & 0x0F0F0F0F; x = (x | (x << 4)) & 0x0F0F0F0F;
@ -590,14 +596,14 @@ function removeNode(p) {
} }
function Node(i, x, y) { function Node(i, x, y) {
// vertice index in coordinates array // vertex index in coordinates array
this.i = i; this.i = i;
// vertex coordinates // vertex coordinates
this.x = x; this.x = x;
this.y = y; this.y = y;
// previous and next vertice nodes in a polygon ring // previous and next vertex nodes in a polygon ring
this.prev = null; this.prev = null;
this.next = null; this.next = null;