wpf - 获取路径或多段线上最接近断开点的点

标签 wpf wpf-controls geometry line polyline

我有一个点和一条路径、折线或一组点来创建直线。

如何找到路径上最接近另一个断开点的点?

像任何 WPF 几何控件一样,可以轻松存储或传输路径/折线,但是这些控件是否带有 GetDistanceFrom 类型方法?有什么简单的方法可以实现这个目标吗?

最佳答案

以下方法 GetClosestPointOnPath() 是 @KirkBroadhurst 的 GetClosestPointOnLine() 方法的泛化,即它适用于任何路径几何图形,即直线、曲线、椭圆等.

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<Point, double> GetClosestPointOnPathFigure(PathFigure figure, Point p)
{
    List<Tuple<Point, double>> closePoints = new List<Tuple<Point,double>>();
    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<Point, double>(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);
}

这是一个小示例程序,演示如何使用此方法:

screen shot

MainWindow.xaml:

<Window x:Class="PathHitTestSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Canvas x:Name="canvas">
        <TextBlock Text="Left-click into this window" Margin="10" Foreground="Gray"/>
        <Path x:Name="path"
              Data="M96,63 C128,122 187,133 275,95 L271,158 C301,224 268,240 187,218 L74,218 95,270 384,268 C345,148 376,106 456,120 494,64 314,60 406,4 A10,10 30 0 1 300,20"
              Stroke="Black" StrokeThickness="1"
              HorizontalAlignment="Left" VerticalAlignment="Top"/>
        <Rectangle x:Name="marker" Fill="Red" Canvas.Left="0" Canvas.Top="0" Width="10" Height="10" Margin="-5,-5,0,0"
                   Visibility="Hidden"/>
    </Canvas>
</Window>

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 ...
    }
}

关于wpf - 获取路径或多段线上最接近断开点的点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18900642/

相关文章:

.net - Silverlight 对比WPF 对比Winforms 有什么特别适合我的目的?

wpf - 更改文本时更改样式

c# - 带图像的 WPF 单选按钮

algorithm - 给定点位于多边形内部或外部

c# - MVVM使用命令混淆将更改保存到 View 模型

c# - 以编程方式更改 StackPanel 在 Canvas 上的位置

wpf - 根据列表框中数据库中的记录动态添加行 mvvm mvvm light

c# - 数据触发器 wpf 不工作

matlab - 计算 2 个向量之间的角度,顺时针从 0 到 2*pi

geometry - 是否可以在不生成线的情况下测试像素是否在 bresenham 生成的线上?