如何在一条曲线上,获取到距离指定点最近的点位置?
与上一篇 C# 曲线上的点(一) 获取指定横坐标对应的纵坐标值 类似,
我们通过曲线上获取的密集点,通过俩点之间连线,获取连线上最近的点。我们能够获取到一系列最近的点集,最近只取距离最小的点即可。
我们这样的算法是否精确呢?不算太精确,但是对于获取曲线上最近点,基本能满足。
斜率变化不大的线段,点不密集;斜率变化较大的线段,点相当密集,所以由此点集得到的最近点,是相对准确的。
实现方案,以下代码可以直接复用:
1 public static Point GetClosestPointOnPath(Point p, Geometry geometry)
2 {
3 PathGeometry pathGeometry = geometry.GetFlattenedPathGeometry();
4
5 var points = pathGeometry.Figures.Select(f => GetClosestPointOnPathFigure(f, p))
6 .OrderBy(t => t.Item2).FirstOrDefault();
7 return points?.Item1 ?? new Point(0, 0);
8 }
9
10 private static Tuple<Point, double> GetClosestPointOnPathFigure(PathFigure figure, Point p)
11 {
12 List<Tuple<Point, double>> closePoints = new List<Tuple<Point, double>>();
13 Point current = figure.StartPoint;
14 foreach (PathSegment s in figure.Segments)
15 {
16 PolyLineSegment segment = s as PolyLineSegment;
17 LineSegment line = s as LineSegment;
18 Point[] points;
19 if (segment != null)
20 {
21 points = segment.Points.ToArray();
22 }
23 else if (line != null)
24 {
25 points = new[] { line.Point };
26 }
27 else
28 {
29 throw new InvalidOperationException();
30 }
31 foreach (Point next in points)
32 {
33 Point closestPoint = GetClosestPointOnLine(current, next, p);
34 double d = (closestPoint - p).LengthSquared;
35 closePoints.Add(new Tuple<Point, double>(closestPoint, d));
36 current = next;
37 }
38 }
39 return closePoints.OrderBy(t => t.Item2).First();
40 }
俩点之间的连线,如果当前点在此方向的投影为负或者大于当前长度,则取俩侧的点:
1 private static Point GetClosestPointOnLine(Point start, Point end, Point p)
2 {
3 double length = (start - end).LengthSquared;
4 if (Math.Abs(length) < 0.01)
5 {
6 return start;
7 }
8 Vector v = end - start;
9 double param = (p - start) * v / length;
10 return (param < 0.0) ? start : (param > 1.0) ? end : (start + param * v);
11 }
效果图: