Beispiel #1
0
        private static void solve(List <Point> points, Hull hull, List <Triad> triads, bool hullOnly)
        {
            int N = points.Count;

            double[] distSqCentre = new double[N];
            int[]    sortedInd    = new int[N];
            for (int i = 0; i < N; i++)
            {
                distSqCentre[i] = points[0].distSqTo(points[i]);
                sortedInd[i]    = i;
            }
            Array.Sort(distSqCentre, sortedInd);
            int    mid = -1;
            double rmin2 = double.PositiveInfinity, circumcentreX = 0, circumcentreY = 0;
            Triad  tri = new Triad(sortedInd[0], sortedInd[1], sortedInd[2]);

            for (int i = 2; i < N; i++)
            {
                tri.c = sortedInd[i];
                if (tri.findCircumcircle(points) && tri.circumcircleR2 < rmin2)
                {
                    mid           = i;
                    rmin2         = tri.circumcircleR2;
                    circumcentreX = tri.circumcircleX;
                    circumcentreY = tri.circumcircleY;
                }
                else if (rmin2 * 4 < distSqCentre[i])
                {
                    break;
                }
            }
            if (mid != 2)
            {
                int    midInd    = sortedInd[mid];
                double distSqMid = distSqCentre[mid];
                Array.Copy(sortedInd, 2, sortedInd, 3, mid - 2);
                Array.Copy(distSqCentre, 2, distSqCentre, 3, mid - 2);
                sortedInd[2]    = midInd;
                distSqCentre[2] = distSqMid;
            }
            tri.c = sortedInd[2];
            tri.makeClockwise(points);
            tri.findCircumcircle(points);
            triads.Add(tri);
            hull.Add(new HullPoint(points, tri.a));
            hull.Add(new HullPoint(points, tri.b));
            hull.Add(new HullPoint(points, tri.c));
            Point centre = new Point(circumcentreX, circumcentreY);

            for (int i = 3; i < N; i++)
            {
                distSqCentre[i] = points[sortedInd[i]].distSqTo(centre);
            }
            Array.Sort(distSqCentre, sortedInd, 3, N - 3);
            int T = 0;

            for (int i = 3; i < N; i++)
            {
                int        pInd = sortedInd[i];
                HullPoint  p = new HullPoint(points, pInd);
                double     dx = p.x - hull[0].x, dy = p.y - hull[0].y;
                int        H = hull.Count, hInd = 0;
                List <int> pInds = new List <int>(), tInds = new List <int>();
                if (hull.edgeVisibleFrom(0, dx, dy))
                {
                    hInd = 0;
                    if (hull.edgeVisibleFrom(H - 1, dx, dy))
                    {
                        pInds.Add(hull[H - 1].pInd);
                        tInds.Add(hull[H - 1].tInd);
                        for (int h = 0; h < H - 1; h++)
                        {
                            pInds.Add(hull[h].pInd);
                            tInds.Add(hull[h].tInd);
                            if (hull.edgeVisibleFrom(h, p))
                            {
                                hull.RemoveAt(h);
                                h--;
                                H--;
                            }
                            else
                            {
                                hull.Insert(0, p);
                                H++;
                                break;
                            }
                        }
                        for (int h = H - 2; h > 0; h--)
                        {
                            if (hull.edgeVisibleFrom(h, p))
                            {
                                pInds.Insert(0, hull[h].pInd);
                                tInds.Insert(0, hull[h].tInd);
                                hull.RemoveAt(h + 1);
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        hInd = 1;
                        pInds.Add(hull[0].pInd);
                        tInds.Add(hull[0].tInd);
                        for (int h = 1; h < H; h++)
                        {
                            pInds.Add(hull[h].pInd);
                            tInds.Add(hull[h].tInd);
                            if (hull.edgeVisibleFrom(h, p))
                            {
                                hull.RemoveAt(h);
                                h--;
                                H--;
                            }
                            else
                            {
                                hull.Insert(h, p);
                                break;
                            }
                        }
                    }
                }
                else
                {
                    int e1 = -1, e2 = H;
                    for (int h = 1; h < H; h++)
                    {
                        if (hull.edgeVisibleFrom(h, p))
                        {
                            if (e1 < 0)
                            {
                                e1 = h;
                            }
                        }
                        else
                        {
                            if (e1 > 0)
                            {
                                e2 = h;
                                break;
                            }
                        }
                    }
                    if (e2 < H)
                    {
                        for (int e = e1; e <= e2; e++)
                        {
                            pInds.Add(hull[e].pInd);
                            tInds.Add(hull[e].tInd);
                        }
                    }
                    else
                    {
                        for (int e = e1; e < e2; e++)
                        {
                            pInds.Add(hull[e].pInd);
                            tInds.Add(hull[e].tInd);
                        }
                        pInds.Add(hull[0].pInd);
                    }
                    if (e1 < e2 - 1)
                    {
                        hull.RemoveRange(e1 + 1, e2 - e1 - 1);
                    }
                    hull.Insert(e1 + 1, p);
                    hInd = e1 + 1;
                }
                if (hullOnly)
                {
                    continue;
                }
                int a = pInd, T0;
                int P = pInds.Count();
                T = T0 = triads.Count();
                for (int j = 0; j < P - 1; j++)
                {
                    Triad t = new Triad(a, pInds[j], pInds[j + 1]);
                    t.findCircumcircle(points);
                    t.bc = tInds[j];
                    if (j > 0)
                    {
                        t.ab = T - 1;
                    }
                    t.ac = T + 1;
                    Triad u = triads[tInds[j]];
                    if ((t.b == u.a && t.c == u.b) || (t.b == u.b && t.c == u.a))
                    {
                        u.ab = T;
                    }
                    else if ((t.b == u.a && t.c == u.c) || (t.b == u.c && t.c == u.a))
                    {
                        u.ac = T;
                    }
                    else if ((t.b == u.b && t.c == u.c) || (t.b == u.c && t.c == u.b))
                    {
                        u.bc = T;
                    }
                    triads.Add(t);
                    T++;
                }
                triads[T - 1].ac = -1;
                hull[hInd].tInd  = T - 1;
                if (hInd > 0)
                {
                    hull[hInd - 1].tInd = T0;
                }
                else
                {
                    H = hull.Count;
                    hull[hull.Count - 1].tInd = T0;
                }
            }
        }
Beispiel #2
0
    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;
            }
        }
    }
        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--;
                    }
                }
            }

            Debug.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;
                }
            }
        }