| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674 | /** * * Earcut https://github.com/mapbox/earcut * * Copyright (c) 2015, Mapbox * * 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 * and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * 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 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF * THIS SOFTWARE. */'use strict';//module.exports = earcut;function earcut(data, holeIndices, dim) {    dim = dim || 2;    var hasHoles = holeIndices && holeIndices.length,        outerLen = hasHoles ? holeIndices[0] * dim : data.length,        outerNode = filterPoints(data, linkedList(data, 0, outerLen, dim, true)),        triangles = [];    if (!outerNode) return triangles;    var minX, minY, maxX, maxY, x, y, size;    if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);    // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox    if (data.length > 80 * dim) {        minX = maxX = data[0];        minY = maxY = data[1];        for (var i = dim; i < outerLen; i += dim) {            x = data[i];            y = data[i + 1];            if (x < minX) minX = x;            if (y < minY) minY = y;            if (x > maxX) maxX = x;            if (y > maxY) maxY = y;        }        // minX, minY and size are later used to transform coords into integers for z-order calculation        size = Math.max(maxX - minX, maxY - minY);    }    earcutLinked(data, outerNode, triangles, dim, minX, minY, size);    return triangles;}// create a circular doubly linked list from polygon points in the specified winding orderfunction linkedList(data, start, end, dim, clockwise) {    var sum = 0,        i, j, last;    // calculate original winding order of a polygon ring    for (i = start, j = end - dim; i < end; i += dim) {        sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);        j = i;    }    // link points into circular doubly-linked list in the specified winding order    if (clockwise === (sum > 0)) {        for (i = start; i < end; i += dim) last = insertNode(i, last);    } else {        for (i = end - dim; i >= start; i -= dim) last = insertNode(i, last);    }    return last;}// eliminate colinear or duplicate pointsfunction filterPoints(data, start, end) {    if (!start) return start;    if (!end) end = start;    var node = start,        again;    do {        again = false;        if (!node.steiner && (equals(data, node.i, node.next.i) || orient(data, node.prev.i, node.i, node.next.i) === 0)) {            removeNode(node);            node = end = node.prev;            if (node === node.next) return null;            again = true;        } else {            node = node.next;        }    } while (again || node !== end);    return end;}// main ear slicing loop which triangulates a polygon (given as a linked list)function earcutLinked(data, ear, triangles, dim, minX, minY, size, pass) {    if (!ear) return;    // interlink polygon nodes in z-order    if (!pass && minX !== undefined) indexCurve(data, ear, minX, minY, size);    var stop = ear,        prev, next;    // iterate through ears, slicing them one by one    while (ear.prev !== ear.next) {        prev = ear.prev;        next = ear.next;        if (isEar(data, ear, minX, minY, size)) {            // cut off the triangle            triangles.push(prev.i / dim);            triangles.push(ear.i / dim);            triangles.push(next.i / dim);            removeNode(ear);            // skipping the next vertice leads to less sliver triangles            ear = next.next;            stop = next.next;            continue;        }        ear = next;        // if we looped through the whole remaining polygon and can't find any more ears        if (ear === stop) {            // try filtering points and slicing again            if (!pass) {                earcutLinked(data, filterPoints(data, ear), triangles, dim, minX, minY, size, 1);            // if this didn't work, try curing all small self-intersections locally            } else if (pass === 1) {                ear = cureLocalIntersections(data, ear, triangles, dim);                earcutLinked(data, ear, triangles, dim, minX, minY, size, 2);            // as a last resort, try splitting the remaining polygon into two            } else if (pass === 2) {                splitEarcut(data, ear, triangles, dim, minX, minY, size);            }            break;        }    }}// check whether a polygon node forms a valid ear with adjacent nodesfunction isEar(data, ear, minX, minY, size) {    var a = ear.prev.i,        b = ear.i,        c = ear.next.i,        ax = data[a], ay = data[a + 1],        bx = data[b], by = data[b + 1],        cx = data[c], cy = data[c + 1],        abd = ax * by - ay * bx,        acd = ax * cy - ay * cx,        cbd = cx * by - cy * bx,        A = abd - acd - cbd;    if (A <= 0) return false; // reflex, can't be an ear    // now make sure we don't have other points inside the potential ear;    // the code below is a bit verbose and repetitive but this is done for performance    var cay = cy - ay,        acx = ax - cx,        aby = ay - by,        bax = bx - ax,        i, px, py, s, t, k, node;    // if we use z-order curve hashing, iterate through the curve    if (minX !== undefined) {        // triangle bbox; min & max are calculated like this for speed        var minTX = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx),            minTY = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy),            maxTX = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx),            maxTY = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy),            // z-order range for the current triangle bbox;            minZ = zOrder(minTX, minTY, minX, minY, size),            maxZ = zOrder(maxTX, maxTY, minX, minY, size);        // first look for points inside the triangle in increasing z-order        node = ear.nextZ;        while (node && node.z <= maxZ) {            i = node.i;            node = node.nextZ;            if (i === a || i === c) continue;            px = data[i];            py = data[i + 1];            s = cay * px + acx * py - acd;            if (s >= 0) {                t = aby * px + bax * py + abd;                if (t >= 0) {                    k = A - s - t;                    if ((k >= 0) && ((s && t) || (s && k) || (t && k))) return false;                }            }        }        // then look for points in decreasing z-order        node = ear.prevZ;        while (node && node.z >= minZ) {            i = node.i;            node = node.prevZ;            if (i === a || i === c) continue;            px = data[i];            py = data[i + 1];            s = cay * px + acx * py - acd;            if (s >= 0) {                t = aby * px + bax * py + abd;                if (t >= 0) {                    k = A - s - t;                    if ((k >= 0) && ((s && t) || (s && k) || (t && k))) return false;                }            }        }    // if we don't use z-order curve hash, simply iterate through all other points    } else {        node = ear.next.next;        while (node !== ear.prev) {            i = node.i;            node = node.next;            px = data[i];            py = data[i + 1];            s = cay * px + acx * py - acd;            if (s >= 0) {                t = aby * px + bax * py + abd;                if (t >= 0) {                    k = A - s - t;                    if ((k >= 0) && ((s && t) || (s && k) || (t && k))) return false;                }            }        }    }    return true;}// go through all polygon nodes and cure small local self-intersectionsfunction cureLocalIntersections(data, start, triangles, dim) {    var node = start;    do {        var a = node.prev,            b = node.next.next;        // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2])        if (a.i !== b.i && intersects(data, a.i, node.i, node.next.i, b.i) &&                locallyInside(data, a, b) && locallyInside(data, b, a) &&                orient(data, a.i, node.i, b.i) && orient(data, a.i, node.next.i, b.i)) {            triangles.push(a.i / dim);            triangles.push(node.i / dim);            triangles.push(b.i / dim);            // remove two nodes involved            removeNode(node);            removeNode(node.next);            node = start = b;        }        node = node.next;    } while (node !== start);    return node;}// try splitting polygon into two and triangulate them independentlyfunction splitEarcut(data, start, triangles, dim, minX, minY, size) {    // look for a valid diagonal that divides the polygon into two    var a = start;    do {        var b = a.next.next;        while (b !== a.prev) {            if (a.i !== b.i && isValidDiagonal(data, a, b)) {                // split the polygon in two by the diagonal                var c = splitPolygon(a, b);                // filter colinear points around the cuts                a = filterPoints(data, a, a.next);                c = filterPoints(data, c, c.next);                // run earcut on each half                earcutLinked(data, a, triangles, dim, minX, minY, size);                earcutLinked(data, c, triangles, dim, minX, minY, size);                return;            }            b = b.next;        }        a = a.next;    } while (a !== start);}// link every hole into the outer loop, producing a single-ring polygon without holesfunction eliminateHoles(data, holeIndices, outerNode, dim) {    var queue = [],        i, len, start, end, list;    for (i = 0, len = holeIndices.length; i < len; i++) {        start = holeIndices[i] * dim;        end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;        list = linkedList(data, start, end, dim, false);        if (list === list.next) list.steiner = true;        list = filterPoints(data, list);        if (list) queue.push(getLeftmost(data, list));    }    queue.sort(function (a, b) {        return data[a.i] - data[b.i];    });    // process holes from left to right    for (i = 0; i < queue.length; i++) {        eliminateHole(data, queue[i], outerNode);        outerNode = filterPoints(data, outerNode, outerNode.next);    }    return outerNode;}// find a bridge between vertices that connects hole with an outer ring and and link itfunction eliminateHole(data, holeNode, outerNode) {    outerNode = findHoleBridge(data, holeNode, outerNode);    if (outerNode) {        var b = splitPolygon(outerNode, holeNode);        filterPoints(data, b, b.next);    }}// David Eberly's algorithm for finding a bridge between hole and outer polygonfunction findHoleBridge(data, holeNode, outerNode) {    var node = outerNode,        i = holeNode.i,        px = data[i],        py = data[i + 1],        qMax = -Infinity,        mNode, a, b;    // find a segment intersected by a ray from the hole's leftmost point to the left;    // segment's endpoint with lesser x will be potential connection point    do {        a = node.i;        b = node.next.i;        if (py <= data[a + 1] && py >= data[b + 1]) {            var qx = data[a] + (py - data[a + 1]) * (data[b] - data[a]) / (data[b + 1] - data[a + 1]);            if (qx <= px && qx > qMax) {                qMax = qx;                mNode = data[a] < data[b] ? node : node.next;            }        }        node = node.next;    } while (node !== outerNode);    if (!mNode) return null;    // look for points strictly inside the triangle of hole point, segment intersection and endpoint;    // if there are no points found, we have a valid connection;    // otherwise choose the point of the minimum angle with the ray as connection point    var bx = data[mNode.i],        by = data[mNode.i + 1],        pbd = px * by - py * bx,        pcd = px * py - py * qMax,        cpy = py - py,        pcx = px - qMax,        pby = py - by,        bpx = bx - px,        A = pbd - pcd - (qMax * by - py * bx),        sign = A <= 0 ? -1 : 1,        stop = mNode,        tanMin = Infinity,        mx, my, amx, s, t, tan;    node = mNode.next;    while (node !== stop) {        mx = data[node.i];        my = data[node.i + 1];        amx = px - mx;        if (amx >= 0 && mx >= bx) {            s = (cpy * mx + pcx * my - pcd) * sign;            if (s >= 0) {                t = (pby * mx + bpx * my + pbd) * sign;                if (t >= 0 && A * sign - s - t >= 0) {                    tan = Math.abs(py - my) / amx; // tangential                    if ((tan < tanMin || (tan === tanMin && mx > bx)) &&                            locallyInside(data, node, holeNode)) {                        mNode = node;                        tanMin = tan;                    }                }            }        }        node = node.next;    }    return mNode;}// interlink polygon nodes in z-orderfunction indexCurve(data, start, minX, minY, size) {    var node = start;    do {        if (node.z === null) node.z = zOrder(data[node.i], data[node.i + 1], minX, minY, size);        node.prevZ = node.prev;        node.nextZ = node.next;        node = node.next;    } while (node !== start);    node.prevZ.nextZ = null;    node.prevZ = null;    sortLinked(node);}// Simon Tatham's linked list merge sort algorithm// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.htmlfunction sortLinked(list) {    var i, p, q, e, tail, numMerges, pSize, qSize,        inSize = 1;    do {        p = list;        list = null;        tail = null;        numMerges = 0;        while (p) {            numMerges++;            q = p;            pSize = 0;            for (i = 0; i < inSize; i++) {                pSize++;                q = q.nextZ;                if (!q) break;            }            qSize = inSize;            while (pSize > 0 || (qSize > 0 && q)) {                if (pSize === 0) {                    e = q;                    q = q.nextZ;                    qSize--;                } else if (qSize === 0 || !q) {                    e = p;                    p = p.nextZ;                    pSize--;                } else if (p.z <= q.z) {                    e = p;                    p = p.nextZ;                    pSize--;                } else {                    e = q;                    q = q.nextZ;                    qSize--;                }                if (tail) tail.nextZ = e;                else list = e;                e.prevZ = tail;                tail = e;            }            p = q;        }        tail.nextZ = null;        inSize *= 2;    } while (numMerges > 1);    return list;}// z-order of a point given coords and size of the data bounding boxfunction zOrder(x, y, minX, minY, size) {    // coords are transformed into non-negative 15-bit integer range    x = 32767 * (x - minX) / size;    y = 32767 * (y - minY) / size;    x = (x | (x << 8)) & 0x00FF00FF;    x = (x | (x << 4)) & 0x0F0F0F0F;    x = (x | (x << 2)) & 0x33333333;    x = (x | (x << 1)) & 0x55555555;    y = (y | (y << 8)) & 0x00FF00FF;    y = (y | (y << 4)) & 0x0F0F0F0F;    y = (y | (y << 2)) & 0x33333333;    y = (y | (y << 1)) & 0x55555555;    return x | (y << 1);}// find the leftmost node of a polygon ringfunction getLeftmost(data, start) {    var node = start,        leftmost = start;    do {        if (data[node.i] < data[leftmost.i]) leftmost = node;        node = node.next;    } while (node !== start);    return leftmost;}// check if a diagonal between two polygon nodes is valid (lies in polygon interior)function isValidDiagonal(data, a, b) {    return a.next.i !== b.i && a.prev.i !== b.i &&           !intersectsPolygon(data, a, a.i, b.i) &&           locallyInside(data, a, b) && locallyInside(data, b, a) &&           middleInside(data, a, a.i, b.i);}// winding order of triangle formed by 3 given pointsfunction orient(data, p, q, r) {    var o = (data[q + 1] - data[p + 1]) * (data[r] - data[q]) - (data[q] - data[p]) * (data[r + 1] - data[q + 1]);    return o > 0 ? 1 :           o < 0 ? -1 : 0;}// check if two points are equalfunction equals(data, p1, p2) {    return data[p1] === data[p2] && data[p1 + 1] === data[p2 + 1];}// check if two segments intersectfunction intersects(data, p1, q1, p2, q2) {    return orient(data, p1, q1, p2) !== orient(data, p1, q1, q2) &&           orient(data, p2, q2, p1) !== orient(data, p2, q2, q1);}// check if a polygon diagonal intersects any polygon segmentsfunction intersectsPolygon(data, start, a, b) {    var node = start;    do {        var p1 = node.i,            p2 = node.next.i;        if (p1 !== a && p2 !== a && p1 !== b && p2 !== b && intersects(data, p1, p2, a, b)) return true;        node = node.next;    } while (node !== start);    return false;}// check if a polygon diagonal is locally inside the polygonfunction locallyInside(data, a, b) {    return orient(data, a.prev.i, a.i, a.next.i) === -1 ?        orient(data, a.i, b.i, a.next.i) !== -1 && orient(data, a.i, a.prev.i, b.i) !== -1 :        orient(data, a.i, b.i, a.prev.i) === -1 || orient(data, a.i, a.next.i, b.i) === -1;}// check if the middle point of a polygon diagonal is inside the polygonfunction middleInside(data, start, a, b) {    var node = start,        inside = false,        px = (data[a] + data[b]) / 2,        py = (data[a + 1] + data[b + 1]) / 2;    do {        var p1 = node.i,            p2 = node.next.i;        if (((data[p1 + 1] > py) !== (data[p2 + 1] > py)) &&                (px < (data[p2] - data[p1]) * (py - data[p1 + 1]) / (data[p2 + 1] - data[p1 + 1]) + data[p1]))            inside = !inside;        node = node.next;    } while (node !== start);    return inside;}// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;// if one belongs to the outer ring and another to a hole, it merges it into a single ringfunction splitPolygon(a, b) {    var a2 = new Node(a.i),        b2 = new Node(b.i),        an = a.next,        bp = b.prev;    a.next = b;    b.prev = a;    a2.next = an;    an.prev = a2;    b2.next = a2;    a2.prev = b2;    bp.next = b2;    b2.prev = bp;    return b2;}// create a node and optionally link it with previous one (in a circular doubly linked list)function insertNode(i, last) {    var node = new Node(i);    if (!last) {        node.prev = node;        node.next = node;    } else {        node.next = last.next;        node.prev = last;        last.next.prev = node;        last.next = node;    }    return node;}function removeNode(node) {    node.next.prev = node.prev;    node.prev.next = node.next;    if (node.prevZ) node.prevZ.nextZ = node.nextZ;    if (node.nextZ) node.nextZ.prevZ = node.prevZ;}function Node(i) {    // vertex coordinates    this.i = i;    // previous and next vertice nodes in a polygon ring    this.prev = null;    this.next = null;    // z-order curve value    this.z = null;    // previous and next nodes in z-order    this.prevZ = null;    this.nextZ = null;    // indicates whether this is a steiner point    this.steiner = false;}
 |