Пример #1
0
        //Find a triangle which has an edge going from p1 to p2
        //private static HalfEdgeFace2 FindTriangleWithEdge(MyVector2 p1, MyVector2 p2, HalfEdgeData2 triangleData)
        //{
        //    HashSet<HalfEdge2> edges = triangleData.edges;

        //    foreach (HalfEdge2 e in edges)
        //    {
        //        //An edge is going TO a vertex
        //        MyVector2 e_p1 = e.prevEdge.v.position;
        //        MyVector2 e_p2 = e.v.position;

        //        if (e_p1.Equals(p1) && e_p2.Equals(p2))
        //        {
        //            return e.face;
        //        }
        //    }

        //    return null;
        //}



        //Find all half-edges that are constraint
        private static HashSet <HalfEdge2> FindAllConstraintEdges(List <MyVector2> constraints, HalfEdgeData2 triangleData)
        {
            HashSet <HalfEdge2> constrainEdges = new HashSet <HalfEdge2>();


            //Create a new set with all constrains, and as we discover new constraints, we delete constrains, which will make searching faster
            //A constraint can only exist once!
            HashSet <Edge2> constraintsEdges = new HashSet <Edge2>();

            for (int i = 0; i < constraints.Count; i++)
            {
                MyVector2 c_p1 = constraints[i];
                MyVector2 c_p2 = constraints[MathUtility.ClampListIndex(i + 1, constraints.Count)];

                constraintsEdges.Add(new Edge2(c_p1, c_p2));
            }


            //All edges we have to search
            HashSet <HalfEdge2> edges = triangleData.edges;

            foreach (HalfEdge2 e in edges)
            {
                //An edge is going TO a vertex
                MyVector2 e_p1 = e.prevEdge.v.position;
                MyVector2 e_p2 = e.v.position;

                //Is this edge a constraint?
                foreach (Edge2 c_edge in constraintsEdges)
                {
                    if (e_p1.Equals(c_edge.p1) && e_p2.Equals(c_edge.p2))
                    {
                        constrainEdges.Add(e);

                        constraintsEdges.Remove(c_edge);

                        //Move on to the next edge
                        break;
                    }
                }

                //We have found all constraint, so don't need to search anymore
                if (constraintsEdges.Count == 0)
                {
                    break;
                }
            }

            return(constrainEdges);
        }
        //The original algorithm calculates the intersection between two polygons, this will instead get the outside
        //Assumes the polygons are oriented counter clockwise
        //poly is the polygon we want to cut
        //Assumes the polygon we want to remove from the other polygon is convex, so clipPolygon has to be convex
        //We will end up with the !intersection of the polygons
        public static List <List <MyVector2> > ClipPolygonInverted(List <MyVector2> poly, List <Plane2> clippingPlanes)
        {
            //The result may be more than one polygons
            List <List <MyVector2> > finalPolygons = new List <List <MyVector2> >();

            List <MyVector2> vertices = new List <MyVector2>(poly);

            //The remaining polygon after each cut
            List <MyVector2> vertices_tmp = new List <MyVector2>();

            //Clip the polygon
            for (int i = 0; i < clippingPlanes.Count; i++)
            {
                Plane2 plane = clippingPlanes[i];

                //A new polygon which is the part of the polygon which is outside of this plane
                List <MyVector2> outsidePolygon = new List <MyVector2>();

                for (int j = 0; j < vertices.Count; j++)
                {
                    int jPlusOne = MathUtility.ClampListIndex(j + 1, vertices.Count);

                    MyVector2 v1 = vertices[j];
                    MyVector2 v2 = vertices[jPlusOne];

                    //Calculate the distance to the plane from each vertex
                    //This is how we will know if they are inside or outside
                    //If they are inside, the distance is positive, which is why the planes normals have to be oriented to the inside
                    float dist_to_v1 = _Geometry.GetSignedDistanceFromPointToPlane(v1, plane);
                    float dist_to_v2 = _Geometry.GetSignedDistanceFromPointToPlane(v2, plane);

                    //TODO: What will happen if they are exactly 0?

                    //Case 1. Both are inside (= to the left), save v2 to the other polygon
                    if (dist_to_v1 >= 0f && dist_to_v2 >= 0f)
                    {
                        vertices_tmp.Add(v2);
                    }
                    //Case 2. Both are outside (= to the right), save v1
                    else if (dist_to_v1 < 0f && dist_to_v2 < 0f)
                    {
                        outsidePolygon.Add(v2);
                    }
                    //Case 3. Outside -> Inside, save intersection point
                    else if (dist_to_v1 < 0f && dist_to_v2 >= 0f)
                    {
                        MyVector2 rayDir = MyVector2.Normalize(v2 - v1);

                        Ray2 ray = new Ray2(v1, rayDir);

                        MyVector2 intersectionPoint = _Intersections.GetRayPlaneIntersectionPoint(plane, ray);

                        outsidePolygon.Add(intersectionPoint);

                        vertices_tmp.Add(intersectionPoint);

                        vertices_tmp.Add(v2);
                    }
                    //Case 4. Inside -> Outside, save intersection point and v2
                    else if (dist_to_v1 >= 0f && dist_to_v2 < 0f)
                    {
                        MyVector2 rayDir = MyVector2.Normalize(v2 - v1);

                        Ray2 ray = new Ray2(v1, rayDir);

                        MyVector2 intersectionPoint = _Intersections.GetRayPlaneIntersectionPoint(plane, ray);

                        outsidePolygon.Add(intersectionPoint);

                        outsidePolygon.Add(v2);

                        vertices_tmp.Add(intersectionPoint);
                    }
                }

                //Add the polygon outside of this plane to the list of all polygons that are outside of all planes
                if (outsidePolygon.Count > 0)
                {
                    finalPolygons.Add(outsidePolygon);
                }

                //Add the polygon which was inside of this and previous planes to the polygon we want to test
                vertices.Clear();

                vertices.AddRange(vertices_tmp);

                vertices_tmp.Clear();
            }

            return(finalPolygons);
        }
Пример #3
0
        //
        // Algorithm 1
        //

        //If you have points on a convex hull, sorted one after each other
        //If you have colinear points, it will ignore some of them but still triangulate the entire area
        //Colinear points are not changing the shape
        public static HashSet <Triangle2> GetTriangles(List <MyVector2> pointsOnHull, bool addColinearPoints)
        {
            //If we hadnt have to deal with colinear points, this algorithm would be really simple:
            HashSet <Triangle2> triangles = new HashSet <Triangle2>();

            //This vertex will be a vertex in all triangles
            MyVector2 a = pointsOnHull[0];

            //And then we just loop through the other edges to make all triangles
            for (int i = 1; i < pointsOnHull.Count; i++)
            {
                MyVector2 b = pointsOnHull[i];
                MyVector2 c = pointsOnHull[MathUtility.ClampListIndex(i + 1, pointsOnHull.Count)];

                //Is this a valid triangle?
                //If a, b, c are on the same line, the triangle has no area and we can't add it
                LeftOnRight pointRelation = Geometry.IsPoint_Left_On_Right_OfVector(a, b, c);

                if (pointRelation == LeftOnRight.On)
                {
                    continue;
                }

                triangles.Add(new Triangle2(a, b, c));
            }


            //Add the missing colinear points by splitting triangles
            //Step 2.1. We have now triangulated the entire convex polygon, but if we have colinear points
            //some of those points were not added
            //We can add them by splitting triangles

            //Find colinear points
            if (addColinearPoints)
            {
                HashSet <MyVector2> colinearPoints = new HashSet <MyVector2>(pointsOnHull);

                //Remove points that are in the triangulation from the points on the convex hull
                //and we can see which where not added = the colinear points
                foreach (Triangle2 t in triangles)
                {
                    colinearPoints.Remove(t.p1);
                    colinearPoints.Remove(t.p2);
                    colinearPoints.Remove(t.p3);
                }

                //Debug.Log("Colinear points: " + colinearPoints.Count);

                //Go through all colinear points and find which edge they should split
                //On the border we only need to split one edge because this edge has no neighbors
                foreach (MyVector2 p in colinearPoints)
                {
                    foreach (Triangle2 t in triangles)
                    {
                        //Is this point in the triangle
                        if (Intersections.PointTriangle(t, p, includeBorder: true))
                        {
                            SplitTriangleEdge(t, p, triangles);

                            break;
                        }
                    }
                }
            }


            return(triangles);
        }
Пример #4
0
        //
        // Add the constraints to the delaunay triangulation
        //

        //timer is for debugging
        private static HalfEdgeData2 AddConstraints(HalfEdgeData2 triangleData, List <MyVector2> constraints, bool shouldRemoveTriangles, System.Diagnostics.Stopwatch timer = null)
        {
            //Validate the data
            if (constraints == null)
            {
                return(triangleData);
            }


            //Get a list with all edges
            //This is faster than first searching for unique edges
            //The report suggest we should do a triangle walk, but it will not work if the mesh has holes
            //The mesh has holes because we remove triangles while adding constraints one-by-one
            //so maybe better to remove triangles after we added all constraints...
            HashSet <HalfEdge2> edges = triangleData.edges;


            //The steps numbering is from the report
            //Step 1. Loop over each constrained edge. For each of these edges, do steps 2-4
            for (int i = 0; i < constraints.Count; i++)
            {
                //Let each constrained edge be defined by the vertices:
                MyVector2 c_p1 = constraints[i];
                MyVector2 c_p2 = constraints[MathUtility.ClampListIndex(i + 1, constraints.Count)];

                //Check if this constraint already exists in the triangulation,
                //if so we are happy and dont need to worry about this edge
                //timer.Start();
                if (IsEdgeInListOfEdges(edges, c_p1, c_p2))
                {
                    continue;
                }
                //timer.Stop();

                //Step 2. Find all edges in the current triangulation that intersects with this constraint
                //Is returning unique edges only, so not one edge going in the opposite direction
                //timer.Start();
                Queue <HalfEdge2> intersectingEdges = FindIntersectingEdges_BruteForce(edges, c_p1, c_p2);
                //timer.Stop();

                //Debug.Log("Intersecting edges: " + intersectingEdges.Count);

                //Step 3. Remove intersecting edges by flipping triangles
                //This takes 0 seconds so is not bottleneck
                //timer.Start();
                List <HalfEdge2> newEdges = RemoveIntersectingEdges(c_p1, c_p2, intersectingEdges);
                //timer.Stop();

                //Step 4. Try to restore delaunay triangulation
                //Because we have constraints we will never get a delaunay triangulation
                //This takes 0 seconds so is not bottleneck
                //timer.Start();
                RestoreDelaunayTriangulation(c_p1, c_p2, newEdges);
                //timer.Stop();
            }

            //Step 5. Remove superfluous triangles, such as the triangles "inside" the constraints
            if (shouldRemoveTriangles)
            {
                //timer.Start();
                RemoveSuperfluousTriangles(triangleData, constraints);
                //timer.Stop();
            }

            return(triangleData);
        }
Пример #5
0
        //
        // Connected line segments
        //
        //isConnected means if the end points are connected to form a loop
        public static HashSet <Triangle2> ConnectedLineSegments(List <MyVector2> points, float width, bool isConnected)
        {
            if (points != null && points.Count < 2)
            {
                Debug.Log("Cant form a line with fewer than two points");

                return(null);
            }



            //Generate the triangles
            HashSet <Triangle2> lineTriangles = new HashSet <Triangle2>();

            //If the lines are connected we need to do plane-plane intersection to find the
            //coordinate where the lines meet at each point, or the line segments will
            //not get the same size
            //(There might be a better way to do it than with plane-plane intersection)
            List <MyVector2> topCoordinate    = new List <MyVector2>();
            List <MyVector2> bottomCoordinate = new List <MyVector2>();

            float halfWidth = width * 0.5f;

            for (int i = 0; i < points.Count; i++)
            {
                MyVector2 p = points[i];

                //First point = special case if the lines are not connected
                if (i == 0 && !isConnected)
                {
                    MyVector2 lineDir = points[1] - points[0];

                    MyVector2 lineNormal = MyVector2.Normalize(new MyVector2(lineDir.y, -lineDir.x));

                    topCoordinate.Add(p + lineNormal * halfWidth);

                    bottomCoordinate.Add(p - lineNormal * halfWidth);
                }
                //Last point = special case if the lines are not connected
                else if (i == points.Count - 1 && !isConnected)
                {
                    MyVector2 lineDir = p - points[points.Count - 2];

                    MyVector2 lineNormal = MyVector2.Normalize(new MyVector2(lineDir.y, -lineDir.x));

                    topCoordinate.Add(p + lineNormal * halfWidth);

                    bottomCoordinate.Add(p - lineNormal * halfWidth);
                }
                else
                {
                    //Now we need to find the intersection points between the top line and the bottom line
                    MyVector2 p_before = points[MathUtility.ClampListIndex(i - 1, points.Count)];

                    MyVector2 p_after = points[MathUtility.ClampListIndex(i + 1, points.Count)];

                    MyVector2 pTop = GetIntersectionPoint(p_before, p, p_after, halfWidth, isTopPoint: true);

                    MyVector2 pBottom = GetIntersectionPoint(p_before, p, p_after, halfWidth, isTopPoint: false);



                    topCoordinate.Add(pTop);

                    bottomCoordinate.Add(pBottom);
                }
            }

            //Debug.Log();

            for (int i = 0; i < points.Count; i++)
            {
                //Skip the first point if it is not connected to the last point
                if (i == 0 && !isConnected)
                {
                    continue;
                }

                int i_minus_one = MathUtility.ClampListIndex(i - 1, points.Count);

                MyVector2 p1_T = topCoordinate[i_minus_one];
                MyVector2 p1_B = bottomCoordinate[i_minus_one];

                MyVector2 p2_T = topCoordinate[i];
                MyVector2 p2_B = bottomCoordinate[i];

                HashSet <Triangle2> triangles = LineSegment(p1_T, p1_B, p2_T, p2_B);

                foreach (Triangle2 t in triangles)
                {
                    lineTriangles.Add(t);
                }
            }

            //Debug.Log(lineTriangles.Count);

            return(lineTriangles);
        }
Пример #6
0
        //We assume an edge is visible from a point if the triangle (formed by travering edges in the convex hull
        //of the existing triangulation) form a clockwise triangle with the point
        //https://stackoverflow.com/questions/8898255/check-whether-a-point-is-visible-from-a-face-of-a-2d-convex-hull
        private static HashSet <Triangle2> TriangulatePointsConvexHull(HashSet <MyVector2> pointsHashset)
        {
            HashSet <Triangle2> triangles = new HashSet <Triangle2>();

            //The points we will add
            List <MyVector2> originalPoints = new List <MyVector2>(pointsHashset);


            //Step 1. Sort the points along x-axis
            //OrderBy is always soring in ascending order - use OrderByDescending to get in the other order
            //originalPoints = originalPoints.OrderBy(n => n.x).ThenBy(n => n.y).ToList();
            originalPoints = originalPoints.OrderBy(n => n.x).ToList();


            //Step 2. Create the first triangle so we can start the algorithm

            //Assumes the convex hull algorithm sorts in the same way in x and y directions as we do above
            MyVector2 p1Start = originalPoints[0];
            MyVector2 p2Start = originalPoints[1];
            MyVector2 p3Start = originalPoints[2];

            //Theese points for the first triangle
            Triangle2 newTriangle = new Triangle2(p1Start, p2Start, p3Start);

            triangles.Add(newTriangle);


            //All points that form the triangles
            HashSet <MyVector2> triangulatedPoints = new HashSet <MyVector2>();

            triangulatedPoints.Add(p1Start);
            triangulatedPoints.Add(p2Start);
            triangulatedPoints.Add(p3Start);


            //Step 3. Add the other points one-by-one
            //Add the other points one by one
            //Starts at 3 because we have already added 0,1,2
            for (int i = 3; i < originalPoints.Count; i++)
            {
                MyVector2 pointToAdd = originalPoints[i];

                //Find the convex hull of the current triangulation
                //It generates a counter-clockwise convex hull
                List <MyVector2> pointsOnHull = _ConvexHull.JarvisMarch(triangulatedPoints);

                if (pointsOnHull == null)
                {
                    Debug.Log("No points on hull when triangulating");

                    continue;
                }

                bool couldFormTriangle = false;

                //Loop through all edges in the convex hull
                for (int j = 0; j < pointsOnHull.Count; j++)
                {
                    MyVector2 p1 = pointsOnHull[j];
                    MyVector2 p2 = pointsOnHull[MathUtility.ClampListIndex(j + 1, pointsOnHull.Count)];

                    //If this triangle is clockwise, then we can see the edge
                    //so we should create a new triangle with this edge and the point
                    if (Geometry.IsTriangleOrientedClockwise(p1, p2, pointToAdd))
                    {
                        triangles.Add(new Triangle2(p1, p2, pointToAdd));

                        couldFormTriangle = true;
                    }
                }

                //Add the point to the list of all points in the current triangulation
                //if the point could form a triangle
                if (couldFormTriangle)
                {
                    triangulatedPoints.Add(pointToAdd);
                }
            }



            return(triangles);
        }