예제 #1
0
        //Help method to calculate the intersection point between two planes offset in normal direction by a width
        private static MyVector2 GetIntersectionPoint(MyVector2 a, MyVector2 b, MyVector2 c, float halfWidth, bool isTopPoint)
        {
            //Direction of the lines going to and from point b
            MyVector2 beforeDir = MyVector2.Normalize(b - a);

            MyVector2 afterDir = MyVector2.Normalize(c - b);

            MyVector2 beforeNormal = GetNormal(a, b);

            MyVector2 afterNormal = GetNormal(b, c);

            //Compare the normals!

            //normalDirFactor is used to determine if we want to top point (same direction as normal)
            float normalDirFactor = isTopPoint ? 1f : -1f;

            //If they are the same it means we have a straight line and thus we cant do plane-plane intersection
            //if (beforeNormal.Equals(afterNormal))
            //When comparing the normals, we cant use the regular small value because then
            //the line width goes to infinity when doing plane-plane intersection
            float dot = MyVector2.Dot(beforeNormal, afterNormal);

            //Dot is 1 if the point in the same dir and -1 if the point in the opposite dir
            float one = 1f - 0.01f;

            if (dot > one || dot < -one)
            {
                MyVector2 averageNormal = MyVector2.Normalize((afterNormal + beforeNormal) * 0.5f);

                MyVector2 intersectionPoint = b + averageNormal * halfWidth * normalDirFactor;

                return(intersectionPoint);
            }
            else
            {
                //Now we can calculate where the plane starts
                MyVector2 beforePlanePos = b + beforeNormal * halfWidth * normalDirFactor;

                MyVector2 afterPlanePos = b + afterNormal * halfWidth * normalDirFactor;

                //Calculate the intersection point
                //We know they are intersecting, so we don't need to test that
                MyVector2 intersectionPoint = Intersections.GetPlanePlaneIntersectionPoint(beforePlanePos, beforeNormal, afterPlanePos, afterNormal);

                return(intersectionPoint);
            }
        }
        //Is a polygon One inside polygon Two?
        private static bool IsPolygonInsidePolygon(List <MyVector2> polyOne, List <MyVector2> polyTwo)
        {
            bool isInside = false;

            for (int i = 0; i < polyOne.Count; i++)
            {
                if (Intersections.PointPolygon(polyTwo, polyOne[i]))
                {
                    //Is inside if at least one point is inside the polygon. We run this method after we have tested
                    //if the polygons are intersecting
                    isInside = true;

                    break;
                }
            }

            return(isInside);
        }
        //Mark entry exit points
        private static void MarkEntryExit(List <ClipVertex> poly, List <MyVector2> clipPolyVector)
        {
            //First see if the first vertex starts inside or outside (we can use the original list)
            bool isInside = Intersections.PointPolygon(clipPolyVector, poly[0].coordinate);

            //Debug.Log(isInside);

            ClipVertex currentVertex = poly[0];

            ClipVertex firstVertex = currentVertex;

            int safety = 0;

            while (true)
            {
                if (currentVertex.isIntersection)
                {
                    //If we were outside, this is an entry
                    currentVertex.isEntry = isInside ? false : true;

                    //Now we know we are either inside or outside
                    isInside = !isInside;
                }

                currentVertex = currentVertex.next;

                //We have travelled around the entire polygon
                if (currentVertex.Equals(firstVertex))
                {
                    break;
                }

                safety += 1;

                if (safety > 100000)
                {
                    Debug.Log("Endless loop in mark entry exit");

                    break;
                }
            }
        }
        //Is an edge crossing another edge?
        private static bool IsEdgeCrossingEdge(MyVector2 e1_p1, MyVector2 e1_p2, MyVector2 e2_p1, MyVector2 e2_p2)
        {
            //We will here run into floating point precision issues so we have to be careful
            //To solve that you can first check the end points
            //and modify the line-line intersection algorithm to include a small epsilon

            //First check if the edges are sharing a point, if so they are not crossing
            if (e1_p1.Equals(e2_p1) || e1_p1.Equals(e2_p2) || e1_p2.Equals(e2_p1) || e1_p2.Equals(e2_p2))
            {
                return(false);
            }

            //Then check if the lines are intersecting
            if (!Intersections.LineLine(e1_p1, e1_p2, e2_p1, e2_p2, shouldIncludeEndPoints: false))
            {
                return(false);
            }

            return(true);
        }
        //
        // Alternative 1. Search through all triangles and use point-in-triangle
        //

        //Simple but slow
        public static HalfEdgeFace2 BruteForce(MyVector2 p, HalfEdgeData2 triangulationData)
        {
            HalfEdgeFace2 intersectingTriangle = null;

            foreach (HalfEdgeFace2 f in triangulationData.faces)
            {
                //The corners of this triangle
                MyVector2 v1 = f.edge.v.position;
                MyVector2 v2 = f.edge.nextEdge.v.position;
                MyVector2 v3 = f.edge.nextEdge.nextEdge.v.position;

                Triangle2 t = new Triangle2(v1, v2, v3);

                //Is the point in this triangle?
                if (Intersections.PointTriangle(t, p, true))
                {
                    intersectingTriangle = f;

                    break;
                }
            }

            return(intersectingTriangle);
        }
예제 #6
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);
        }
예제 #7
0
        //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.DistanceFromPointToPlane(plane.normal, plane.pos, v1);
                    float dist_to_v2 = Geometry.DistanceFromPointToPlane(plane.normal, plane.pos, v2);

                    //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);

                        MyVector2 intersectionPoint = Intersections.GetRayPlaneIntersectionPoint(plane.pos, plane.normal, v1, rayDir);

                        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);

                        MyVector2 intersectionPoint = Intersections.GetRayPlaneIntersectionPoint(plane.pos, plane.normal, v1, rayDir);

                        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);
        }
        public static List <List <MyVector2> > ClipPolygons(List <MyVector2> polyVector2, List <MyVector2> clipPolyVector2, BooleanOperation booleanOperation)
        {
            List <List <MyVector2> > finalPoly = new List <List <MyVector2> >();



            //Step 0. Create the data structure needed
            List <ClipVertex> poly = InitDataStructure(polyVector2);

            List <ClipVertex> clipPoly = InitDataStructure(clipPolyVector2);



            //Step 1. Find intersection points
            //Need to test if we have found an intersection point,
            //if none is found, the polygons dont intersect, or one polygon is inside the other
            bool hasFoundIntersection = false;

            for (int i = 0; i < poly.Count; i++)
            {
                ClipVertex currentVertex = poly[i];

                //Important to use iPlusOne because poly.next may change
                int iPlusOne = MathUtility.ClampListIndex(i + 1, poly.Count);

                MyVector2 a = poly[i].coordinate;

                MyVector2 b = poly[iPlusOne].coordinate;

                //Gizmos.DrawWireSphere(poly[i].coordinate, 0.02f);
                //Gizmos.DrawWireSphere(poly[i].next.coordinate, 0.02f);

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

                    MyVector2 c = clipPoly[j].coordinate;

                    MyVector2 d = clipPoly[jPlusOne].coordinate;

                    //Are these lines intersecting?
                    if (Intersections.LineLine(a, b, c, d, true))
                    {
                        hasFoundIntersection = true;

                        MyVector2 intersectionPoint2D = Intersections.GetLineLineIntersectionPoint(a, b, c, d);

                        //Vector3 intersectionPoint = new Vector3(intersectionPoint2D.x, 0f, intersectionPoint2D.y);

                        //Gizmos.color = Color.red;

                        //Gizmos.DrawWireSphere(intersectionPoint, 0.04f);

                        //We need to insert this intersection vertex into both polygons
                        //Insert into the polygon
                        ClipVertex vertexOnPolygon = InsertIntersectionVertex(a, b, intersectionPoint2D, currentVertex);

                        //Insert into the clip polygon
                        ClipVertex vertexOnClipPolygon = InsertIntersectionVertex(c, d, intersectionPoint2D, clipPoly[j]);

                        //Also connect the intersection vertices with each other
                        vertexOnPolygon.neighbor = vertexOnClipPolygon;

                        vertexOnClipPolygon.neighbor = vertexOnPolygon;
                    }
                }
            }


            //Debug in which order the vertices are in the linked list
            //InWhichOrderAreVerticesAdded(poly);

            //InWhichOrderAreVerticesAdded(clipPoly);



            //If the polygons are intersecting
            if (hasFoundIntersection)
            {
                //Step 2. Trace each polygon and mark entry and exit points to the other polygon's interior
                MarkEntryExit(poly, clipPolyVector2);

                MarkEntryExit(clipPoly, polyVector2);

                //Debug entry exit points
                DebugEntryExit(poly);
                //DebugEntryExit(clipPoly);



                //Step 3. Create the desired clipped polygon
                if (booleanOperation == BooleanOperation.Intersection)
                {
                    //Where the two polygons intersect
                    List <ClipVertex> intersectionVertices = GetClippedPolygon(poly, true);

                    //Debug.Log(intersectionVertices.Count);

                    AddPolygonToList(intersectionVertices, finalPoly, false);

                    //Debug.Log();
                }
                else if (booleanOperation == BooleanOperation.Difference)
                {
                    //Whats outside of the polygon that doesnt intersect
                    List <ClipVertex> outsidePolyVertices = GetClippedPolygon(poly, false);

                    AddPolygonToList(outsidePolyVertices, finalPoly, true);
                }
                else if (booleanOperation == BooleanOperation.ExclusiveOr)
                {
                    //Whats outside of the polygon that doesnt intersect
                    List <ClipVertex> outsidePolyVertices = GetClippedPolygon(poly, false);

                    AddPolygonToList(outsidePolyVertices, finalPoly, true);

                    //Whats outside of the polygon that doesnt intersect
                    List <ClipVertex> outsideClipPolyVertices = GetClippedPolygon(clipPoly, false);

                    AddPolygonToList(outsideClipPolyVertices, finalPoly, true);
                }
                else if (booleanOperation == BooleanOperation.Union)
                {
                    //Where the two polygons intersect
                    List <ClipVertex> intersectionVertices = GetClippedPolygon(poly, true);

                    AddPolygonToList(intersectionVertices, finalPoly, false);

                    //Whats outside of the polygon that doesnt intersect
                    List <ClipVertex> outsidePolyVertices = GetClippedPolygon(poly, false);

                    AddPolygonToList(outsidePolyVertices, finalPoly, true);

                    //Whats outside of the polygon that doesnt intersect
                    List <ClipVertex> outsideClipPolyVertices = GetClippedPolygon(clipPoly, false);

                    AddPolygonToList(outsideClipPolyVertices, finalPoly, true);
                }

                //Where the two polygons intersect
                //List<ClipVertex> intersectionVertices = GetClippedPolygon(poly, true);
                //Whats outside of the polygon that doesnt intersect
                //These will be in clockwise order so remember to change to counter clockwise
                //List<ClipVertex> outsidePolyVertices = GetClippedPolygon(poly, false);
                //List<ClipVertex> outsideClipPolyVertices = GetClippedPolygon(clipPoly, false);
            }
            //Check if one polygon is inside the other
            else
            {
                //Is the polygon inside the clip polygon?
                //Depending on the type of boolean operation, we might get a hole
                if (IsPolygonInsidePolygon(polyVector2, clipPolyVector2))
                {
                    Debug.Log("Poly is inside clip poly");
                }
                else if (IsPolygonInsidePolygon(clipPolyVector2, polyVector2))
                {
                    Debug.Log("Clip poly is inside poly");
                }
                else
                {
                    Debug.Log("Polygons are not intersecting");
                }
            }

            return(finalPoly);
        }
        //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 <MyVector2> ClipPolygon(List <MyVector2> poly, List <Plane2> clippingPlanes)
        {
            //Clone the vertices because we will remove vertices from this list
            List <MyVector2> vertices = new List <MyVector2>(poly);

            //Save the new vertices temporarily in this list before transfering them to vertices
            List <MyVector2> vertices_tmp = new List <MyVector2>();

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

                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.DistanceFromPointToPlane(plane.normal, plane.pos, v1);
                    float dist_to_v2 = Geometry.DistanceFromPointToPlane(plane.normal, plane.pos, v2);

                    //TODO: What will happen if they are exactly 0? Should maybe use a tolerance of 0.001

                    //Case 1. Both are outside (= to the right), do nothing

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

                        MyVector2 intersectionPoint = Intersections.GetRayPlaneIntersectionPoint(plane.pos, plane.normal, v1, rayDir);

                        vertices_tmp.Add(intersectionPoint);

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

                        MyVector2 intersectionPoint = Intersections.GetRayPlaneIntersectionPoint(plane.pos, plane.normal, v1, rayDir);

                        vertices_tmp.Add(intersectionPoint);
                    }
                }

                //Add the new vertices to the list of vertices
                vertices.Clear();

                vertices.AddRange(vertices_tmp);

                vertices_tmp.Clear();
            }

            return(vertices);
        }