Bir noktanın, bir çizgi parçası üzerindeki diğer iki nokta arasında olduğunu nasıl belirleyebilirsin?


97

Diyelim ki, üzerinde her nokta için bir x tamsayısı ve bir tamsayı ile temsil edilen 2 nokta (a ve b olarak adlandırılır) olan iki boyutlu bir düzleminiz var.

A ve b ile tanımlanan doğru parçası üzerinde başka bir c noktasının olup olmadığını nasıl anlarsınız?

En çok python kullanıyorum, ancak herhangi bir dildeki örnekler yardımcı olacaktır.


4
Bu cevaplarda bir sürü uzunluk = sqrt (x) şey görüyorum; işe yarayabilir, ancak hızlı değiller. Uzunluk karesini kullanmayı düşünün; sadece kare uzunluk değerlerini birbirleriyle karşılaştırıyorsanız, doğruluk kaybı olmaz ve sqrt () 'ye yavaş çağrıları kaydedersiniz.
ojrac

1
C noktası da 2 tamsayı ile temsil ediliyor mu? Öyleyse, c'nin tam olarak a ve b arasındaki gerçek bir düz çizgi boyunca mı yoksa a ve b arasındaki düz çizginin raster yaklaşıklığı üzerinde mi olduğunu bilmek ister misiniz? Bu önemli bir açıklamadır.
RobS

Burada da benzer bir soru soruldu: stackoverflow.com/q/31346862/1914034 , hattan arabellek mesafesi gerektiğinde bir çözümle
Radar


1
Gelecekteki okuyucular için uyarı: Makul sayıda yanıt yanlış veya eksiktir. Sıklıkla çalışmayan birkaç uç durum, yatay ve dikey çizgilerdir.
Stefnotch

Yanıtlar:


131

Darius Bacon'a söylediği gibi (ba) ve (ca) 'nın çarpımının 0 olup olmadığını kontrol edin, size a, b ve c noktalarının hizalı olup olmadığını söyler.

Ancak, c'nin a ile b arasında olup olmadığını bilmek istediğiniz gibi , (ba) ve (ca) 'nın iç çarpımının pozitif olduğunu ve a ile b arasındaki mesafenin karesinden daha küçük olduğunu da kontrol etmelisiniz .

Optimize edilmemiş sözde kodda:

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)yeterli değil mi?
jfs

9
Çapraz ürünün mutlak değeri, üç noktanın oluşturduğu üçgenin alanının iki katıdır (işaret, üçüncü noktayı gösterir), bu nedenle IMHO, iki uç nokta arasındaki mesafeyle orantılı bir epsilon kullanmalısınız.
bart

2
Neden tamsayılarla çalışmadığını bize söyleyebilir misiniz? Epsilon kontrolünün "! = 0" ile değiştirilmesi koşuluyla, sorunu görmüyorum.
Cyrille Ka

2
Evet, fazladan parantez sadece bir yazım hatasıdır. Birinin bir şey söylemesinden önce 4 yıl geçti. :)
Cyrille Ka

4
Hangilerinin segment uç noktaları ve hangisinin sorgu noktası olduğunu daha net hale getirmek için a, b, c'yi yeniden adlandırmalısınız.
Craig Gidney

53

İşte bunu nasıl yapacağım:

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)

8
Bununla ilgili tek sorun, sayısal kararlılıktır - sayıların farklılığını almak vb. Hassasiyeti kaybetme eğilimindedir.
Jonathan Leffler

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

1
@jfs bununla ne demek istiyorsun? Epsilon'un kontrolü ne için?
Neon Warge

3
@NeonWarge: sqrt () bir kayan nokta döndürür. ==çoğu durumda şamandıralar için yanlış bir şeydir . math.isclose()bunun yerine kullanılabilir. Hiçbir oldu math.isclose()2008 yılında ve bu nedenle birlikte açık eşitsizliği sağladık epsilon( abs_toliçinde math.isclose()konuşmak).
jfs

Bu kesinlikle doğru, ancak bu yöntem math.isclose () ile bile pek iyi değil. Koordinatlar tam sayı olduğunda, kesin bir test yaptırmak istersiniz. Diğer cevabım bunun için bir formül veriyor: stackoverflow.com/a/29301940/40078
Jules

36

Çapraz ürünü olmadığını kontrol edin b-ave c-aolduğu 0anlamına gelir tüm noktaları collinear vardır. Eğer öyleyse, ckoordinatlarının a's ile b' arasında olup olmadığını kontrol edin . Ya x ya da y koordinatlarını, o eksende ave bayrı oldukları sürece kullanın (ya da her ikisinde de aynıdırlar).

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

Bu cevap, üç güncellemeden oluşan bir karmaşaydı. Onlardan değerli bilgiler: Brian Hayes'in Beautiful Code'daki bölümü , bir doğrusallık testi işlevi için tasarım alanını kapsıyor - kullanışlı arka plan. Vincent'ın cevabı bunu geliştirmeye yardımcı oldu. Ve x veya y koordinatlarından yalnızca birini test etmeyi öneren Hayes'di; başlangıçta kodun yerini almıştır .andif a.x != b.x else


Aralık kontrolü daha hızlı olduğundan, önce aralığı kontrol etmek daha sonra sınırlayıcı kutudaysa eşdoğrusal olup olmadığını kontrol etmek daha iyi olacaktır.
Grant M

1
İs_on (a, b, c) işlevi, a == b! = C olduğu durumda yanlıştır. Böyle bir durumda, c, a'dan b'ye doğru bir doğru parçasıyla kesişmese bile, doğru dönecektir.
Mikko Virkkilä

@SuperFlux, bunu çalıştırmayı denedim ve False aldım.
Darius Bacon

2
Bu cevabın şu anda kabul edilen cevaba göre oldukça açık olduğunu düşünüyorum.
Rick Monica'yı

1
collinear (a, b, c), kayan nokta sayılarını eşitlikle test etmektir. Bir epsilon / tolerans kullanması gerekmez mi?
jwezorek

7

İşte başka bir yaklaşım:

  • İki noktanın A (x1, y1) ve B (x2, y2) olduğunu varsayalım
  • Bu noktalardan geçen doğrunun denklemi (x-x1) / (y-y1) = (x2-x1) / (y2-y1) .. (sadece eğimleri eşitleyerek)

C (x3, y3) noktası aşağıdaki durumlarda A ve B arasında olacaktır:

  • x3, y3 yukarıdaki denklemi karşılar.
  • x3, x1 ve x2 arasında ve y3, y1 ve y2 arasında uzanır (önemsiz kontrol)

Bu, yuvarlama hatalarını (koordinatların kesin olmaması) hesaba katmaz.
bart

Bence bu doğru fikir, ancak ayrıntıda kısa (pratikte bu denklemi nasıl kontrol ederiz?) Ve biraz hatalı: son y3 y2 olmalı.
Darius Bacon

@Darius: bu yazım hatası düzeltildi
Harley Holcombe

7

Segmentin uzunluğu önemli değildir, bu nedenle bir karekök kullanmak gerekli değildir ve biraz hassasiyet kaybedebileceğimiz için bundan kaçınılmalıdır.

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)

Bazı rastgele kullanım örnekleri:

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
Eğer cx veya cy float ise, o zaman ilk ==giriş is_between()başarısız olabilir (btw, kılık değiştirmiş bir çapraz üründür).
jfs

ekle is_between():a, b = self.a, self.b
jfs

Görünüşe göre bu, üç noktanın tümü aynıysa (ki sorun değil, imho) doğru, ancak noktalardan ikisi aynıysa yanlış - arayı tanımlamanın oldukça tutarsız bir yolu. Cevabımda bir alternatif yayınladım.
Darius Bacon

bunu başka bir cmp numarasıyla düzelttim, ancak bu kod kokmaya başladı ;-)
vincent

5

İşte C ++ 'da verilen kodla bunu yapmanın farklı bir yolu. İki nokta verildiğinde, l1 ve l2 aralarındaki doğru parçasını şu şekilde ifade etmek önemsizdir:

l1 + A(l2 - l1)

burada 0 <= A <= 1. Bu, sadece bu problem için kullanmanın ötesinde bir şeyle ilgileniyorsanız, bir çizginin vektör gösterimi olarak bilinir. Bunun x ve y bileşenlerini bölerek:

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

Bir nokta (x, y) alın ve A'yı çözmek için x ve y bileşenlerini bu iki ifadeye koyun. Her iki ifadede de A'nın çözümleri eşitse ve 0 <= A <= 1 ise nokta doğrudur. Çünkü A için çözmek bölme gerektirir, çizgi parçası yatay veya dikey olduğunda sıfıra bölmeyi durdurmak için işlem yapılması gereken özel durumlar vardır. Nihai çözüm aşağıdaki gibidir:

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

Daha geometrik bir yaklaşım kullanarak aşağıdaki mesafeleri hesaplayın:

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)

ve test edip ac + bc eşittir ab :

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

Bunun nedeni üç olasılığın olmasıdır:

  • 3 nokta bir üçgen oluşturur => ac + bc> ab
  • Doğrusaldırlar ve c , ab segmentinin dışında => ac + bc> ab
  • Doğrusaldırlar ve c , ab segmentinin içindedir => ac + bc = ab

Jonathan Leffler'ın başka bir yorumda bahsettiği gibi, bu, çapraz ürün gibi diğer yaklaşımların kaçındığı sayısal konulara sahiptir. Cevabımda bağlantı verdiğim bölüm açıklıyor.
Darius Bacon

3

Tamam, doğrusal cebirden (vektörlerin çapraz çarpımı) çok söz edilir ve bu gerçek (yani sürekli veya kayan nokta) bir uzayda çalışır, ancak soru özellikle iki noktanın tamsayı olarak ifade edildiğini ve dolayısıyla bir çapraz çarpımın doğru olmadığını belirtti. çözüm olsa da yaklaşık bir çözüm verebilir.

Doğru çözüm, iki nokta arasında Bresenham'ın Çizgi Algoritmasını kullanmak ve üçüncü noktanın çizgideki noktalardan biri olup olmadığını görmektir. Noktalar, algoritmanın hesaplanmasının sonuç vermeyeceği kadar uzaksa (ve bunun böyle olması için gerçekten büyük olması gerekir), eminim etrafı kazabilir ve optimizasyonları bulabilirsiniz.


İki rasgele nokta arasındaki iki boyutlu bir tamsayı boşluğundan nasıl bir çizgi çizileceğini ve matematiksel olarak doğru olduğunu çözer. Üçüncü nokta, bu doğrudaki noktalardan biriyse, tanım gereği bu iki nokta arasındadır.
cletus

1
Hayır, Bresenham'ın Çizgi Algoritması, iki boyutlu bir tamsayı uzayında bir doğru parçasının bir yaklaşımının nasıl oluşturulacağını çözer . Orijinal posterin mesajından rasterleştirme ile ilgili bir soru olduğunu anlamıyorum.
Cyrille Ka

"Diyelim ki, üzerinde 2 nokta (a ve b olarak adlandırılır) x INTEGER ve her nokta için ay INTEGER ile temsil edilen iki boyutlu bir düzleminiz var." (vurgu benim tarafımdan eklendi).
cletus

1
Bence Bresenham'ın Çizgi Algoritması bir çizgiye tam sayı noktaları verir ve bu daha sonra çizgiyi çizmek için kullanılabilir. Hatta olmayabilir. Örneğin, (0,0) ila (11,13) için algoritma çizilecek bir piksel sayısı verir, ancak bitiş noktaları dışında tam sayı noktaları yoktur, çünkü 11 ve 13 eş asaldır.
Grant M

Gerçek uzay (ℝ × ℝ) için doğru olan bir çözüm, tamsayı uzay (ℕ × ℕ) için as gibi nasıl doğru olmayabilir? Ya da "... için uygun değil" yerine 'doğru değil mi?
Ideogram

2

(Ca) ve (ba) arasındaki skaler çarpım, uzunluklarının çarpımına eşit olmalıdır (bu, (ca) ve (ba) vektörlerinin hizalı ve aynı yönde olduğu anlamına gelir). Ayrıca, (ca) 'nın uzunluğu (ba)' nınkinden daha az veya ona eşit olmalıdır. Sözde kod:

# 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

Son koşul şu şekilde olmamalı mı: ABS (ürün - uzunluk * uzunlukba) <epsilon?
Jonathan Leffler

Bunun yerine kare uzunlukları karşılaştırmanız gerekmez mi? Kareköklerden kaçınılmalıdır. Ayrıca, taşma nedeniyle bu kaçınılmazsa, math.sqrt yerine math.hypot kullanabilirsiniz (uygun argüman değişikliği ile).
Darius Bacon

Ben de o epsilonu merak ediyorum. Bunu açıklayabilir misin? Elbette, şamandıralarla uğraşmamız gerekiyorsa, karşılaştırmalar konusunda dikkatli olmalıyız, ancak bir epsilon'un bu özel karşılaştırmayı neden daha doğru hale getirdiği benim için net değil.
Darius Bacon

Katılıyorum. Bu sorunun birkaç iyi cevabı var ve bu sorun değil. Ancak bu kodun sqrt kullanmayacak şekilde değiştirilmesi gerekiyor ve son karşılaştırma düzeltildi.
Cyrille Ka

@Jonathan: Gerçekten de kod abs kullanarak daha tanıdık ve zarif. Teşekkürler.
Federico A.Ramponi

2

Buna, kullanıcıların imlecinin belirli bir satırın üzerinde mi yoksa yakınında mı olduğunu algılamak için bir html5 tuvalinde kullanmak üzere javascript için buna ihtiyacım vardı. Ben de Darius Bacon'un verdiği cevabı kahve kağıdına dönüştürdüm:

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

Kama ve nokta ürününü kullanabilirsiniz:

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

İşte okulda bunu nasıl yaptım. Bunun neden iyi bir fikir olmadığını unutmuşum.

DÜZENLE:

@Darius Bacon: Aşağıdaki kodun neden iyi bir fikir olmadığına dair bir açıklama içeren "Güzel Kod" kitabından alıntı yapıyor.

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

Çizgi parçası (herhangi bir yerindeki bir , b ) (burada bir ve b vektörler) bir şekilde ifade edilebilir bir doğrusal kombinasyonunun iki vektörün bir ve b :

Diğer bir deyişle, c doğru parçası ( a , b ) üzerindeyse:

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

M için çözdüğümüzde :

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

Yani testimiz (Python'da):

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/ adresinden -> Konu 1.02: Bir noktadan bir çizgiye olan mesafeyi nasıl bulabilirim?

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;
        }

Diğer yaklaşımların çoğunda hassaslık sorunlarını önlemenin doğru yolu. Ayrıca diğer uygulamaların çoğundan önemli ölçüde daha verimli.
Robin Davies

1

İşte benim için çalışan bazı Java kodları:

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 yalnızca hizalamadan bahsedebilir. Kodunuz eksik !!! A (0,0), b (4,0), c (1,1) ile nokta ürün = (1-0) * (1-4) + (1-0) * (1-0) = - 3 + 1 = -3
user43968

0

eğimin aynı olmasını ve noktanın diğerleri arasında olmasını sağlamaya ne dersiniz?

verilen (x1, y1) ve (x2, y2) (x2> x1 ile) ve aday nokta (a, b)

eğer (b-y1) / (a-x1) = (y2-y2) / (x2-x1) Ve x1 <a <x2

O halde (a, b), (x1, y1) ve (x2, y2) arasında aynı hizada olmalıdır


Koordinatlardan bazıları birbirine yakın veya aynı olduğunda çılgın kayan nokta hassaslık problemlerine ne dersiniz?
Robin Davies

Bilgisayarlar kayan noktaları iyi yapmaz. Bir bilgisayarda sonsuz aralıklarla ayarlanabilen değerler diye bir şey yoktur. Dolayısıyla, Kayan noktalar kullanıyorsanız, belirleyici olarak bazı küçük epsilon değerini tanımlamanız ve kullanmanız gerekir ve bu epsilon'dan daha yakın olan herhangi iki nokta aynı nokta olarak değerlendirilmelidir. Aynı çizgide olduğu noktayı ve bitiş noktalarından aynı uzaklıkta olan noktayı belirleyin. Aday noktanız bu hesaplanan noktanın epsilon'unun içindeyse, o zaman onu aynı olarak adlandırın.
Charles Bretana

Demek istediğim, bu cevabın kodda gerçekten uyguladığınızda ortaya çıkan hassasiyet problemleri nedeniyle kullanılamaz olduğuydu. Yani kimse kullanmamalı. Matematik testinde güzel bir cevap. Ancak comp-sci kursunda rekabet başarısız. Buraya nokta-çarpım yöntemini (doğru olan) aramaya geldim; Bu yüzden, bu başlıktaki birçok yanıtı işaretlemek için birkaç dakikanızı ayıracağımı düşündüm, böylece doğru çözüme aşina olan diğerleri onları kullanmak için cazip gelmeyecekler.
Robin Davies

Bilgisayarların bir satırdaki olası her gerçek sayıyı temsil edememesi nedeniyle ortaya çıkan sorunlar konusunda haklısınız. Herhangi bir çözümün (nokta ürün yöntemi dahil) bu sorunlara karşı bağışık olabileceği konusunda yanılıyorsunuz. Herhangi bir çözüm bu sorunlardan zarar görebilir. Kabul edilebilir epsilon için bir miktar pay ayırmadığınız sürece, tam olarak doğru üzerinde olan (ancak koordinatları kayan nokta ikili gösteriminde gösterilemeyen) nokta çarpım testinde de başarısız olacaktır, çünkü bilgisayar noktanın koordinatlarını yanlış bir şekilde temsil edecektir. bir miktar.
Charles Bretana

0

Vector2D sınıfını kullanarak C # dilinde bir cevap

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;
}

Bunu not et

s * s

C # 'da operatör aşırı yüklemesi yoluyla segment vektörünün iç çarpımıdır

Anahtar nokta, noktanın sonsuz çizgiye izdüşümünden yararlanmak ve izdüşümün skaler miktarının bize projeksiyonun segmentte olup olmadığını önemsiz bir şekilde söylediğini gözlemlemektir. Bulanık bir tolerans kullanmak için skaler miktarın sınırlarını ayarlayabiliriz.

Projeksiyon sınırlar dahilindeyse, sadece noktadan projeksiyona olan mesafenin sınırlar içinde olup olmadığını test ederiz.

Çapraz ürün yaklaşımına göre fayda, toleransın anlamlı bir değere sahip olmasıdır.


0

İşte Unity'de C # ile çözümüm.

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;
}

Görünüşe göre bu kod sadece dikey ve yatay çizgi parçalarıyla çalışıyor. PtLineStart (0,0), ptLineEnd (2,2) ve ptPoint (1, 1) ise ne olur?
vac

0

Jules'un cevabının C # versiyonu:

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

Bunu, o noktanın doğru üzerinde olup olmadığını bileceğiniz nokta koordinatlarıyla o doğru parçası için çizgi denklemini çözerek ve ardından parçanın içinde mi yoksa dışında mı olduğunu anlamak için parçanın sınırlarını kontrol ederek yapabilirsiniz. Bir eşik uygulayabilirsiniz, çünkü uzayda bir yerlerde büyük olasılıkla bir kayan nokta değeriyle tanımlanır ve tam olarak bir eşiğe ulaşmamalısınız. Php'de örnek

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;
    
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.