ฉันจะตรวจสอบได้อย่างไรว่าสองส่วนตัดกัน?


93

จะตรวจสอบได้อย่างไรว่า 2 ส่วนตัดกัน?

ฉันมีข้อมูลต่อไปนี้:

Segment1 [ {x1,y1}, {x2,y2} ]
Segment2 [ {x1,y1}, {x2,y2} ] 

ฉันต้องเขียนอัลกอริทึมเล็ก ๆ ใน Python เพื่อตรวจสอบว่า 2 บรรทัดตัดกันหรือไม่


ข้อความแสดงแทน


2
ดูtopcoder.com/tc?module=Static&d1=tutorials&d2=geometry2
FMc

คำตอบ:


64

สมการของเส้นคือ:

f(x) = A*x + b = y

สำหรับเซ็กเมนต์นั้นจะเหมือนกันทุกประการยกเว้นว่า x จะรวมอยู่ในช่วงเวลา I

หากคุณมีสองส่วนให้กำหนดดังนี้:

Segment1 = {(X1, Y1), (X2, Y2)}
Segment2 = {(X3, Y3), (X4, Y4)}

abcisse Xa ของจุดตัดที่เป็นไปได้ (Xa, Ya) ต้องอยู่ในช่วง I1 และ I2 ซึ่งกำหนดไว้ดังต่อไปนี้:

I1 = [min(X1,X2), max(X1,X2)]
I2 = [min(X3,X4), max(X3,X4)]

และเราสามารถพูดได้ว่า Xa รวมอยู่ใน:

Ia = [max( min(X1,X2), min(X3,X4) ),
      min( max(X1,X2), max(X3,X4) )]

ตอนนี้เราต้องตรวจสอบว่ามีช่วงเวลานี้อยู่หรือไม่:

if (max(X1,X2) < min(X3,X4)):
    return False  # There is no mutual abcisses

ดังนั้นเราจึงมีสูตรสองบรรทัดและช่วงเวลาร่วมกัน สูตรบรรทัดของคุณคือ:

f1(x) = A1*x + b1 = y
f2(x) = A2*x + b2 = y

เมื่อเราได้คะแนนสองจุดตามส่วนเราจึงสามารถกำหนด A1, A2, b1 และ b2 ได้:

A1 = (Y1-Y2)/(X1-X2)  # Pay attention to not dividing by zero
A2 = (Y3-Y4)/(X3-X4)  # Pay attention to not dividing by zero
b1 = Y1-A1*X1 = Y2-A1*X2
b2 = Y3-A2*X3 = Y4-A2*X4

หากส่วนขนานกัน A1 == A2:

if (A1 == A2):
    return False  # Parallel segments

จุด (Xa, Ya) ที่ยืนอยู่บนทั้งสองบรรทัดต้องตรวจสอบทั้งสูตร f1 และ f2:

Ya = A1 * Xa + b1
Ya = A2 * Xa + b2
A1 * Xa + b1 = A2 * Xa + b2
Xa = (b2 - b1) / (A1 - A2)   # Once again, pay attention to not dividing by zero

สิ่งสุดท้ายที่ต้องทำคือตรวจสอบว่า Xa รวมอยู่ใน Ia:

if ( (Xa < max( min(X1,X2), min(X3,X4) )) or
     (Xa > min( max(X1,X2), max(X3,X4) )) ):
    return False  # intersection is out of bound
else:
    return True

นอกจากนี้คุณสามารถตรวจสอบเมื่อเริ่มต้นว่าสองในสี่คะแนนที่ให้มานั้นไม่เท่ากับเพื่อหลีกเลี่ยงการทดสอบทั้งหมด


1
เซ็กเมนต์พวกมันเป็นเซ็กเมนต์ขออภัย คุณช่วยอัปเดตคำตอบของคุณในกลุ่มได้ไหม
aneuryzm

13
สิ่งนี้ไม่ซับซ้อนมากฉันเขียนขั้นตอนกลาง (ไม่จำเป็น?) จำนวนมากในจุดประสงค์เพื่อความเข้าใจ ประเด็นหลักในการดำเนินการมีเพียง: ตรวจสอบการมีอยู่ของช่วงเวลาร่วมกันคำนวณ A1, A2, b1, b2 และ Xa จากนั้นตรวจสอบว่า Xa รวมอยู่ในช่วงเวลาร่วมกัน นั่นคือทั้งหมด :)
OMG_peanuts

3
A1 - A2 จะไม่เป็นศูนย์เพราะถ้า (A1 == A2) จะกลับมาก่อนการคำนวณนี้ในกรณีนั้น
inkredibl

3
ถ้า A1 == A2 และ b1 == b2 ส่วนต่างๆจะอยู่ด้านบนของกันและกันและมีทางแยกมากมายอย่างไม่สิ้นสุด
lynxoid

5
สูตร A1 * x + b1 = y ไม่รองรับเส้นแนวตั้งดังนั้นควรจัดการส่วนแนวตั้งแยกกันด้วยวิธีนี้
dmitri

78

User @ i_4_got ชี้ไปที่หน้านี้ด้วยโซลูชันที่มีประสิทธิภาพมากใน Python ฉันทำซ้ำที่นี่เพื่อความสะดวก (เพราะมันทำให้ฉันมีความสุขที่ได้มาที่นี่):

def ccw(A,B,C):
    return (C.y-A.y) * (B.x-A.x) > (B.y-A.y) * (C.x-A.x)

# Return true if line segments AB and CD intersect
def intersect(A,B,C,D):
    return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D)

8
เรียบง่ายและสง่างามมาก แต่ไม่สามารถจัดการกับ colinearity ได้ดีดังนั้นจึงจำเป็นต้องมีรหัสเพิ่มเติมสำหรับจุดประสงค์นั้น
charles

7
สำหรับโซลูชันที่จัดการ collinearity ได้โปรดดูที่geeksforgeeks.org/check-if-two-given-line-se segment
Zsolt Safrany

รักโซลูชันนี้ ง่ายและสั้นมาก! ฉันสร้างโปรแกรม wxPython ที่ลากเส้นและดูว่ามันตัดกับบรรทัดอื่นหรือไม่ ฉันวางที่นี่ไม่ได้ดังนั้นจึงอยู่ใต้ความคิดเห็นนี้
user1766438

33

คุณไม่จำเป็นต้องคำนวณว่าส่วนต่างๆตัดกันตรงไหนแต่ต้องเข้าใจว่าทั้งสองส่วนตัดกันหรือไม่ วิธีนี้จะทำให้การแก้ปัญหาง่ายขึ้น

แนวคิดคือการปฏิบัติต่อส่วนหนึ่งเป็น "จุดยึด" และแยกส่วนที่สองออกเป็น 2 จุด
ตอนนี้คุณจะต้องค้นหาตำแหน่งสัมพัทธ์ของแต่ละจุดไปยังส่วนที่ "ยึด" (OnLeft, OnRight หรือ Collinear)
หลังจากทำทั้งสองจุดแล้วให้ตรวจสอบว่าจุดใดจุดหนึ่งคือ OnLeft และอีกจุดคือ OnRight (หรืออาจรวมถึงตำแหน่ง Collinear หากคุณต้องการรวมที่ไม่เหมาะสมจุดตัดที่ด้วย)

จากนั้นคุณต้องทำซ้ำกระบวนการโดยใช้บทบาทของจุดยึดและเซ็กเมนต์ที่แยกออก

จุดตัดจะมีอยู่ถ้าจุดใดจุดหนึ่งคือ OnLeft และอีกจุดคือ OnRight ดูลิงค์นี้เพื่อดูคำอธิบายโดยละเอียดพร้อมภาพตัวอย่างสำหรับแต่ละกรณีที่เป็นไปได้

การใช้วิธีการดังกล่าวจะง่ายกว่าการใช้วิธีที่พบจุดตัด (เนื่องจากมีหลายกรณีที่คุณจะต้องจัดการเช่นกัน)

อัปเดต

ฟังก์ชันต่อไปนี้ควรแสดงให้เห็นถึงแนวคิด (ที่มา: Computational Geometry ใน C )
หมายเหตุ:ตัวอย่างนี้ถือว่าการใช้จำนวนเต็ม หากคุณกำลังใช้การแสดงจุดลอยตัวแทน (ซึ่งอาจทำให้สิ่งต่าง ๆ ซับซ้อนขึ้นอย่างเห็นได้ชัด) คุณควรกำหนดค่า epsilon เพื่อระบุ "ความเท่าเทียมกัน" (ส่วนใหญ่ใช้สำหรับการIsCollinearประเมิน)

แน่นอนเมื่อใช้ฟังก์ชันเหล่านี้เราต้องอย่าลืมตรวจสอบว่าแต่ละส่วนอยู่ "ระหว่าง" ส่วนอื่น ๆ (เนื่องจากเป็นส่วนที่ จำกัด และไม่ใช่เส้นที่ไม่มีที่สิ้นสุด)

นอกจากนี้การใช้ฟังก์ชั่นเหล่านี้คุณสามารถเข้าใจว่าคุณมีที่เหมาะสมหรือไม่เหมาะสมสี่แยก

  • เหมาะสม : ไม่มีจุด collinear ส่วนต่างๆข้ามซึ่งกันและกัน "จากด้านหนึ่งไปอีกด้านหนึ่ง"
  • ไม่เหมาะสม : ส่วนหนึ่ง "แตะ" อีกส่วนหนึ่งเท่านั้น (อย่างน้อยหนึ่งในจุดที่อยู่ตรงข้ามกับส่วนที่ยึด)

+1 ความคิดของฉันสวยมากเช่นกัน หากคุณแค่คิดถึงจุดที่มีความสัมพันธ์กันคุณสามารถตัดสินใจได้ว่าส่วนของพวกเขาจะต้องตัดกันหรือไม่โดยไม่ต้องคำนวณอะไรเลย
Jochen Ritzel

และ @ THC4k อืมมันไม่ชัดเจนจริงๆ ตัวอย่าง F หรือตรวจสอบรูปภาพที่ฉันเพิ่มในคำถาม: 2 จุดคือ "OnLeft" และ "OnRight" แต่ทั้ง 2 ส่วนไม่ได้ตัดกัน
aneuryzm

@ แพทริคไม่มีจริง ทั้งนี้ขึ้นอยู่กับว่าส่วนใดเป็น "จุดยึด" ดังนั้นทั้งสองจุดจึงเป็น OnLeft หรือ OnRight ในกรณีนี้ (ดูคำตอบที่อัปเดตของฉัน)
Liran

1
+1 ฉันได้เห็นคำตอบมากมายสำหรับปัญหานี้ แต่นี่เป็นคำตอบที่ชัดเจนง่ายที่สุดและมีประสิทธิภาพที่สุดเท่าที่ฉันเคยเห็นมา :)
มิเกล

16

สมมติว่าทั้งสองกลุ่มมีจุดสิ้นสุด A, B และ C, D วิธีที่มีประสิทธิภาพเชิงตัวเลขในการกำหนดจุดตัดคือการตรวจสอบสัญลักษณ์ของปัจจัยทั้งสี่:

| Ax-Cx  Bx-Cx |    | Ax-Dx  Bx-Dx |
| Ay-Cy  By-Cy |    | Ay-Dy  By-Dy |

| Cx-Ax  Dx-Ax |    | Cx-Bx  Dx-Bx |
| Cy-Ay  Dy-Ay |    | Cy-By  Dy-By |

สำหรับจุดตัดดีเทอร์มิแนนต์แต่ละตัวทางด้านซ้ายจะต้องมีเครื่องหมายตรงข้ามของตัวที่อยู่ทางขวา แต่ไม่จำเป็นต้องมีความสัมพันธ์ใด ๆ ระหว่างสองบรรทัด โดยพื้นฐานแล้วคุณกำลังตรวจสอบแต่ละจุดของส่วนกับส่วนอื่น ๆ เพื่อให้แน่ใจว่าพวกเขาอยู่คนละฟากของเส้นที่กำหนดโดยส่วนอื่น ๆ

ดูที่นี่: http://www.cs.cmu.edu/~quake/robust.html


มันใช้ได้กับทางแยกที่ไม่เหมาะสมเช่นเมื่อจุดตัดอยู่บนส่วนของเส้นตรงเดียวหรือไม่?
Sayam Qazi

@SayamQazi ดูเหมือนว่าจะล้มเหลวในการตัดกันหากคุณกำลังผ่านจุดสิ้นสุดของส่วนของเส้นตรงอย่างน้อย หากคุณอยู่ในกลุ่ม: ฉันคิดว่ามันจะเป็นการเปรียบเทียบ 0 vs 1 / -1 ดังนั้นมันจะตรวจไม่พบจุดตัด
Warty

1
โดยวิธีการที่จะอธิบายสิ่งนี้: ดีเทอร์มิแนนต์แต่ละตัวกำลังคำนวณผลคูณระหว่างจุดสิ้นสุดเวกเตอร์ของเซ็กเมนต์สองบรรทัด ด้านบนซ้ายคือ CA x CB เทียบกับ DA x DB ด้านขวาบนเช่น สิ่งนี้เป็นการทดสอบว่าจุดยอดอยู่ด้านใด (สัญญาณนาฬิกา) ยังคงพยายามหาวิธีการทำงานสำหรับส่วนของเส้นที่ไม่ขยายออกไปอย่างไม่มีที่สิ้นสุด
Warty

8

การตรวจสอบว่าส่วนของเส้นตัดกันนั้นง่ายมากหรือไม่ด้วยไลบรารีShapelyโดยใช้intersectsวิธีการ:

from shapely.geometry import LineString

line = LineString([(0, 0), (1, 1)])
other = LineString([(0, 1), (1, 0)])
print(line.intersects(other))
# True

ป้อนคำอธิบายภาพที่นี่

line = LineString([(0, 0), (1, 1)])
other = LineString([(0, 1), (1, 2)])
print(line.intersects(other))
# False

ป้อนคำอธิบายภาพที่นี่


6

จากคำตอบที่ยอดเยี่ยมของ LiranและGrumdrigนี่คือรหัส Python ที่สมบูรณ์เพื่อตรวจสอบว่าส่วนที่ปิดตัดกันหรือไม่ ใช้ได้กับเซกเมนต์ collinear, เซกเมนต์ขนานกับแกน Y, เซกเมนต์เสื่อม (ปีศาจอยู่ในรายละเอียด) ถือว่าพิกัดจำนวนเต็ม พิกัดจุดลอยต้องมีการปรับเปลี่ยนเพื่อทดสอบความเท่าเทียมกัน

def side(a,b,c):
    """ Returns a position of the point c relative to the line going through a and b
        Points a, b are expected to be different
    """
    d = (c[1]-a[1])*(b[0]-a[0]) - (b[1]-a[1])*(c[0]-a[0])
    return 1 if d > 0 else (-1 if d < 0 else 0)

def is_point_in_closed_segment(a, b, c):
    """ Returns True if c is inside closed segment, False otherwise.
        a, b, c are expected to be collinear
    """
    if a[0] < b[0]:
        return a[0] <= c[0] and c[0] <= b[0]
    if b[0] < a[0]:
        return b[0] <= c[0] and c[0] <= a[0]

    if a[1] < b[1]:
        return a[1] <= c[1] and c[1] <= b[1]
    if b[1] < a[1]:
        return b[1] <= c[1] and c[1] <= a[1]

    return a[0] == c[0] and a[1] == c[1]

#
def closed_segment_intersect(a,b,c,d):
    """ Verifies if closed segments a, b, c, d do intersect.
    """
    if a == b:
        return a == c or a == d
    if c == d:
        return c == a or c == b

    s1 = side(a,b,c)
    s2 = side(a,b,d)

    # All points are collinear
    if s1 == 0 and s2 == 0:
        return \
            is_point_in_closed_segment(a, b, c) or is_point_in_closed_segment(a, b, d) or \
            is_point_in_closed_segment(c, d, a) or is_point_in_closed_segment(c, d, b)

    # No touching and on the same side
    if s1 and s1 == s2:
        return False

    s1 = side(c,d,a)
    s2 = side(c,d,b)

    # No touching and on the same side
    if s1 and s1 == s2:
        return False

    return True

"เซ็กเมนต์ปิด" หมายความว่าอย่างไร
แซม

@Sam เซ็กเมนต์ปิดมีจุดสิ้นสุด เช่นส่วนปิดของจุดจากRจะเป็น [0, 1] (0 <= x <= 1) ตรงข้ามกับการพูดว่า] 0, 1] (0 <x <= 1)
dmitri

6

นี่คือวิธีแก้ปัญหาโดยใช้ผลิตภัณฑ์ดอท:

# assumes line segments are stored in the format [(x0,y0),(x1,y1)]
def intersects(s0,s1):
    dx0 = s0[1][0]-s0[0][0]
    dx1 = s1[1][0]-s1[0][0]
    dy0 = s0[1][1]-s0[0][1]
    dy1 = s1[1][1]-s1[0][1]
    p0 = dy1*(s1[1][0]-s0[0][0]) - dx1*(s1[1][1]-s0[0][1])
    p1 = dy1*(s1[1][0]-s0[1][0]) - dx1*(s1[1][1]-s0[1][1])
    p2 = dy0*(s0[1][0]-s1[0][0]) - dx0*(s0[1][1]-s1[0][1])
    p3 = dy0*(s0[1][0]-s1[1][0]) - dx0*(s0[1][1]-s1[1][1])
    return (p0*p1<=0) & (p2*p3<=0)

นี่คือการแสดงภาพใน Desmos: Line Segment Intersection


นี่ยอดเยี่ยมมากและฉันลืม Desmos ไปแล้ว - เหมาะสำหรับปัญหานี้! ขอบคุณ!
ponadto

รักวิธีการแก้ปัญหาของคุณ แต่มันดูเหมือนกับว่ามันล้มเหลวถ้าทั้งสองกลุ่มสายอยู่ในสาย
เอชสมเด็จพระสันตะปาปา

ดีมาก. สำหรับคนอื่น porting นี้ให้เป็นภาษาที่แตกต่างกันให้แน่ใจว่าจะใช้ลอยหรือ int64 เป็น int32 จะล้นสวยได้อย่างรวดเร็วกับตัวเลขน้อยกว่า 1280x720
ที่คนอื่น

4

คุณมีสองส่วนบรรทัด กำหนดส่วนหนึ่งตามจุดสิ้นสุด A & B และส่วนที่สองตามจุดสิ้นสุด C & D มีเคล็ดลับที่ดีในการแสดงว่าพวกเขาต้องตัดกันภายในขอบเขตของเซ็กเมนต์ (โปรดทราบว่าเส้นอาจตัดกันเกินขอบเขตของส่วนดังนั้นคุณต้องระวังโค้ดที่ดีจะคอยดูเส้นขนานด้วย)

เคล็ดลับคือการทดสอบว่าจุด A และ B ต้องเรียงกันคนละด้านของเส้นซีดีและจุด C และ D ต้องอยู่คนละฟากของเส้น AB

เนื่องจากนี่คือการบ้านฉันจะไม่ให้คำตอบที่ชัดเจนแก่คุณ แต่การทดสอบง่ายๆเพื่อดูว่าจุดตกที่ด้านใดของเส้นคือการใช้ผลิตภัณฑ์ดอท ดังนั้นสำหรับซีดีบรรทัดที่กำหนดให้คำนวณเวกเตอร์ปกติกับบรรทัดนั้น (ฉันจะเรียกมันว่า N_C) ตอนนี้เพียงแค่ทดสอบสัญญาณของผลลัพธ์ทั้งสองนี้:

dot(A-C,N_C)

และ

dot(B-C,N_C)

หากผลลัพธ์เหล่านั้นมีเครื่องหมายตรงกันข้ามแสดงว่า A และ B จะอยู่ตรงข้ามกันของไลน์ซีดี ตอนนี้ทำแบบทดสอบเดียวกันกับอีกบรรทัด AB มันมีเวกเตอร์ปกติ N_A เปรียบเทียบสัญญาณของ

dot(C-A,N_A)

และ

dot(D-A,N_A)

ผมจะปล่อยให้คุณหาวิธีคำนวณเวกเตอร์ปกติ (ใน 2 มิตินั่นเป็นเรื่องเล็กน้อย แต่โค้ดของคุณจะกังวลหรือไม่ว่า A และ B เป็นจุดที่แตกต่างกันหรือไม่เช่นเดียวกัน C และ D แตกต่างกันหรือไม่)

คุณยังคงต้องกังวลเกี่ยวกับส่วนของเส้นตรงที่อยู่ตามเส้นไม่มีที่สิ้นสุดเดียวกันหรือว่าจุดใดจุดหนึ่งตกลงบนส่วนของเส้นตรงอื่น ๆ รหัสที่ดีจะตอบสนองทุกปัญหาที่เป็นไปได้


3

นี่คือรหัส C เพื่อตรวจสอบว่าจุดสองจุดอยู่ที่ด้านตรงข้ามของส่วนของเส้นตรงหรือไม่ การใช้รหัสนี้คุณสามารถตรวจสอบว่าทั้งสองส่วนตัดกันด้วยหรือไม่

// true if points p1, p2 lie on the opposite sides of segment s1--s2
bool oppositeSide (Point2f s1, Point2f s2, Point2f p1, Point2f p2) {

//calculate normal to the segment
Point2f vec = s1-s2;
Point2f normal(vec.y, -vec.x); // no need to normalize

// vectors to the points
Point2f v1 = p1-s1;
Point2f v2 = p2-s1;

// compare signs of the projections of v1, v2 onto the normal
float proj1 = v1.dot(normal);
float proj2 = v2.dot(normal);
if (proj1==0 || proj2==0)
        cout<<"collinear points"<<endl;

return(SIGN(proj1) != SIGN(proj2));

}


3

นี่คือรหัสหลามอื่นเพื่อตรวจสอบว่าส่วนที่ปิดตัดกันหรือไม่ เป็นโค้ด C ++ รุ่นที่เขียนขึ้นใหม่ในhttp://www.cdn.geeksforgeeks.org/check-if-two-given-line-se segment-intersect / การใช้งานนี้ครอบคลุมกรณีพิเศษทั้งหมด (เช่นจุดโคลิเนียร์ทุกจุด)

def on_segment(p, q, r):
    '''Given three colinear points p, q, r, the function checks if 
    point q lies on line segment "pr"
    '''
    if (q[0] <= max(p[0], r[0]) and q[0] >= min(p[0], r[0]) and
        q[1] <= max(p[1], r[1]) and q[1] >= min(p[1], r[1])):
        return True
    return False

def orientation(p, q, r):
    '''Find orientation of ordered triplet (p, q, r).
    The function returns following values
    0 --> p, q and r are colinear
    1 --> Clockwise
    2 --> Counterclockwise
    '''

    val = ((q[1] - p[1]) * (r[0] - q[0]) - 
            (q[0] - p[0]) * (r[1] - q[1]))
    if val == 0:
        return 0  # colinear
    elif val > 0:
        return 1   # clockwise
    else:
        return 2  # counter-clockwise

def do_intersect(p1, q1, p2, q2):
    '''Main function to check whether the closed line segments p1 - q1 and p2 
       - q2 intersect'''
    o1 = orientation(p1, q1, p2)
    o2 = orientation(p1, q1, q2)
    o3 = orientation(p2, q2, p1)
    o4 = orientation(p2, q2, q1)

    # General case
    if (o1 != o2 and o3 != o4):
        return True

    # Special Cases
    # p1, q1 and p2 are colinear and p2 lies on segment p1q1
    if (o1 == 0 and on_segment(p1, p2, q1)):
        return True

    # p1, q1 and p2 are colinear and q2 lies on segment p1q1
    if (o2 == 0 and on_segment(p1, q2, q1)):
        return True

    # p2, q2 and p1 are colinear and p1 lies on segment p2q2
    if (o3 == 0 and on_segment(p2, p1, q2)):
        return True

    # p2, q2 and q1 are colinear and q1 lies on segment p2q2
    if (o4 == 0 and on_segment(p2, q1, q2)):
        return True

    return False # Doesn't fall in any of the above cases

ด้านล่างนี้เป็นฟังก์ชันทดสอบเพื่อตรวจสอบว่าใช้งานได้จริง

import matplotlib.pyplot as plt

def test_intersect_func():
    p1 = (1, 1)
    q1 = (10, 1)
    p2 = (1, 2)
    q2 = (10, 2)
    fig, ax = plt.subplots()
    ax.plot([p1[0], q1[0]], [p1[1], q1[1]], 'x-')
    ax.plot([p2[0], q2[0]], [p2[1], q2[1]], 'x-')
    print(do_intersect(p1, q1, p2, q2))

    p1 = (10, 0)
    q1 = (0, 10)
    p2 = (0, 0)
    q2 = (10, 10)
    fig, ax = plt.subplots()
    ax.plot([p1[0], q1[0]], [p1[1], q1[1]], 'x-')
    ax.plot([p2[0], q2[0]], [p2[1], q2[1]], 'x-')
    print(do_intersect(p1, q1, p2, q2))

    p1 = (-5, -5)
    q1 = (0, 0)
    p2 = (1, 1)
    q2 = (10, 10)
    fig, ax = plt.subplots()
    ax.plot([p1[0], q1[0]], [p1[1], q1[1]], 'x-')
    ax.plot([p2[0], q2[0]], [p2[1], q2[1]], 'x-')
    print(do_intersect(p1, q1, p2, q2))

    p1 = (0, 0)
    q1 = (1, 1)
    p2 = (1, 1)
    q2 = (10, 10)
    fig, ax = plt.subplots()
    ax.plot([p1[0], q1[0]], [p1[1], q1[1]], 'x-')
    ax.plot([p2[0], q2[0]], [p2[1], q2[1]], 'x-')
    print(do_intersect(p1, q1, p2, q2))

1
closed_segment_intersect()จากรหัสทดสอบไม่ได้กำหนดไว้
hhquark

1
@hhquark ขอบคุณครับ. ตอนนี้ฉันได้ลบบรรทัดเหล่านี้แล้ว ฉันรวมบรรทัดเหล่านี้ไว้ในขณะทดสอบเพื่อตรวจสอบว่าการใช้งานของฉันเห็นด้วยกับการใช้งานจากคำตอบอื่น ( ฉันคิดว่าstackoverflow.com/a/18524383/7474256 )
Fabian Ying

1

สำหรับเซ็กเมนต์ AB และ CD ให้ค้นหาความชันของซีดี

slope=(Dy-Cy)/(Dx-Cx)

ขยายซีดีบน A และ B และใช้ระยะทางไปยังซีดีตรงขึ้น

dist1=slope*(Cx-Ax)+Ay-Cy
dist2=slope*(Dx-Ax)+Ay-Dy

ตรวจสอบว่าอยู่คนละฟากกันหรือไม่

return dist1*dist2<0

1
คุณแน่ใจเกี่ยวกับสูตรหรือไม่? เนื่องจากไม่ได้ใช้พิกัด B ดังนั้นคุณจะหาจุดตัดของ AB และ CD โดยไม่พิจารณาจุดยอดทั้ง 4 ได้อย่างไร?
mac13k

1
ฉันคิดว่าควรมี: dist2 = ความชัน * (Dx-Bx) + By-Dy
mac13k

1

เนื่องจากคุณไม่ได้ระบุว่าคุณต้องการหาจุดตัดกันของเส้นจึงแก้ปัญหาได้ง่ายขึ้น หากคุณต้องการจุดตัดคำตอบโดยOMG_peanutsเป็นแนวทางที่เร็วกว่า อย่างไรก็ตามหากคุณต้องการทราบว่าเส้นตัดกันหรือไม่คุณสามารถทำได้โดยใช้สมการเส้น (ax + by + c = 0) แนวทางมีดังนี้:

  1. เริ่มจากสองส่วนของเส้นตรง: ส่วนที่ 1 และส่วนที่ 2

    segment1 = [[x1,y1], [x2,y2]]
    segment2 = [[x3,y3], [x4,y4]]
    
  2. ตรวจสอบว่าส่วนของบรรทัดทั้งสองเป็นเส้นที่มีความยาวไม่เป็นศูนย์และส่วนที่แตกต่างกันหรือไม่

  3. จากตรงนี้ฉันถือว่าทั้งสองส่วนมีความยาวไม่เป็นศูนย์และแตกต่างกัน สำหรับแต่ละส่วนของเส้นตรงให้คำนวณความชันของเส้นแล้วรับสมการของเส้นในรูปแบบของ ax + by + c = 0 ทีนี้คำนวณค่า f = ax + by + c สำหรับจุดสองจุดของ ส่วนของเส้นตรงอื่น ๆ (ทำซ้ำสำหรับส่วนของเส้นตรงอื่นด้วย)

    a2 = (y3-y4)/(x3-x4);
    b1 = -1;
    b2 = -1;
    c1 = y1 - a1*x1;
    c2 = y3 - a2*x3;
    // using the sign function from numpy
    f1_1 = sign(a1*x3 + b1*y3 + c1);
    f1_2 = sign(a1*x4 + b1*y4 + c1);
    f2_1 = sign(a2*x1 + b2*y1 + c2);
    f2_2 = sign(a2*x2 + b2*y2 + c2);
    
  4. ตอนนี้สิ่งที่เหลืออยู่คือกรณีต่างๆ ถ้า f = 0 สำหรับจุดใด ๆ ทั้งสองเส้นจะแตะที่จุดใดจุดหนึ่ง ถ้า f1_1 และ f1_2 เท่ากันหรือ f2_1 และ f2_2 เท่ากันเส้นจะไม่ตัดกัน ถ้า f1_1 และ f1_2 ไม่เท่ากันและ f2_1 และ f2_2 ไม่เท่ากันส่วนของเส้นก็จะตัดกัน ขึ้นอยู่กับว่าคุณต้องการพิจารณาเส้นที่สัมผัสเป็น "จุดตัด" หรือไม่คุณสามารถปรับเงื่อนไขของคุณได้


รหัสนี้ไม่คำนวณa1และใช้ไม่ได้กับเส้นมุมฉาก
Björn Lindqvist

1

เรายังสามารถแก้ปัญหานี้โดยใช้เวกเตอร์

[start, end]ลองกำหนดกลุ่มเป็น ด้วยสองส่วนดังกล่าว[A, B]และ[C, D]ทั้งสองมีความยาวไม่เป็นศูนย์เราสามารถเลือกจุดสิ้นสุดจุดใดจุดหนึ่งเพื่อใช้เป็นจุดอ้างอิงเพื่อให้เราได้เวกเตอร์สามตัว:

x = 0
y = 1
p = A-C = [C[x]-A[x], C[y]-A[y]]
q = B-A = [B[x]-A[x], B[y]-A[y]]
r = D-C = [D[x]-C[x], D[y]-C[y]]

จากนั้นเราสามารถมองหาจุดตัดโดยการคำนวณค่า t และ u p + t*r = u*qใน หลังจากเล่นกับสมการเล็กน้อยเราจะได้รับ:

t = (q[y]*p[x] - q[x]*p[y])/(q[x]*r[y] - q[y]*r[x])
u = (p[x] + t*r[x])/q[x]

ดังนั้นฟังก์ชั่นคือ:

def intersects(a, b):
    p = [b[0][0]-a[0][0], b[0][1]-a[0][1]]
    q = [a[1][0]-a[0][0], a[1][1]-a[0][1]]
    r = [b[1][0]-b[0][0], b[1][1]-b[0][1]]

    t = (q[1]*p[0] - q[0]*p[1])/(q[0]*r[1] - q[1]*r[0]) \
        if (q[0]*r[1] - q[1]*r[0]) != 0 \
        else (q[1]*p[0] - q[0]*p[1])
    u = (p[0] + t*r[0])/q[0] \
        if q[0] != 0 \
        else (p[1] + t*r[1])/q[1]

    return t >= 0 and t <= 1 and u >= 0 and u <= 1

1

นี่คือวิธีตรวจสอบการข้ามเส้นและจุดตัดที่เกิดขึ้น ให้ใช้ x1 ถึง x4 และ y1 ถึง y4

Segment1 = {(X1, Y1), (X2, Y2)}
Segment2 = {(X3, Y3), (X4, Y4)}

จากนั้นเราต้องการเวกเตอร์เพื่อแสดงถึงพวกมัน

dx1 = X2 - X1
dx2 = X4 - X4
dy1 = Y2 - Y1
dy2 = Y4 - Y3

ตอนนี้เราดูดีเทอร์มิแนนต์

det = dx1 * dy2 - dx2 * dy1

ถ้าดีเทอร์มิแนนต์เท่ากับ 0.0 ส่วนของเส้นจะขนานกัน อาจหมายความว่ามันทับซ้อนกัน หากพวกมันทับซ้อนกันที่จุดสิ้นสุดแสดงว่ามีทางแยกหนึ่งทาง มิฉะนั้นจะมีทางออกที่ไม่สิ้นสุด ด้วยวิธีแก้ปัญหามากมายอะไรที่บอกว่าเป็นจุดตัดของคุณ? ดังนั้นจึงเป็นกรณีพิเศษที่น่าสนใจ หากคุณรู้ล่วงหน้าว่าเส้นไม่สามารถทับซ้อนกันได้คุณก็สามารถตรวจสอบว่าdet == 0.0และถ้าเป็นเช่นนั้นเพียงแค่บอกว่ามันไม่ได้ตัดกันและเสร็จสิ้น มิฉะนั้นให้ดำเนินการต่อ

dx3 = X3 - X1
dy3 = Y3 - Y1

det1 = dx1 * dy3 - dx3 * dy1
det2 = dx2 * dy3 - dx3 * dy2

ตอนนี้ถ้า det, det1 และ det2 ทั้งหมดเป็นศูนย์แสดงว่าเส้นของคุณเป็นเส้นตรงและอาจทับซ้อนกันได้ ถ้า det เป็นศูนย์ แต่ det1 หรือ det2 ไม่ใช่แสดงว่าไม่เป็น co-linear แต่ขนานกันจึงไม่มีจุดตัด ตอนนี้สิ่งที่เหลืออยู่ถ้า det เป็นศูนย์คือปัญหา 1D แทนที่จะเป็น 2D เราจะต้องตรวจสอบวิธีใดวิธีหนึ่งในสองวิธีขึ้นอยู่กับว่า dx1 เป็นศูนย์หรือไม่ (ดังนั้นเราจึงสามารถหลีกเลี่ยงการหารด้วยศูนย์ได้) ถ้า dx1 เป็นศูนย์ให้ใช้ตรรกะเดียวกันกับค่า y แทนที่จะเป็น x ด้านล่าง

s = X3 / dx1
t = X4 / dx1

สิ่งนี้จะคำนวณสเกลเลอร์สองตัวเช่นถ้าเราปรับขนาดเวกเตอร์ (dx1, dy1) ด้วย s เราจะได้จุด (x3, y3) และโดย t เราจะได้ (x4, y4) ดังนั้นถ้า s หรือ t อยู่ระหว่าง 0.0 ถึง 1.0 ดังนั้นจุด 3 หรือ 4 อยู่บนบรรทัดแรกของเรา ค่าลบจะหมายถึงจุดที่อยู่หลังจุดเริ่มต้นของเวกเตอร์ของเราในขณะที่> 1.0 หมายความว่าอยู่ก่อนจุดสิ้นสุดของเวกเตอร์ของเรา 0.0 หมายถึงอยู่ที่ (x1, y1) และ 1.0 หมายถึงอยู่ที่ (x2, y2) ถ้าทั้ง s และ t <0.0 หรือทั้งคู่เท่ากับ> 1.0 แสดงว่าทั้งคู่ไม่ตัดกัน และที่จัดการกรณีพิเศษของเส้นขนาน

ทีนี้ถ้าอย่างdet != 0.0นั้น

s = det1 / det
t = det2 / det
if (s < 0.0 || s > 1.0 || t < 0.0 || t > 1.0)
    return false  // no intersect

สิ่งนี้คล้ายกับสิ่งที่เราทำข้างต้นจริงๆ ตอนนี้ถ้าเราผ่านการทดสอบข้างต้นแล้วส่วนของเส้นของเราจะตัดกันและเราสามารถคำนวณจุดตัดได้ค่อนข้างง่ายดังนี้:

Ix = X1 + t * dx1
Iy = Y1 + t * dy1

หากคุณต้องการเจาะลึกลงไปในสิ่งที่คณิตศาสตร์กำลังทำอยู่ให้ดูที่กฎของ Cramer


1
พิมพ์ผิด: "dx2 = X4 - X4" ควรเป็น "dx2 = X4 - X3"
geowar

1

คำตอบโดยGeorgyคือวิธีที่สะอาดที่สุดในการนำไปใช้ ต้องไล่สิ่งนี้ลงเนื่องจากตัวอย่าง brycboe ในขณะที่เรียบง่ายเช่นกันมีปัญหาเกี่ยวกับ colinearity

รหัสสำหรับการทดสอบ:

#!/usr/bin/python
#
# Notes on intersection:
#
# https://bryceboe.com/2006/10/23/line-segment-intersection-algorithm/
#
# /programming/3838329/how-can-i-check-if-two-segments-intersect

from shapely.geometry import LineString

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

def ccw(A,B,C):
    return (C.y-A.y)*(B.x-A.x) > (B.y-A.y)*(C.x-A.x)

def intersect(A,B,C,D):
    return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D)


def ShapelyIntersect(A,B,C,D):
    return LineString([(A.x,A.y),(B.x,B.y)]).intersects(LineString([(C.x,C.y),(D.x,D.y)]))


a = Point(0,0)
b = Point(0,1)
c = Point(1,1)
d = Point(1,0)

'''
Test points:

b(0,1)   c(1,1)




a(0,0)   d(1,0)
'''

# F
print(intersect(a,b,c,d))

# T
print(intersect(a,c,b,d))
print(intersect(b,d,a,c))
print(intersect(d,b,a,c))

# F
print(intersect(a,d,b,c))

# same end point cases:
print("same end points")
# F - not intersected
print(intersect(a,b,a,d))
# T - This shows as intersected
print(intersect(b,a,a,d))
# F - this does not
print(intersect(b,a,d,a))
# F - this does not
print(intersect(a,b,d,a))

print("same end points, using shapely")
# T
print(ShapelyIntersect(a,b,a,d))
# T
print(ShapelyIntersect(b,a,a,d))
# T
print(ShapelyIntersect(b,a,d,a))
# T
print(ShapelyIntersect(a,b,d,a))

0

ถ้าข้อมูลของคุณกำหนดเส้นคุณก็ต้องพิสูจน์ว่ามันไม่ขนานกัน คุณสามารถคำนวณได้

alpha = float(y2 - y1) / (x2 - x1).

ถ้าค่าสัมประสิทธิ์นี้เท่ากันสำหรับทั้ง Line1 และ Line2 หมายความว่าเส้นนั้นขนานกัน ถ้าไม่แสดงว่ามันจะตัดกัน

ถ้ามันขนานกันคุณต้องพิสูจน์ว่ามันไม่เหมือนกัน ด้วยเหตุนี้คุณจึงคำนวณ

beta = y1 - alpha*x1

หากเบต้าเหมือนกันสำหรับ Line1 และ Line2 หมายความว่าเส้นของคุณตัดกันเนื่องจากมีค่าเท่ากัน

หากเป็นกลุ่มคุณยังคงต้องคำนวณอัลฟ่าและเบต้าตามที่อธิบายไว้ข้างต้นสำหรับแต่ละบรรทัด จากนั้นคุณต้องตรวจสอบว่า (beta1 - beta2) / (alpha1 - alpha2) มากกว่า Min (x1_line1, x2_line1) และน้อยกว่า Max (x1_line1, x2_line1)


0

คำนวณจุดตัดกันของเส้นที่วางบนส่วนของคุณ (โดยทั่วไปหมายถึงการแก้ระบบสมการเชิงเส้น) จากนั้นตรวจสอบว่าอยู่ระหว่างจุดเริ่มต้นและจุดสิ้นสุดของกลุ่มของคุณหรือไม่


0

นี่คือสิ่งที่ฉันมีสำหรับ AS3 ไม่รู้เกี่ยวกับ python มากนัก แต่มีแนวคิดอยู่ที่นั่น

    public function getIntersectingPointF($A:Point, $B:Point, $C:Point, $D:Point):Number {
        var A:Point = $A.clone();
        var B:Point = $B.clone();
        var C:Point = $C.clone();
        var D:Point = $D.clone();
        var f_ab:Number = (D.x - C.x) * (A.y - C.y) - (D.y - C.y) * (A.x - C.x);

        // are lines parallel
        if (f_ab == 0) { return Infinity };

        var f_cd:Number = (B.x - A.x) * (A.y - C.y) - (B.y - A.y) * (A.x - C.x);
        var f_d:Number = (D.y - C.y) * (B.x - A.x) - (D.x - C.x) * (B.y - A.y);
        var f1:Number = f_ab/f_d
        var f2:Number = f_cd / f_d
        if (f1 == Infinity || f1 <= 0 || f1 >= 1) { return Infinity };
        if (f2 == Infinity || f2 <= 0 || f2 >= 1) { return Infinity };
        return f1;
    }

    public function getIntersectingPoint($A:Point, $B:Point, $C:Point, $D:Point):Point
    {
        var f:Number = getIntersectingPointF($A, $B, $C, $D);
        if (f == Infinity || f <= 0 || f >= 1) { return null };

        var retPoint:Point = Point.interpolate($A, $B, 1 - f);
        return retPoint.clone();
    }

0

ดำเนินการใน JAVA อย่างไรก็ตามดูเหมือนว่าจะใช้ไม่ได้กับเส้นเชิงเส้นร่วม (ส่วนของเส้นตรงที่มีอยู่ภายในซึ่งกันและกัน L1 (0,0) (10,10) L2 (1,1) (2,2)

public class TestCode
{

  public class Point
  {
    public double x = 0;
    public double y = 0;
    public Point(){}
  }

  public class Line
  {
    public Point p1, p2;
    public Line( double x1, double y1, double x2, double y2) 
    {
      p1 = new Point();
      p2 = new Point();
      p1.x = x1;
      p1.y = y1;
      p2.x = x2;
      p2.y = y2;
    }
  }

  //line segments
  private static Line s1;
  private static Line s2;

  public TestCode()
  {
    s1 = new Line(0,0,0,10);
    s2 = new Line(-1,0,0,10);
  }

  public TestCode(double x1, double y1, 
    double x2, double y2,
    double x3, double y3,
    double x4, double y4)
  {
    s1 = new Line(x1,y1, x2,y2);
    s2 = new Line(x3,y3, x4,y4);
  }

  public static void main(String args[])
  {
     TestCode code  = null;
////////////////////////////
     code = new TestCode(0,0,0,10,
                         0,1,0,5);
     if( intersect(code) )
     { System.out.println( "OK COLINEAR: INTERSECTS" ); }
     else
     { System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); }
////////////////////////////
     code = new TestCode(0,0,0,10,
                         0,1,0,10);
     if( intersect(code) )
     { System.out.println( "OK COLINEAR: INTERSECTS" ); }
     else
     { System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); }
////////////////////////////
     code = new TestCode(0,0,10,0,
                         5,0,15,0);
     if( intersect(code) )
     { System.out.println( "OK COLINEAR: INTERSECTS" ); }
     else
     { System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); }
////////////////////////////
     code = new TestCode(0,0,10,0,
                         0,0,15,0);
     if( intersect(code) )
     { System.out.println( "OK COLINEAR: INTERSECTS" ); }
     else
     { System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); }

////////////////////////////
     code = new TestCode(0,0,10,10,
                         1,1,5,5);
     if( intersect(code) )
     { System.out.println( "OK COLINEAR: INTERSECTS" ); }
     else
     { System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); }
////////////////////////////
     code = new TestCode(0,0,0,10,
                         -1,-1,0,10);
     if( intersect(code) )
     { System.out.println( "OK SLOPE END: INTERSECTS" ); }
     else
     { System.out.println( "ERROR SLOPE END: DO NOT INTERSECT" ); }
////////////////////////////
     code = new TestCode(-10,-10,10,10,
                         -10,10,10,-10);
     if( intersect(code) )
     { System.out.println( "OK SLOPE Intersect(0,0): INTERSECTS" ); }
     else
     { System.out.println( "ERROR SLOPE Intersect(0,0): DO NOT INTERSECT" ); }
////////////////////////////
     code = new TestCode(-10,-10,10,10,
                         -3,-2,50,-2);
     if( intersect(code) )
     { System.out.println( "OK SLOPE Line2 VERTIAL: INTERSECTS" ); }
     else
     { System.out.println( "ERROR SLOPE Line2 VERTICAL: DO NOT INTERSECT" ); }
////////////////////////////
     code = new TestCode(-10,-10,10,10,
                         50,-2,-3,-2);
     if( intersect(code) )
     { System.out.println( "OK SLOPE Line2 (reversed) VERTIAL: INTERSECTS" ); }
     else
     { System.out.println( "ERROR SLOPE Line2 (reversed) VERTICAL: DO NOT INTERSECT" ); }
////////////////////////////
     code = new TestCode(0,0,0,10,
                         1,0,1,10);
     if( intersect(code) )
     { System.out.println( "ERROR PARALLEL VERTICAL: INTERSECTS" ); }
     else
     { System.out.println( "OK PARALLEL VERTICAL: DO NOT INTERSECT" ); }
////////////////////////////
     code = new TestCode(0,2,10,2,
                         0,10,10,10);
     if( intersect(code) )
     { System.out.println( "ERROR PARALLEL HORIZONTAL: INTERSECTS" ); }
     else
     { System.out.println( "OK PARALLEL HORIZONTAL: DO NOT INTERSECT" ); }
////////////////////////////
     code = new TestCode(0,10,5,13.75,
                         0,18.75,10,15);
     if( intersect(code) )
     { System.out.println( "ERROR PARALLEL SLOPE=.75: INTERSECTS" ); }
     else
     { System.out.println( "OK PARALLEL SLOPE=.75: DO NOT INTERSECT" ); }
////////////////////////////
     code = new TestCode(0,0,1,1,
                         2,-1,2,10);
     if( intersect(code) )
     { System.out.println( "ERROR SEPERATE SEGMENTS: INTERSECTS" ); }
     else
     { System.out.println( "OK SEPERATE SEGMENTS: DO NOT INTERSECT" ); }
////////////////////////////
     code = new TestCode(0,0,1,1,
                         -1,-10,-5,10);
     if( intersect(code) )
     { System.out.println( "ERROR SEPERATE SEGMENTS 2: INTERSECTS" ); }
     else
     { System.out.println( "OK SEPERATE SEGMENTS 2: DO NOT INTERSECT" ); }
  }

  public static boolean intersect( TestCode code )
  {
    return intersect( code.s1, code.s2);
  }

  public static boolean intersect( Line line1, Line line2 )
  {
    double i1min = Math.min(line1.p1.x, line1.p2.x);
    double i1max = Math.max(line1.p1.x, line1.p2.x);
    double i2min = Math.min(line2.p1.x, line2.p2.x);
    double i2max = Math.max(line2.p1.x, line2.p2.x);

    double iamax = Math.max(i1min, i2min);
    double iamin = Math.min(i1max, i2max);

    if( Math.max(line1.p1.x, line1.p2.x) < Math.min(line2.p1.x, line2.p2.x) )
      return false;

    double m1 = (line1.p2.y - line1.p1.y) / (line1.p2.x - line1.p1.x );
    double m2 = (line2.p2.y - line2.p1.y) / (line2.p2.x - line2.p1.x );

    if( m1 == m2 )
        return false;

    //b1 = line1[0][1] - m1 * line1[0][0]
    //b2 = line2[0][1] - m2 * line2[0][0]
    double b1 = line1.p1.y - m1 * line1.p1.x;
    double b2 = line2.p1.y - m2 * line2.p1.x;
    double x1 = (b2 - b1) / (m1 - m2);
    if( (x1 < Math.max(i1min, i2min)) || (x1 > Math.min(i1max, i2max)) )
        return false;
    return true;
  }
}

เอาท์พุทป่านนี้

ERROR COLINEAR: DO NOT INTERSECT
ERROR COLINEAR: DO NOT INTERSECT
ERROR COLINEAR: DO NOT INTERSECT
ERROR COLINEAR: DO NOT INTERSECT
ERROR COLINEAR: DO NOT INTERSECT
OK SLOPE END: INTERSECTS
OK SLOPE Intersect(0,0): INTERSECTS
OK SLOPE Line2 VERTIAL: INTERSECTS
OK SLOPE Line2 (reversed) VERTIAL: INTERSECTS
OK PARALLEL VERTICAL: DO NOT INTERSECT
OK PARALLEL HORIZONTAL: DO NOT INTERSECT
OK PARALLEL SLOPE=.75: DO NOT INTERSECT
OK SEPERATE SEGMENTS: DO NOT INTERSECT
OK SEPERATE SEGMENTS 2: DO NOT INTERSECT

0

ฉันคิดว่าฉันจะมีส่วนร่วมในโซลูชัน Swift ที่ดี:

struct Pt {
    var x: Double
    var y: Double
}

struct LineSegment {
    var p1: Pt
    var p2: Pt
}

func doLineSegmentsIntersect(ls1: LineSegment, ls2: LineSegment) -> Bool {

    if (ls1.p2.x-ls1.p1.x == 0) { //handle vertical segment1
        if (ls2.p2.x-ls2.p1.x == 0) {
            //both lines are vertical and parallel
            return false
        }

        let x = ls1.p1.x

        let slope2 = (ls2.p2.y-ls2.p1.y)/(ls2.p2.x-ls2.p1.x)
        let c2 = ls2.p1.y-slope2*ls2.p1.x

        let y = x*slope2+c2 // y intersection point

        return (y > ls1.p1.y && x < ls1.p2.y) || (y > ls1.p2.y && y < ls1.p1.y) // check if y is between y1,y2 in segment1
    }

    if (ls2.p2.x-ls2.p1.x == 0) { //handle vertical segment2

        let x = ls2.p1.x

        let slope1 = (ls1.p2.y-ls1.p1.y)/(ls1.p2.x-ls1.p1.x)
        let c1 = ls1.p1.y-slope1*ls1.p1.x

        let y = x*slope1+c1 // y intersection point

        return (y > ls2.p1.y && x < ls2.p2.y) || (y > ls2.p2.y && y < ls2.p1.y) // validate that y is between y1,y2 in segment2

    }

    let slope1 = (ls1.p2.y-ls1.p1.y)/(ls1.p2.x-ls1.p1.x)
    let slope2 = (ls2.p2.y-ls2.p1.y)/(ls2.p2.x-ls2.p1.x)

    if (slope1 == slope2) { //segments are parallel
        return false
    }

    let c1 = ls1.p1.y-slope1*ls1.p1.x
    let c2 = ls2.p1.y-slope2*ls2.p1.x

    let x = (c2-c1)/(slope1-slope2)

    return (((x > ls1.p1.x && x < ls1.p2.x) || (x > ls1.p2.x && x < ls1.p1.x)) &&
        ((x > ls2.p1.x && x < ls2.p2.x) || (x > ls2.p2.x && x < ls2.p1.x)))
    //validate that x is between x1,x2 in both segments

}

0

หนึ่งในวิธีแก้ปัญหาข้างต้นทำงานได้ดีฉันจึงตัดสินใจเขียนโปรแกรมสาธิตที่สมบูรณ์โดยใช้ wxPython คุณควรจะรันโปรแกรมได้ดังนี้ python " your file name "

# Click on the window to draw a line.
# The program will tell you if this and the other line intersect.

import wx

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

app = wx.App()
frame = wx.Frame(None, wx.ID_ANY, "Main")
p1 = Point(90,200)
p2 = Point(150,80)
mp = Point(0,0) # mouse point
highestX = 0


def ccw(A,B,C):
    return (C.y-A.y) * (B.x-A.x) > (B.y-A.y) * (C.x-A.x)

# Return true if line segments AB and CD intersect
def intersect(A,B,C,D):
    return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D)

def is_intersection(p1, p2, p3, p4):
    return intersect(p1, p2, p3, p4)

def drawIntersection(pc):
    mp2 = Point(highestX, mp.y)
    if is_intersection(p1, p2, mp, mp2):
        pc.DrawText("intersection", 10, 10)
    else:
        pc.DrawText("no intersection", 10, 10)

def do_paint(evt):
    pc = wx.PaintDC(frame)
    pc.DrawLine(p1.x, p1.y, p2.x, p2.y)
    pc.DrawLine(mp.x, mp.y, highestX, mp.y)
    drawIntersection(pc)

def do_left_mouse(evt):
    global mp, highestX
    point = evt.GetPosition()
    mp = Point(point[0], point[1])
    highestX = frame.Size[0]
    frame.Refresh()

frame.Bind(wx.EVT_PAINT, do_paint)
frame.Bind(wx.EVT_LEFT_DOWN, do_left_mouse)
frame.Show()
app.MainLoop()

0

ใช้โซลูชันOMG_Peanutsฉันแปลเป็น SQL (ฟังก์ชัน HANA Scalar)

ขอบคุณ OMG_Peanuts มันใช้งานได้ดี ฉันใช้ดินกลม แต่ระยะทางมีขนาดเล็กดังนั้นฉันคิดว่ามันโอเค

FUNCTION GA_INTERSECT" ( IN LAT_A1 DOUBLE,
         IN LONG_A1 DOUBLE,
         IN LAT_A2 DOUBLE,
         IN LONG_A2 DOUBLE,
         IN LAT_B1 DOUBLE,
         IN LONG_B1 DOUBLE,
         IN LAT_B2 DOUBLE,
         IN LONG_B2 DOUBLE) 
    
RETURNS RET_DOESINTERSECT DOUBLE
    LANGUAGE SQLSCRIPT
    SQL SECURITY INVOKER AS
BEGIN

    DECLARE MA DOUBLE;
    DECLARE MB DOUBLE;
    DECLARE BA DOUBLE;
    DECLARE BB DOUBLE;
    DECLARE XA DOUBLE;
    DECLARE MAX_MIN_X DOUBLE;
    DECLARE MIN_MAX_X DOUBLE;
    DECLARE DOESINTERSECT INTEGER;
    
    SELECT 1 INTO DOESINTERSECT FROM DUMMY;
    
    IF LAT_A2-LAT_A1 != 0 AND LAT_B2-LAT_B1 != 0 THEN
        SELECT (LONG_A2 - LONG_A1)/(LAT_A2 - LAT_A1) INTO MA FROM DUMMY; 
        SELECT (LONG_B2 - LONG_B1)/(LAT_B2 - LAT_B1) INTO MB FROM DUMMY;
        IF MA = MB THEN
            SELECT 0 INTO DOESINTERSECT FROM DUMMY;
        END IF;
    END IF;
    
    SELECT LONG_A1-MA*LAT_A1 INTO BA FROM DUMMY;
    SELECT LONG_B1-MB*LAT_B1 INTO BB FROM DUMMY;
    SELECT (BB - BA) / (MA - MB) INTO XA FROM DUMMY;
    
    -- Max of Mins
    IF LAT_A1 < LAT_A2 THEN         -- MIN(LAT_A1, LAT_A2) = LAT_A1
        IF LAT_B1 < LAT_B2 THEN        -- MIN(LAT_B1, LAT_B2) = LAT_B1
            IF LAT_A1 > LAT_B1 THEN       -- MAX(LAT_A1, LAT_B1) = LAT_A1
                SELECT LAT_A1 INTO MAX_MIN_X FROM DUMMY;
            ELSE                          -- MAX(LAT_A1, LAT_B1) = LAT_B1
                SELECT LAT_B1 INTO MAX_MIN_X FROM DUMMY;
            END IF;
        ELSEIF LAT_B2 < LAT_B1 THEN   -- MIN(LAT_B1, LAT_B2) = LAT_B2
            IF LAT_A1 > LAT_B2 THEN       -- MAX(LAT_A1, LAT_B2) = LAT_A1
                SELECT LAT_A1 INTO MAX_MIN_X FROM DUMMY;
            ELSE                          -- MAX(LAT_A1, LAT_B2) = LAT_B2
                SELECT LAT_B2 INTO MAX_MIN_X FROM DUMMY;
            END IF;
        END IF;
    ELSEIF LAT_A2 < LAT_A1 THEN     -- MIN(LAT_A1, LAT_A2) = LAT_A2
        IF LAT_B1 < LAT_B2 THEN        -- MIN(LAT_B1, LAT_B2) = LAT_B1
            IF LAT_A2 > LAT_B1 THEN       -- MAX(LAT_A2, LAT_B1) = LAT_A2
                SELECT LAT_A2 INTO MAX_MIN_X FROM DUMMY;
            ELSE                          -- MAX(LAT_A2, LAT_B1) = LAT_B1
                SELECT LAT_B1 INTO MAX_MIN_X FROM DUMMY;
            END IF;
        ELSEIF LAT_B2 < LAT_B1 THEN   -- MIN(LAT_B1, LAT_B2) = LAT_B2
            IF LAT_A2 > LAT_B2 THEN       -- MAX(LAT_A2, LAT_B2) = LAT_A2
                SELECT LAT_A2 INTO MAX_MIN_X FROM DUMMY;
            ELSE                          -- MAX(LAT_A2, LAT_B2) = LAT_B2
                SELECT LAT_B2 INTO MAX_MIN_X FROM DUMMY;
            END IF;
        END IF;
    END IF;
    
    -- Min of Max
    IF LAT_A1 > LAT_A2 THEN         -- MAX(LAT_A1, LAT_A2) = LAT_A1
        IF LAT_B1 > LAT_B2 THEN        -- MAX(LAT_B1, LAT_B2) = LAT_B1
            IF LAT_A1 < LAT_B1 THEN       -- MIN(LAT_A1, LAT_B1) = LAT_A1
                SELECT LAT_A1 INTO MIN_MAX_X FROM DUMMY;
            ELSE                          -- MIN(LAT_A1, LAT_B1) = LAT_B1
                SELECT LAT_B1 INTO MIN_MAX_X FROM DUMMY;
            END IF;
        ELSEIF LAT_B2 > LAT_B1 THEN   -- MAX(LAT_B1, LAT_B2) = LAT_B2
            IF LAT_A1 < LAT_B2 THEN       -- MIN(LAT_A1, LAT_B2) = LAT_A1
                SELECT LAT_A1 INTO MIN_MAX_X FROM DUMMY;
            ELSE                          -- MIN(LAT_A1, LAT_B2) = LAT_B2
                SELECT LAT_B2 INTO MIN_MAX_X FROM DUMMY;
            END IF;
        END IF;
    ELSEIF LAT_A2 > LAT_A1 THEN     -- MAX(LAT_A1, LAT_A2) = LAT_A2
        IF LAT_B1 > LAT_B2 THEN        -- MAX(LAT_B1, LAT_B2) = LAT_B1
            IF LAT_A2 < LAT_B1 THEN       -- MIN(LAT_A2, LAT_B1) = LAT_A2
                SELECT LAT_A2 INTO MIN_MAX_X FROM DUMMY;
            ELSE                          -- MIN(LAT_A2, LAT_B1) = LAT_B1
                SELECT LAT_B1 INTO MIN_MAX_X FROM DUMMY;
            END IF;
        ELSEIF LAT_B2 > LAT_B1 THEN   -- MAX(LAT_B1, LAT_B2) = LAT_B2
            IF LAT_A2 < LAT_B2 THEN       -- MIN(LAT_A2, LAT_B2) = LAT_A2
                SELECT LAT_A2 INTO MIN_MAX_X FROM DUMMY;
            ELSE                          -- MIN(LAT_A2, LAT_B2) = LAT_B2
                SELECT LAT_B2 INTO MIN_MAX_X FROM DUMMY;
            END IF;
        END IF;
    END IF;
        
    
    IF XA < MAX_MIN_X OR
       XA > MIN_MAX_X THEN  
       SELECT 0 INTO DOESINTERSECT FROM DUMMY;
    END IF;
    
    RET_DOESINTERSECT := :DOESINTERSECT;
END;

-2

ได้รับการแก้ไขแล้ว แต่ทำไมไม่ใช้ python ... :)

def islineintersect(line1, line2):
    i1 = [min(line1[0][0], line1[1][0]), max(line1[0][0], line1[1][0])]
    i2 = [min(line2[0][0], line2[1][0]), max(line2[0][0], line2[1][0])]
    ia = [max(i1[0], i2[0]), min(i1[1], i2[1])]
    if max(line1[0][0], line1[1][0]) < min(line2[0][0], line2[1][0]):
        return False
    m1 = (line1[1][1] - line1[0][1]) * 1. / (line1[1][0] - line1[0][0]) * 1.
    m2 = (line2[1][1] - line2[0][1]) * 1. / (line2[1][0] - line2[0][0]) * 1.
    if m1 == m2:
        return False
    b1 = line1[0][1] - m1 * line1[0][0]
    b2 = line2[0][1] - m2 * line2[0][0]
    x1 = (b2 - b1) / (m1 - m2)
    if (x1 < max(i1[0], i2[0])) or (x1 > min(i1[1], i2[1])):
        return False
    return True

นี้:

print islineintersect([(15, 20), (100, 200)], [(210, 5), (23, 119)])

เอาท์พุต:

True

และนี่:

print islineintersect([(15, 20), (100, 200)], [(-1, -5), (-5, -5)])

เอาท์พุต:

False
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.