Ejemplo n.º 1
0
        //
        // Find the closest point on a line segment from a point
        //
        //From https://www.youtube.com/watch?v=KHuI9bXZS74
        //Maybe better version https://stackoverflow.com/questions/3120357/get-closest-point-to-a-line
        public static MyVector2 GetClosestPointOnLineSegment(MyVector2 a, MyVector2 b, MyVector2 p)
        {
            MyVector2 a_p = p - a;
            MyVector2 a_b = b - a;

            //This is using vector projections???

            //Square magnitude of AB vector
            float sqrMagnitudeAB = MyVector2.SqrMagnitude(a_b);

            //The DOT product of a_p and a_b
            float ABAPproduct = MyVector2.Dot(a_p, a_b);

            //The normalized "distance" from a to the closest point
            float distance = ABAPproduct / sqrMagnitudeAB;

            //This point may not be on the line segment, if so return one of the end points
            //Check if P projection is over vectorAB
            if (distance < 0)
            {
                return(a);
            }
            else if (distance > 1)
            {
                return(b);
            }
            else
            {
                return(a + a_b * distance);
            }
        }
        //Insert intersection vertex
        private static ClipVertex InsertIntersectionVertex(MyVector2 a, MyVector2 b, MyVector2 intersectionPoint, ClipVertex currentVertex)
        {
            //Calculate alpha which is how far the intersection coordinate is between a and b
            //so we can insert this vertex at the correct position
            //pos = start + dir * alpha
            float alpha = MyVector2.SqrMagnitude(a - intersectionPoint) / MyVector2.SqrMagnitude(a - b);

            //Debug.Log(alpha);

            //Create a new vertex
            ClipVertex intersectionVertex = new ClipVertex(intersectionPoint);

            intersectionVertex.isIntersection = true;
            intersectionVertex.alpha          = alpha;

            //Now we need to insert this intersection point somewhere after currentVertex
            ClipVertex insertAfterThisVertex = currentVertex;

            int safety = 0;

            while (true)
            {
                //If the next vertex is an intersectionvertex with a higher alpha
                //or if the next vertex is not an intersectionvertex, we cant improve, so break
                if (insertAfterThisVertex.next.alpha > alpha || !insertAfterThisVertex.next.isIntersection)
                {
                    break;
                }

                insertAfterThisVertex = insertAfterThisVertex.next;

                safety += 1;

                if (safety > 100000)
                {
                    Debug.Log("Stuck in loop in insert intersection vertices");

                    break;
                }
            }

            //Connect the vertex to the surrounding vertices
            intersectionVertex.next = insertAfterThisVertex.next;

            intersectionVertex.prev = insertAfterThisVertex;

            insertAfterThisVertex.next.prev = intersectionVertex;

            insertAfterThisVertex.next = intersectionVertex;

            return(intersectionVertex);
        }
Ejemplo n.º 3
0
        //
        // Line-point calculations
        //
        //From https://stackoverflow.com/questions/3120357/get-closest-point-to-a-line
        //and https://www.youtube.com/watch?v=_ENEsV_kNx8
        public static MyVector2 GetClosestPointOnLine(Edge2 e, MyVector2 p, bool withinSegment)
        {
            MyVector2 a = e.p1;
            MyVector2 b = e.p2;

            //Assume the line goes from a to b
            MyVector2 ab = b - a;
            //Vector from "start" of the line to the point outside of line
            MyVector2 ap = p - a;

            //Scalar projection https://en.wikipedia.org/wiki/Scalar_projection
            //The scalar projection is a scalar, equal to the length of the orthogonal projection of ap on ab, with a negative sign if the projection has an opposite direction with respect to ab.
            //scalarProjection = Dot(ap, ab) / Magnitude(ab) where the magnitude of ab is the distance between a and b
            //If ab is normalized, we get scalarProjection = Dot(ap, ab)

            //The distance from a to q (the closes point on the line):
            //float aq_distance = MyVector2.Dot(ap, ab) / MyVector2.Magnitude(ab);

            //To get the closest point on the line:
            //MyVector2 q = a + MyVector2.Normalize(ab) * aq_distance;


            //Can we do better?
            //Magnitude is defined as: Mathf.Sqrt((ab * ab))
            //Normalization is defined as (ab / magnitude(ab))
            //We get: q = a + (ab / magnitude(ab)) * (1 / magnitude(ab)) * dot(ap, ab)
            //Ignore the q and the dot and we get: (ab / Mathf.Sqrt((ab * ab))) * (1 / Mathf.Sqrt((ab * ab))) = ab / (ab * ab)
            //So we can use the square magnitude of ab and then we don't need to normalize ab (to get q), so we save two square roots, which is good because square root is a slow operation

            //The normalized "distance" from a to the closest point, so between 0 and 1 if we are within the line segment
            float distance = MyVector2.Dot(ap, ab) / MyVector2.SqrMagnitude(ab);

            //This point may not be on the line segment, if so return one of the end points
            float epsilon = MathUtility.EPSILON;

            if (withinSegment && distance < 0f - epsilon)
            {
                return(a);
            }
            else if (withinSegment && distance > 1f + epsilon)
            {
                return(b);
            }
            else
            {
                //This works because a_b is not normalized and distance is [0,1] if distance is within ab
                return(a + ab * distance);
            }
        }
Ejemplo n.º 4
0
        //
        // Is a point p between point a and b (we assume all 3 are on the same line)
        //
        public static bool IsPointBetweenPoints(MyVector2 a, MyVector2 b, MyVector2 p)
        {
            bool isBetween = false;

            //Entire line segment
            MyVector2 ab = b - a;
            //The intersection and the first point
            MyVector2 ap = p - a;

            //Need to check 2 things:
            //1. If the vectors are pointing in the same direction = if the dot product is positive
            //2. If the length of the vector between the intersection and the first point is smaller than the entire line
            if (MyVector2.Dot(ab, ap) > 0f && MyVector2.SqrMagnitude(ab) >= MyVector2.SqrMagnitude(ap))
            {
                isBetween = true;
            }

            return(isBetween);
        }
Ejemplo n.º 5
0
        //
        // Calculate the center of circle in 2d space given three coordinates - Simple version
        //
        //From the book "Geometric Tools for Computer Graphics"
        public static MyVector2 CalculateCircleCenter(MyVector2 a, MyVector2 b, MyVector2 c)
        {
            //Make sure the triangle a-b-c is counterclockwise
            if (!IsTriangleOrientedClockwise(a, b, c))
            {
                //Swap two vertices to change orientation
                (a, b) = (b, a);

                //Debug.Log("Swapped vertices");
            }


            //The area of the triangle
            float X_1 = b.x - a.x;
            float X_2 = c.x - a.x;
            float Y_1 = b.y - a.y;
            float Y_2 = c.y - a.y;

            float A = 0.5f * MathUtility.Det2(X_1, Y_1, X_2, Y_2);

            //Debug.Log(A);


            //The center coordinates:
            //float L_10 = MyVector2.Magnitude(b - a);
            //float L_20 = MyVector2.Magnitude(c - a);

            //float L_10_square = L_10 * L_10;
            //float L_20_square = L_20 * L_20;

            float L_10_square = MyVector2.SqrMagnitude(b - a);
            float L_20_square = MyVector2.SqrMagnitude(c - a);

            float one_divided_by_4_A = 1f / (4f * A);

            float x = a.x + one_divided_by_4_A * ((Y_2 * L_10_square) - (Y_1 * L_20_square));
            float y = a.y + one_divided_by_4_A * ((X_1 * L_20_square) - (X_2 * L_10_square));

            MyVector2 center = new MyVector2(x, y);

            return(center);
        }
        //Add colinear points to the convex hull
        private static List <MyVector2> AddColinearPoints(List <MyVector2> pointsOnConvexHull, List <MyVector2> points)
        {
            List <MyVector2> pointsOnConvexHull_IncludingColinear = new List <MyVector2>();

            //From the original points we dont have to remove the points that are on the convex hull
            //because they will be added anyway

            //Loop through all edges
            for (int i = 0; i < pointsOnConvexHull.Count; i++)
            {
                MyVector2 p1 = pointsOnConvexHull[i];
                MyVector2 p2 = pointsOnConvexHull[MathUtility.ClampListIndex(i + 1, pointsOnConvexHull.Count)];

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

                foreach (MyVector2 p in points)
                {
                    LeftOnRight pointRelation = _Geometry.IsPoint_Left_On_Right_OfVector(p1, p2, p);

                    if (pointRelation == LeftOnRight.On)
                    {
                        colinearPoints.Add(p);
                    }
                }

                //Sort the colinear points so the are added on the correct order from p1 to p2
                colinearPoints = colinearPoints.OrderBy(n => MyVector2.SqrMagnitude(n - p1)).ToList();

                //Remove the last colinear point to avoid doubles
                colinearPoints.RemoveAt(colinearPoints.Count - 1);

                pointsOnConvexHull_IncludingColinear.AddRange(colinearPoints);
            }


            return(pointsOnConvexHull_IncludingColinear);
        }
        public static List <MyVector2> GenerateConvexHull(List <MyVector2> points)
        {
            List <MyVector2> pointsOnConvexHull = new List <MyVector2>();


            //Step 0. Normalize the data to range [0, 1] or everything will break at larger sizes :(
            //Make sure the data is already normalized!!!



            //Step 1. Find the vertex with the smallest x coordinate
            //If several points have the same x coordinate, find the one with the smallest y
            MyVector2 startPos = points[0];

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

                //Because of precision issues, we use a small value to test if they are the same
                if (testPos.x < startPos.x || ((Mathf.Abs(testPos.x - startPos.x) < MathUtility.EPSILON && testPos.y < startPos.y)))
                {
                    startPos = points[i];
                }
            }

            //This vertex is always on the convex hull
            pointsOnConvexHull.Add(startPos);

            //But we can't remove it from the list of all points because we need it to stop the algorithm
            //points.Remove(startPos);



            //Step 2. Loop to find the other points on the hull
            MyVector2 previousPoint = pointsOnConvexHull[0];

            int counter = 0;

            while (true)
            {
                //We might have colinear points, so we need a list to save all points added this iteration
                List <MyVector2> pointsToAddToTheHull = new List <MyVector2>();


                //Pick next point randomly
                MyVector2 nextPoint = points[Random.Range(0, points.Count)];

                //If we are coming from the first point on the convex hull
                //then we are not allowed to pick it as next point, so we have to try again
                if (previousPoint.Equals(pointsOnConvexHull[0]) && nextPoint.Equals(pointsOnConvexHull[0]))
                {
                    counter += 1;

                    continue;
                }

                //This point is assumed to be on the convex hull
                pointsToAddToTheHull.Add(nextPoint);


                //But this randomly selected point might not be the best next point, so we have to see if we can improve
                //by finding a point that is more to the right
                //We also have to check if this point has colinear points if it happens to be on the hull
                for (int i = 0; i < points.Count; i++)
                {
                    MyVector2 testPoint = points[i];

                    //Dont test the point we picked randomly
                    //Or the point we are coming from which might happen when we move from the first point on the hull
                    if (testPoint.Equals(nextPoint) || testPoint.Equals(previousPoint))
                    {
                        continue;
                    }

                    //Where is the test point in relation to the line between the point we are coming from
                    //which we know is on the hull, and the point we think is on the hull
                    LeftOnRight pointRelation = _Geometry.IsPoint_Left_On_Right_OfVector(previousPoint, nextPoint, testPoint);

                    //The test point is on the line, so we have found a colinear point
                    if (pointRelation == LeftOnRight.On)
                    {
                        pointsToAddToTheHull.Add(testPoint);
                    }
                    //To the right = better point, so pick it as next point we want to test if it is on the hull
                    else if (pointRelation == LeftOnRight.Right)
                    {
                        nextPoint = testPoint;

                        //Clear colinear points because they belonged to the old point which was worse
                        pointsToAddToTheHull.Clear();

                        pointsToAddToTheHull.Add(nextPoint);

                        //We dont have to start over because the previous points weve gone through were worse
                    }
                    //To the left = worse point so do nothing
                }



                //Sort this list, so we can add the colinear points in correct order
                pointsToAddToTheHull = pointsToAddToTheHull.OrderBy(n => MyVector2.SqrMagnitude(n - previousPoint)).ToList();

                pointsOnConvexHull.AddRange(pointsToAddToTheHull);

                //Remove the points that are now on the convex hull, which should speed up the algorithm
                //Or will it be slower because it also takes some time to remove points?
                for (int i = 0; i < pointsToAddToTheHull.Count; i++)
                {
                    points.Remove(pointsToAddToTheHull[i]);
                }


                //The point we are coming from in the next iteration
                previousPoint = pointsOnConvexHull[pointsOnConvexHull.Count - 1];


                //Have we found the first point on the hull? If so we have completed the hull
                if (previousPoint.Equals(pointsOnConvexHull[0]))
                {
                    //Then remove it because it is the same as the first point, and we want a convex hull with no duplicates
                    pointsOnConvexHull.RemoveAt(pointsOnConvexHull.Count - 1);

                    //Stop the loop!
                    break;
                }


                //Safety
                if (counter > 100000)
                {
                    Debug.Log("Stuck in endless loop when generating convex hull with jarvis march");

                    break;
                }

                counter += 1;
            }



            //Dont forget to unnormalize the points!



            return(pointsOnConvexHull);
        }