// // Is a point intersecting with a circle? // //Is a point d inside, outside or on the same circle where a, b, c are all on the circle's edge public static IntersectionCases PointCircle(MyVector2 a, MyVector2 b, MyVector2 c, MyVector2 testPoint) { //Center of circle MyVector2 circleCenter = _Geometry.CalculateCircleCenter(a, b, c); //The radius sqr of the circle float radiusSqr = MyVector2.SqrDistance(a, circleCenter); //The distance sqr from the point to the circle center float distPointCenterSqr = MyVector2.SqrDistance(testPoint, circleCenter); //Add/remove a small value becuse we will never be exactly on the edge because of floating point precision issues //Mutiply epsilon by two because we are using sqr root??? if (distPointCenterSqr < radiusSqr - MathUtility.EPSILON * 2f) { return(IntersectionCases.IsInside); } else if (distPointCenterSqr > radiusSqr + MathUtility.EPSILON * 2f) { return(IntersectionCases.NoIntersection); } else { return(IntersectionCases.IsOnEdge); } }
//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); }
//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); }
//Find a vertex on the hull that should be visible from the hole private static void FindVisibleVertexOnHUll(EarClippingPolygon hull, EarClippingPolygon hole, Edge2 line_hole_to_outside, out int closestEdge, out MyVector2 visibleVertex) { //The first and second point on the hull is defined as edge 0, and so on... closestEdge = -1; //The vertex that should be visible to the hole (which is the max of the line that's intersecting with the line) visibleVertex = new MyVector2(-1f, -1f); //Do line-line intersection to find intersectionVertex which is the point of intersection that's the closest to the hole MyVector2 intersectionVertex = new MyVector2(-1f, -1f); float minDistanceSqr = Mathf.Infinity; List <MyVector2> verticesHull = hull.Vertices; for (int i = 0; i < verticesHull.Count; i++) { MyVector2 p1_hull = verticesHull[i]; MyVector2 p2_hull = verticesHull[MathUtility.ClampListIndex(i + 1, verticesHull.Count)]; //We dont need to check this line if it's to the left of the point on the hole //If so they cant intersect if (p1_hull.x < hole.maxX_Vert.x && p2_hull.x < hole.maxX_Vert.x) { continue; } Edge2 line_hull = new Edge2(p1_hull, p2_hull); bool isIntersecting = _Intersections.LineLine(line_hole_to_outside, line_hull, includeEndPoints: true); //Here we can maybe add a check if any of the vertices is on the line??? if (isIntersecting) { MyVector2 testIntersectionVertex = _Intersections.GetLineLineIntersectionPoint(line_hole_to_outside, line_hull); //if (hole.id == 3) TestAlgorithmsHelpMethods.DebugDrawCircle(testIntersectionVertex.ToVector3(), 0.2f, Color.green); float distanceSqr = MyVector2.SqrDistance(line_hole_to_outside.p1, testIntersectionVertex); if (distanceSqr < minDistanceSqr) { closestEdge = i; minDistanceSqr = distanceSqr; intersectionVertex = testIntersectionVertex; } } } //This means we couldn't find a closest edge if (closestEdge == -1) { Debug.Log("Couldn't find a closest edge to hole"); return; } //But we can't connect the hole with this intersection point, so we need to find a vertex which is visible from the hole //The closest edge has two vertices. Pick the one with the highest x-value, which is the vertex //that should be visible from the hole MyVector2 p1 = hull.Vertices[closestEdge]; MyVector2 p2 = hull.Vertices[MathUtility.ClampListIndex(closestEdge + 1, hull.Vertices.Count)]; visibleVertex = p1; //They are the same so pick the one that is the closest if (Mathf.Abs(p1.x - p2.x) < MathUtility.EPSILON) { float hole_p1 = MyVector2.SqrDistance(hole.maxX_Vert, p1); float hole_p2 = MyVector2.SqrDistance(hole.maxX_Vert, p2); visibleVertex = hole_p1 < hole_p2 ? p1 : p2; } else if (p2.x > visibleVertex.x) { visibleVertex = p2; } if (hole.id == 3) { //TestAlgorithmsHelpMethods.DebugDrawCircle(intersectionVertex.ToVector3(), 0.4f, Color.black); //TestAlgorithmsHelpMethods.DebugDrawCircle(visibleVertex.ToVector3(), 0.4f, Color.green); } //But the hull may still intersect with this edge between the point on the hole and the visible point on the hull, //so the visible point on the hull might not be visible after all //So we might have to find a new point which is visible FindActualVisibleVertexOnHull(hull, hole, intersectionVertex, ref visibleVertex); }