/// <summary> /// Determine the closest point on a segmentwise-defined line to a test point /// </summary> /// <param name="point">The test point</param> /// <param name="linePointList">List of points defining the line</param> /// <param name="extrusionAmountAbs">Absolute value of the extrusion distance</param> /// <param name="closestSegmentDifference">Start-to-end vector of the segment containing the closest point</param> /// <param name="getPoint">Deleate to return the position <see cref="Vector2"/> from the point type <typeparamref name="T"/></param> /// <param name="average">Delegate to average two points of type <typeparamref name="T"/></param> private static T ClosestPointAlongSegmentwiseLine <T>(Vector2 point, SegmentwisePointList <T> linePointList, float extrusionAmountAbs, Func <T, Vector2> getPoint, Func <T, T, float, T> average, out float fractionAlongClosestSegment, out Vector2 closestSegmentDifference, out int closestSegmentIndex) where T : struct { var linePoints = linePointList.Points; var linePointsMaximumDelta = linePointList.MaxSegmentDistance; var numSegments = linePoints.Count - 1; float minDistanceSquared = float.MaxValue; T closestPoint = new T(); closestSegmentDifference = Vector2.zero; closestSegmentIndex = 0; fractionAlongClosestSegment = 0f; //We'll look every *numSteps* points to see what's closest. We will increase it if the line point is much further away than the extrusion distance. int numSteps = 1; for (int i = 0; i < numSegments; i += numSteps) { float distanceSquaredToSegment; var segmentStart = linePoints[i]; var segmentEnd = linePoints[i + 1]; Vector2 startVector = getPoint(segmentStart); Vector2 endVector = getPoint(segmentEnd); var currentClosestFractionToSegment = LineSegmentUtil.ClosestFractionAlongSegment(startVector, endVector, point, out distanceSquaredToSegment); if (distanceSquaredToSegment < minDistanceSquared) { minDistanceSquared = distanceSquaredToSegment; closestPoint = average(segmentStart, segmentEnd, currentClosestFractionToSegment); fractionAlongClosestSegment = currentClosestFractionToSegment; closestSegmentDifference = endVector - startVector; closestSegmentIndex = i; } var distanceDiff = Math.Max(Math.Sqrt(distanceSquaredToSegment) - extrusionAmountAbs, 0); numSteps = Mathf.FloorToInt(Mathf.Max(1f, (float)distanceDiff / linePointsMaximumDelta)); } return(closestPoint); }
/// <summary> /// Determines if an extruded point is closer to a line (on a segment-by-segment basis) than the extrusion amount. /// </summary> /// <param name="extrudedPoint">Extruded test point</param> /// <param name="linePointList">List of points defining the line</param> /// <param name="extrusionAmount">The extrusion amount</param> internal static bool IsCloserThanExtrusionDistance_Segmentwise(ExtrudedPointUV extrudedPoint, SegmentwiseLinePointListUV linePointList, float extrusionAmount) { var linePoints = linePointList.Points; var linePointsMaximumDelta = linePointList.MaxSegmentDistance; var numSegments = linePoints.Count - 1; Vector2 point = extrudedPoint.Point; var segmentIndex = Math.Max(0, Math.Min(numSegments - 1, extrudedPoint.LinePointSegmentIndex)); var segmentIndex2 = Math.Max(0, Math.Min(numSegments - 1, extrudedPoint.LinePointSegmentIndex2)); bool isCloser = false; var extrusionAmountAbs = Mathf.Abs(extrusionAmount); var extrusionAmountSquared = extrusionAmount * extrusionAmount; //We'll look every *numSteps* points to see what's closest. We will increase it if the line point is much further away than the extrusion distance. int numSteps = 1; for (int i = 0; i < numSegments; i += numSteps) { if (i != segmentIndex && i != segmentIndex2) { isCloser = LineSegmentUtil.IsPointWithinDistanceOfSegment(linePoints[i].Point, linePoints[i + 1].Point, point, extrusionAmountSquared); } if (isCloser) { break; } else { var distanceDiff = (linePoints[i].Point - point).magnitude - extrusionAmountAbs; numSteps = Mathf.FloorToInt(Mathf.Max(1f, distanceDiff / linePointsMaximumDelta)); } } return(isCloser); }