Bagaimana Anda bisa menentukan sebuah titik di antara dua titik lain pada ruas garis?


94

Misalkan Anda memiliki bidang dua dimensi dengan 2 titik (disebut a dan b) di atasnya yang diwakili oleh bilangan bulat x dan bilangan bulat ay untuk setiap titik.

Bagaimana Anda dapat menentukan jika titik c lain berada pada ruas garis yang ditentukan oleh a dan b?

Saya paling sering menggunakan python, tetapi contoh dalam bahasa apa pun akan membantu.


4
Saya melihat BANYAK hal-hal dengan panjang = akar (x) terjadi dalam jawaban ini; mereka mungkin berhasil, tetapi mereka tidak cepat. Pertimbangkan untuk menggunakan panjang-kuadrat; jika Anda hanya membandingkan nilai panjang kuadrat satu sama lain, tidak ada kehilangan keakuratan, dan Anda menyimpan panggilan lambat ke sqrt ().
ojrac

1
Apakah titik c juga diwakili oleh 2 bilangan bulat? Jika demikian, maka apakah Anda ingin mengetahui apakah c persis di sepanjang garis lurus nyata antara a dan b atau terletak pada pendekatan raster dari garis lurus antara a dan b? Ini adalah klarifikasi penting.
RobS

Pertanyaan serupa ditanyakan di sini: stackoverflow.com/q/31346862/1914034 dengan solusi ketika jarak buffer dari garis diperlukan
Di bawah Radar


1
Peringatan untuk pembaca selanjutnya: Cukup banyak jawaban yang tidak benar atau tidak lengkap. Beberapa casing tepi yang sering tidak berfungsi adalah garis horizontal dan vertikal.
Stefnotch

Jawaban:


127

Periksa apakah produk silang dari (ba) dan (ca) adalah 0, seperti yang dikatakan Darius Bacon, memberi tahu Anda jika titik a, b dan c sejajar.

Tetapi, karena Anda ingin mengetahui apakah c berada di antara a dan b, Anda juga harus memeriksa bahwa hasil perkalian titik dari (ba) dan (ca) positif dan lebih kecil dari kuadrat jarak antara a dan b.

Dalam pseudocode yang tidak dioptimalkan:

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)sudah cukup, bukan?
jfs

9
Nilai absolut produk silang adalah dua kali luas segitiga yang dibentuk oleh tiga titik (dengan tanda menunjukkan sisi titik ketiga) jadi IMHO Anda harus menggunakan epsilon yang sebanding dengan jarak antara dua titik ujung.
bart

2
Bisakah Anda memberi tahu kami mengapa itu tidak berfungsi dengan bilangan bulat? Saya tidak melihat masalahnya, asalkan pemeriksaan epsilon diganti dengan "! = 0".
Cyrille Ka

2
Ya, tanda kurung tambahan hanyalah salah ketik. 4 tahun telah berlalu sebelum seseorang mengatakan sesuatu. :)
Cyrille Ka

4
Anda harus mengganti nama a, b, c untuk memperjelas mana yang merupakan titik akhir segmen dan yang merupakan titik kueri.
Craig Gidney

48

Inilah cara saya melakukannya:

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
Ini adalah solusi yang elegan.
Paul D. Eden

6
Satu-satunya masalah dengan ini adalah stabilitas numerik - perbedaan angka dan sebagainya cenderung kehilangan presisi.
Jonathan Leffler

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

1
@jfs apa yang kamu maksud dengan itu? Untuk apa cek dengan epsilon?
Neon Warge

3
@NeonWarge: sqrt () mengembalikan float. ==adalah hal yang salah untuk float dalam banyak kasus . math.isclose()bisa digunakan sebagai gantinya. Tidak ada math.isclose()di tahun 2008 dan oleh karena itu saya telah memberikan ketidaksetaraan eksplisit dengan epsilon( abs_toldalam math.isclose()pembicaraan).
jfs

36

Periksa apakah perkalian silang dari b-adan c-aadalah 0: itu berarti semua titik collinear. Jika ya, periksa apakah ckoordinatnya antara as dan b. Gunakan koordinat x atau y, selama adan bterpisah pada sumbu tersebut (atau keduanya sama pada keduanya).

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

Jawaban ini dulunya adalah kekacauan dari tiga pembaruan. Info berharga dari mereka: bab Brian Hayes dalam Beautiful Code mencakup ruang desain untuk fungsi uji-collinearity - latar belakang yang berguna. Jawaban Vincent membantu memperbaiki yang ini. Dan Hayes yang menyarankan pengujian hanya satu dari koordinat x atau y; awalnya kode telah andmenggantikan if a.x != b.x else.


Karena pemeriksaan jarak lebih cepat, akan lebih baik memeriksa jarak terlebih dahulu kemudian memeriksa collinear jika di kotak pembatas.
Berikan M

1
Fungsi is_on (a, b, c) salah untuk kasus di mana a == b! = C. Dalam kasus seperti itu akan mengembalikan nilai true, meskipun c tidak memotong ruas garis dari a ke b.
Mikko Virkkilä

@SuperFlux, saya baru saja mencoba menjalankannya, dan mendapatkan False.
Darius Bacon

2
Saya pikir jawaban ini jelas lebih unggul dari jawaban yang diterima saat ini.
Rick mendukung Monica

1
collinear (a, b, c) menguji bilangan floating point dengan persamaan. Bukankah seharusnya itu menggunakan epsilon / toleransi?
jwezorek

7

Berikut pendekatan lain:

  • Mari kita asumsikan dua titik menjadi A (x1, y1) dan B (x2, y2)
  • Persamaan garis yang melewati titik-titik tersebut adalah (x-x1) / (y-y1) = (x2-x1) / (y2-y1) .. (hanya menyamakan lereng)

Titik C (x3, y3) akan berada di antara A & B jika:

  • x3, y3 memenuhi persamaan di atas.
  • x3 terletak di antara x1 & x2 dan y3 terletak di antara y1 & y2 (cek sepele)

Itu tidak memperhitungkan kesalahan pembulatan (ketidaktepatan koordinat).
bart

Ini adalah ide yang tepat, menurut saya, tapi singkatnya (bagaimana kita memeriksa persamaan itu dalam praktiknya?) Dan agak bermasalah: y3 terakhir seharusnya y2.
Darius Bacon

@Darius: memperbaiki kesalahan ketik itu
Harley Holcombe

7

Panjang segmen tidak penting, sehingga penggunaan akar kuadrat tidak diperlukan dan harus dihindari karena kita dapat kehilangan beberapa presisi.

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)

Beberapa contoh penggunaan acak:

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
Jika cx atau cy mengambang maka yang pertama ==masuk is_between()bisa gagal (btw itu adalah produk silang yang disamarkan).
jfs

tambahkan ke is_between():a, b = self.a, self.b
jfs

Sepertinya itu akan mengembalikan nilai true jika ketiga poin sama (yang baik-baik saja, imho) tetapi salah jika tepat dua poin yang sama - cara yang cukup tidak konsisten untuk mendefinisikan antara. Saya memposting alternatif dalam jawaban saya.
Darius Bacon

memperbaikinya dengan trik cmp lain, tetapi kode ini mulai berbau ;-)
vincent

5

Berikut cara lain untuk melakukannya, dengan kode yang diberikan dalam C ++. Diberikan dua poin, l1 dan l2 adalah sepele untuk mengekspresikan ruas garis di antara mereka sebagai

l1 + A(l2 - l1)

dimana 0 <= A <= 1. Ini dikenal sebagai representasi vektor dari sebuah garis jika Anda tertarik lebih dari sekadar menggunakannya untuk soal ini. Kita dapat membagi komponen x dan y ini, memberikan:

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

Ambil satu titik (x, y) dan gantikan komponen x dan y menjadi dua ekspresi ini untuk menyelesaikan A. Titik tersebut berada pada garis jika solusi untuk A di kedua ekspresi adalah sama dan 0 <= A <= 1. Karena Penyelesaian untuk A membutuhkan pembagian, ada kasus khusus yang membutuhkan penanganan untuk menghentikan pembagian dengan nol ketika ruas garis horizontal atau vertikal. Solusi akhirnya adalah sebagai berikut:

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

Dengan menggunakan pendekatan yang lebih geometris, hitung jarak berikut:

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)

dan uji apakah ac + bc sama dengan ab :

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

Itu karena ada tiga kemungkinan:

  • 3 titik membentuk segitiga => ac + bc> ab
  • Mereka collinear dan c berada di luar segmen ab => ac + bc> ab
  • Mereka collinear dan c berada di dalam segmen ab => ac + bc = ab

Seperti yang disebutkan Jonathan Leffler dalam komentar lain, ini memiliki masalah numerik yang dihindari oleh pendekatan lain seperti produk silang. Bab yang saya tautkan dalam jawaban saya menjelaskan.
Darius Bacon

3

Ok, banyak penyebutan aljabar linier (perkalian vektor) dan ini bekerja dalam ruang nyata (yaitu titik kontinu atau mengambang) tetapi pertanyaannya secara khusus menyatakan bahwa kedua titik tersebut dinyatakan sebagai bilangan bulat dan dengan demikian perkalian silang tidak benar solusi meskipun dapat memberikan solusi perkiraan.

Solusi yang tepat adalah dengan menggunakan Algoritma Garis Bresenham antara dua titik dan untuk melihat apakah titik ketiga adalah salah satu titik pada garis. Jika poinnya cukup jauh sehingga menghitung algoritme tidak berkinerja baik (dan itu harus sangat besar untuk menjadi kasusnya), saya yakin Anda dapat menggali dan menemukan optimisasi.


Ini memecahkan cara menggambar garis melalui ruang integer dua dimensi antara dua titik sembarang dan benar secara matematis. Jika titik ketiga adalah salah satu titik pada garis itu maka itu, menurut definisi, berada di antara dua titik itu.
cletus

1
Tidak, Algoritma Garis Bresenham memecahkan cara membuat perkiraan ruas garis dalam ruang bilangan bulat dua dimensi. Saya tidak melihat dari pesan poster asli bahwa itu adalah pertanyaan tentang rasterisasi.
Cyrille Ka

"Katakanlah Anda memiliki bidang dua dimensi dengan 2 titik (disebut a dan b) di atasnya yang diwakili oleh x INTEGER dan ay INTEGER untuk setiap titik." (penekanan ditambahkan oleh saya).
cletus

1
Saya pikir Algoritma Garis Bresenham memberikan poin integer lemari ke sebuah garis, yang kemudian dapat digunakan untuk menarik garis. Mereka mungkin tidak di telepon. Misal jika untuk (0,0) sampai (11,13) algoritma akan memberikan sejumlah piksel untuk digambar tetapi tidak ada titik integer kecuali titik akhir, karena 11 dan 13 adalah coprime.
Grant M

Bagaimana solusi yang tepat untuk ruang nyata (ℝ × ℝ) tidak benar untuk ruang bilangan bulat (ℕ × ℕ), sebagai ℕ∈ℝ. Atau maksud Anda: "tidak optimal untuk…" bukannya 'tidak benar?
Ideogram

2

Hasil kali skalar antara (ca) dan (ba) harus sama dengan hasil kali panjangnya (ini berarti vektor (ca) dan (ba) sejajar dan searah). Selain itu, panjang (ca) harus kurang dari atau sama dengan (ba). Pseudocode:

# 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

Bukankah seharusnya kondisi terakhir lebih seperti: ABS (product - lengthca * lengthba) <epsilon?
Jonathan Leffler

Bukankah Anda seharusnya membandingkan panjang kuadrat? Akar kuadrat harus dihindari. Selain itu, jika ini tidak dapat dihindari karena overflow, Anda dapat menggunakan math.hypot sebagai ganti math.sqrt (dengan perubahan argumen yang sesuai).
Darius Bacon

Aku juga ingin tahu tentang epsilon itu. Bisakah Anda menjelaskannya? Tentu saja, jika kita harus berurusan dengan pelampung, kita harus berhati-hati tentang perbandingan, tetapi tidak jelas bagi saya mengapa epsilon membuat perbandingan khusus ini lebih akurat.
Darius Bacon

Saya setuju. Ada beberapa jawaban bagus untuk pertanyaan ini, dan yang ini bagus. Tetapi kode ini perlu diubah agar tidak menggunakan sqrt, dan perbandingan terakhir diperbaiki.
Cyrille Ka

@Jonathan: memang kodenya lebih familiar dan elegant menggunakan abs. Terima kasih.
Federico A. Ramponi

2

Saya membutuhkan ini untuk javascript untuk digunakan dalam kanvas html5 untuk mendeteksi apakah kursor pengguna berada di atas atau di dekat baris tertentu. Maka saya memodifikasi jawaban yang diberikan oleh Darius Bacon menjadi 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

Anda dapat menggunakan produk wedge dan dot:

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

Begini cara saya melakukannya di sekolah. Saya lupa mengapa itu bukan ide yang bagus.

EDIT:

@Darius Bacon: mengutip buku "Beautiful Code" yang berisi penjelasan mengapa kode di bawah ini bukan ide yang bagus.

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

Setiap titik pada ruas garis ( a , b ) (di mana a dan b adalah vektor) dapat dinyatakan sebagai kombinasi linier dari dua vektor a dan b :

Dengan kata lain, jika c terletak pada ruas garis ( a , b ):

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

Memecahkan m , kita dapatkan:

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

Jadi, pengujian kami menjadi (dengan 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 # Dari http://www.faqs.org/faqs/graphics/algorithms-faq/ -> Subjek 1.02: Bagaimana cara mencari jarak dari satu titik ke garis?

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

Cara yang benar untuk menghindari masalah presisi di sebagian besar pendekatan lainnya. Juga jauh lebih efisien daripada kebanyakan approraches lainnya.
Robin Davies

1

Berikut beberapa kode Java yang berhasil untuk saya:

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 hanya dapat menjelaskan tentang keselarasan. Kode Anda tidak lengkap !!! Dengan a (0,0), b (4,0), c (1,1) Anda memiliki dotproduct = (1-0) * (1-4) + (1-0) * (1-0) = - 3 + 1 = -3
pengguna43968

0

bagaimana dengan memastikan bahwa kemiringannya sama dan titiknya berada di antara yang lain?

diberikan poin (x1, y1) dan (x2, y2) (dengan x2> x1) dan kandidat poin (a, b)

if (b-y1) / (a-x1) = (y2-y2) / (x2-x1) Dan x1 <a <x2

Maka (a, b) harus berada pada garis antara (x1, y1) dan (x2, y2)


Bagaimana dengan masalah ketepatan floating point gila ketika beberapa koordinat dekat atau identik?
Robin Davies

Komputer tidak melakukan floating point dengan baik. Di komputer tidak ada yang namanya nilai yang dapat disesuaikan secara terus menerus tanpa batas. Jadi jika Anda menggunakan titik mengambang, Anda harus menetapkan define dan menggunakan beberapa nilai epsilon kecil sebagai determinan, dan dua titik yang lebih dekat dari epsilon itu harus dianggap sebagai titik yang sama. Tentukan titik yang IS pada garis yang sama dan jarak yang sama dari titik akhir. Jika titik kandidat Anda berada dalam epsilon dari titik yang dihitung itu, maka sebut itu identik.
Charles Bretana

Maksud saya adalah bahwa jawaban ini tidak dapat digunakan karena masalah presisi ketika Anda benar-benar menerapkannya dalam kode. Jadi tidak ada yang harus menggunakannya. Jawaban yang bagus untuk tes matematika. Tapi kegagalan bersaing dalam kursus comp-sci. Saya datang ke sini mencari metode produk titik (yang benar); jadi saya pikir saya akan mengambil beberapa saat untuk menandai banyak jawaban di utas ini yang salah sehingga orang lain yang terbiasa dengan solusi yang benar tidak akan tergoda untuk menggunakannya.
Robin Davies

Anda benar tentang masalah yang muncul karena ketidakmampuan komputer untuk mewakili setiap kemungkinan bilangan real dalam satu baris. Anda tidak benar bahwa solusi apa pun (termasuk metode produk titik) dapat kebal terhadap masalah ini. Solusi apa pun dapat mengalami masalah ini. Kecuali Anda membuat beberapa kelonggaran untuk epsilon yang dapat diterima, titik yang persis pada garis (tetapi yang koordinatnya tidak dapat direpresentasikan dalam representasi biner titik mengambang ieee), juga akan gagal dalam uji produk titik, karena komputer akan mewakili koordinat titik secara tidak akurat dengan jumlah tertentu.
Charles Bretana

0

Jawaban di C # menggunakan kelas Vector2D

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

Catat itu

s * s

adalah produk titik dari vektor segmen melalui kelebihan beban operator di C #

Kuncinya adalah mengambil keuntungan dari proyeksi titik ke garis tak hingga dan mengamati bahwa kuantitas skalar dari proyeksi memberi tahu kita secara sepele apakah proyeksi itu ada di segmen atau tidak. Kami dapat menyesuaikan batas-batas kuantitas skalar untuk menggunakan toleransi fuzzy.

Jika proyeksi dalam batas-batas kami hanya menguji apakah jarak dari titik ke proyeksi dalam batas-batas.

Manfaat dari pendekatan produk silang adalah bahwa toleransi memiliki nilai yang berarti.


0

Inilah solusi saya dengan C # di Unity.

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

Sepertinya kode ini hanya berfungsi dengan segmen garis vertikal dan horizontal. Bagaimana jika ptLineStart adalah (0,0), ptLineEnd adalah (2,2) dan ptPoint adalah (1, 1)?
vakum

0

Versi C # dari jawaban Jules:

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

Anda dapat melakukannya dengan menyelesaikan persamaan garis untuk ruas garis tersebut dengan koordinat titik. Anda akan mengetahui apakah titik itu ada di garis dan kemudian memeriksa batas-batas ruas tersebut untuk mengetahui apakah itu di dalam atau di luarnya. Anda dapat menerapkan beberapa ambang batas karena itu adalah suatu tempat di ruang yang kemungkinan besar ditentukan oleh nilai titik mengambang dan Anda tidak boleh mencapai yang tepat. Contoh di 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;
    
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.