[language-agnostic] 점과 선분 사이의 최단 거리

점과 선 세그먼트 사이의 최단 거리를 찾으려면 기본 기능이 필요합니다. 원하는 언어로 솔루션을 자유롭게 작성하십시오. (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를 위해 구현 된 것으로, 대부분의 다른 함수처럼 무한한 라인이 아닙니다 (그래서 내가 만든 이유).

Paul Bourke의 이론 구현 .

파이썬 :

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선분 사이 의 거리는 다음 ab같습니다.

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/swith 의 내적 c-a은 무한 선과 점 사이의 가장 가까운 접근 점의 매개 변수 를 제공합니다 c. minmax기능의 범위에이 매개 변수를 고정하는데 사용되는 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}]

대체 텍스트

컷오프 거리 보다 가까운 점을 그립니다 .

대체 텍스트

등고선 플롯 :

여기에 이미지 설명을 입력하십시오