Wpf 获取距离断开点最近的路径或多段线上的点
我有一个点和一条路径、多段线或一组点来创建直线 如何在路径上找到距离另一个断开连接点最近的点Wpf 获取距离断开点最近的路径或多段线上的点,wpf,wpf-controls,geometry,line,polyline,Wpf,Wpf Controls,Geometry,Line,Polyline,我有一个点和一条路径、多段线或一组点来创建直线 如何在路径上找到距离另一个断开连接点最近的点 作为任何WPF几何体控件,存储或传输路径/多段线都很容易,但是这些控件中是否有任何一个带有GetDistanceFromtype方法?有什么简单的方法可以实现这个目标吗?这是我作为解决方案实现的算法。如果你花了超过十分钟的时间思考,这里没有什么“不明显的” 我将参考您可以在此处找到的距离算法: 将多段线收集为一组有序线 遍历此集合,测试从目标点到每条线的距离 确定最近的直线后,运行下面的命令以确定直线上
作为任何WPF几何体控件,存储或传输路径/多段线都很容易,但是这些控件中是否有任何一个带有
GetDistanceFrom
type方法?有什么简单的方法可以实现这个目标吗?这是我作为解决方案实现的算法。如果你花了超过十分钟的时间思考,这里没有什么“不明显的”
我将参考您可以在此处找到的距离算法:
private Point GetClosestPointOnLine(Point start, Point end, Point p)
{
var length = (start - end).LengthSquared;
if (length == 0.0)
return start;
// Consider the line extending the segment, parameterized as v + t (w - v).
// We find projection of point p onto the line.
// It falls where t = [(p-v) . (w-v)] / |w-v|^2
var t = (p - start) * (end - start) / length;
if (t < 0.0)
return start; // Beyond the 'v' end of the segment
else if (t > 1.0)
return end; // Beyond the 'w' end of the segment
// Projection falls on the segment
var projection = start + t * (end - start);
return projection;
}
专用点GetClosestPointOnLine(点开始、点结束、点p)
{
变量长度=(开始-结束).LengthSquared;
如果(长度==0.0)
返回启动;
/考虑扩展该段的行,参数化为V+T(W-V)。
//我们找到点p在直线上的投影。
//它落在t=[(p-v)。(w-v)]/| w-v |^2处
var t=(p-开始)*(结束-开始)/长度;
如果(t<0.0)
return start;//超出段的“v”结尾
否则如果(t>1.0)
return end;//超出段的“w”端
//投影落在片段上
var投影=开始+t*(结束-开始);
回归投影;
}
以下方法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);
}
谢谢你详尽的回答。我在为路径和直线使用一组代码时遇到了困难,尤其是将多段线的简单直线集合转换为更复杂的几何图形/路径格式时。我运行了您的代码,它工作得非常出色。但是,当使用边距定位点时,我无法使其工作。如果可以的话,请帮助我。我仔细思考了这个问题,它让我想起了
PathGeometry
class:。最奇怪的是我真的需要用它。。。
<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>
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 ...
}
}