예제 #1
0
파일: Delaunay.cs 프로젝트: mewbak/psxdev
    private void Analyse(List <Vertex> suppliedPoints, Hull hull, List <Triad> triads, bool rejectDuplicatePoints, bool hullOnly)
    {
        if (suppliedPoints.Count < 3)
        {
            throw new ArgumentException("Number of points supplied must be >= 3");
        }

        this.points = suppliedPoints;
        int nump = points.Count;

        float[] distance2ToCentre = new float[nump];
        int[]   sortedIndices     = new int[nump];

        // Choose first point as the seed
        for (int k = 0; k < nump; k++)
        {
            distance2ToCentre[k] = points[0].distance2To(points[k]);
            sortedIndices[k]     = k;
        }

        // Sort by distance to seed point
        Array.Sort(distance2ToCentre, sortedIndices);

        // Duplicates are more efficiently rejected now we have sorted the vertices
        if (rejectDuplicatePoints)
        {
            // Search backwards so each removal is independent of any other
            for (int k = nump - 2; k >= 0; k--)
            {
                // If the points are identical then their distances will be the same,
                // so they will be adjacent in the sorted list
                if ((points[sortedIndices[k]].x == points[sortedIndices[k + 1]].x) &&
                    (points[sortedIndices[k]].y == points[sortedIndices[k + 1]].y))
                {
                    // Duplicates are expected to be rare, so this is not particularly efficient
                    Array.Copy(sortedIndices, k + 2, sortedIndices, k + 1, nump - k - 2);
                    Array.Copy(distance2ToCentre, k + 2, distance2ToCentre, k + 1, nump - k - 2);
                    nump--;
                }
            }
        }

        //Console.WriteLine((points.Count - nump).ToString() + " duplicate points rejected");

        if (nump < 3)
        {
            throw new ArgumentException("Number of unique points supplied must be >= 3");
        }

        int   mid = -1;
        float romin2 = float.MaxValue, circumCentreX = 0, circumCentreY = 0;

        // Find the point which, with the first two points, creates the triangle with the smallest circumcircle
        Triad tri = new Triad(sortedIndices[0], sortedIndices[1], 2);

        for (int kc = 2; kc < nump; kc++)
        {
            tri.c = sortedIndices[kc];
            if (tri.FindCircumcirclePrecisely(points) && tri.circumcircleR2 < romin2)
            {
                mid = kc;
                // Centre of the circumcentre of the seed triangle
                romin2        = tri.circumcircleR2;
                circumCentreX = tri.circumcircleX;
                circumCentreY = tri.circumcircleY;
            }
            else if (romin2 * 4 < distance2ToCentre[kc])
            {
                break;
            }
        }

        // Change the indices, if necessary, to make the 2th point produce the smallest circumcircle with the 0th and 1th
        if (mid != 2)
        {
            int   indexMid     = sortedIndices[mid];
            float distance2Mid = distance2ToCentre[mid];

            Array.Copy(sortedIndices, 2, sortedIndices, 3, mid - 2);
            Array.Copy(distance2ToCentre, 2, distance2ToCentre, 3, mid - 2);
            sortedIndices[2]     = indexMid;
            distance2ToCentre[2] = distance2Mid;
        }

        // These three points are our seed triangle
        tri.c = sortedIndices[2];
        tri.MakeClockwise(points);
        tri.FindCircumcirclePrecisely(points);

        // Add tri as the first triad, and the three points to the convex hull
        triads.Add(tri);
        hull.Add(new HullVertex(points, tri.a));
        hull.Add(new HullVertex(points, tri.b));
        hull.Add(new HullVertex(points, tri.c));

        // Sort the remainder according to their distance from its centroid
        // Re-measure the points' distances from the centre of the circumcircle
        Vertex centre = new Vertex(circumCentreX, circumCentreY);

        for (int k = 3; k < nump; k++)
        {
            distance2ToCentre[k] = points[sortedIndices[k]].distance2To(centre);
        }

        // Sort the _other_ points in order of distance to circumcentre
        Array.Sort(distance2ToCentre, sortedIndices, 3, nump - 3);

        // Add new points into hull (removing obscured ones from the chain)
        // and creating triangles....
        int numt = 0;

        for (int k = 3; k < nump; k++)
        {
            int        pointsIndex = sortedIndices[k];
            HullVertex ptx         = new HullVertex(points, pointsIndex);

            float dx = ptx.x - hull[0].x, dy = ptx.y - hull[0].y;  // outwards pointing from hull[0] to pt.

            int        numh = hull.Count, numh_old = numh;
            List <int> pidx = new List <int>(), tridx = new List <int>();
            int        hidx; // new hull point location within hull.....

            if (hull.EdgeVisibleFrom(0, dx, dy))
            {
                // starting with a visible hull facet !!!
                int e2 = numh;
                hidx = 0;

                // check to see if segment numh is also visible
                if (hull.EdgeVisibleFrom(numh - 1, dx, dy))
                {
                    // visible.
                    pidx.Add(hull[numh - 1].pointsIndex);
                    tridx.Add(hull[numh - 1].triadIndex);

                    for (int h = 0; h < numh - 1; h++)
                    {
                        // if segment h is visible delete h
                        pidx.Add(hull[h].pointsIndex);
                        tridx.Add(hull[h].triadIndex);
                        if (hull.EdgeVisibleFrom(h, ptx))
                        {
                            hull.RemoveAt(h);
                            h--;
                            numh--;
                        }
                        else
                        {
                            // quit on invisibility
                            hull.Insert(0, ptx);
                            numh++;
                            break;
                        }
                    }
                    // look backwards through the hull structure
                    for (int h = numh - 2; h > 0; h--)
                    {
                        // if segment h is visible delete h + 1
                        if (hull.EdgeVisibleFrom(h, ptx))
                        {
                            pidx.Insert(0, hull[h].pointsIndex);
                            tridx.Insert(0, hull[h].triadIndex);
                            hull.RemoveAt(h + 1);  // erase end of chain
                        }
                        else
                        {
                            break; // quit on invisibility
                        }
                    }
                }
                else
                {
                    hidx = 1;  // keep pt hull[0]
                    tridx.Add(hull[0].triadIndex);
                    pidx.Add(hull[0].pointsIndex);

                    for (int h = 1; h < numh; h++)
                    {
                        // if segment h is visible delete h
                        pidx.Add(hull[h].pointsIndex);
                        tridx.Add(hull[h].triadIndex);
                        if (hull.EdgeVisibleFrom(h, ptx))
                        {                     // visible
                            hull.RemoveAt(h);
                            h--;
                            numh--;
                        }
                        else
                        {
                            // quit on invisibility
                            hull.Insert(h, ptx);
                            break;
                        }
                    }
                }
            }
            else
            {
                int e1 = -1, e2 = numh;
                for (int h = 1; h < numh; h++)
                {
                    if (hull.EdgeVisibleFrom(h, ptx))
                    {
                        if (e1 < 0)
                        {
                            e1 = h;  // first visible
                        }
                    }
                    else
                    {
                        if (e1 > 0)
                        {
                            // first invisible segment.
                            e2 = h;
                            break;
                        }
                    }
                }

                // triangle pidx starts at e1 and ends at e2 (inclusive).
                if (e2 < numh)
                {
                    for (int e = e1; e <= e2; e++)
                    {
                        pidx.Add(hull[e].pointsIndex);
                        tridx.Add(hull[e].triadIndex);
                    }
                }
                else
                {
                    for (int e = e1; e < e2; e++)
                    {
                        pidx.Add(hull[e].pointsIndex);
                        tridx.Add(hull[e].triadIndex);   // there are only n-1 triangles from n hull pts.
                    }
                    pidx.Add(hull[0].pointsIndex);
                }

                // erase elements e1+1 : e2-1 inclusive.
                if (e1 < e2 - 1)
                {
                    hull.RemoveRange(e1 + 1, e2 - e1 - 1);
                }

                // insert ptx at location e1+1.
                hull.Insert(e1 + 1, ptx);
                hidx = e1 + 1;
            }

            // If we're only computing the hull, we're done with this point
            if (hullOnly)
            {
                continue;
            }

            int a = pointsIndex, T0;

            int npx = pidx.Count - 1;
            numt = triads.Count;
            T0   = numt;

            for (int p = 0; p < npx; p++)
            {
                Triad trx = new Triad(a, pidx[p], pidx[p + 1]);
                trx.FindCircumcirclePrecisely(points);

                trx.bc = tridx[p];
                if (p > 0)
                {
                    trx.ab = numt - 1;
                }
                trx.ac = numt + 1;

                // index back into the triads.
                Triad txx = triads[tridx[p]];
                if ((trx.b == txx.a && trx.c == txx.b) | (trx.b == txx.b && trx.c == txx.a))
                {
                    txx.ab = numt;
                }
                else if ((trx.b == txx.a && trx.c == txx.c) | (trx.b == txx.c && trx.c == txx.a))
                {
                    txx.ac = numt;
                }
                else if ((trx.b == txx.b && trx.c == txx.c) | (trx.b == txx.c && trx.c == txx.b))
                {
                    txx.bc = numt;
                }

                triads.Add(trx);
                numt++;
            }
            // Last edge is on the outside
            triads[numt - 1].ac = -1;

            hull[hidx].triadIndex = numt - 1;
            if (hidx > 0)
            {
                hull[hidx - 1].triadIndex = T0;
            }
            else
            {
                numh = hull.Count;
                hull[numh - 1].triadIndex = T0;
            }
        }
    }