public static void ClosestPointOnPolygon(RenderVertex3D[] rgv, Vertex2D pvin, bool fClosed, out Vertex2D pvOut, out int piSeg) { var count = rgv.Length; var minDist = Constants.FloatMax; piSeg = -1; // in case we are not next to the line pvOut = new Vertex2D(); var loopCount = count; if (!fClosed) { --loopCount; // Don"t check segment running from the end point to the beginning point } // Go through line segment, calculate distance from point to the line // then pick the shortest distance for (var i = 0; i < loopCount; ++i) { var p2 = i < count - 1 ? i + 1 : 0; var rgvi = new RenderVertex3D(); rgvi.Set(rgv[i].X, rgv[i].Y, rgv[i].Z); var rgvp2 = new RenderVertex3D(); rgvp2.Set(rgv[p2].X, rgv[p2].Y, rgv[p2].Z); var a = rgvi.Y - rgvp2.Y; var b = rgvp2.X - rgvi.X; var c = -(a * rgvi.X + b * rgvi.Y); var dist = MathF.Abs(a * pvin.X + b * pvin.Y + c) / MathF.Sqrt(a * a + b * b); if (dist < minDist) { // Assuming we got a segment that we are closet to, calculate the intersection // of the line with the perpendicular line projected from the point, // to find the closest point on the line var d = -b; var f = -(d * pvin.X + a * pvin.Y); var det = a * a - b * d; var invDet = det != 0.0f ? 1.0f / det : 0.0f; var intersectX = (b * f - a * c) * invDet; var intersectY = (c * d - a * f) * invDet; // If the intersect point lies on the polygon segment // (not out in space), then make this the closest known point if (intersectX >= MathF.Min(rgvi.X, rgvp2.X) - 0.1 && intersectX <= MathF.Max(rgvi.X, rgvp2.X) + 0.1 && intersectY >= MathF.Min(rgvi.Y, rgvp2.Y) - 0.1 && intersectY <= MathF.Max(rgvi.Y, rgvp2.Y) + 0.1) { minDist = dist; var seg = i; pvOut.X = intersectX; pvOut.Y = intersectY; piSeg = seg; } } } }
private static bool AdvancePoint(IReadOnlyList <IRenderVertex> rgv, IReadOnlyList <int> poly, int a, int b, int c, int pre, int post) { var pv1 = rgv[a]; var pv2 = rgv[b]; var pv3 = rgv[c]; var pvPre = rgv[pre]; var pvPost = rgv[post]; if (GetDot(pv1, pv2, pv3) < 0 || // Make sure angle created by new triangle line falls inside existing angles // If the existing angle is a concave angle, then new angle must be smaller, // because our triangle can"t have angles greater than 180 GetDot(pvPre, pv1, pv2) > 0 && GetDot(pvPre, pv1, pv3) < 0 || // convex angle, make sure new angle is smaller than it GetDot(pv2, pv3, pvPost) > 0 && GetDot(pv1, pv3, pvPost) < 0) { return(false); } // Now make sure the interior segment of this triangle (line ac) does not // intersect the polygon anywhere // sort our static line segment var minX = MathF.Min(pv1.GetX(), pv3.GetX()); var maxX = MathF.Max(pv1.GetX(), pv3.GetX()); var minY = MathF.Min(pv1.GetY(), pv3.GetY()); var maxY = MathF.Max(pv1.GetY(), pv3.GetY()); for (var i = 0; i < poly.Count; ++i) { var pvCross1 = rgv[poly[i]]; var pvCross2 = rgv[poly[i < poly.Count - 1 ? i + 1 : 0]]; if (pvCross1 != pv1 && pvCross2 != pv1 && pvCross1 != pv3 && pvCross2 != pv3 && (pvCross1.GetY() >= minY || pvCross2.GetY() >= minY) && (pvCross1.GetY() <= maxY || pvCross2.GetY() <= maxY) && (pvCross1.GetX() >= minX || pvCross2.GetX() >= minX) && (pvCross1.GetX() <= maxX || pvCross2.GetX() <= maxX) && LinesIntersect(pv1, pv3, pvCross1, pvCross2)) { return(false); } } return(true); }
private static bool LinesIntersect(IRenderVertex start1, IRenderVertex start2, IRenderVertex end1, IRenderVertex end2) { var x1 = start1.GetX(); var y1 = start1.GetY(); var x2 = start2.GetX(); var y2 = start2.GetY(); var x3 = end1.GetX(); var y3 = end1.GetY(); var x4 = end2.GetX(); var y4 = end2.GetY(); var d123 = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1); if (d123 == 0.0) // p3 lies on the same line as p1 and p2 { return(x3 >= MathF.Min(x1, x2) && x3 <= MathF.Max(x2, x1)); } var d124 = (x2 - x1) * (y4 - y1) - (x4 - x1) * (y2 - y1); if (d124 == 0.0) // p4 lies on the same line as p1 and p2 { return(x4 >= MathF.Min(x1, x2) && x4 <= MathF.Max(x2, x1)); } if (d123 * d124 >= 0.0) { return(false); } var d341 = (x3 - x1) * (y4 - y1) - (x4 - x1) * (y3 - y1); if (d341 == 0.0) // p1 lies on the same line as p3 and p4 { return(x1 >= MathF.Min(x3, x4) && x1 <= MathF.Max(x3, x4)); } var d342 = d123 - d124 + d341; if (d342 == 0.0) // p1 lies on the same line as p3 and p4 { return(x2 >= MathF.Min(x3, x4) && x2 <= MathF.Max(x3, x4)); } return(d341 * d342 < 0.0); }