점과 선 세그먼트 사이의 최단 거리를 찾으려면 기본 기능이 필요합니다. 원하는 언어로 솔루션을 자유롭게 작성하십시오. (Javascript)를 사용중인 것으로 번역 할 수 있습니다.
편집 : 내 선 세그먼트는 두 개의 끝점으로 정의됩니다. 따라서 내 선분 AB
은 두 점 A (x1,y1)
과로 정의됩니다 B (x2,y2)
. 이 선분과 점 사이의 거리를 찾으려고합니다 C (x3,y3)
. 내 지오메트리 기술이 녹슨 것이므로 내가 본 예제가 혼란스러워서 죄송합니다.
답변
엘리, 정하신 코드가 잘못되었습니다. 선분이있는 선 근방이지만 선분의 한쪽 끝에서 멀리 떨어진 지점은 선분 근처에서 잘못 판단됩니다. 업데이트 : 언급 된 잘못된 답변은 더 이상 허용되는 답변이 아닙니다.
다음은 C ++에서 올바른 코드입니다. class vec2 {float x,y;}
기본적으로 2D- 벡터 클래스를 가정합니다 . 기본적으로 연산자는 더하기, 빼기, 크기 조절 등의 거리와 내적과 함수 (예 :)를 갖습니다 x1 x2 + y1 y2
.
float minimum_distance(vec2 v, vec2 w, vec2 p) {
// Return minimum distance between line segment vw and point p
const float l2 = length_squared(v, w); // i.e. |w-v|^2 - avoid a sqrt
if (l2 == 0.0) return distance(p, v); // v == w case
// 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
// We clamp t from [0,1] to handle points outside the segment vw.
const float t = max(0, min(1, dot(p - v, w - v) / l2));
const vec2 projection = v + t * (w - v); // Projection falls on the segment
return distance(p, projection);
}
편집 : Javascript 구현이 필요했기 때문에 여기에 종속성 (또는 주석이 없지만 위의 직접 포트)이 없습니다. 포인트와 객체로 표현 x
하고 y
속성.
function sqr(x) { return x * x }
function dist2(v, w) { return sqr(v.x - w.x) + sqr(v.y - w.y) }
function distToSegmentSquared(p, v, w) {
var l2 = dist2(v, w);
if (l2 == 0) return dist2(p, v);
var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
t = Math.max(0, Math.min(1, t));
return dist2(p, { x: v.x + t * (w.x - v.x),
y: v.y + t * (w.y - v.y) });
}
function distToSegment(p, v, w) { return Math.sqrt(distToSegmentSquared(p, v, w)); }
편집 2 : Java 버전이 필요했지만 더 중요하게는 2d 대신 3d로 필요했습니다.
float dist_to_segment_squared(float px, float py, float pz, float lx1, float ly1, float lz1, float lx2, float ly2, float lz2) {
float line_dist = dist_sq(lx1, ly1, lz1, lx2, ly2, lz2);
if (line_dist == 0) return dist_sq(px, py, pz, lx1, ly1, lz1);
float t = ((px - lx1) * (lx2 - lx1) + (py - ly1) * (ly2 - ly1) + (pz - lz1) * (lz2 - lz1)) / line_dist;
t = constrain(t, 0, 1);
return dist_sq(px, py, pz, lx1 + t * (lx2 - lx1), ly1 + t * (ly2 - ly1), lz1 + t * (lz2 - lz1));
}
답변
다음은 자바 스크립트에서 가장 간단한 코드입니다.
x, y는 목표 점이고 x1, y1에서 x2, y2는 선분입니다.
업데이트 : 주석에서 길이가 0 인 문제가 수정되었습니다.
function pDistance(x, y, x1, y1, x2, y2) {
var A = x - x1;
var B = y - y1;
var C = x2 - x1;
var D = y2 - y1;
var dot = A * C + B * D;
var len_sq = C * C + D * D;
var param = -1;
if (len_sq != 0) //in case of 0 length line
param = dot / len_sq;
var xx, yy;
if (param < 0) {
xx = x1;
yy = y1;
}
else if (param > 1) {
xx = x2;
yy = y2;
}
else {
xx = x1 + param * C;
yy = y1 + param * D;
}
var dx = x - xx;
var dy = y - yy;
return Math.sqrt(dx * dx + dy * dy);
}
답변
이것은 FINITE LINE SEGMENTS를 위해 구현 된 것으로, 대부분의 다른 함수처럼 무한한 라인이 아닙니다 (그래서 내가 만든 이유).
파이썬 :
def dist(x1, y1, x2, y2, x3, y3): # x3,y3 is the point
px = x2-x1
py = y2-y1
norm = px*px + py*py
u = ((x3 - x1) * px + (y3 - y1) * py) / float(norm)
if u > 1:
u = 1
elif u < 0:
u = 0
x = x1 + u * px
y = y1 + u * py
dx = x - x3
dy = y - y3
# Note: If the actual distance does not matter,
# if you only want to compare what this function
# returns to other results of this function, you
# can just return the squared distance instead
# (i.e. remove the sqrt) to gain a little performance
dist = (dx*dx + dy*dy)**.5
return dist
AS3 :
public static function segmentDistToPoint(segA:Point, segB:Point, p:Point):Number
{
var p2:Point = new Point(segB.x - segA.x, segB.y - segA.y);
var something:Number = p2.x*p2.x + p2.y*p2.y;
var u:Number = ((p.x - segA.x) * p2.x + (p.y - segA.y) * p2.y) / something;
if (u > 1)
u = 1;
else if (u < 0)
u = 0;
var x:Number = segA.x + u * p2.x;
var y:Number = segA.y + u * p2.y;
var dx:Number = x - p.x;
var dy:Number = y - p.y;
var dist:Number = Math.sqrt(dx*dx + dy*dy);
return dist;
}
자바
private double shortestDistance(float x1,float y1,float x2,float y2,float x3,float y3)
{
float px=x2-x1;
float py=y2-y1;
float temp=(px*px)+(py*py);
float u=((x3 - x1) * px + (y3 - y1) * py) / (temp);
if(u>1){
u=1;
}
else if(u<0){
u=0;
}
float x = x1 + u * px;
float y = y1 + u * py;
float dx = x - x3;
float dy = y - y3;
double dist = Math.sqrt(dx*dx + dy*dy);
return dist;
}
답변
내 자신의 질문 스레드 에서 C, C # / .NET 2.0 또는 Java의 모든 경우에 점과 선 세그먼트 사이의 가장 짧은 2D 거리를 계산하는 방법은 무엇입니까? 내가 하나 찾을 때 여기에 C #을 대답을 넣어 질문을 받았다 : 그래서 여기가에서 수정 http://www.topcoder.com/tc?d1=tutorials&d2=geometry1&module=Static :
//Compute the dot product AB . BC
private double DotProduct(double[] pointA, double[] pointB, double[] pointC)
{
double[] AB = new double[2];
double[] BC = new double[2];
AB[0] = pointB[0] - pointA[0];
AB[1] = pointB[1] - pointA[1];
BC[0] = pointC[0] - pointB[0];
BC[1] = pointC[1] - pointB[1];
double dot = AB[0] * BC[0] + AB[1] * BC[1];
return dot;
}
//Compute the cross product AB x AC
private double CrossProduct(double[] pointA, double[] pointB, double[] pointC)
{
double[] AB = new double[2];
double[] AC = new double[2];
AB[0] = pointB[0] - pointA[0];
AB[1] = pointB[1] - pointA[1];
AC[0] = pointC[0] - pointA[0];
AC[1] = pointC[1] - pointA[1];
double cross = AB[0] * AC[1] - AB[1] * AC[0];
return cross;
}
//Compute the distance from A to B
double Distance(double[] pointA, double[] pointB)
{
double d1 = pointA[0] - pointB[0];
double d2 = pointA[1] - pointB[1];
return Math.Sqrt(d1 * d1 + d2 * d2);
}
//Compute the distance from AB to C
//if isSegment is true, AB is a segment, not a line.
double LineToPointDistance2D(double[] pointA, double[] pointB, double[] pointC,
bool isSegment)
{
double dist = CrossProduct(pointA, pointB, pointC) / Distance(pointA, pointB);
if (isSegment)
{
double dot1 = DotProduct(pointA, pointB, pointC);
if (dot1 > 0)
return Distance(pointB, pointC);
double dot2 = DotProduct(pointB, pointA, pointC);
if (dot2 > 0)
return Distance(pointA, pointC);
}
return Math.Abs(dist);
}
나는 대답하지 않고 질문을하는 @SO이므로 어떤 이유로 백만 표를 얻지 않고 비판을하기를 바랍니다. 이 스레드의 솔루션이 이국적인 언어 (Fortran, Mathematica)를 사용하거나 누군가가 잘못 표시했기 때문에 다른 사람의 아이디어를 공유하고 싶었습니다. 나를 위해 유일하게 유용한 것은 (Grumdrig) C ++로 작성되었으며 아무도 태그를 잘못 붙였습니다. 그러나 호출 된 메소드 (도트 등)가 없습니다.
답변
F #에서 점 과 c
선분 사이 의 거리는 다음 a
과 b
같습니다.
let pointToLineSegmentDistance (a: Vector, b: Vector) (c: Vector) =
let d = b - a
let s = d.Length
let lambda = (c - a) * d / s
let p = (lambda |> max 0.0 |> min s) * d / s
(a + p - c).Length
벡터 d
에서 점 a
으로 b
선분을 따라. d/s
with 의 내적 c-a
은 무한 선과 점 사이의 가장 가까운 접근 점의 매개 변수 를 제공합니다 c
. min
및 max
기능의 범위에이 매개 변수를 고정하는데 사용되는 0..s
포인트와 거짓말되도록 a
하고 b
. 마지막으로 길이는 선분에서 가장 가까운 점 a+p-c
까지의 거리입니다 c
.
사용 예 :
pointToLineSegmentDistance (Vector(0.0, 0.0), Vector(1.0, 0.0)) (Vector(-1.0, 1.0))
답변
관심있는 사람은 Joshua의 Javascript 코드를 Objective-C로 간단하게 변환합니다.
- (double)distanceToPoint:(CGPoint)p fromLineSegmentBetween:(CGPoint)l1 and:(CGPoint)l2
{
double A = p.x - l1.x;
double B = p.y - l1.y;
double C = l2.x - l1.x;
double D = l2.y - l1.y;
double dot = A * C + B * D;
double len_sq = C * C + D * D;
double param = dot / len_sq;
double xx, yy;
if (param < 0 || (l1.x == l2.x && l1.y == l2.y)) {
xx = l1.x;
yy = l1.y;
}
else if (param > 1) {
xx = l2.x;
yy = l2.y;
}
else {
xx = l1.x + param * C;
yy = l1.y + param * D;
}
double dx = p.x - xx;
double dy = p.y - yy;
return sqrtf(dx * dx + dy * dy);
}
이 솔루션을 사용하려면 MKMapPoint
다른 사람이 필요로 할 때 공유 할 것입니다. 약간의 변화 만 있으면 거리가 미터 단위로 반환됩니다.
- (double)distanceToPoint:(MKMapPoint)p fromLineSegmentBetween:(MKMapPoint)l1 and:(MKMapPoint)l2
{
double A = p.x - l1.x;
double B = p.y - l1.y;
double C = l2.x - l1.x;
double D = l2.y - l1.y;
double dot = A * C + B * D;
double len_sq = C * C + D * D;
double param = dot / len_sq;
double xx, yy;
if (param < 0 || (l1.x == l2.x && l1.y == l2.y)) {
xx = l1.x;
yy = l1.y;
}
else if (param > 1) {
xx = l2.x;
yy = l2.y;
}
else {
xx = l1.x + param * C;
yy = l1.y + param * D;
}
return MKMetersBetweenMapPoints(p, MKMapPointMake(xx, yy));
}
답변
Mathematica에서
세그먼트에 대한 파라 메트릭 설명을 사용하고 세그먼트에 의해 정의 된 선에 점을 투영합니다. 세그먼트에서 매개 변수가 0에서 1로 이동함에 따라 투영이이 범위를 벗어나면 세그먼트에 수직 인 직선 대신 해당 엔트 포인트까지의 거리를 계산합니다.
Clear["Global`*"];
distance[{start_, end_}, pt_] :=
Module[{param},
param = ((pt - start).(end - start))/Norm[end - start]^2; (*parameter. the "."
here means vector product*)
Which[
param < 0, EuclideanDistance[start, pt], (*If outside bounds*)
param > 1, EuclideanDistance[end, pt],
True, EuclideanDistance[pt, start + param (end - start)] (*Normal distance*)
]
];
플로팅 결과 :
Plot3D[distance[{{0, 0}, {1, 0}}, {xp, yp}], {xp, -1, 2}, {yp, -1, 2}]
컷오프 거리 보다 가까운 점을 그립니다 .
등고선 플롯 :