한 점이 선분의 다른 두 점 사이에 있는지 어떻게 확인할 수 있습니까?


93

각 점에 대해 x 정수와 ay 정수로 표시되는 2 개의 점 (a 및 b라고 함)이있는 2 차원 평면이 있다고 가정 해 보겠습니다.

다른 점 c가 a와 b로 정의 된 선분에 있는지 어떻게 확인할 수 있습니까?

나는 파이썬을 가장 많이 사용하지만 어떤 언어로 된 예제가 도움이 될 것입니다.


4
이 답변에서 많은 길이 = sqrt (x) 항목이 진행되는 것을 봅니다. 작동 할 수도 있지만 빠르지는 않습니다. 길이 제곱 사용을 고려하십시오. 제곱 길이 값을 서로 비교하는 경우 정확도 손실이 없으며 sqrt ()에 대한 느린 호출을 절약 할 수 있습니다.
ojrac

1
점 c도 2 개의 정수로 표시됩니까? 그렇다면 c가 a와 b 사이의 실제 직선을 정확히 따라 있는지 아니면 a와 b 사이의 직선의 래스터 근사치에 있는지 알고 싶습니까? 이것은 중요한 설명입니다.
RobS

비슷한 질문이 여기에 제기되었습니다. stackoverflow.com/q/31346862/1914034 라인에서 버퍼 거리가 필요할 때 솔루션이있는 솔루션
Under the Radar


1
미래 독자에 대한 경고 : 상당수의 답변이 잘못되었거나 불완전합니다. 자주 작동하지 않는 몇 가지 가장자리 사례는 수평선과 수직선입니다.
Stefnotch

답변:


127

있는지 확인 외적 점은, b와 c가 일치하는 경우를 알려줍니다, 다리우스 베이컨을 알려줍니다로 (바) 및 (CA)의은 0입니다.

그러나 c가 a와 b 사이인지 알고 싶기 때문에 (ba)와 (ca) 의 내적양수 이고 a와 b 사이의 거리의 제곱 보다 작은 지 확인해야합니다.

최적화되지 않은 의사 코드에서 :

def isBetween(a, b, c):
    crossproduct = (c.y - a.y) * (b.x - a.x) - (c.x - a.x) * (b.y - a.y)

    # compare versus epsilon for floating point values, or != 0 if using integers
    if abs(crossproduct) > epsilon:
        return False

    dotproduct = (c.x - a.x) * (b.x - a.x) + (c.y - a.y)*(b.y - a.y)
    if dotproduct < 0:
        return False

    squaredlengthba = (b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y)
    if dotproduct > squaredlengthba:
        return False

    return True

5
-epsilon < crossproduct < epsilon and min(a.x, b.x) <= c.x <= max(a.x, b.x) and min(a.y, b.y) <= c.y <= max(a.y, b.y)충분하지 않습니까?
jfs

9
외적의 절대 값은 세 점에 의해 형성된 삼각형 영역의 두 배 (기호가 세 번째 점을 나타내는 측면)이므로 IMHO 두 끝점 사이의 거리에 비례하는 엡실론을 사용해야합니다.
bart

2
정수로 작동하지 않는 이유를 말씀해 주시겠습니까? 엡실론 검사가 "! = 0"으로 바뀌면 문제가 보이지 않습니다.
Cyrille Ka

2
예, 추가 괄호는 오타 일뿐입니다. 누군가가 말을하기까지 4 년이 지났습니다. :)
Cyrille Ka

4
a, b, c의 이름을 변경하여 세그먼트 끝점과 쿼리 지점을 명확하게해야합니다.
Craig Gidney 2013

48

방법은 다음과 같습니다.

def distance(a,b):
    return sqrt((a.x - b.x)**2 + (a.y - b.y)**2)

def is_between(a,c,b):
    return distance(a,c) + distance(c,b) == distance(a,b)

7
이것은 우아한 솔루션입니다.
Paul D. Eden

6
이것의 유일한 문제는 수치 적 안정성입니다. 숫자의 차이 등을 받아들이는 것은 정밀도를 잃기 쉽습니다.
Jonathan Leffler

26
-epsilon < (distance(a, c) + distance(c, b) - distance(a, b)) < epsilon
jfs

1
@jfs 그게 무슨 뜻이야? 엡실론의 수표는 무엇입니까?
Neon Warge

3
@NeonWarge : sqrt ()는 float를 반환합니다. ==대부분의 경우 수레에 대해 잘못된 것입니다 . math.isclose()대신 사용할 수 있습니다. math.isclose()2008 년 에는 없었기 때문에 epsilon( abs_tolin math.isclose()speak) 에 명시 적 불평등을 제공했습니다 .
jfs

35

의 외적 있는지 확인 b-a하고 c-a있습니다 0수단의 모든 지점이 동일 선상에 있음 :. 이러한 경우 있는지 확인 c의 좌표 사이 a의과 ' b의. 사용은 x 또는 y 좌표,만큼 하나 ab그 축에서 분리 된 (또는 그들은 모두 같은 것).

def is_on(a, b, c):
    "Return true iff point c intersects the line segment from a to b."
    # (or the degenerate case that all 3 points are coincident)
    return (collinear(a, b, c)
            and (within(a.x, c.x, b.x) if a.x != b.x else 
                 within(a.y, c.y, b.y)))

def collinear(a, b, c):
    "Return true iff a, b, and c all lie on the same line."
    return (b.x - a.x) * (c.y - a.y) == (c.x - a.x) * (b.y - a.y)

def within(p, q, r):
    "Return true iff q is between p and r (inclusive)."
    return p <= q <= r or r <= q <= p

이 답변은 세 가지 업데이트가 엉망이었습니다. 그들로부터 가치있는 정보 : 브라이언 헤이즈의 에서 아름다운 코드 - 유용한 배경 공선 테스트 기능에 대한 설계 공간을 다룹니다. Vincent의 답변 은이 문제를 개선하는 데 도움이되었습니다. 그리고 x 또는 y 좌표 중 하나만 테스트 할 것을 제안한 것은 Hayes였습니다. 원래 코드는 한 and자리에 if a.x != b.x else.


범위 검사가 더 빠르기 때문에 먼저 범위 검사를 한 다음 경계 상자에 있으면 동일 선상을 확인하는 것이 좋습니다.
Grant M

1
함수 is_on (a, b, c)는 a == b! = c 인 경우에 잘못되었습니다. 이 경우 c가 a에서 b까지의 선분과 교차하지 않더라도 true를 반환합니다.
Mikko Virkkilä 2015 년

@SuperFlux, 방금 실행 해 보았고 False를 얻었습니다.
Darius Bacon

2
나는이 대답이 현재 받아 들여지는 대답보다 분명히 우수하다고 생각합니다.
릭 모니카 지원

1
collinear (a, b, c)는 부동 소수점 수를 동등하게 테스트합니다. 엡실론 / 공차를 사용하지 않아야합니까?
jwezorek

7

다음은 또 다른 접근 방식입니다.

  • 두 점이 A (x1, y1) 및 B (x2, y2)라고 가정합니다.
  • 이 점들을 통과하는 선의 방정식은 (x-x1) / (y-y1) = (x2-x1) / (y2-y1) .. (그냥 기울기를 같게 만드는 것입니다)

다음과 같은 경우 점 C (x3, y3)는 A와 B 사이에 있습니다.

  • x3, y3는 위의 방정식을 만족합니다.
  • x3은 x1과 x2 사이에 있고 y3은 y1과 y2 사이에 있습니다 (사소한 확인)

반올림 오류 (좌표의 부정확성)는 고려하지 않습니다.
bart

이것은 옳은 생각이라고 생각하지만 세부 사항이 짧고 (실제로이 방정식을 어떻게 확인합니까?) 약간 버그가 있습니다. 마지막 y3은 y2 여야합니다.
Darius Bacon

@Darius : 오타 수정
Harley Holcombe

7

세그먼트의 길이는 중요하지 않으므로 제곱근을 사용할 필요가 없으며 정밀도를 잃을 수 있으므로 피해야합니다.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class Segment:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def is_between(self, c):
        # Check if slope of a to c is the same as a to b ;
        # that is, when moving from a.x to c.x, c.y must be proportionally
        # increased than it takes to get from a.x to b.x .

        # Then, c.x must be between a.x and b.x, and c.y must be between a.y and b.y.
        # => c is after a and before b, or the opposite
        # that is, the absolute value of cmp(a, b) + cmp(b, c) is either 0 ( 1 + -1 )
        #    or 1 ( c == a or c == b)

        a, b = self.a, self.b             

        return ((b.x - a.x) * (c.y - a.y) == (c.x - a.x) * (b.y - a.y) and 
                abs(cmp(a.x, c.x) + cmp(b.x, c.x)) <= 1 and
                abs(cmp(a.y, c.y) + cmp(b.y, c.y)) <= 1)

임의의 사용 예 :

a = Point(0,0)
b = Point(50,100)
c = Point(25,50)
d = Point(0,8)

print Segment(a,b).is_between(c)
print Segment(a,b).is_between(d)

1
CX 또는 CY 후 제 플로트 경우 ==에이 is_between()실패 할 수있다 (BTW 변장의 벡터 곱이다).
jfs

추가 is_between():a, b = self.a, self.b
jfs

세 점이 모두 같으면 true를 반환하지만 (괜찮습니다, imho) 정확히 두 점이 같으면 false를 반환합니다. 이것은 사이를 정의하는 매우 일관성이없는 방법입니다. 내 대답에 대안을 게시했습니다.
Darius Bacon

다른 cmp 트릭에 의해 수정되었지만이 코드는 냄새가 나기 시작합니다 ;-)
vincent

5

여기에 C ++로 제공된 코드를 사용하는 다른 방법이 있습니다. l1과 l2의 두 점이 주어지면 그 사이의 선분을 다음과 같이 표현하는 것은 간단합니다.

l1 + A(l2 - l1)

여기서 0 <= A <= 1.이 문제에 사용하는 것 이상으로 관심이 있다면 이것은 선의 벡터 표현으로 알려져 있습니다. 이것의 x 및 y 구성 요소를 분리하여 다음을 제공 할 수 있습니다.

x = l1.x + A(l2.x - l1.x)
y = l1.y + A(l2.y - l1.y)

점 (x, y)을 취하고 x 및 y 성분을이 두 표현식으로 대체하여 A를 해결합니다. 두 표현식의 A에 대한 해가 같고 0 <= A <= 1이면 점이 선 위에 있습니다. A를 해결하려면 분할이 필요합니다. 선 세그먼트가 수평 또는 수직 일 때 0으로 분할을 중지하기 위해 처리해야하는 특수한 경우가 있습니다. 최종 솔루션은 다음과 같습니다.

// Vec2 is a simple x/y struct - it could very well be named Point for this use

bool isBetween(double a, double b, double c) {
    // return if c is between a and b
    double larger = (a >= b) ? a : b;
    double smaller = (a != larger) ? a : b;

    return c <= larger && c >= smaller;
}

bool pointOnLine(Vec2<double> p, Vec2<double> l1, Vec2<double> l2) {
    if(l2.x - l1.x == 0) return isBetween(l1.y, l2.y, p.y); // vertical line
    if(l2.y - l1.y == 0) return isBetween(l1.x, l2.x, p.x); // horizontal line

    double Ax = (p.x - l1.x) / (l2.x - l1.x);
    double Ay = (p.y - l1.y) / (l2.y - l1.y);

    // We want Ax == Ay, so check if the difference is very small (floating
    // point comparison is fun!)

    return fabs(Ax - Ay) < 0.000001 && Ax >= 0.0 && Ax <= 1.0;
}

4

보다 기하학적 인 접근 방식을 사용하여 다음 거리를 계산하십시오.

ab = sqrt((a.x-b.x)**2 + (a.y-b.y)**2)
ac = sqrt((a.x-c.x)**2 + (a.y-c.y)**2)
bc = sqrt((b.x-c.x)**2 + (b.y-c.y)**2)

ac + bc가 ab 와 같은지 테스트합니다 .

is_on_segment = abs(ac + bc - ab) < EPSILON

세 가지 가능성이 있기 때문입니다.

  • 3 개의 점은 삼각형을 형성합니다 => ac + bc> ab
  • 그들은 동일 선상에 있고 cab 세그먼트 외부에 있습니다 => ac + bc> ab
  • 동일 선상에 있고 cab 세그먼트 내부에 있음 => ac + bc = ab

Jonathan Leffler가 다른 의견에서 언급했듯이 이것은 교차 제품과 같은 다른 접근 방식이 피할 수있는 수치 문제를 가지고 있습니다. 내 답변에서 링크하는 장에 대해 설명합니다.
Darius Bacon

3

좋아, 선형 대수 (벡터의 외적)에 대한 많은 언급이 있으며 이것은 실제 (즉, 연속 또는 부동 소수점) 공간에서 작동하지만 두 점이 정수 로 표현되었다고 구체적으로 언급했습니다. 되었으므로 교차 곱이 정확하지 않다고 대략적인 솔루션을 제공 할 수 있지만 솔루션입니다.

올바른 해결책은 두 점 사이에 Bresenham의 선 알고리즘 을 사용 하고 세 번째 점이 선의 점 중 하나인지 확인하는 것입니다. 포인트가 충분히 멀어 알고리즘 계산이 성능이 좋지 않다면 (그리고 그렇게하려면 정말 커야합니다) 저는 여러분이 주변을 파헤쳐 서 최적화를 찾을 수있을 것이라고 확신합니다.


두 임의의 점 사이의 2 차원 정수 공간을 통해 선을 그리는 방법과 수학적으로 올바른 방법을 해결합니다. 세 번째 점이 해당 선의 점 중 하나이면 정의에 따라 두 점 사이에있는 것입니다.
cletus

1
아니요, Bresenham의 Line Algorithm 은 2 차원 정수 공간에서 선분 의 근사치 를 만드는 방법을 해결합니다 . 원래 포스터의 메시지에서는 래스터 화에 대한 질문이라는 것을 알 수 없습니다.
Cyrille Ka

"각 점에 대해 x INTEGER 및 ay INTEGER로 표시되는 2 개의 점 (a 및 b라고 함)이있는 2 차원 평면이 있다고 가정 해 보겠습니다." (내가 강조 추가).
cletus

1
Bresenham의 Line Algorithm은 벽장 정수 포인트를 선에 부여하여 선을 그리는 데 사용할 수 있다고 생각합니다. 그들은 줄에 있지 않을 수 있습니다. 예를 들어 (0,0) ~ (11,13)에 대해 알고리즘은 그릴 수 픽셀을 제공하지만 11과 13이 코 프라임이기 때문에 끝점을 제외한 정수 점이 없습니다.
Grant M

ℕ∈ℝ처럼 실제 공간 (ℝ × ℝ)에 대해 올바른 솔루션이 정수 공간 (ℕ × ℕ)에 대해 올바르지 않을 수 있습니다. 아니면 '정확하지 않음'대신에 '다음에 최적이 아닙니다.'를 의미합니까?
표의 문자

2

(ca)와 (ba) 사이의 스칼라 곱은 길이의 곱과 같아야합니다 (이는 벡터 (ca)와 (ba)가 같은 방향으로 정렬되어 있음을 의미 함). 또한 (ca)의 길이는 (ba)의 길이보다 작거나 같아야합니다. 의사 코드 :

# epsilon = small constant

def isBetween(a, b, c):
    lengthca2  = (c.x - a.x)*(c.x - a.x) + (c.y - a.y)*(c.y - a.y)
    lengthba2  = (b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y)
    if lengthca2 > lengthba2: return False
    dotproduct = (c.x - a.x)*(b.x - a.x) + (c.y - a.y)*(b.y - a.y)
    if dotproduct < 0.0: return False
    if abs(dotproduct*dotproduct - lengthca2*lengthba2) > epsilon: return False 
    return True

마지막 조건은 ABS (product-lengthca * lengthba) <epsilon과 비슷하지 않아야합니다.
Jonathan Leffler

대신 제곱 길이를 비교해야하지 않습니까? 제곱근은 피해야합니다. 또한 오버플로로 인해 피할 수없는 경우 math.sqrt 대신 math.hypot을 사용할 수 있습니다 (적절한 인수 변경 포함).
Darius Bacon

저도 그 엡실론에 대해 궁금합니다. 설명 할 수 있습니까? 물론, 수레를 다루어야한다면 비교에주의해야하지만 엡실론이이 특정 비교를 더 정확하게 만드는 이유는 분명하지 않습니다.
Darius Bacon

동의합니다. 이 질문에 대한 몇 가지 좋은 대답이 있으며 이것은 괜찮습니다. 그러나이 코드는 sqrt를 사용하지 않도록 수정해야하며 마지막 비교는 수정되었습니다.
Cyrille Ka

@Jonathan : 실제로 코드는 abs를 사용하면 더 친숙하고 우아합니다. 감사.
Federico A. Ramponi

2

사용자 커서가 특정 줄 위에 있는지 또는 근처에 있는지 감지하기 위해 html5 캔버스에서 사용하기 위해 자바 스크립트에 필요했습니다. 그래서 Darius Bacon이 제공 한 답변을 coffeescript로 수정했습니다.

is_on = (a,b,c) ->
    # "Return true if point c intersects the line segment from a to b."
    # (or the degenerate case that all 3 points are coincident)
    return (collinear(a,b,c) and withincheck(a,b,c))

withincheck = (a,b,c) ->
    if a[0] != b[0]
        within(a[0],c[0],b[0]) 
    else 
        within(a[1],c[1],b[1])

collinear = (a,b,c) ->
    # "Return true if a, b, and c all lie on the same line."
    ((b[0]-a[0])*(c[1]-a[1]) < (c[0]-a[0])*(b[1]-a[1]) + 1000) and ((b[0]-a[0])*(c[1]-a[1]) > (c[0]-a[0])*(b[1]-a[1]) - 1000)

within = (p,q,r) ->
    # "Return true if q is between p and r (inclusive)."
    p <= q <= r or r <= q <= p

2

쐐기 및 내적을 사용할 수 있습니다.

def dot(v,w): return v.x*w.x + v.y*w.y
def wedge(v,w): return v.x*w.y - v.y*w.x

def is_between(a,b,c):
   v = a - b
   w = b - c
   return wedge(v,w) == 0 and dot(v,w) > 0

1

제가 학교에서 한 방법은 다음과 같습니다. 좋은 생각이 아닌 이유를 잊었습니다.

편집하다:

@Darius Bacon : 아래 코드가 좋은 생각이 아닌 이유를 설명 하는 "아름다운 코드"책인용합니다 .

#!/usr/bin/env python
from __future__ import division

epsilon = 1e-6

class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

class LineSegment:
    """
    >>> ls = LineSegment(Point(0,0), Point(2,4))
    >>> Point(1, 2) in ls
    True
    >>> Point(.5, 1) in ls
    True
    >>> Point(.5, 1.1) in ls
    False
    >>> Point(-1, -2) in ls
    False
    >>> Point(.1, 0.20000001) in ls
    True
    >>> Point(.1, 0.2001) in ls
    False
    >>> ls = LineSegment(Point(1, 1), Point(3, 5))
    >>> Point(2, 3) in ls
    True
    >>> Point(1.5, 2) in ls
    True
    >>> Point(0, -1) in ls
    False
    >>> ls = LineSegment(Point(1, 2), Point(1, 10))
    >>> Point(1, 6) in ls
    True
    >>> Point(1, 1) in ls
    False
    >>> Point(2, 6) in ls 
    False
    >>> ls = LineSegment(Point(-1, 10), Point(5, 10))
    >>> Point(3, 10) in ls
    True
    >>> Point(6, 10) in ls
    False
    >>> Point(5, 10) in ls
    True
    >>> Point(3, 11) in ls
    False
    """
    def __init__(self, a, b):
        if a.x > b.x:
            a, b = b, a
        (self.x0, self.y0, self.x1, self.y1) = (a.x, a.y, b.x, b.y)
        self.slope = (self.y1 - self.y0) / (self.x1 - self.x0) if self.x1 != self.x0 else None

    def __contains__(self, c):
        return (self.x0 <= c.x <= self.x1 and
                min(self.y0, self.y1) <= c.y <= max(self.y0, self.y1) and
                (not self.slope or -epsilon < (c.y - self.y(c.x)) < epsilon))

    def y(self, x):        
        return self.slope * (x - self.x0) + self.y0

if __name__ == '__main__':
    import  doctest
    doctest.testmod()

1

선분 ( a , b ) (여기서 ab 는 벡터) 의 모든 점은 두 벡터 ab선형 조합 으로 표현 될 수 있습니다 .

즉, c가 선분 ( a , b )에있는 경우 :

c = ma + (1 - m)b, where 0 <= m <= 1

m을 구하면 다음을 얻습니다.

m = (c.x - b.x)/(a.x - b.x) = (c.y - b.y)/(a.y - b.y)

따라서 테스트는 다음과 같습니다 (Python에서).

def is_on(a, b, c):
    """Is c on the line segment ab?"""

    def _is_zero( val ):
        return -epsilon < val < epsilon

    x1 = a.x - b.x
    x2 = c.x - b.x
    y1 = a.y - b.y
    y2 = c.y - b.y

    if _is_zero(x1) and _is_zero(y1):
        # a and b are the same point:
        # so check that c is the same as a and b
        return _is_zero(x2) and _is_zero(y2)

    if _is_zero(x1):
        # a and b are on same vertical line
        m2 = y2 * 1.0 / y1
        return _is_zero(x2) and 0 <= m2 <= 1
    elif _is_zero(y1):
        # a and b are on same horizontal line
        m1 = x2 * 1.0 / x1
        return _is_zero(y2) and 0 <= m1 <= 1
    else:
        m1 = x2 * 1.0 / x1
        if m1 < 0 or m1 > 1:
            return False
        m2 = y2 * 1.0 / y1
        return _is_zero(m2 - m1)

1

c # From http://www.faqs.org/faqs/graphics/algorithms-faq/- > Subject 1.02 : 점에서 선까지의 거리를 어떻게 찾습니까?

Boolean Contains(PointF from, PointF to, PointF pt, double epsilon)
        {

            double segmentLengthSqr = (to.X - from.X) * (to.X - from.X) + (to.Y - from.Y) * (to.Y - from.Y);
            double r = ((pt.X - from.X) * (to.X - from.X) + (pt.Y - from.Y) * (to.Y - from.Y)) / segmentLengthSqr;
            if(r<0 || r>1) return false;
            double sl = ((from.Y - pt.Y) * (to.X - from.X) - (from.X - pt.X) * (to.Y - from.Y)) / System.Math.Sqrt(segmentLengthSqr);
            return -epsilon <= sl && sl <= epsilon;
        }

대부분의 다른 접근 방식에서 정밀도 문제를 방지하는 올바른 방법입니다. 또한 대부분의 다른 접근 방식보다 훨씬 더 효율적입니다.
Robin Davies

1

다음은 나를 위해 일한 Java 코드입니다.

boolean liesOnSegment(Coordinate a, Coordinate b, Coordinate  c) {

    double dotProduct = (c.x - a.x) * (c.x - b.x) + (c.y - a.y) * (c.y - b.y);
    if (dotProduct < 0) return true;
    return false;
}

1
dotProduct는 정렬에 대해서만 알 수 있습니다. 코드가 불완전합니다 !!! a (0,0), b (4,0), c (1,1)을 사용하면 내적 = (1-0) * (1-4) + (1-0) * (1-0) =- 3 + 1 = -3
user43968

0

경사가 동일하고 지점이 다른 지점 사이에 있는지 확인하는 것은 어떻습니까?

주어진 포인트 (x1, y1) 및 (x2, y2) (x2> x1 포함) 및 후보 포인트 (a, b)

if (b-y1) / (a-x1) = (y2-y2) / (x2-x1) 그리고 x1 <a <x2

그런 다음 (a, b)는 (x1, y1)과 (x2, y2) 사이의 줄에 있어야합니다.


일부 좌표가 가깝거나 동일 할 때 미친 부동 소수점 정밀도 문제는 어떻습니까?
Robin Davies

컴퓨터는 부동 소수점을 잘 수행하지 않습니다. 컴퓨터에는 무한히 지속적으로 조정 가능한 값과 같은 것이 없습니다. 따라서 부동 소수점을 사용하는 경우 결정자로 작은 엡실론 값을 정의하고 사용해야하며 해당 엡실론보다 가까운 두 점은 동일한 점으로 간주되어야합니다. 끝점에서 같은 선과 같은 거리에있는 점을 결정합니다. 후보 포인트가 계산 된 포인트의 엡실론 내에 있으면 동일하다고 부릅니다.
Charles Bretana

내 요점은이 답변을 실제로 코드로 구현할 때 정밀도 문제로 인해 사용할 수 없다는 것입니다. 그래서 아무도 그것을 사용해서는 안됩니다. 수학 시험에 대한 멋진 대답. 그러나 comp-sci 과정에서 경쟁 실패. 나는 내적 방법을 찾기 위해 여기에 왔습니다. 그래서 나는이 스레드에서 잘못된 많은 답변을 표시하는 데 잠시 시간을 할애하여 올바른 솔루션에 익숙한 다른 사람들이 사용하고 싶지 않을 것이라고 생각했습니다.
Robin Davies

컴퓨터가 가능한 모든 실수를 한 줄에 표시 할 수 없기 때문에 발생하는 문제에 대해 정확합니다. 모든 솔루션 (내적 방법 포함)이 이러한 문제에 영향을받지 않을 수 있다는 것은 잘못된 것입니다. 모든 솔루션은 이러한 문제를 겪을 수 있습니다. 허용되는 엡실론에 대해 약간의 허용을하지 않는 한, 정확히 선 위에있는 (하지만 좌표가 부동 소수점 이진 표현으로 표현할 수없는) 점은 컴퓨터가 점의 좌표를 부정확하게 나타 내기 때문에 내적 테스트에도 실패합니다. 어느 정도.
Charles Bretana

0

Vector2D 클래스를 사용하는 C #의 답변

public static bool IsOnSegment(this Segment2D @this, Point2D c, double tolerance)
{
     var distanceSquared = tolerance*tolerance;
     // Start of segment to test point vector
     var v = new Vector2D( @this.P0, c ).To3D();
     // Segment vector
     var s = new Vector2D( @this.P0, @this.P1 ).To3D();
     // Dot product of s
     var ss = s*s;
     // k is the scalar we multiply s by to get the projection of c onto s
     // where we assume s is an infinte line
     var k = v*s/ss;
     // Convert our tolerance to the units of the scalar quanity k
     var kd = tolerance / Math.Sqrt( ss );
     // Check that the projection is within the bounds
     if (k <= -kd || k >= (1+kd))
     {
        return false;
     }
     // Find the projection point
     var p = k*s;
     // Find the vector between test point and it's projection
     var vp = (v - p);
     // Check the distance is within tolerance.
     return vp * vp < distanceSquared;
}

참고

s * s

C #에서 연산자 오버로딩을 통한 세그먼트 벡터의 내적입니다.

핵심은 무한 선에 대한 점의 투영을 활용하고 투영의 스칼라 양이 투영이 세그먼트에 있는지 여부를 사소하게 알려주는 것을 관찰하는 것입니다. 퍼지 허용 오차를 사용하기 위해 스칼라 수량의 경계를 조정할 수 있습니다.

투영이 경계 내에 있으면 점에서 투영까지의 거리가 경계 내에 있는지 테스트합니다.

외적 접근법에 비해 이점은 공차에 의미있는 값이 있다는 것입니다.


0

다음은 Unity에서 C #을 사용한 솔루션입니다.

private bool _isPointOnLine( Vector2 ptLineStart, Vector2 ptLineEnd, Vector2 ptPoint )
{
    bool bRes = false;
    if((Mathf.Approximately(ptPoint.x, ptLineStart.x) || Mathf.Approximately(ptPoint.x, ptLineEnd.x)))
    {
        if(ptPoint.y > ptLineStart.y && ptPoint.y < ptLineEnd.y)
        {
            bRes = true;
        }
    }
    else if((Mathf.Approximately(ptPoint.y, ptLineStart.y) || Mathf.Approximately(ptPoint.y, ptLineEnd.y)))
    {
        if(ptPoint.x > ptLineStart.x && ptPoint.x < ptLineEnd.x)
        {
            bRes = true;
        }
    }
    return bRes;
}

이 코드는 수직 및 수평 선 세그먼트에서만 작동하는 것 같습니다. ptLineStart가 (0,0)이고 ptLineEnd가 (2,2)이고 ptPoint가 (1, 1)이면 어떻게됩니까?
vac

0

Jules의 답변의 C # 버전 :

public static double CalcDistanceBetween2Points(double x1, double y1, double x2, double y2)
{
    return Math.Sqrt(Math.Pow (x1-x2, 2) + Math.Pow (y1-y2, 2));
}

public static bool PointLinesOnLine (double x, double y, double x1, double y1, double x2, double y2, double allowedDistanceDifference)
{
    double dist1 = CalcDistanceBetween2Points(x, y, x1, y1);
    double dist2 = CalcDistanceBetween2Points(x, y, x2, y2);
    double dist3 = CalcDistanceBetween2Points(x1, y1, x2, y2);
    return Math.Abs(dist3 - (dist1 + dist2)) <= allowedDistanceDifference;
}

0

점 좌표로 해당 선분에 대한 선 방정식을 풀면 해당 점이 선에 있는지 여부를 알 수 있으며 선분의 경계를 확인하여 선분의 내부 또는 외부에 있는지 확인할 수 있습니다. 공간 어딘가에 부동 소수점 값으로 정의 될 가능성이 높고 정확한 값에 도달해서는 안되므로 임계 값을 적용 할 수 있습니다. PHP의 예

function getLineDefinition($p1=array(0,0), $p2=array(0,0)){
    
    $k = ($p1[1]-$p2[1])/($p1[0]-$p2[0]);
    $q = $p1[1]-$k*$p1[0];
    
    return array($k, $q);
    
}

function isPointOnLineSegment($line=array(array(0,0),array(0,0)), $pt=array(0,0)){
    
    // GET THE LINE DEFINITION y = k.x + q AS array(k, q) 
    $def = getLineDefinition($line[0], $line[1]);
    
    // use the line definition to find y for the x of your point
    $y = $def[0]*$pt[0]+$def[1];

    $yMin = min($line[0][1], $line[1][1]);
    $yMax = max($line[0][1], $line[1][1]);

    // exclude y values that are outside this segments bounds
    if($y>$yMax || $y<$yMin) return false;
    
    // calculate the difference of your points y value from the reference value calculated from lines definition 
    // in ideal cases this would equal 0 but we are dealing with floating point values so we need some threshold value not to lose results
    // this is up to you to fine tune
    $diff = abs($pt[1]-$y);
    
    $thr = 0.000001;
    
    return $diff<=$thr;
    
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.