public static List <Polyline> DelaunayTriangulation(List <Point> points, Plane plane = null, double tolerance = Tolerance.Distance)
        {
            //Preform check all inputs that triangulation can be done
            if (points == null || points.Count < 3)
            {
                Base.Compute.RecordError("Insufficient points for triangulation. Please provide at least 3 Points.");
                return(new List <Polyline>());
            }

            if (points.IsCollinear(tolerance))
            {
                Base.Compute.RecordError("Points are colinear and can not be triangulated.");
                return(new List <Polyline>());
            }

            //Basic edge case, simply create a triangle
            if (points.Count == 3)
            {
                Polyline pLine = new Polyline {
                    ControlPoints = points
                };
                pLine.ControlPoints.Add(points[0]);
                return(new List <Polyline> {
                    pLine
                });
            }

            //Try fit plane if no is provided
            if (plane == null)
            {
                plane = points.FitPlane(tolerance);
            }

            if (plane == null)
            {
                Engine.Base.Compute.RecordError("Could not fit a plane through the Points and no plane was provided.");
                return(new List <Polyline>());
            }

            //Check all points within distance of the plane
            if (points.Any(x => x.Distance(plane) > tolerance))
            {
                BH.Engine.Base.Compute.RecordError("Can only handle coplanar points. Please make sure all your points lie in the same plane.");
                return(new List <Polyline>());
            }

            //Calculate the local coordinates
            Vector localX = points[1] - points[0];
            Vector localY = plane.Normal.CrossProduct(localX);
            Point  min    = points.Min();
            Point  refPt  = new Point {
                X = min.X, Y = min.Y
            };
            Cartesian localSystem  = Create.CartesianCoordinateSystem(min, localX, localY);
            Cartesian globalSystem = Create.CartesianCoordinateSystem(refPt, Vector.XAxis, Vector.YAxis);

            //Transform to xy-plane
            List <Point> xyPoints = points.Select(x => x.Orient(localSystem, globalSystem)).ToList();

            //Convert to TringleNet vertecies
            List <TriangleNet.Geometry.Vertex> vertecies = xyPoints.Select(p => new TriangleNet.Geometry.Vertex(p.X, p.Y)).ToList();

            // Triangulate
            TriangleNet.Meshing.ITriangulator triangulator = new TriangleNet.Meshing.Algorithm.Dwyer();
            TriangleNet.Configuration         config       = new TriangleNet.Configuration();
            TriangleNet.Mesh mesh = (TriangleNet.Mesh)triangulator.Triangulate(vertecies, config);

            // Convert triangulations back to BHoM geometry
            List <Polyline> translatedPolylines = new List <Polyline>();

            foreach (var face in mesh.Triangles)
            {
                // List points defining the triangle
                List <Point> pts = new List <Point>();
                pts.Add(BH.Engine.Geometry.Create.Point(face.GetVertex(0).X, face.GetVertex(0).Y));
                pts.Add(BH.Engine.Geometry.Create.Point(face.GetVertex(1).X, face.GetVertex(1).Y));
                pts.Add(BH.Engine.Geometry.Create.Point(face.GetVertex(2).X, face.GetVertex(2).Y));
                pts.Add(pts.First());
                translatedPolylines.Add(BH.Engine.Geometry.Create.Polyline(pts));
            }

            return(translatedPolylines.Select(x => x.Orient(globalSystem, localSystem)).ToList());
        }
Ejemplo n.º 2
0
        public static List <Polyline> VoronoiRegions(List <Point> points, Plane plane = null, double boundarySize = -1, double tolerance = Tolerance.Distance)
        {
            List <Point> uniquePoints = points.CullDuplicates(tolerance);

            if (points.Count != uniquePoints.Count)
            {
                Base.Compute.RecordError("Some points are overlapping with others. Duplicates need to be culled out to create voronoi regions.");
                return(new List <Polyline>());
            }

            //Preform check all inputs that triangulation can be done
            if (points == null || points.Count < 2)
            {
                Base.Compute.RecordError("Insuffient points for generating the diagram. Please provide at least 2 Points.");
                return(new List <Polyline>());
            }

            //Special case for colinear points. No need to triangulate
            if (points.IsCollinear(tolerance))
            {
                return(ColinearVoronoiRegions(points, plane, boundarySize, tolerance));
            }

            //Try fit plane if no is provided
            if (plane == null)
            {
                plane = points.FitPlane(tolerance);
            }

            if (plane == null)
            {
                Engine.Base.Compute.RecordError("Could not fit a plane through the Points and no plane was provided.");
                return(new List <Polyline>());
            }

            //Check all points within distance of the plane
            if (points.Any(x => x.Distance(plane) > tolerance))
            {
                BH.Engine.Base.Compute.RecordError("Can only handle coplanar points. Please make sure all your points lie in the same plane.");
                return(new List <Polyline>());
            }

            //Calculate the local coordinates
            Vector localX = points[1] - points[0];
            Vector localY = plane.Normal.CrossProduct(localX);
            Point  min    = points.Min();
            Point  refPt  = new Point {
                X = min.X, Y = min.Y
            };
            Cartesian localSystem  = Create.CartesianCoordinateSystem(min, localX, localY);
            Cartesian globalSystem = Create.CartesianCoordinateSystem(refPt, Vector.XAxis, Vector.YAxis);

            //Transform to xy-plane
            List <Point> xyPoints = points.Select(x => x.Orient(localSystem, globalSystem)).ToList();


            //Add point at all corners. This is to try to handle weirdness at the boundaries
            BoundingBox bounds = xyPoints.Bounds();

            double lengthX;
            double lengthY;

            //If boundary size is or equal to 0, calculate based on the bounds
            if (boundarySize <= 0)
            {
                lengthX = (bounds.Max.X - bounds.Min.X);
                lengthY = (bounds.Max.X - bounds.Min.X);
            }
            else
            {
                //if positive, use value provided
                lengthX = boundarySize;
                lengthY = boundarySize;
            }

            double minX = bounds.Min.X - lengthX;
            double minY = bounds.Min.Y - lengthY;
            double maxX = bounds.Max.X + lengthX;
            double maxY = bounds.Max.Y + lengthY;

            xyPoints.Add(new Point {
                X = minX, Y = minY
            });
            xyPoints.Add(new Point {
                X = minX, Y = maxY
            });
            xyPoints.Add(new Point {
                X = maxX, Y = maxY
            });
            xyPoints.Add(new Point {
                X = maxX, Y = minY
            });

            //Convert to TringleNet vertecies
            List <TriangleNet.Geometry.Vertex> vertecies = xyPoints.Select(p => new TriangleNet.Geometry.Vertex(p.X, p.Y)).ToList();

            // Triangulate
            TriangleNet.Meshing.ITriangulator triangulator = new TriangleNet.Meshing.Algorithm.Dwyer();
            TriangleNet.Configuration         config       = new TriangleNet.Configuration();
            TriangleNet.Mesh mesh = (TriangleNet.Mesh)triangulator.Triangulate(vertecies, config);

            TriangleNet.Voronoi.StandardVoronoi voronoi = new TriangleNet.Voronoi.StandardVoronoi(mesh);

            List <Polyline> translatedPolylines = new List <Polyline>();
            double          sqTol = tolerance * tolerance;

            // Convert regions to BHoMGeometry. Skip the last 4 faces as they correspond to the added boundary points.
            for (int i = 0; i < voronoi.Faces.Count - 4; i++)
            {
                var face = voronoi.Faces[i];
                try
                {
                    // List points defining the region
                    List <Point>  pts        = new List <Point>();
                    HashSet <int> visitedIds = new HashSet <int>();

                    int bailOut = 100000;
                    int counter = 0;

                    TriangleNet.Topology.DCEL.HalfEdge halfEdge = face.Edge;
                    int nextId;
                    do
                    {
                        visitedIds.Add(halfEdge.ID);
                        Point pt = new Point {
                            X = halfEdge.Origin.X, Y = halfEdge.Origin.Y
                        };
                        //Make sure two of the same points are not added. This could maybe be done in a more cleaver way with the half edge
                        if (pts.Count == 0 || pt.SquareDistance(pts.Last()) > sqTol)
                        {
                            pts.Add(pt);
                        }

                        halfEdge = halfEdge.Next;

                        if (halfEdge == null)
                        {
                            break;
                        }

                        nextId = halfEdge.ID;
                        counter++;
                    } while (!visitedIds.Contains(nextId) && counter < bailOut);

                    if (pts.Count > 0 && pts.First().SquareDistance(pts.Last()) > sqTol)
                    {
                        pts.Add(pts.First());
                    }

                    translatedPolylines.Add(BH.Engine.Geometry.Create.Polyline(pts));
                }
                catch (Exception)
                {
                    Engine.Base.Compute.RecordWarning("Failed to generate the region for at least one cell. An empty polyline as been added in its place.");
                    translatedPolylines.Add(new Polyline());
                }
            }

            //Orient back the regions to the local plane
            return(translatedPolylines.Select(x => x.Orient(globalSystem, localSystem)).ToList());
        }