Beispiel #1
0
        protected virtual void SetDelaunayResult(List <Triangle> delaunay)
        {
            Delaunay = delaunay;

            if (BuildDiagonstics)
            {
                foreach (var tri in Delaunay)
                {
                    var radius = Math.Sqrt(GeoMath.EuclideanDistance2(tri.Circumcenter, tri.A));
                    DiagnosticGeometry.Add(new DiagnosticGeometry(DiagGeometryType.Circle, DiagColor.Yellow, tri.Circumcenter, new Point(radius, 0)));
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Calculates Delaunay triangulation using the more efficient Sweep-Circle algorithm.
        /// Considered one of the fastest algorithms in empirical tests.
        /// Expected runtime is O(n log n).
        /// </summary>
        /// <remarks>
        /// http://cglab.ca/~biniaz/papers/Sweep%20Circle.pdf
        /// </remarks>
        public override void CalculateDelaunay()
        {
            if (!CheckDelaunayConditions())
            {
                return;
            }

            // Calculate origin point and seed triangle
            (Point origin, Triangle seed) = CalculateOriginSeedTriangle(out int i0, out int i1, out int i2);
            center = origin;

            if (i0 == -1)
            {
                SetDelaunayResult(new List <Triangle>());
                return; // No Delaunay triangulation exists
            }

            var len = Points.Length;

            polarPoints = new PolarPoint[len];

            var seed1 = new PolarPoint();
            var seed2 = new PolarPoint();
            var seed3 = new PolarPoint();

            for (var i = 0; i < len; i++)
            {
                var p  = Points[i];
                var pp = new PolarPoint(
                    p,
                    GeoMath.EuclideanDistance2(center, p),
                    GeoMath.PolarPseudoAngle(center, p)
                    );

                if (p.Equals(seed.A))
                {
                    seed1 = pp;
                }
                else if (p.Equals(seed.B))
                {
                    seed2 = pp;
                }
                else if (p.Equals(seed.C))
                {
                    seed3 = pp;
                }

                polarPoints[i] = pp;
            }

            // Sort polar points by radius in ascending order
            QuickSortPoints(polarPoints, 0, len - 1);

            // Hashtable for edges of convex hull
            hashSize = (int)Math.Sqrt(len);
            hash     = new PointNode[hashSize + 2];

            // Circular doubly-linked list which will hold the convex hull
            var e = hull = InsertNode(seed1);

            HashEdge(e);
            e.t = 0;
            e   = InsertNode(seed2, e);
            HashEdge(e);
            e.t = 1;
            e   = InsertNode(seed3, e);
            HashEdge(e);
            e.t = 2;

            var maxTriangles = 2 * len - 5;

            triangles = new PolarPoint[maxTriangles * 3];
            halfedges = new int[maxTriangles * 3];

            AddTriangle(seed1, seed2, seed3, -1, -1, -1);

            var prev = new PolarPoint(new Point(-1, -1), -1, -1);

            for (var i = 0; i < len; i++)
            {
                var pp = polarPoints[i];

                if (seed.HasVertex(pp.CartesianPoint))
                {
                    continue; // Skip seed triangle points
                }
                if (prev.CartesianPoint.Equals(pp.CartesianPoint))
                {
                    continue; // Ignore duplicate point
                }
                // Find a visible edge on the convex hull using edge hash
                var       startKey = HashKey(pp);
                var       key      = startKey;
                PointNode start;
                do
                {
                    start = hash[key];
                    key   = (key + 1) % hashSize;
                }while ((start == null || start.Removed) && key != startKey);

                e = start;
                while (GeoMath.TriangleAreaDeterminant(pp.CartesianPoint, e.Point.CartesianPoint, e.Next.Point.CartesianPoint) <= 0)
                {
                    e = e.Next;
#if DEBUG
                    if (e == start)
                    {
                        throw new Exception("Processing error. Input points invalid or seed triangle wrongly oriented.");
                    }
#endif
                }

                var walkBack = e == start;

                // Add first triangle
                var t = AddTriangle(e.Point, pp, e.Next.Point, -1, -1, e.t);

                e.t = t; // Keep track of boundary triangles on hull
                e   = InsertNode(pp, e);

                // Recursively flip triangles from the point until they satisfy the Delaunay condition
                e.t = Legalize(t + 2);
                if (e.Previous.Previous.t == halfedges[t + 1])
                {
                    e.Previous.Previous.t = t + 2;
                }

                // Walk forward through the hull, adding more triangles and flipping recursively
                var q = e.Next;
                while (GeoMath.TriangleAreaDeterminant(pp.CartesianPoint, q.Point.CartesianPoint, q.Next.Point.CartesianPoint) > 0)
                {
                    t            = AddTriangle(q.Point, pp, q.Next.Point, q.Previous.t, -1, q.t);
                    q.Previous.t = Legalize(t + 2); // Flipping
                    hull         = RemoveNode(q);
                    q            = q.Next;
                }

                if (walkBack)
                {
                    // Walk backward from the other side, adding more triangles and flipping recursively
                    q = e.Previous;
                    while (GeoMath.TriangleAreaDeterminant(pp.CartesianPoint, q.Previous.Point.CartesianPoint, q.Point.CartesianPoint) > 0)
                    {
                        t = AddTriangle(q.Previous.Point, pp, q.Point, -1, q.t, q.Previous.t);
                        Legalize(t + 2); // Flipping
                        q.Previous.t = t;
                        hull         = RemoveNode(q);
                        q            = q.Previous;
                    }
                }

                // Save the two new edges in the hashtable
                HashEdge(e);
                HashEdge(e.Previous);

                prev = pp;
            }

            // Create Delaunay triangles from points
            var delaunay = new List <Triangle>(trianglesPointIndex / 3);
            for (var i = 0; i < trianglesPointIndex;)
            {
                delaunay.Add(new Triangle(
                                 triangles[i++].CartesianPoint,
                                 triangles[i++].CartesianPoint,
                                 triangles[i++].CartesianPoint
                                 ));
            }

            SetDelaunayResult(delaunay);
        }
Beispiel #3
0
        protected (Point, Triangle) CalculateOriginSeedTriangle(out int idx1, out int idx2, out int idx3)
        {
            idx1 = -1;
            idx2 = -1;
            idx3 = -1;

            // Calculate origin point
            (Point min, Point max) = CalculateBoundaryBox();

            var origin = new Point((min.X + max.X) / 2, (min.Y + max.Y) / 2);

            // Find first seed point closest to origin
            {
                var minDist = double.MaxValue;
                for (var i = 0; i < Points.Length; i++)
                {
                    var dist = GeoMath.EuclideanDistance2(Points[i], origin);
                    if (dist < minDist)
                    {
                        idx1    = i;
                        minDist = dist;
                    }
                }
            }

            // Find point closest to seed point
            {
                var minDist = double.MaxValue;
                for (var i = 0; i < Points.Length; i++)
                {
                    if (i == idx1)
                    {
                        continue;
                    }
                    var dist = GeoMath.EuclideanDistance2(Points[i], Points[idx1]);
                    if (dist < minDist)
                    {
                        idx2    = i;
                        minDist = dist;
                    }
                }
            }

            // Find point which creates the smallest circumcircle
            {
                var minCircRadius2 = double.MaxValue;
                for (var i = 0; i < Points.Length; i++)
                {
                    if (i == idx1 || i == idx2)
                    {
                        continue;
                    }
                    var circRadius2 = GeoMath.Circumradius2(Points[idx1], Points[idx2], Points[i]);
                    if (circRadius2 < minCircRadius2)
                    {
                        idx3           = i;
                        minCircRadius2 = circRadius2;
                    }
                }

                if (idx3 == -1)
                {
                    idx1 = -1;
                    idx2 = -1;
                    return(new Point(), Triangle.Zero);  // No Delaunay triangulation exists
                }
            }

            // Make sure seed triangle points are oriented counter-clockwise
            if (GeoMath.TriangleAreaDeterminant(Points[idx1], Points[idx2], Points[idx3]) > 0)
            {
                var tmp = idx2;
                idx2 = idx3;
                idx3 = tmp;
            }

            // Update origin to represent midpoint of seed triangle
            origin = GeoMath.CalculateMidpoint(Points[idx1], Points[idx2], Points[idx3]);

            var seed = new Triangle(Points[idx1], Points[idx2], Points[idx3]);

            if (BuildDiagonstics)
            {
                // Add boundary box and seed point/triangle to diagnostics
                DiagnosticGeometry.Add(new DiagnosticGeometry(DiagGeometryType.Line, DiagColor.Red, seed.GetEdgeA().Start, seed.GetEdgeA().End));
                DiagnosticGeometry.Add(new DiagnosticGeometry(DiagGeometryType.Line, DiagColor.Red, seed.GetEdgeB().Start, seed.GetEdgeB().End));
                DiagnosticGeometry.Add(new DiagnosticGeometry(DiagGeometryType.Line, DiagColor.Red, seed.GetEdgeC().Start, seed.GetEdgeC().End));
                DiagnosticGeometry.Add(new DiagnosticGeometry(DiagGeometryType.Line, DiagColor.Red, min, new Point(min.X, max.Y), new Point(max.X, max.Y), new Point(max.X, max.Y), new Point(max.X, min.Y), min));
                DiagnosticGeometry.Add(new DiagnosticGeometry(DiagGeometryType.Vertex, DiagColor.Red, origin));
            }

            return(origin, seed);
        }