点が線分上の他の2つの点の間にあるとどうやって判断できますか?


93

2点の平面(aとbと呼ばれる)があり、各点のx整数とy整数で表されているとします。

別の点cがaとbで定義された線分上にあるかどうかをどのように判断できますか?

私はpythonを最も使用していますが、どの言語の例も役に立ちます。


4
これらの回答では、長さ= sqrt(x)のLOTが起こっていることがわかります。それらは機能するかもしれませんが、速くはありません。長さの2乗の使用を検討してください。二乗された長さの値を互いに比較するだけの場合は、精度が失われることはなく、sqrt()への遅い呼び出しを節約できます。
ojrac 2008年

1
ポイントcも2つの整数で表されていますか?もしそうなら、cがaとbの間の実際の直線に正確に沿っているか、またはaとbの間の直線のラスター近似にあるかどうかを知りたいですか?これは重要な説明です。
RobS 2008

同様の質問がここで行われました:stackoverflow.com/q/31346862/1914034、ラインからのバッファー距離が必要な場合の解決策
レーダーの下


1
将来の読者への警告:かなりの数の回答が正しくないか不完全です。頻繁に機能しないいくつかのエッジケースは、水平線と垂直線です。
Stefnotch

回答:


127

(ba)と(ca)の外積が0であるかどうかをチェックします。これは、Darius Baconが指示するように、点a、b、cが整列しているかどうかを示します。

ただし、cがaとbの間にあるかどうかを知りたい場合は、(ba)と(ca)のドット積正であり、aとbの間の距離の2乗よりも小さいことも確認する必要があります。

最適化されていない疑似コード:

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 2008年

9
クロス積の絶対値は、3つの点で構成される三角形の面積の2倍です(記号は辺が3番目の点であることを示します)。したがって、2つの端点間の距離に比例するイプシロンを使用する必要があります。
バート、

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
これはエレガントなソリューションです。
ポールD.エデン

6
これに関する唯一の問題は、数値の安定性です-数値の違いなどを取ると精度が失われがちです。
ジョナサンレフラー

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

1
@jfsそれはどういう意味ですか?イプシロンのチェックはどうですか?
ネオンワージ2017

3
@NeonWarge:sqrt()は浮動小数点数を返します。==ほとんどの場合、フロートの場合は間違っています。math.isclose()代わりに使用できます。そこにはなかったmath.isclose()2008年に、したがって、私は、明示的な不平等を提供してきましたepsilonabs_tolmath.isclose()話します)。
jfs 2017

35

外積かどうかをチェックb-aしてc-aいる0:手段すべての点が同一直線上にあること。彼らはしている場合ならば、チェックcの座標が間にあるaSと' bさん。x座標またはy座標のいずれかを使用し、その軸上でaかつbである(または両方で同じである)限り。

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

この答えは、以前は3つの更新の混乱でした。彼らからの有益な情報:Beautiful CodeのBrian Hayesのは、共線性テスト関数の設計空間をカバーしています-有用な背景。ヴィンセントの答えはこれを改善するのに役立ちました。そして、x座標またはy座標のいずれか1つのみをテストすることを提案したのはヘイズでした。元々はの代わりにコードがありました。andif a.x != b.x else


範囲チェックの方が速いので、最初に範囲チェックを行ってから、境界ボックス内にある場合は同一線上を確認することをお勧めします。
Grant M

1
関数is_on(a、b、c)は、a == b!= cの場合は間違っています。このような場合、cはaからbへの線分と交差していませんが、trueを返します。
MikkoVirkkilä、2015年

@SuperFlux、私はそれを実行しようとしただけで、Falseになりました。
ダライアスベーコン

2
この答えは、現在受け入れられている答えよりも明らかに優れていると思います。
Rickはモニカ

1
collinear(a、b、c)は、等価性によって浮動小数点数をテストしています。イプシロン/トレランスを使用する必要はありませんか?
jwezorek

7

ここに別のアプローチがあります:

  • 2つの点を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の間にあります(簡単なチェック)

これは、丸め誤差(座標の不正確さ)を考慮していません。
バート、

これは正しい考えだと思いますが、詳細(この方程式を実際にどのようにチェックするのですか?)と少しバグがあります。最後のy3はy2でなければなりません。
ダライアスベーコン

@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()(ところで、それは変装でのCrossProductです)失敗する可能性があります。
jfs 2008年

追加is_between()a, b = self.a, self.b
jfs

3つのポイントがすべて同じである場合はtrueを返します(これで問題ありません)、2つのポイントが同じである場合はfalseを返します。私は私の答えに代替案を投稿しました。
ダライアスベーコン

別のcmpトリックでそれを修正しましたが、このコードはにおいがし始めます;-)
vincent

5

C ++で提供されるコードを使用して、これを実行する別の方法を次に示します。2つの点、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コンポーネントをこれら2つの式に代入してAを解きます。両方の式のAの解が等しく、0 <= A <= 1の場合、点は線上にあります。 Aを解くには除算が必要です。線分が水平または垂直の場合、ゼロによる除算を停止する処理が必要な特別な場合があります。最終的な解決策は次のとおりです。

// 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 + bcabと等しいかどうかをテストします。

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

これは、次の3つの可能性があるためです。

  • 3つの点は三角形を形成します=> ac + bc> ab
  • それらは同一線上にあり、cabセグメントの外側にあります=> ac + bc> ab
  • それらは同一線上にあり、cabセグメント内にあります=> ac + bc = ab

Jonathan Lefflerが別のコメントで言及しているように、これにはクロス積のような他のアプローチが回避する数値的な問題があります。私の回答でリンクしている章で説明しています。
ダライアスベーコン

3

わかりました、線形代数(ベクトルの外積)についての言及がたくさんあり、これは実(つまり、連続または浮動小数点)空間で機能しますが、質問では2つの点が整数として表現されているため、外積は正しくありません近似解を与えることができますが、解。

正しい解決策は、2点間でBresenhamの線アルゴリズムを使用し、3番目の点が線上の点の1つであるかどうかを確認することです。アルゴリズムの計算が非パフォーマンスになるほどポイントが遠い場合(そして、そうなるには実際に大きくする必要があります)、あなたは掘り下げて最適化を見つけることができると私は確信しています。


これは、2つの任意の点とその数学的に正しい点の間の2次元整数空間を通る線を描画する方法を解決します。3番目の点がその線上の点の1つである場合、定義上、2つの点の間にあります。
cletus 2008

1
いいえ、ブレゼンハムの線アルゴリズムは、2次元整数空間で線セグメントの近似を作成する方法を解決します。元の投稿者のメッセージから、それがラスタライゼーションについての質問だったことはわかりません。
Cyrille Ka

「2点の平面(aとbと呼ばれる)があり、各点のx INTEGERとy INTEGERで表されているとします。」(強調は私が追加)。
cletus 2008

1
ブレゼンハムの線アルゴリズムは線に最も近い整数点を与え、それを使って線を描くことができると思います。それらはラインにないかもしれません。たとえば、(0,0)から(11,13)の場合、アルゴリズムは描画するピクセル数を示しますが、11と13は素数であるため、端点以外の整数点はありません。
Grant M

実空間(ℝ×ℝ)に対して正しい解が整数空間(ℕ×ℕ)に対して正しくない場合、どうしてasのようになりますか?あるいは、「ではない」の代わりに「に最適ではない」という意味ですか?
表意文字

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のようになるべきではありませんか?
ジョナサンレフラー

代わりに長さの2乗を比較するべきではありませんか?平方根は避けてください。また、オーバーフローのためにこれが避けられない場合は、math.sqrtの代わりにmath.hypotを使用できます(引数を適切に変更します)。
ダライアスベーコン

そのイプシロンも不思議です。説明できますか?もちろん、浮動小数点数を処理する必要がある場合は、比較に注意する必要がありますが、イプシロンがこの特定の比較をより正確にする理由は明らかではありません。
ダライアスベーコン

私は同意します。この質問にはいくつかの良い答えがありますが、これは問題ありません。ただし、sqrtを使用しないようにこのコードを修正し、最後の比較を修正する必要があります。
Cyrille Ka

@ジョナサン:確かに、コードはabsを使用してより親しみやすくエレガントです。ありがとう。
Federico A. Ramponi 08年

2

ユーザーカーソルが特定の行の上または近くにあるかどうかを検出するためにhtml5キャンバスで使用するjavascriptにこれが必要でした。そこで、Darius Baconの回答をコーヒースクリプトに変更しました。

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:以下のコードが良い考えではない理由を説明した「Beautiful Code」の本引用しています。

#!/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

線分上の任意の点(ab)(aおよびbはベクトルです)は、2つのベクトルaおよびbの線形結合として表すことができます。

つまり、cが線分(ab)にある場合:

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#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)の場合、dotproduct =(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)And x1 <a <x2

次に、(a、b)は(x1、y1)と(x2、y2)の間の線上になければなりません


一部の座標が近いか同一である場合の、狂った浮動小数点精度の問題はどうですか?
Robin Davies

コンピュータは浮動小数点をうまく処理しません。コンピュータには、無限に連続的に調整可能な値などはありません。したがって、浮動小数点を使用している場合は、定義を確立し、いくつかの小さなイプシロン値を行列式として使用する必要があります。そのイプシロンよりも近い2つのポイントは同じポイントと見なされます。同じ線上にあり、端点から同じ距離にある点を決定します。候補点がその計算された点のイプシロン内にある場合は、同一と呼びます。
Charles Bretana 2017

私のポイントは、実際にコードでそれを実装するときの精度の問題のため、この答えは使用できないということでした。したがって、誰もそれを使用するべきではありません。数学のテストで素敵な答え。しかし、comp-sciコースでの競争の失敗。私はドット積法を探してここに来ました(これは正しいです)。そのため、このスレッドで誤っている多くの回答にフラグを立てるのに少し時間がかかると思ったので、正しいソリューションに精通している他の回答がそれらを使用したくありません。
Robin Davies

あなたはコンピュータがライン上のすべての可能な実数を表すことができないために発生する問題について正しいです。ソリューション(ドット積法を含む)がこれらの問題の影響を受けないことは間違いです。どのようなソリューションでも、これらの問題に悩まされる可能性があります。許容できるイプシロンをある程度考慮しない限り、ライン上に正確にあるポイント(ただし、その座標はieee浮動小数点バイナリ表現では表現できません)は、コンピュータがそのポイントの座標を不正確に表すため、ドット積テストにも失敗します。ある程度。
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 2018

0

ジュールの回答の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.