Get point on a path or polyline which is closest to a disconnected point

后端 未结 2 1867
小鲜肉
小鲜肉 2020-12-09 13:40

I have a point and a path, polyline, or set of points to create lines.

How can I find the point on my path which closest to another disconnected point?

It\'

2条回答
  •  暗喜
    暗喜 (楼主)
    2020-12-09 14:29

    The following method GetClosestPointOnPath() is a generalization of @KirkBroadhurst's GetClosestPointOnLine() method, i.e. it works with any path geometry, i.e. lines, curves, ellipses, etc.

    public Point GetClosestPointOnPath(Point p, Geometry geometry)
    {
        PathGeometry pathGeometry = geometry.GetFlattenedPathGeometry();
    
        var points = pathGeometry.Figures.Select(f => GetClosestPointOnPathFigure(f, p))
            .OrderBy(t => t.Item2).FirstOrDefault();
        return (points == null) ? new Point(0, 0) : points.Item1;
    }
    
    private Tuple GetClosestPointOnPathFigure(PathFigure figure, Point p)
    {
        List> closePoints = new List>();
        Point current = figure.StartPoint;
        foreach (PathSegment s in figure.Segments)
        {
            PolyLineSegment segment = s as PolyLineSegment;
            LineSegment line = s as LineSegment;
            Point[] points;
            if (segment != null)
            {
                points = segment.Points.ToArray();
            }
            else if (line != null)
            {
                points = new[] { line.Point };
            }
            else
            {
                throw new InvalidOperationException("Unexpected segment type");
            }
            foreach (Point next in points)
            {
                Point closestPoint = GetClosestPointOnLine(current, next, p);
                double d = (closestPoint - p).LengthSquared;
                closePoints.Add(new Tuple(closestPoint, d));
                current = next;
            }
        }
        return closePoints.OrderBy(t => t.Item2).First();
    }
    
    private Point GetClosestPointOnLine(Point start, Point end, Point p)
    {
        double length = (start - end).LengthSquared;
        if (length == 0.0)
        {
            return start;
        }
        Vector v = end - start;
        double param = (p - start) * v / length;
        return (param < 0.0) ? start : (param > 1.0) ? end : (start + param * v);
    }
    

    Here is a small sample program that demonstrates how to use this method:

    screen shot

    MainWindow.xaml:

    
        
            
            
            
        
    
    

    MainWindow.xaml.cs:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    
    namespace PathHitTestSample
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
            {
                Point p = e.GetPosition(canvas);
                Point pointOnPath = GetClosestPointOnPath(p, path.Data);
                marker.Visibility = Visibility.Visible;
                Canvas.SetLeft(marker, pointOnPath.X);
                Canvas.SetTop(marker, pointOnPath.Y);
            }
    
            ... add above methods here ...
        }
    }
    

提交回复
热议问题