Exemple #1
0
        //Is a vertex an ear?
        private static bool IsVertexEar(LinkedVertex vertex, HashSet <LinkedVertex> reflectVertices)
        {
            //Consider the triangle
            MyVector2 p_prev = vertex.prevLinkedVertex.pos;
            MyVector2 p      = vertex.pos;
            MyVector2 p_next = vertex.nextLinkedVertex.pos;

            Triangle2 t = new Triangle2(p_prev, p, p_next);

            //If any of the other vertices is within this triangle, then this vertex is not an ear
            //We only need to check the reflect vertices
            foreach (LinkedVertex otherVertex in reflectVertices)
            {
                MyVector2 test_p = otherVertex.pos;

                //Dont compare with any of the vertices the triangle consist of
                if (test_p.Equals(p_prev) || test_p.Equals(p) || test_p.Equals(p_next))
                {
                    continue;
                }

                //If a relect vertex intersects with the triangle, then this vertex is not an ear
                if (_Intersections.PointTriangle(t, test_p, includeBorder: true))
                {
                    return(false);
                }
            }


            //No vertex is intersecting with the triangle, so this vertex must be an ear
            return(true);
        }
        //Remove the supertriangle
        private static void RemoveSuperTriangle(Triangle2 superTriangle, HalfEdgeData2 triangulationData)
        {
            //The super triangle doesnt exists anymore because we have split it into many new triangles
            //But we can use its vertices to figure out which new triangles (or faces belonging to the triangle)
            //we should delete

            HashSet <HalfEdgeFace2> triangleFacesToDelete = new HashSet <HalfEdgeFace2>();

            //Loop through all vertices belongin to the triangulation
            foreach (HalfEdgeVertex2 v in triangulationData.vertices)
            {
                //If the face attached to this vertex already exists in the list of faces we want to delete
                //Then dont add it again
                if (triangleFacesToDelete.Contains(v.edge.face))
                {
                    continue;
                }

                MyVector2 v1 = v.position;

                //Is this vertex in the triangulation a vertex in the super triangle?
                if (v1.Equals(superTriangle.p1) || v1.Equals(superTriangle.p2) || v1.Equals(superTriangle.p3))
                {
                    triangleFacesToDelete.Add(v.edge.face);
                }
            }

            //Debug.Log("Triangles to delete: " + trianglesToDelete.Count);

            //Delete the new triangles with vertices attached to the super triangle
            foreach (HalfEdgeFace2 f in triangleFacesToDelete)
            {
                HalfEdgeHelpMethods.DeleteTriangleFace(f, triangulationData, shouldSetOppositeToNull: true);
            }
        }
        public static HashSet <Triangle2> TriangulatePoints(HashSet <MyVector2> points, bool addColinearPoints)
        {
            //Step 1. Generate the convex hull
            List <MyVector2> pointsOnConvexHull = _ConvexHull.JarvisMarch_2D(points);


            //Step 2. Triangulate the convex hull
            HashSet <Triangle2> triangles = _TriangulatePoints.PointsOnConvexHull(pointsOnConvexHull, addColinearPoints: true);


            //Step 3. From the points we should add, remove those that are already a part of the triangulation
            foreach (MyVector2 v in pointsOnConvexHull)
            {
                points.Remove(v);
            }


            //Step 4. Add the remaining points while splitting the triangles they end up in
            foreach (MyVector2 currentPoint in points)
            {
                //Which triangle is this point in?
                foreach (Triangle2 t in triangles)
                {
                    if (_Intersections.PointTriangle(t, currentPoint, includeBorder: true))
                    {
                        //Split the triangle into three new triangles
                        //We ignore if it ends up on the edge of a triangle
                        //If that happens we should split the edge
                        //But it will most likely not end up exactly on the edge because of floating point precision issues
                        //And we are most likely going to run a Delaunay algorithm on this "bad" triangulation
                        //so it doesn't matter anyway

                        //Create 3 new  with correct orientation = clockwise
                        Triangle2 t1 = new Triangle2(t.p1, t.p2, currentPoint);
                        Triangle2 t2 = new Triangle2(t.p2, t.p3, currentPoint);
                        Triangle2 t3 = new Triangle2(t.p3, t.p1, currentPoint);

                        //Remove the old triangle
                        triangles.Remove(t);

                        //Add the new triangles
                        triangles.Add(t1);
                        triangles.Add(t2);
                        triangles.Add(t3);

                        break;
                    }
                }
            }



            return(triangles);
        }
Exemple #4
0
        //
        // Arrow
        //
        public static HashSet <Triangle2> Arrow(MyVector2 p1, MyVector2 p2, float lineWidth, float arrowSize)
        {
            HashSet <Triangle2> arrowTriangles = new HashSet <Triangle2>();

            //An arrow consists of two parts: the pointy part and the rectangular part

            //First we have to see if we can fit the parts
            MyVector2 lineDir = p2 - p1;

            float lineLength = MyVector2.Magnitude(lineDir);

            if (lineLength < arrowSize)
            {
                Debug.Log("Cant make arrow because line is too short");

                return(null);
            }


            //Make the arrow tip
            MyVector2 lineDirNormalized = MyVector2.Normalize(lineDir);

            MyVector2 arrowBottom = p2 - lineDirNormalized * arrowSize;

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

            MyVector2 arrowBottom_R = arrowBottom + lineNormal * arrowSize * 0.5f;
            MyVector2 arrowBottom_L = arrowBottom - lineNormal * arrowSize * 0.5f;

            Triangle2 arrowTipTriangle = new Triangle2(p2, arrowBottom_R, arrowBottom_L);

            arrowTriangles.Add(arrowTipTriangle);


            //Make the arrow rectangle
            float halfWidth = lineWidth * 0.5f;

            MyVector2 p1_T = p1 + lineNormal * halfWidth;
            MyVector2 p1_B = p1 - lineNormal * halfWidth;

            MyVector2 p2_T = arrowBottom + lineNormal * halfWidth;
            MyVector2 p2_B = arrowBottom - lineNormal * halfWidth;

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

            foreach (Triangle2 t in rectangle)
            {
                arrowTriangles.Add(t);
            }

            return(arrowTriangles);
        }
Exemple #5
0
        //Generate two triangles if we know the corners of the rectangle
        public static HashSet <Triangle2> LineSegment(MyVector2 p1_T, MyVector2 p1_B, MyVector2 p2_T, MyVector2 p2_B)
        {
            HashSet <Triangle2> lineTriangles = new HashSet <Triangle2>();

            //Create the triangles
            Triangle2 t1 = new Triangle2(p1_T, p1_B, p2_T);
            Triangle2 t2 = new Triangle2(p1_B, p2_B, p2_T);

            lineTriangles.Add(t1);
            lineTriangles.Add(t2);

            return(lineTriangles);
        }
Exemple #6
0
        //Optimize a new triangle according to Delaunay triangulation
        //TODO: This process would have been easier if we had used the HalfEdge data structure
        private static void OptimizeTriangle(Triangle2 t, HashSet <Triangle2> triangulation)
        {
            bool hasOppositeEdge;

            Triangle2 tOpposite;

            Edge2 edgeToSwap;

            FindEdgeInTriangulation(t, triangulation, out hasOppositeEdge, out tOpposite, out edgeToSwap);

            //If it has no opposite edge we just add triangle to the triangulation because it can't be improved
            if (!hasOppositeEdge)
            {
                triangulation.Add(t);

                return;
            }

            //Debug.Log("Has opposite edge");

            //Step 3. Check if we should swap this edge according to Delaunay triangulation rules
            //a, b, c belongs to the triangle and d is the point on the other triangle
            //a-c is the edge, which is important so we can flip it, by making the edge b-d
            MyVector2 a = edgeToSwap.p2;
            MyVector2 c = edgeToSwap.p1;
            MyVector2 b = t.GetVertexWhichIsNotPartOfEdge(edgeToSwap);
            MyVector2 d = tOpposite.GetVertexWhichIsNotPartOfEdge(edgeToSwap);

            bool shouldFlipEdge = DelaunayMethods.ShouldFlipEdge(a, b, c, d);

            //bool shouldFlipEdge = DelaunayMethods.ShouldFlipEdgeStable(a, b, c, d);

            if (shouldFlipEdge)
            {
                //First remove the old triangle
                triangulation.Remove(tOpposite);

                //Build two new triangles
                Triangle2 t1 = new Triangle2(a, b, d);
                Triangle2 t2 = new Triangle2(b, c, d);

                triangulation.Add(t1);
                triangulation.Add(t2);

                //Debug.Log("Flipped edge");
            }
            else
            {
                triangulation.Add(t);
            }
        }
        //Is a point inside, outside, or on the border of a triangle
        //-1 if outside, 0 if on the border, 1 if inside the triangle
        //BROKEN use if the point is to the left or right of all edges in the triangle
        //public static int IsPointInOutsideOnTriangle(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p)
        //{
        //    //To avoid floating point precision issues we can add a small value
        //    float epsilon = MathUtility.EPSILON;

        //    float zero = 0f + epsilon;
        //    float one = 1f - epsilon;

        //    //Based on Barycentric coordinates
        //    float denominator = ((p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y));

        //    float a = ((p2.y - p3.y) * (p.x - p3.x) + (p3.x - p2.x) * (p.y - p3.y)) / denominator;
        //    float b = ((p3.y - p1.y) * (p.x - p3.x) + (p1.x - p3.x) * (p.y - p3.y)) / denominator;
        //    float c = 1 - a - b;

        //    int returnValue = -1;

        //    //The point is on the border, meaning exactly 0 or 1 in a world with no floating precision issues

        //    //The point is on or within the triangle
        //    if (a >= zero && a <= one && b >= zero && b <= one && c >= zero && c <= one)
        //    {
        //        returnValue = 1;
        //    }

        //    return returnValue;
        //}



        //
        // Is a triangle inside a triangle
        //
        //Is triangle 1 inside triangle 2?
        public static bool IsTriangleInsideTriangle(Triangle2 t1, Triangle2 t2)
        {
            bool isWithin = false;

            if (
                PointTriangle(t2, t1.p1, false) &&
                PointTriangle(t2, t1.p2, false) &&
                PointTriangle(t2, t1.p3, false))
            {
                isWithin = true;
            }

            return(isWithin);
        }
Exemple #8
0
        //Is a point inside, outside, or on the border of a triangle
        //-1 if outside, 0 if on the border, 1 if inside the triangle
        //BROKEN use if the point is to the left or right of all edges in the triangle
        //public static int IsPointInOutsideOnTriangle(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p)
        //{
        //    //To avoid floating point precision issues we can add a small value
        //    float epsilon = MathUtility.EPSILON;

        //    float zero = 0f + epsilon;
        //    float one = 1f - epsilon;

        //    //Based on Barycentric coordinates
        //    float denominator = ((p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y));

        //    float a = ((p2.y - p3.y) * (p.x - p3.x) + (p3.x - p2.x) * (p.y - p3.y)) / denominator;
        //    float b = ((p3.y - p1.y) * (p.x - p3.x) + (p1.x - p3.x) * (p.y - p3.y)) / denominator;
        //    float c = 1 - a - b;

        //    int returnValue = -1;

        //    //The point is on the border, meaning exactly 0 or 1 in a world with no floating precision issues

        //    //The point is on or within the triangle
        //    if (a >= zero && a <= one && b >= zero && b <= one && c >= zero && c <= one)
        //    {
        //        returnValue = 1;
        //    }

        //    return returnValue;
        //}



        //
        // Is a triangle inside a triangle
        //
        //Is triangle 1 inside triangle 2?
        public static bool IsTriangleInsideTriangle(Triangle2 t1, Triangle2 t2)
        {
            bool isWithin = false;

            //Test if each vertex is inside the triangle
            if (
                PointTriangle(t2, t1.p1, false) &&
                PointTriangle(t2, t1.p2, false) &&
                PointTriangle(t2, t1.p3, false))
            {
                isWithin = true;
            }

            return(isWithin);
        }
Exemple #9
0
        //HashSet<Triangle2>
        public static HashSet <Triangle2> UnNormalize(HashSet <Triangle2> normalized, AABB2 aabb, float dMax)
        {
            HashSet <Triangle2> unNormalized = new HashSet <Triangle2>();

            foreach (Triangle2 t in normalized)
            {
                MyVector2 p1 = HelpMethods.UnNormalize(t.p1, aabb, dMax);
                MyVector2 p2 = HelpMethods.UnNormalize(t.p2, aabb, dMax);
                MyVector2 p3 = HelpMethods.UnNormalize(t.p3, aabb, dMax);

                Triangle2 tUnNormalized = new Triangle2(p1, p2, p3);

                unNormalized.Add(tUnNormalized);
            }

            return(unNormalized);
        }
        //
        // Unity mesh to triangle
        //
        //The vertices and triangles are the same as in Unitys built-in Mesh, but in 2d space
        public static HashSet <Triangle2> MeshToTriangle2(Vector2[] meshVertices, int[] meshTriangles)
        {
            HashSet <Triangle2> triangles = new HashSet <Triangle2>();

            for (int i = 0; i < meshTriangles.Length; i += 3)
            {
                MyVector2 v1 = new MyVector2(meshVertices[meshTriangles[i + 0]].x, meshVertices[meshTriangles[i + 0]].y);
                MyVector2 v2 = new MyVector2(meshVertices[meshTriangles[i + 1]].x, meshVertices[meshTriangles[i + 1]].y);
                MyVector2 v3 = new MyVector2(meshVertices[meshTriangles[i + 2]].x, meshVertices[meshTriangles[i + 2]].y);

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

                triangles.Add(t);
            }

            return(triangles);
        }
        //Remove from points from hashset that are within a triangle
        private static void RemovePointsWithinTriangle(Triangle2 t, HashSet <MyVector2> points)
        {
            HashSet <MyVector2> pointsToRemove = new HashSet <MyVector2>();

            foreach (MyVector2 p in points)
            {
                if (_Intersections.PointTriangle(t, p, includeBorder: true))
                {
                    pointsToRemove.Add(p);
                }
            }

            foreach (MyVector2 p in pointsToRemove)
            {
                points.Remove(p);
            }
        }
        //HashSet<Triangle2>
        public HashSet <Triangle2> UnNormalize(HashSet <Triangle2> normalized)
        {
            HashSet <Triangle2> unNormalized = new HashSet <Triangle2>();

            foreach (Triangle2 t in normalized)
            {
                MyVector2 p1 = UnNormalize(t.p1);
                MyVector2 p2 = UnNormalize(t.p2);
                MyVector2 p3 = UnNormalize(t.p3);

                Triangle2 tUnNormalized = new Triangle2(p1, p2, p3);

                unNormalized.Add(tUnNormalized);
            }

            return(unNormalized);
        }
Exemple #13
0
        //Find an edge in a triangulation and return the triangle the edge is attached to
        private static void FindEdgeInTriangulation(Triangle2 tNew, HashSet <Triangle2> triangulation, out bool hasOppositeEdge, out Triangle2 tOpposite, out Edge2 edgeToSwap)
        {
            //Step 1. Find the triangle's biggest interior angle and its opposite edge
            float angleP1 = CalculateInteriorAngle(tNew.p3, tNew.p1, tNew.p2);
            float angleP2 = CalculateInteriorAngle(tNew.p1, tNew.p2, tNew.p3);
            float angleP3 = Mathf.PI - (angleP1 + angleP2);

            MyVector2 vertexWithBiggestInteriorAngle = tNew.p1;

            if (angleP2 > angleP1)
            {
                vertexWithBiggestInteriorAngle = tNew.p2;

                if (angleP3 > angleP2)
                {
                    vertexWithBiggestInteriorAngle = tNew.p3;
                }
            }
            else if (angleP3 > angleP1)
            {
                vertexWithBiggestInteriorAngle = tNew.p3;
            }

            edgeToSwap = tNew.FindOppositeEdgeToVertex(vertexWithBiggestInteriorAngle);


            //Step 2. Check if this edge exists among the already generated triangles, which means we have a neighbor
            hasOppositeEdge = false;

            tOpposite = new Triangle2();

            foreach (Triangle2 tTest in triangulation)
            {
                if (tTest.IsEdgePartOfTriangle(edgeToSwap))
                {
                    hasOppositeEdge = true;

                    tOpposite = tTest;

                    break;
                }
            }
        }
        //
        // Half-edge to triangle if we know the half-edge consists of triangles
        //
        public static HashSet <Triangle2> HalfEdge2ToTriangle2(HalfEdgeData2 data)
        {
            if (data == null)
            {
                return(null);
            }

            HashSet <Triangle2> triangles = new HashSet <Triangle2>();

            foreach (HalfEdgeFace2 face in data.faces)
            {
                MyVector2 p1 = face.edge.v.position;
                MyVector2 p2 = face.edge.nextEdge.v.position;
                MyVector2 p3 = face.edge.nextEdge.nextEdge.v.position;

                Triangle2 t = new Triangle2(p1, p2, p3);

                triangles.Add(t);
            }

            return(triangles);
        }
        //
        // Is a point inside a triangle?
        //
        //From http://totologic.blogspot.se/2014/01/accurate-point-in-triangle-test.html
        public static bool PointTriangle(Triangle2 t, MyVector2 p, bool includeBorder)
        {
            //To avoid floating point precision issues we can add a small value
            float epsilon = MathUtility.EPSILON;

            //Based on Barycentric coordinates
            float denominator = ((t.p2.y - t.p3.y) * (t.p1.x - t.p3.x) + (t.p3.x - t.p2.x) * (t.p1.y - t.p3.y));

            float a = ((t.p2.y - t.p3.y) * (p.x - t.p3.x) + (t.p3.x - t.p2.x) * (p.y - t.p3.y)) / denominator;
            float b = ((t.p3.y - t.p1.y) * (p.x - t.p3.x) + (t.p1.x - t.p3.x) * (p.y - t.p3.y)) / denominator;
            float c = 1 - a - b;

            bool isWithinTriangle = false;

            if (includeBorder)
            {
                float zero = 0f - epsilon;
                float one  = 1f + epsilon;

                //The point is within the triangle or on the border
                if (a >= zero && a <= one && b >= zero && b <= one && c >= zero && c <= one)
                {
                    isWithinTriangle = true;
                }
            }
            else
            {
                float zero = 0f + epsilon;
                float one  = 1f - epsilon;

                //The point is within the triangle
                if (a > zero && a < one && b > zero && b < one && c > zero && c < one)
                {
                    isWithinTriangle = true;
                }
            }

            return(isWithinTriangle);
        }
Exemple #16
0
        //Help method to split triangle edge
        private static void SplitTriangleEdge(Triangle2 t, MyVector2 p, HashSet <Triangle2> triangles)
        {
            MyVector2 a = t.p1;
            MyVector2 b = t.p2;
            MyVector2 c = t.p3;

            //Which edge should we split?
            if (Geometry.IsPoint_Left_On_Right_OfVector(a, b, p) == LeftOnRight.On)
            {
                Triangle2 t1_new = new Triangle2(a, c, p);
                Triangle2 t2_new = new Triangle2(b, c, p);

                triangles.Remove(t);

                triangles.Add(t1_new);
                triangles.Add(t2_new);
            }
            else if (Geometry.IsPoint_Left_On_Right_OfVector(b, c, p) == LeftOnRight.On)
            {
                Triangle2 t1_new = new Triangle2(b, a, p);
                Triangle2 t2_new = new Triangle2(c, a, p);

                triangles.Remove(t);

                triangles.Add(t1_new);
                triangles.Add(t2_new);
            }
            else if (Geometry.IsPoint_Left_On_Right_OfVector(c, a, p) == LeftOnRight.On)
            {
                Triangle2 t1_new = new Triangle2(c, b, p);
                Triangle2 t2_new = new Triangle2(a, b, p);

                triangles.Remove(t);

                triangles.Add(t1_new);
                triangles.Add(t2_new);
            }
        }
Exemple #17
0
        //Calculate the angle between the vectors if we are going from p1-p2-p3
        //Return +180 if "small" or -180 if "large"
        //public static float CalculateAngleBetweenVectors(MyVector2 p1, MyVector2 p2, MyVector2 p3)
        //{
        //    MyVector2 from = p1 - p2;

        //    MyVector2 to = p3 - p2;

        //    float angle = Vector2.SignedAngle(from, to);

        //    return angle;
        //}



        //Create a supertriangle that contains all other points
        //According to the book "Geometric tools for computer graphics" a reasonably sized triangle
        //is one that contains a circle that contains the axis-aligned bounding rectangle of the points
        //Is currently not used anywhere because our points are normalized to the range 0-1
        //and then we can make a supertriangle by just setting its size to 100
        public static Triangle2 GenerateSupertriangle(HashSet <MyVector2> points)
        {
            //Step 1. Create a AABB around the points
            AABB2 aabb = new AABB2(new List <MyVector2>(points));

            MyVector2 TL = new MyVector2(aabb.minX, aabb.maxY);
            MyVector2 TR = new MyVector2(aabb.maxX, aabb.maxY);
            MyVector2 BR = new MyVector2(aabb.maxX, aabb.minY);


            //Step2. Find the inscribed circle - the smallest circle that surrounds the AABB
            MyVector2 circleCenter = (TL + BR) * 0.5f;

            float circleRadius = MyVector2.Magnitude(circleCenter - TR);


            //Step 3. Create the smallest triangle that surrounds the circle
            //All edges of this triangle have the same length
            float halfSideLenghth = circleRadius / Mathf.Tan(30f * Mathf.Deg2Rad);

            //The center position of the bottom-edge
            MyVector2 t_B = new MyVector2(circleCenter.x, circleCenter.y - circleRadius);

            MyVector2 t_BL = new MyVector2(t_B.x - halfSideLenghth, t_B.y);
            MyVector2 t_BR = new MyVector2(t_B.x + halfSideLenghth, t_B.y);

            //The height from the bottom edge to the top vertex
            float triangleHeight = halfSideLenghth * Mathf.Tan(60f * Mathf.Deg2Rad);

            MyVector2 t_T = new MyVector2(circleCenter.x, t_B.y + triangleHeight);


            //The final triangle
            Triangle2 superTriangle = new Triangle2(t_BR, t_BL, t_T);

            return(superTriangle);
        }
        //
        // 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);
        }
Exemple #19
0
        //
        // Orient triangles so they have the correct orientation
        //
        public static HashSet <Triangle2> OrientTrianglesClockwise(HashSet <Triangle2> triangles)
        {
            //Convert to list or we will no be able to update the orientation
            List <Triangle2> trianglesList = new List <Triangle2>(triangles);

            for (int i = 0; i < trianglesList.Count; i++)
            {
                Triangle2 t = trianglesList[i];

                if (!_Geometry.IsTriangleOrientedClockwise(t.p1, t.p2, t.p3))
                {
                    t.ChangeOrientation();

                    trianglesList[i] = t;

                    //Debug.Log("Changed orientation");
                }
            }

            //Back to hashset
            triangles = new HashSet <Triangle2>(trianglesList);

            return(triangles);
        }
Exemple #20
0
        //If you have points on a convex hull, sorted one after each other
        public static HashSet <Triangle2> GetTriangles(List <MyVector2> points)
        {
            List <Triangle2> triangles = new List <Triangle2>();

            //This vertex will be a vertex in all triangles (except for some of those that forms colinear points)
            MyVector2 a = points[0];

            List <MyVector2> colinearPoints = new List <MyVector2>();

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

                //If we hadnt had colinear points this would have been the last line
                //triangles.Add(new Triangle2(a, b, c));

                //But we can always make a triangle if the corners of the triangle are co-linear
                //Then the triangle will be flat. So we have to check if b is one the line between a and c
                LeftOnRight orientation = Geometry.IsPoint_Left_On_Right_OfVector(a, c, b);

                if (orientation == LeftOnRight.On)
                {
                    colinearPoints.Add(b);

                    continue;
                }
                else
                {
                    //First check if we have colinear points that have to be added
                    if (colinearPoints.Count > 0)
                    {
                        //First add the colinear points
                        for (int j = 0; j < colinearPoints.Count; j++)
                        {
                            if (j == 0)
                            {
                                triangles.Add(new Triangle2(a, colinearPoints[j], c));
                            }
                            else
                            {
                                triangles.Add(new Triangle2(colinearPoints[j - 1], colinearPoints[j], c));
                            }
                        }

                        //Add the last triangle
                        triangles.Add(new Triangle2(colinearPoints[colinearPoints.Count - 1], b, c));

                        colinearPoints.Clear();
                    }
                    else
                    {
                        triangles.Add(new Triangle2(a, b, c));
                    }
                }
            }

            //We might still have colinear points to add
            if (colinearPoints.Count > 0)
            {
                //Remove the last triangle because it's not valid anymore
                Triangle2 lastTriangle = triangles[triangles.Count - 1];

                triangles.RemoveAt(triangles.Count - 1);

                //We also have to add the last point on the hull if its colinear
                MyVector2 lastOnHull   = points[points.Count - 1];
                MyVector2 lastColinear = colinearPoints[colinearPoints.Count - 1];

                LeftOnRight orientation = Geometry.IsPoint_Left_On_Right_OfVector(a, lastColinear, lastOnHull);

                if (orientation == LeftOnRight.On)
                {
                    colinearPoints.Add(lastOnHull);
                }

                //Add the colinear points

                //First we have to identify our new a - the point we will anchor all new triangles to
                //This new a is part of the triangle we removed
                MyVector2 newA = lastTriangle.p2;

                //We also add the first point on the hull to colinear points to make it easier to build triangles
                colinearPoints.Add(a);

                for (int i = 1; i < colinearPoints.Count; i++)
                {
                    MyVector2 b = colinearPoints[i - 1];
                    MyVector2 c = colinearPoints[i];

                    triangles.Add(new Triangle2(newA, b, c));

                    //Debug.DrawLine(colinearPoints[i].ToVector3(), Vector3.zero, Color.white, 3f);
                }
            }


            HashSet <Triangle2> finalTriangles = new HashSet <Triangle2>(triangles);

            return(finalTriangles);
        }
Exemple #21
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);
        }
Exemple #22
0
        //The hull may still intersect with the edge between the point on the hole and the "visible" point on the hull,
        //so the point on the hull might not be visible, so we should try to find a better point
        private static void FindActualVisibleVertexOnHull(EarClippingPolygon hull, EarClippingPolygon hole, MyVector2 intersectionVertex, ref MyVector2 visibleVertex)
        {
            //Form a triangle
            Triangle2 t = new Triangle2(hole.maxX_Vert, intersectionVertex, visibleVertex);

            //According to litterature, we check if a reflect vertex is within this triangle
            //If so, one of them is a better visible vertex on the hull
            List <MyVector2> reflectVertices = FindReflectVertices(hull, hole);

            //Pick the reflect vertex with the smallest angle
            //The angle is measure from the point on the hole towards:
            //- intersection point on the hull
            //- reflect vertex
            float minAngle = Mathf.Infinity;

            //If more than one reflect vertex have the same angle then pick the one closest to the point on the hole
            float minDistSqr = Mathf.Infinity;

            foreach (MyVector2 v in reflectVertices)
            {
                if (_Intersections.PointTriangle(t, v, includeBorder: true))
                {
                    float angle = MathUtility.AngleBetween(intersectionVertex - hole.maxX_Vert, v - hole.maxX_Vert);

                    //Debug.DrawLine(v.ToVector3(1f), hole.maxX_Vert.ToVector3(1f), Color.blue, 2f);

                    //Debug.DrawLine(intersectionVertex.ToVector3(1f), hole.maxX_Vert.ToVector3(1f), Color.black, 2f);

                    //TestAlgorithmsHelpMethods.DebugDrawCircle(v.ToVector3(1f), 0.3f, Color.blue);

                    //Debug.Log(angle * Mathf.Rad2Deg);

                    if (angle < minAngle)
                    {
                        minAngle = angle;

                        visibleVertex = v;

                        //We also need to calculate this in case a future point has the same angle
                        minDistSqr = MyVector2.SqrDistance(v, hole.maxX_Vert);

                        //Debug.Log(minDistanceSqr);

                        //TestAlgorithmsHelpMethods.DebugDrawCircle(v.ToVector3(1f), 0.3f, Color.green);
                    }
                    //If the angle is the same, then pick the vertex which is the closest to the point on the hull
                    else if (Mathf.Abs(angle - minAngle) < MathUtility.EPSILON)
                    {
                        float distSqr = MyVector2.SqrDistance(v, hole.maxX_Vert);

                        //Debug.Log(minDistanceSqr);

                        if (distSqr < minDistSqr)
                        {
                            visibleVertex = v;

                            minDistSqr = distSqr;

                            //TestAlgorithmsHelpMethods.DebugDrawCircle(v.ToVector3(1f), 0.3f, Color.red);

                            //Debug.Log(distSqr);
                        }
                    }
                }
            }

            //Will show how the holes are connected with the hull
            //Debug.DrawLine(visibleVertex.ToVector3(1f), hole.maxX_Vert.ToVector3(1f), Color.red, 5f);

            //TestAlgorithmsHelpMethods.DebugDrawCircle(visibleVertex.ToVector3(1f), 0.3f, Color.red);
            //TestAlgorithmsHelpMethods.DebugDrawCircle(hole.maxX_Vert.ToVector3(1f), 0.3f, Color.red);
        }
        //The grid is always a square
        //witdh - the width of the entire chunk
        //cells - the number of cells in one row
        public static HashSet <Triangle2> GenerateGrid(float width, int cells)
        {
            //We cant have a grid with 0 cells
            if (cells <= 0)
            {
                Debug.Log("The grid needs at least one cell");

                return(null);
            }

            //The width has to be greater than 0
            if (width <= 0f)
            {
                Debug.Log("The grid needs a positive width");

                return(null);
            }



            //The number of vertices in one row is always cells + 1
            int verticesInOneRow = cells + 1;

            //The width of one cell
            float cellWidth = width / (float)cells;

            //What's the half width of the grid?
            float halfWidth = width * 0.5f;

            //Generate vertices
            List <MyVector2> vertices = new List <MyVector2>();

            for (int i = 0; i < verticesInOneRow; i++)
            {
                for (int j = 0; j < verticesInOneRow; j++)
                {
                    MyVector2 vertexPos = new MyVector2(-halfWidth + i * cellWidth, -halfWidth + j * cellWidth);

                    vertices.Add(vertexPos);
                }
            }


            //Generate triangles by using the 1d list as if it was 2d
            //List<int> triangles = new List<int>();

            HashSet <Triangle2> triangles = new HashSet <Triangle2>();

            for (int i = 0; i < verticesInOneRow; i++)
            {
                for (int j = 0; j < verticesInOneRow; j++)
                {
                    //We cant build triangles from the first row/column
                    if (i == 0 || j == 0)
                    {
                        continue;
                    }
                    else
                    {
                        //Four vertices
                        int BL_pos = ConvertArrayPos(verticesInOneRow, i - 1, j - 1);
                        int BR_pos = ConvertArrayPos(verticesInOneRow, i - 0, j - 1);
                        int TL_pos = ConvertArrayPos(verticesInOneRow, i - 1, j - 0);
                        int TR_pos = ConvertArrayPos(verticesInOneRow, i - 0, j - 0);

                        MyVector2 BL = vertices[BL_pos];
                        MyVector2 BR = vertices[BR_pos];
                        MyVector2 TL = vertices[TL_pos];
                        MyVector2 TR = vertices[TR_pos];

                        //Triangle 1
                        //triangles.Add(TR);
                        //triangles.Add(BL);
                        //triangles.Add(TL);

                        //Triangle 2
                        //triangles.Add(TR);
                        //triangles.Add(BR);
                        //triangles.Add(BL);

                        Triangle2 t1 = new Triangle2(TR, BL, TL);
                        Triangle2 t2 = new Triangle2(TR, BR, BL);

                        triangles.Add(t1);
                        triangles.Add(t2);
                    }
                }
            }


            //Generate the mesh
            //Mesh mesh = new Mesh();

            //mesh.name = "Grid";

            //mesh.vertices = vertices.ToArray();
            //mesh.triangles = triangles.ToArray();

            //mesh.RecalculateBounds();
            //mesh.RecalculateNormals();


            return(triangles);
        }
        public static HalfEdgeData2 GenerateTriangulation(HashSet <MyVector2> points, HalfEdgeData2 triangulationData)
        {
            //We need more than 1 point to
            if (points.Count < 2)
            {
                Debug.Log("Can make a delaunay with sloan with less than 2 points");

                return(null);
            }



            //Step 1.Normalize the points to the range(0 - 1), which assumes we have more than 1 point
            //Is not being done here, we assume the points are already normalized



            //Step 2. Sort the points into bins to make it faster to find which triangle a point is in
            //TODO



            //Step 3. Establish the supertriangle
            //The report says that the supertriangle should be at (-100, 100) which is way
            //outside of the points which are in the range(0, 1)
            //So make sure you have NORMALIZED the points
            Triangle2 superTriangle = new Triangle2(new MyVector2(-100f, -100f), new MyVector2(100f, -100f), new MyVector2(0f, 100f));

            //Create the triangulation data with the only triangle we have
            HashSet <Triangle2> triangles = new HashSet <Triangle2>();

            triangles.Add(superTriangle);

            //Change to half-edge data structure
            _TransformBetweenDataStructures.Triangle2ToHalfEdge2(triangles, triangulationData);



            //Step 4. Loop over each point we want to insert and do Steps 5-7

            //These are for display purposes only
            int missedPoints = 0;
            int flippedEdges = 0;

            foreach (MyVector2 p in points)
            {
                //Step 5-7
                InsertNewPointInTriangulation(p, triangulationData, ref missedPoints, ref flippedEdges);
            }



            //Step 8. Delete the vertices belonging to the supertriangle
            RemoveSuperTriangle(superTriangle, triangulationData);



            //Step 9.Reset the coordinates to their original values because they are currently in the range (0,1)
            //Is being done outside of this method

            //TODO: replace this with StringBuilder
            string meshDataString = "Delaunay with sloan created a triangulation with: ";

            meshDataString += "Faces: " + triangulationData.faces.Count;
            meshDataString += " - Vertices: " + triangulationData.vertices.Count;
            meshDataString += " - Edges: " + triangulationData.edges.Count;
            meshDataString += " - Flipped egdes: " + flippedEdges;
            meshDataString += " - Missed points: " + missedPoints;

            Debug.Log(meshDataString);


            return(triangulationData);
        }
Exemple #25
0
        //The points on the hull (vertices) should be ordered counter-clockwise (and no doubles)
        //The holes should be ordered clockwise (and no doubles)
        //Optimize triangles means that we will get a better-looking triangulation, which resembles a constrained Delaunay triangulation
        public static HashSet <Triangle2> Triangulate(List <MyVector2> vertices, List <List <MyVector2> > allHoleVertices = null, bool optimizeTriangles = true)
        {
            //Validate the data
            if (vertices == null || vertices.Count <= 2)
            {
                Debug.LogWarning("Can't triangulate with Ear Clipping because too few vertices on the hull");

                return(null);
            }



            //Step -1. Merge the holes with the points on the hull into one big polygon with invisible edges between the holes and the hull
            if (allHoleVertices != null && allHoleVertices.Count > 0)
            {
                vertices = EarClippingHoleMethods.MergeHolesWithHull(vertices, allHoleVertices);
            }


            //TestAlgorithmsHelpMethods.DebugDrawCircle(vertices[29].ToVector3(1f), 0.3f, Color.red);


            //Step 0. Create a linked list connecting all vertices with each other which will make the calculations easier and faster
            List <LinkedVertex> verticesLinked = new List <LinkedVertex>();

            for (int i = 0; i < vertices.Count; i++)
            {
                LinkedVertex v = new LinkedVertex(vertices[i]);

                verticesLinked.Add(v);
            }

            //Link them to each other
            for (int i = 0; i < verticesLinked.Count; i++)
            {
                LinkedVertex v = verticesLinked[i];

                v.prevLinkedVertex = verticesLinked[MathUtility.ClampListIndex(i - 1, verticesLinked.Count)];
                v.nextLinkedVertex = verticesLinked[MathUtility.ClampListIndex(i + 1, verticesLinked.Count)];
            }

            //Debug.Log("Number of vertices: " + CountLinkedVertices(verticesLinked[0]));



            //Step 1. Find:
            //- Convex vertices (interior angle smaller than 180 degrees)
            //- Reflect vertices (interior angle greater than 180 degrees) so should maybe be called concave vertices?
            //Interior angle is the angle between two vectors inside the polygon if we move around the polygon counter-clockwise
            //If they are neither we assume they are reflect (or we will end up with odd triangulations)
            HashSet <LinkedVertex> convexVerts  = new HashSet <LinkedVertex>();
            HashSet <LinkedVertex> reflectVerts = new HashSet <LinkedVertex>();

            foreach (LinkedVertex v in verticesLinked)
            {
                bool isConvex = IsVertexConvex(v);

                if (isConvex)
                {
                    convexVerts.Add(v);
                }
                else
                {
                    reflectVerts.Add(v);
                }
            }



            //Step 2. Find the initial ears
            HashSet <LinkedVertex> earVerts = new HashSet <LinkedVertex>();

            //An ear is always a convex vertex
            foreach (LinkedVertex v in convexVerts)
            {
                //And we only need to test the reflect vertices
                if (IsVertexEar(v, reflectVerts))
                {
                    earVerts.Add(v);
                }
            }


            //Debug
            //DisplayVertices(earVertices);



            //Step 3. Build the triangles
            HashSet <Triangle2> triangulation = new HashSet <Triangle2>();

            //We know how many triangles we will get (number of vertices - 2) which is true for all simple polygons
            //This can be used to stop the algorithm
            int maxTriangles = verticesLinked.Count - 2;

            //Because we use a while loop, having an extra safety is always good so we dont get stuck in infinite loop
            int safety = 0;

            while (true)
            {
                //Pick an ear vertex and form a triangle
                LinkedVertex ear = GetEarVertex(earVerts, optimizeTriangles);

                if (ear == null)
                {
                    Debug.Log("Cant find ear");

                    break;
                }

                LinkedVertex v_prev = ear.prevLinkedVertex;
                LinkedVertex v_next = ear.nextLinkedVertex;

                Triangle2 t = new Triangle2(ear.pos, v_prev.pos, v_next.pos);

                //Try to flip this triangle according to Delaunay triangulation
                if (optimizeTriangles)
                {
                    OptimizeTriangle(t, triangulation);
                }
                else
                {
                    triangulation.Add(t);
                }



                //Check if we have found all triangles
                //This should also prevent us from getting stuck in an infinite loop
                if (triangulation.Count >= maxTriangles)
                {
                    break;
                }


                //If we havent found all triangles we have to reconfigure the data structure

                //Remove the ear we used to build a triangle
                convexVerts.Remove(ear);
                earVerts.Remove(ear);

                //Reconnect the vertices because one vertex has now been removed
                v_prev.nextLinkedVertex = v_next;
                v_next.prevLinkedVertex = v_prev;

                //Reconfigure the adjacent vertices
                ReconfigureAdjacentVertex(v_prev, convexVerts, reflectVerts, earVerts);
                ReconfigureAdjacentVertex(v_next, convexVerts, reflectVerts, earVerts);


                //if (safety > 4)
                //{
                //    Debug.Log(earVerts.Count);

                //    Debug.DrawLine(v_next.pos.ToVector3(), Vector3.zero, Color.blue, 3f);

                //    //Debug.Log(IsVertexEar(v_next, reflectVerts));

                //    Debug.Log(earVerts.Contains(v_next));

                //    break;
                //}



                safety += 1;

                if (safety > 50000)
                {
                    Debug.Log("Ear Clipping is stuck in an infinite loop!");

                    break;
                }
            }



            //Step 4. Improve triangulation
            //Some triangles may be too sharp, and if you want a nice looking triangle, you should try to swap edges
            //according to Delaunay triangulation
            //A report suggests that should be done while createing the triangulation
            //But maybe it's easier to do it afterwards with some standardized constrained Delaunay triangulation?
            //But that would also be stupid because then we could have used the constrained Delaunay from the beginning!


            return(triangulation);
        }
        //
        // Are two triangles intersecting in 2d space
        //
        public static bool TriangleTriangle2D(Triangle2 t1, Triangle2 t2, bool do_AABB_test)
        {
            bool isIntersecting = false;

            //Step 0. AABB intersection which may speed up the algorithm if the triangles are far apart
            if (do_AABB_test)
            {
                //Rectangle that covers t1
                AABB2 r1 = new AABB2(t1.MinX(), t1.MaxX(), t1.MinY(), t1.MaxY());

                //Rectangle that covers t2
                AABB2 r2 = new AABB2(t2.MinX(), t2.MaxX(), t2.MinY(), t2.MaxY());

                if (!AABB_AABB_2D(r1, r2))
                {
                    return(false);
                }
            }


            //Step 1. Line-line instersection

            //Line 1 of t1 against all lines of t2
            if (
                LineLine(t1.p1, t1.p2, t2.p1, t2.p2, true) ||
                LineLine(t1.p1, t1.p2, t2.p2, t2.p3, true) ||
                LineLine(t1.p1, t1.p2, t2.p3, t2.p1, true)
                )
            {
                isIntersecting = true;
            }
            //Line 2 of t1 against all lines of t2
            else if (
                LineLine(t1.p2, t1.p3, t2.p1, t2.p2, true) ||
                LineLine(t1.p2, t1.p3, t2.p2, t2.p3, true) ||
                LineLine(t1.p2, t1.p3, t2.p3, t2.p1, true)
                )
            {
                isIntersecting = true;
            }
            //Line 3 of t1 against all lines of t2
            else if (
                LineLine(t1.p3, t1.p1, t2.p1, t2.p2, true) ||
                LineLine(t1.p3, t1.p1, t2.p2, t2.p3, true) ||
                LineLine(t1.p3, t1.p1, t2.p3, t2.p1, true)
                )
            {
                isIntersecting = true;
            }

            //Now we can return if we are intersecting so we dont need to spend time testing something else
            if (isIntersecting)
            {
                return(isIntersecting);
            }


            //Step 2. Point-in-triangle intersection
            //We only need to test one corner from each triangle
            //If this point is not in the triangle, then the other points can't be in the triangle, because if this point is outside
            //and another point is inside, then the line between them would have been covered by step 1: line-line intersections test
            if (PointTriangle(t2, t1.p1, true) || PointTriangle(t1, t2.p1, true))
            {
                isIntersecting = true;
            }


            return(isIntersecting);
        }
        //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> points)
        {
            if (points.Count < 3)
            {
                Debug.Log("You need at least 3 points to form a triangle!");

                return(null);
            }


            //Step 0. Init the triangles we will return
            HashSet <Triangle2> triangles = new HashSet <Triangle2>();



            //Step 1. Sort the points
            List <MyVector2> sortedPoints = new List <MyVector2>(points);

            //OrderBy is always soring in ascending order - use OrderByDescending to get in the other order
            //sortedPoints = sortedPoints.OrderBy(n => n.x).ToList();

            //If we have colinear points we have to sort in both x and y
            sortedPoints = sortedPoints.OrderBy(n => n.x).ThenBy(n => n.y).ToList();



            //Step 2. Create the first triangle so we can start the algorithm because we need edges to look at
            //and see if they are visible

            //Pick the first two points in the sorted list - These are always a part of the first triangle
            MyVector2 p1 = sortedPoints[0];
            MyVector2 p2 = sortedPoints[1];

            //Remove them
            sortedPoints.RemoveAt(0);
            sortedPoints.RemoveAt(0);

            //The problem is the third point
            //If we have colinear points, then the third point in the sorted list is not always a valid point
            //to form a triangle because then it will be flat
            //So we have to look for a better point
            for (int i = 0; i < sortedPoints.Count; i++)
            {
                //We have found a non-colinear point
                LeftOnRight pointRelation = _Geometry.IsPoint_Left_On_Right_OfVector(p1, p2, sortedPoints[i]);

                if (pointRelation == LeftOnRight.Left || pointRelation == LeftOnRight.Right)
                {
                    MyVector2 p3 = sortedPoints[i];

                    //Remove this point
                    sortedPoints.RemoveAt(i);

                    //Build the first triangle
                    Triangle2 newTriangle = new Triangle2(p1, p2, p3);

                    triangles.Add(newTriangle);

                    break;
                }
            }

            //If we have finished search and not found a triangle, that means that all points
            //are colinear and we cant form any triangles
            if (triangles.Count == 0)
            {
                Debug.Log("All points you want to triangulate a co-linear");

                return(null);
            }



            //Step 3. Add the other points one-by-one

            //For each point we add we have to calculate a convex hull of the previous points
            //An optimization is to not use all points in the triangulation
            //to calculate the hull because many of them might be inside of the hull
            //So we will use the previous points on the hull and add the point we added last iteration
            //to generate the new convex hull

            //TODO a faster way to test if an edge is visible is to use "plane test" accoding to Quickhull paper???
            //On the other hand its faster to test just edges on the convex hull. The problem with using just planes
            //is that some points planes are "inside" of the convex polygon. Can we remove these points without
            //generating the convex hull?

            //First we need to init the convex hull
            HashSet <MyVector2> triangulatePoints = new HashSet <MyVector2>();

            foreach (Triangle2 t in triangles)
            {
                triangulatePoints.Add(t.p1);
                triangulatePoints.Add(t.p2);
                triangulatePoints.Add(t.p3);
            }

            //Calculate the first convex hull
            List <MyVector2> pointsOnHull = _ConvexHull.JarvisMarch_2D(triangulatePoints);

            //Add the other points one-by-one
            foreach (MyVector2 pointToAdd in sortedPoints)
            {
                bool couldFormTriangle = false;

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

                    //First we have to check if the points are colinear, then we cant form a triangle
                    LeftOnRight pointRelation = _Geometry.IsPoint_Left_On_Right_OfVector(hull_p1, hull_p2, pointToAdd);

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

                    //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(hull_p1, hull_p2, pointToAdd))
                    {
                        triangles.Add(new Triangle2(hull_p1, hull_p2, pointToAdd));

                        couldFormTriangle = true;
                    }
                }

                //Add the point to the list of points on the hull
                //Will re-generate the hull by using these points so dont worry that the
                //list is not valid anymore
                if (couldFormTriangle)
                {
                    pointsOnHull.Add(pointToAdd);

                    //Find the convex hull of the current triangulation
                    //It generates a counter-clockwise convex hull
                    pointsOnHull = _ConvexHull.JarvisMarch_2D(new HashSet <MyVector2>(pointsOnHull));
                }
                else
                {
                    Debug.Log("This point could not form any triangles " + pointToAdd.x + " " + pointToAdd.y);
                }
            }



            return(triangles);
        }
        //Used for debugging so we can see what's going on
        //public static List<MyVector2> GenerateConvexHull(List<MyVector2> originalPoints, bool includeColinearPoints, AABB normalizingbox, float dMax)


        public static List <MyVector2> GenerateConvexHull(List <MyVector2> originalPoints, bool includeColinearPoints)
        {
            List <MyVector2> pointsOnConvexHull = new List <MyVector2>();


            //Step 1.
            //Find the extreme points along each axis
            //This is similar to AABB but we need both x and y coordinates at each extreme point
            MyVector2 maxX = originalPoints[0];
            MyVector2 minX = originalPoints[0];
            MyVector2 maxY = originalPoints[0];
            MyVector2 minY = originalPoints[0];

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

                if (p.x > maxX.x)
                {
                    maxX = p;
                }
                if (p.x < minX.x)
                {
                    minX = p;
                }

                if (p.y > maxY.y)
                {
                    maxY = p;
                }
                if (p.y < minY.y)
                {
                    minY = p;
                }
            }


            //Step 2.
            //From the 4 extreme points, choose the pair that's furthest appart
            //These two are the first two points on the hull
            List <MyVector2> extremePoints = new List <MyVector2>()
            {
                maxX, minX, maxY, minY
            };

            //Just pick some points as start value
            MyVector2 p1 = maxX;
            MyVector2 p2 = minX;

            //Can use sqr because we are not interested in the exact distance
            float maxDistanceSqr = -Mathf.Infinity;

            //Loop through all points and compare them
            //TODO is comparing too many times: example maxX with maxY, and then maxY with maxX
            //https://stackoverflow.com/questions/12249051/unique-combinations-of-list
            for (int i = 0; i < extremePoints.Count; i++)
            {
                MyVector2 p1_test = extremePoints[i];

                for (int j = 0; j < extremePoints.Count; j++)
                {
                    //Dont compare the point with itself
                    if (i == j)
                    {
                        continue;
                    }

                    MyVector2 p2_test = extremePoints[j];

                    float distSqr = MyVector2.SqrDistance(p1_test, p2_test);

                    if (distSqr > maxDistanceSqr)
                    {
                        maxDistanceSqr = distSqr;

                        p1 = p1_test;
                        p2 = p2_test;
                    }
                }
            }

            //Convert the list to hashset to easier remove points which are on the hull or are inside of the hull
            HashSet <MyVector2> pointsToAdd = new HashSet <MyVector2>(originalPoints);

            //Remove the first 2 points on the hull
            pointsToAdd.Remove(p1);
            pointsToAdd.Remove(p2);


            //Step 3.
            //Find the third point on the hull, by finding the point which is the furthest
            //from the line between p1 and p2
            MyVector2 p3 = FindPointFurthestFromEdge(p1, p2, pointsToAdd);

            //Remove it from the points we want to add
            pointsToAdd.Remove(p3);


            //Step 4. Form the intitial triangle

            //Make sure the hull is oriented counter-clockwise
            Triangle2 tStart = new Triangle2(p1, p2, p3);

            if (_Geometry.IsTriangleOrientedClockwise(tStart.p1, tStart.p2, tStart.p3))
            {
                tStart.ChangeOrientation();
            }

            //New p1-p3
            p1 = tStart.p1;
            p2 = tStart.p2;
            p3 = tStart.p3;

            //pointsOnConvexHull.Add(p1);
            //pointsOnConvexHull.Add(p2);
            //pointsOnConvexHull.Add(p3);

            //Remove the points that we now know are within the hull triangle
            RemovePointsWithinTriangle(tStart, pointsToAdd);


            //Step 5.
            //Associate the rest of the points to their closest edge
            HashSet <MyVector2> edge_p1p2_points = new HashSet <MyVector2>();
            HashSet <MyVector2> edge_p2p3_points = new HashSet <MyVector2>();
            HashSet <MyVector2> edge_p3p1_points = new HashSet <MyVector2>();

            foreach (MyVector2 p in pointsToAdd)
            {
                //p1 p2
                LeftOnRight pointRelation1 = _Geometry.IsPoint_Left_On_Right_OfVector(p1, p2, p);

                if (pointRelation1 == LeftOnRight.On || pointRelation1 == LeftOnRight.Right)
                {
                    edge_p1p2_points.Add(p);

                    continue;
                }

                //p2 p3
                LeftOnRight pointRelation2 = _Geometry.IsPoint_Left_On_Right_OfVector(p2, p3, p);

                if (pointRelation2 == LeftOnRight.On || pointRelation2 == LeftOnRight.Right)
                {
                    edge_p2p3_points.Add(p);

                    continue;
                }

                //p3 p1
                //If the point hasnt been added yet, we know it belong to this edge
                edge_p3p1_points.Add(p);
            }


            //Step 6
            //For each edge, find the point furthest away and create a new triangle
            //and repeat the above steps by finding which points are inside of the hull
            //and which points are outside and belong to a new edge

            //Will automatically ignore the last point on this sub-hull to avoid doubles
            List <MyVector2> pointsOnHUll_p1p2 = CreateSubConvexHUll(p1, p2, edge_p1p2_points);

            List <MyVector2> pointsOnHUll_p2p3 = CreateSubConvexHUll(p2, p3, edge_p2p3_points);

            List <MyVector2> pointsOnHUll_p3p1 = CreateSubConvexHUll(p3, p1, edge_p3p1_points);


            //Create the final hull by combing the points
            pointsOnConvexHull.Clear();

            pointsOnConvexHull.AddRange(pointsOnHUll_p1p2);
            pointsOnConvexHull.AddRange(pointsOnHUll_p2p3);
            pointsOnConvexHull.AddRange(pointsOnHUll_p3p1);



            //Step 7. Add colinear points
            //I think the easiest way to add colinear points is to add them when the algorithm is finished
            if (includeColinearPoints)
            {
                pointsOnConvexHull = AddColinearPoints(pointsOnConvexHull, originalPoints);
            }


            //Debug.Log("Points on hull: " + pointsOnConvexHull.Count);


            return(pointsOnConvexHull);
        }