Ejemplo n.º 1
0
    // try splitting polygon into two and triangulate them independently
    static void SplitEarcut(nativeLL aLL, int startID, NativeList <int> triangles, double minX, double minY, double invSize)
    {
        // look for a valid diagonal that divides the polygon into two
        int  aID = startID;
        int  aNextID, bID, aPrevID, cID, cNextID;
        Node a, b;

        do
        {
            aPrevID = aLL.PrevLists[aID];
            aNextID = aLL.NextLists[aID];
            bID     = aLL.NextLists[aNextID];

            while (bID != aPrevID)
            {
                a = aLL.NodeLists[aID];
                b = aLL.NodeLists[bID];
                if (a.i != b.i && IsValidDiagonal(aLL, aLL, aID, bID))
                {
                    // split the polygon in two by the diagonal
                    cID = SplitPolygon(aLL, aLL, aID, bID);

                    // filter colinear points around the cuts
                    aNextID = aLL.NextLists[aID];
                    aID     = FilterPoints(aLL, aID, aNextID);

                    cNextID = aLL.NextLists[cID];
                    cID     = FilterPoints(aLL, cID, cNextID);

                    // run earcut on each half
                    EarcutLinked(aLL, aID, triangles, minX, minY, invSize);
                    EarcutLinked(aLL, cID, triangles, minX, minY, invSize);
                    return;
                }
                bID = aLL.NextLists[bID];
            }
            aID = aLL.NextLists[aID];
        } while (aID != startID);
    }
Ejemplo n.º 2
0
    // interlink polygon nodes in z-order
    static void IndexCurve(nativeLL aLL, int startID, double minX, double minY, double invSize)
    {
        int  pID = startID;
        Node p;

        do
        {
            p = aLL.NodeLists[pID];
            if (p.z == -1)
            {
                p.z = ZOrder(p.x, p.y, minX, minY, invSize);
                aLL.NodeLists[pID] = p;
            }
            aLL.PrevZLists[pID] = aLL.PrevLists[pID];
            aLL.NextZLists[pID] = aLL.NextLists[pID];
            pID = aLL.NextLists[pID];
        } while (pID != startID);
        int pPrevZID = aLL.PrevZLists[pID];

        aLL.NextZLists[pPrevZID] = -1;
        aLL.PrevZLists[pID]      = -1;
        SortLinked(aLL, pID);
    }
Ejemplo n.º 3
0
    // check whether a polygon node forms a valid ear with adjacent nodes
    static bool IsEar(nativeLL aLL, int earID)
    {
        int prevID = aLL.PrevLists[earID];
        int nextID = aLL.NextLists[earID];


        Node a = aLL.NodeLists[prevID];
        Node b = aLL.NodeLists[earID];
        Node c = aLL.NodeLists[nextID];

        if (Area(a, b, c) >= 0)
        {
            return(false); // reflex, can't be an ear
        }
        // now make sure we don't have other points inside the potential ear
        int pID     = aLL.NextLists[nextID];
        int pPrevID = aLL.PrevLists[pID];
        int pNextID;

        Node p, pPrev, pNext;

        while (pID != prevID)
        {
            p       = aLL.NodeLists[pID];
            pNextID = aLL.NextLists[pID];
            pPrevID = aLL.PrevLists[pID];
            pPrev   = aLL.NodeLists[pPrevID];
            pNext   = aLL.NodeLists[pNextID];
            if (PointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
                Area(pPrev, p, pNext) >= 0)
            {
                return(false);
            }
            pID = aLL.NextLists[pID];
        }
        return(true);
    }
Ejemplo n.º 4
0
    // 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 ring
    static int SplitPolygon(nativeLL aLL, nativeLL bLL, int aID, int bID)
    {
        Node a2 = aLL.NodeLists[aID];
        Node b2 = bLL.NodeLists[bID];
        Node holeNode;
        int  right = aID;
        int  aNextID = aLL.NextLists[aID];
        int  bPrevID = bLL.PrevLists[bID];
        int  a2ID, b2ID;

        if (Equals(aLL.NodeLists, bLL.NodeLists)) ////split Polygon
        {
            //first Polygon
            aLL.NextLists[aID] = bID;
            aLL.PrevLists[bID] = aID;
            //second Polygon
            b2ID = AddNodeAfter(aLL, b2, bPrevID);
            a2ID = AddNodeBefore(aLL, a2, aNextID);
            aLL.PrevLists[a2ID] = b2ID;
            aLL.NextLists[b2ID] = a2ID;
            right = b2ID;
        }
        else //merge Polygon
        {
            int stop = bID;
            do
            {
                holeNode = bLL.NodeLists[bID];
                InsertNode(aLL, aNextID, holeNode);
                bID = bLL.NextLists[bID];
            } while (bID != stop);
            right = InsertNode(aLL, aNextID, b2);
            InsertNode(aLL, aNextID, a2);
        }

        return(right);
    }
Ejemplo n.º 5
0
    // Simon Tatham's linked list merge sort algorithm
    // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
    static int SortLinked(nativeLL aLL, int headID)
    {
        int i;
        int pID, pNextZID, qID, qNextZID, eID, tailID;

        qNextZID = -1;
        Node p;
        Node q;
        int  numMerges;
        int  pSize;
        int  qSize;
        int  inSize = 1;

        do
        {
            pID       = headID;
            headID    = -1;
            tailID    = -1;
            numMerges = 0;

            while (pID != -1)
            {
                numMerges++;
                qID   = pID;
                pSize = 0;
                for (i = 0; i < inSize; i++)
                {
                    pSize++;
                    qID = aLL.NextZLists[qID];
                    if (qID == -1)
                    {
                        break;
                    }
                }
                qSize = inSize;

                while (pSize > 0 || (qSize > 0 && qID != -1))
                {
                    if (qID != -1)
                    {
                        q        = aLL.NodeLists[qID];
                        qNextZID = aLL.NextZLists[qID];
                    }
                    else
                    {
                        q = default;
                    }
                    p        = aLL.NodeLists[pID];
                    pNextZID = aLL.NextZLists[pID];
                    if (pSize != 0 && (qSize == 0 || qID == -1 || p.z <= q.z))
                    {
                        eID = pID;
                        pID = pNextZID;
                        pSize--;
                    }
                    else
                    {
                        eID = qID;
                        qID = qNextZID;
                        qSize--;
                    }

                    if (tailID != -1)
                    {
                        aLL.NextZLists[tailID] = eID;
                    }
                    else
                    {
                        headID = eID;
                    }
                    aLL.PrevZLists[eID] = tailID;
                    tailID = eID;
                }
                pID = qID;
            }
            if (tailID != -1)
            {
                aLL.NextZLists[tailID] = -1;
            }
            inSize *= 2;
        } while (numMerges > 1);
        return(headID);
    }
Ejemplo n.º 6
0
    // David Eberly's algorithm for finding a bridge between hole and outer polygon
    static int FindHoleBridge(nativeLL oLL, nativeLL hLL, int holeLeftmostID, int outerNodeID)
    {
        int  nextID;
        int  pID = outerNodeID;
        Node p, pNext, m;
        var  hx  = hLL.NodeLists[holeLeftmostID].x;
        var  hy  = hLL.NodeLists[holeLeftmostID].y;
        var  qx  = double.NegativeInfinity;
        int  mID = -1;

        // 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
        {
            p      = oLL.NodeLists[pID];
            nextID = oLL.NextLists[pID];
            pNext  = oLL.NodeLists[nextID];
            if (p.i == 8511)
            {
            }

            //When testing outer polygone p clockwise, then first bridge candiate to hole h has py < hy and next py > hy
            //otherwise py > hy and next py < hy. Will not happen, as LinkedList is constructed such that outer polygone = clockwise
            if (hy >= p.y && hy <= pNext.y && pNext.y != p.y)
            {
                var x = p.x + (hy - p.y) * (pNext.x - p.x) / (pNext.y - p.y);
                if (x <= hx && x > qx)
                {
                    qx = x;
                    if (x == hx)
                    {
                        if (hy == p.y)
                        {
                            return(pID);
                        }

                        if (hy == pNext.y)
                        {
                            return(nextID);
                        }
                    }
                    mID = p.x < pNext.x ? pID : nextID;
                }
            }
            pID = oLL.NextLists[pID];
        } while (pID != outerNodeID);


        if (mID == -1)
        {
            return(-1);
        }

        if (hx == qx)
        {
            return(mID); // hole touches outer segment; pick leftmost endpoint
        }

        // look for points 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 stop = mID;

        m = oLL.NodeLists[mID];
        var    mx     = m.x;
        var    my     = m.y;
        var    tanMin = double.PositiveInfinity;
        double tan;

        pID = mID;

        do
        {
            p = oLL.NodeLists[pID];
            m = oLL.NodeLists[mID];
            if (hx >= p.x && p.x >= mx && hx != p.x && PointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y))
            {
                tan = math.abs(hy - p.y) / (hx - p.x); // tangential

                if (LocallyInside(oLL, hLL, pID, holeLeftmostID) && (tan < tanMin || (tan == tanMin && p.x > m.x || (p.x == m.x && sectorContainsSector(oLL, oLL, mID, pID)))))
                {
                    mID    = pID;
                    tanMin = tan;
                }
            }

            pID = oLL.NextLists[pID];
        } while (pID != stop);

        return(mID);
    }
Ejemplo n.º 7
0
    // link every hole into the outer loop, producing a single-ring polygon without holes
    static int EliminateHoles(nativeLL oLL, NativeArray <float2> data, NativeArray <int> holeIndices)
    {
        NativeList <PolygonPOI> queue = new NativeList <PolygonPOI>(Allocator.Temp);
        int    outerNode = 0;
        float2 leftmost  = new float2();

        var len = holeIndices.Length;

        for (var i = 0; i < len; i++)
        {
            var start      = holeIndices[i];
            var end        = i < len - 1 ? holeIndices[i + 1] : data.Length;
            int leftMostID = 0;
            leftmost = data[start];
            for (int k = start; k < end; k++)
            {
                if (data[k].x < leftmost.x || (data[k].x == leftmost.x && data[k].y < leftmost.y))
                {
                    leftMostID = k - start;
                    leftmost   = data[k];
                }
            }
            queue.Add(new PolygonPOI(new Node(leftMostID + start, leftmost.x, leftmost.y), leftMostID, i));
        }
        CompareX compareX = new CompareX();

        queue.Sort(compareX);

        nativeLL hLL = new nativeLL();

        hLL.NodeLists  = new NativeList <Node>(Allocator.Temp);
        hLL.PrevLists  = new NativeList <int>(Allocator.Temp);
        hLL.NextLists  = new NativeList <int>(Allocator.Temp);
        hLL.PrevZLists = new NativeList <int>(Allocator.Temp);
        hLL.NextZLists = new NativeList <int>(Allocator.Temp);

        // process holes from left to right
        for (var i = 0; i < queue.Length; i++)
        {
            int start = holeIndices[queue[i].PolygonID];
            var end   = queue[i].PolygonID < holeIndices.Length - 1 ? holeIndices[queue[i].PolygonID + 1] : data.Length;
            if (queue[i].PolygonID >= holeIndices.Length - 1)
            {
            }
            var firstNode = LinkedList(hLL, data, start, end, false);
            int nextID    = hLL.NextLists[firstNode];
            if (firstNode == nextID)
            {
                var temp = hLL.NodeLists[firstNode];
                temp.steiner             = true;
                hLL.NodeLists[firstNode] = temp;
            }
            var test1 = queue[i].poiID;
            EliminateHole(oLL, hLL, test1, outerNode);

            int outerNodeNextID = oLL.NextLists[outerNode];
            outerNode = FilterPoints(oLL, outerNode, outerNodeNextID);
            hLL.NodeLists.Clear();
            hLL.PrevLists.Clear();
            hLL.NextLists.Clear();
        }
        return(outerNode);
    }
Ejemplo n.º 8
0
    public static void Tessellate(NativeArray <float2> data, NativeArray <int> holeIndices, NativeList <int> triangles, NativeArray <float3> normals)
    {
        bool hasHoles = holeIndices.Length > 0;
        int  outerLen = hasHoles ? holeIndices[0] : data.Length;

        nativeLL oLL = new nativeLL();

        oLL.NodeLists  = new NativeList <Node>(Allocator.Temp);
        oLL.PrevLists  = new NativeList <int>(Allocator.Temp);
        oLL.NextLists  = new NativeList <int>(Allocator.Temp);
        oLL.PrevZLists = new NativeList <int>(Allocator.Temp);
        oLL.NextZLists = new NativeList <int>(Allocator.Temp);

        var outerNode = LinkedList(oLL, data, 0, outerLen, true);

        int prev = oLL.PrevLists[outerNode];
        int next = oLL.NextLists[outerNode];

        if (Equals(oLL.NodeLists[next], oLL.NodeLists[prev]))
        {
            return;
        }

        var minX    = double.PositiveInfinity;
        var minY    = double.PositiveInfinity;
        var maxX    = double.NegativeInfinity;
        var maxY    = double.NegativeInfinity;
        var invSize = default(double);

        if (hasHoles)
        {
            outerNode = EliminateHoles(oLL, data, holeIndices);
        }
        // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
        if (data.Length > 80)
        {
            double x, y;
            for (int i = 0; i < outerLen; i++)
            {
                x = data[i].x;
                y = data[i].y;
                if (x < minX)
                {
                    minX = x;
                }
                if (y < minY)
                {
                    minY = y;
                }
                if (x > maxX)
                {
                    maxX = x;
                }
                if (y > maxY)
                {
                    maxY = y;
                }
            }

            // minX, minY and invSize are later used to transform coords into integers for z-order calculation
            invSize = math.max(maxX - minX, maxY - minY);
            invSize = invSize != 0 ? 1 / invSize : 0;
        }

        EarcutLinked(oLL, outerNode, triangles, minX, minY, invSize, 0);
        CalculateNormals(data, triangles, normals);
        return;
    }
Ejemplo n.º 9
0
    static bool IsEarHashed(nativeLL aLL, int earID, double minX, double minY, double invSize)
    {
        int prevID = aLL.PrevLists[earID];
        int nextID = aLL.NextLists[earID];

        Node a = aLL.NodeLists[prevID];
        Node b = aLL.NodeLists[earID];
        Node c = aLL.NodeLists[nextID];

        if (Area(a, b, c) >= 0)
        {
            return(false); // reflex, can't be an ear
        }

        // triangle bbox; min & max are calculated like this for speed
        var minTX = a.x < b.x ? (a.x < c.x ? a.x : c.x) : (b.x < c.x ? b.x : c.x);
        var minTY = a.y < b.y ? (a.y < c.y ? a.y : c.y) : (b.y < c.y ? b.y : c.y);
        var maxTX = a.x > b.x ? (a.x > c.x ? a.x : c.x) : (b.x > c.x ? b.x : c.x);
        var 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;
        var minZ = ZOrder(minTX, minTY, minX, minY, invSize);
        var maxZ = ZOrder(maxTX, maxTY, minX, minY, invSize);

        // look for points inside the triangle in both directions
        int  pPrevID, pNextID, nPrevID, nNextID;
        Node p, pPrev, pNext, n, nPrev, nNext;

        int pID = aLL.PrevZLists[earID];
        int nID = aLL.NextZLists[earID];

        if (nID != -1)
        {
            n = aLL.NodeLists[nID];
        }
        else
        {
            n = default;
        }
        if (pID != -1)
        {
            p = aLL.NodeLists[pID];
        }
        else
        {
            p = default;
        }

        while (pID != -1 && p.z >= minZ && nID != -1 && n.z <= maxZ)
        {
            pPrevID = aLL.PrevLists[pID];
            pNextID = aLL.NextLists[pID];
            pPrev   = aLL.NodeLists[pPrevID];
            pNext   = aLL.NodeLists[pNextID];

            if (!Equals(p, a) && !Equals(p, c) &&
                PointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
                Area(pPrev, p, pNext) >= 0)
            {
                return(false);
            }
            pID = aLL.PrevZLists[pID];
            if (pID != -1)
            {
                p = aLL.NodeLists[pID];
            }

            nPrevID = aLL.PrevLists[nID];
            nNextID = aLL.NextLists[nID];
            nPrev   = aLL.NodeLists[nPrevID];
            nNext   = aLL.NodeLists[nNextID];
            if (!Equals(n, a) && !Equals(n, c) &&
                PointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) &&
                Area(nPrev, n, nNext) >= 0)
            {
                return(false);
            }
            nID = aLL.NextZLists[nID];
            if (nID != -1)
            {
                n = aLL.NodeLists[nID];
            }
        }


        // look for remaining points in decreasing z-order
        while (pID != -1 && p.z >= minZ)
        {
            pPrevID = aLL.PrevLists[pID];
            pNextID = aLL.NextLists[pID];
            pPrev   = aLL.NodeLists[pPrevID];
            pNext   = aLL.NodeLists[pNextID];
            if (!Equals(p, a) && !Equals(p, c) &&
                PointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
                Area(pPrev, p, pNext) >= 0)
            {
                return(false);
            }
            pID = aLL.PrevZLists[pID];
            if (pID != -1)
            {
                p = aLL.NodeLists[pID];
            }
        }

        // look for remaining points in increasing z-order
        while (nID != -1 && n.z <= maxZ)
        {
            nPrevID = aLL.PrevLists[nID];
            nNextID = aLL.NextLists[nID];
            nPrev   = aLL.NodeLists[nPrevID];
            nNext   = aLL.NodeLists[nNextID];

            if (!Equals(n, a) && !Equals(n, c) &&
                PointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) &&
                Area(nPrev, n, nNext) >= 0)
            {
                return(false);
            }
            nID = aLL.NextZLists[nID];
            if (nID != -1)
            {
                n = aLL.NodeLists[nID];
            }
        }


        return(true);
    }
Ejemplo n.º 10
0
    // main ear slicing loop which triangulates a polygon (given as a linked list)
    static void EarcutLinked(nativeLL aLL, int earID, NativeList <int> triangles, double minX, double minY, double invSize, int pass = 0)
    {
        if (earID == -1)
        {
            return;
        }

        // interlink polygon nodes in z-order
        if (pass == 0 && invSize != 0)
        {
            IndexCurve(aLL, earID, minX, minY, invSize);
        }

        var  stopID = earID;
        Node ear, prev, next;
        int  earNextID = aLL.NextLists[earID];
        int  earPrevID = aLL.PrevLists[earID];

        // iterate through ears, slicing them one by one
        while (earPrevID != earNextID)
        {
            ear       = aLL.NodeLists[earID];
            earNextID = aLL.NextLists[earID];
            earPrevID = aLL.PrevLists[earID];
            prev      = aLL.NodeLists[earPrevID];
            next      = aLL.NodeLists[earNextID];

            if (invSize != 0 ? IsEarHashed(aLL, earID, minX, minY, invSize) : IsEar(aLL, earID))
            {
                // cut off the triangle
                triangles.Add(prev.i);
                triangles.Add(ear.i);
                triangles.Add(next.i);

                RemoveNode(aLL, earID);

                // skipping the next vertex leads to less sliver triangles
                earID     = aLL.NextLists[earNextID];
                stopID    = aLL.NextLists[earNextID];
                earNextID = aLL.NextLists[earID];
                earPrevID = aLL.PrevLists[earID];

                continue;
            }
            earID     = earNextID;
            earNextID = aLL.NextLists[earID];
            earPrevID = aLL.PrevLists[earID];

            // if we looped through the whole remaining polygon and can't find any more ears
            if (earID == stopID)
            {
                // try filtering points and slicing again
                if (pass == 0)
                {
                    EarcutLinked(aLL, FilterPoints(aLL, earID), triangles, minX, minY, invSize, 1);

                    // if this didn't work, try curing all small self-intersections locally
                }
                else if (pass == 1)
                {
                    EarcutLinked(aLL, earID, triangles, minX, minY, invSize, 2);

                    // as a last resort, try splitting the remaining polygon into two
                }
                else if (pass == 2)
                {
                    SplitEarcut(aLL, earID, triangles, minX, minY, invSize);
                }

                break;
            }
        }
    }