อัลกอริทึมในการตรวจหาจุดตัดของสองรูปสี่เหลี่ยม?


143

ฉันกำลังมองหาอัลกอริทึมเพื่อตรวจสอบว่าสี่เหลี่ยมสองรูปตัดกันหรือไม่

ทดสอบว่ามุมหนึ่งอยู่ในอีก ALMOST หรือไม่ มันจะล้มเหลวถ้ารูปสี่เหลี่ยมสร้างรูปร่างคล้ายกากบาท

ดูเหมือนว่าเป็นความคิดที่ดีที่จะหลีกเลี่ยงการใช้ความชันของเส้นซึ่งต้องใช้ตัวพิมพ์พิเศษสำหรับเส้นแนวตั้ง


ถ้าคุณเพิ่มในการตรวจสอบมุมของคุณตรวจสอบเพื่อดูว่าสี่เหลี่ยมที่สองอยู่ภายในขอบเขต (สี่เหลี่ยม) ของสี่เหลี่ยมมุมหรือไม่
Wes P

คุณจะใช้ภาษาอะไร เพราะใน Java มีคลาสในตัวที่ให้คุณทำสิ่งนี้
Martijn

ฉันคิดว่ากราฟิก API และไลบรารี GUI ส่วนใหญ่ (เช่นการแกว่ง) มีการใช้งานนี้
l_39217_l

ที่สามารถพลาดกรณีที่พวกเขาทับซ้อน แต่ไม่มีมุมอยู่ภายในสี่เหลี่ยมใด ๆ
Florian BOSCH

1
คำถามนี้เป็นคำถามเกือบเหมือน: stackoverflow.com/questions/306316/... ถึงแม้ว่าสิ่งนี้กำลังมองหาวิธีแก้ปัญหาสำหรับ C ++ โดยเฉพาะ คำตอบที่ได้รับการยอมรับนั้นค่อนข้างเรียบง่ายและตรงไปตรงมา
Silver Gonzales

คำตอบ:


162

วิธีมาตรฐานจะทำการทดสอบแกนแยก (ทำการค้นหา google ในนั้น)

ในระยะสั้น:

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

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

ใน 2D คุณสามารถทำได้โดยไม่ต้องใช้ทางลาด ขอบถูกกำหนดเพียงแค่เป็นความแตกต่างระหว่างสองจุดยอดเช่น

  edge = v(n) - v(n-1)

คุณสามารถตั้งฉากกับสิ่งนี้ได้โดยหมุน 90 องศา ใน 2D มันง่ายเหมือน:

  rotated.x = -unrotated.y
  rotated.y =  unrotated.x

ดังนั้นจึงไม่มีตรีโกณมิติหรือทางลาดเข้ามาเกี่ยวข้อง การทำให้เวกเตอร์เป็นความยาวหน่วยปกติไม่จำเป็นต้องทำเช่นกัน

หากคุณต้องการทดสอบว่ามีจุดใดจุดหนึ่งอยู่ด้านใดด้านหนึ่งของบรรทัดคุณสามารถใช้ dot-product ได้ เครื่องหมายจะบอกคุณว่าคุณอยู่ฝ่ายใด

  // rotated: your rotated edge
  // v(n-1) any point from the edge.
  // testpoint: the point you want to find out which side it's on.

  side = sign (rotated.x * (testpoint.x - v(n-1).x) + 
               rotated.y * (testpoint.y - v(n-1).y);

ตอนนี้ทดสอบทุกจุดของสี่เหลี่ยม A กับขอบของสี่เหลี่ยม B และในทางกลับกัน หากคุณพบว่ามีขอบแยกวัตถุจะไม่ตัดกัน (หากมีจุดอื่น ๆ ทั้งหมดใน B อยู่ที่อีกด้านหนึ่งของขอบที่ทดสอบ - ดูภาพวาดด้านล่าง) หากคุณพบว่าไม่มีการแยกขอบอย่างใดอย่างหนึ่งสี่เหลี่ยมจะตัดกันหรือสี่เหลี่ยมหนึ่งอยู่ในอื่น ๆ

การทดสอบทำงานร่วมกับรูปหลายเหลี่ยมนูนใด ๆ btw ..

การแก้ไข:เมื่อต้องการระบุขอบแยกมันไม่เพียงพอที่จะทดสอบจุดทั้งหมดของสี่เหลี่ยมหนึ่งกับแต่ละขอบของอีก ผู้สมัคร E-edge (ด้านล่าง) จะถูกระบุว่าเป็นขอบที่แยกเนื่องจากจุดทั้งหมดใน A อยู่ในระนาบครึ่งเดียวของ E อย่างไรก็ตามมันไม่ใช่ขอบที่แยกเนื่องจากจุดยอด Vb1 และ Vb2 ของ B ยังอยู่ในครึ่งระนาบนั้น มันจะเป็นเพียงการแยกขอบหากไม่ได้เป็นกรณีที่ http://www.iassess.com/collision.png


2
อัลกอริทึมนี้ใช้ไม่ได้กับทุกกรณี เป็นไปได้ที่จะวางสี่เหลี่ยมที่สองหมุน 45 องศาไปที่สี่เหลี่ยมแรกและชดเชยตามแนวทแยงมุมเพื่อให้มันผ่านการทดสอบสี่แยกข้างต้น แต่ไม่ตัดกัน
Skizz

6
Skizz ตรวจสอบขอบทั้งแปด หากวัตถุไม่ตัดกันหนึ่งในแปดขอบจะแยกพวกมันออก ทำไมคุณไม่โพสต์ภาพที่แสดงกรณีของคุณ? ฉันสามารถแสดงให้คุณเห็นแกน ..
Nils Pipenbrinck

2
ความผิดพลาดของฉันมันจัดการกับเงื่อนไขนั้น
Skizz

2
รูปนี้ตายแล้ว (พฤศจิกายน 2012)
John Dvorak

2
ฉันมีปัญหาในการมองเห็นสิ่งนี้เป็นอย่างมากดังนั้นฉันจึงสร้างสิ่งที่ฉันคิดว่าภาพที่ถูกอ้างอิงนั้นดูเหมือน imgur.com/bNwrzsv
Rjdlee

16

โดยทั่วไปดูภาพต่อไปนี้:


หากกล่องสองกล่องชนกันเส้น A และ B จะทับซ้อนกัน

โปรดทราบว่าสิ่งนี้จะต้องทำในทั้งแกน X และแกน Y และทั้งคู่จำเป็นต้องทับซ้อนกันเพื่อให้รูปสี่เหลี่ยมชนกัน

มีบทความที่ดีในgamasutra.comที่ตอบคำถาม (ภาพมาจากบทความ) ฉันทำอัลกอริทึมที่คล้ายกันเมื่อ 5 ปีที่แล้วและฉันต้องค้นหาข้อมูลโค้ดเพื่อโพสต์ไว้ที่นี่ในภายหลัง

การแก้ไข : ทฤษฎีบทการแยกระบุว่ารูปทรงนูนสองรูปนั้นไม่ทับซ้อนกันหากมีแกนที่แยกอยู่ (เช่นรูปที่มุมที่ฉายตามที่แสดงไม่ทับซ้อนกัน) ดังนั้น "แกนการแยกอยู่" => "ไม่มีการทับซ้อน" นี่ไม่ใช่นัยยะดังนั้นคุณไม่สามารถสรุปการสนทนาได้


1
เห็นได้ชัดว่าในขณะที่สองสี่เหลี่ยม (0,0,1,1) และ (0,3,1,4) ไม่ทับซ้อนกัน แต่การคาดการณ์ของพวกเขาในแกน x ทับซ้อนกันอย่างสมบูรณ์ การทดสอบทั้งสองมีความจำเป็นรวมกันเพียงพอ
MSalters

18
มันไม่เพียงพอสำหรับการประมาณการ x และ y ที่จะทับซ้อนกัน: ใช้เช่นสี่เหลี่ยม [(0,0), (0,3), (3,3), (3,3), (3,0)] และ [(2,5), (5,2), (7,4), (4,7)]
Joel ในGö

4
ฉันเห็นด้วยกับ @Joel ในGö วิธีนี้พลาดกรณีจำนวนมากที่สี่เหลี่ยมผืนผ้าไม่ซ้อนกัน แต่รัศมีที่ฉายของพวกมันทำทั้งใน x และ y
Scottie T

5
คำตอบนี้ไม่ผิด แต่มันทำให้เข้าใจผิด เป็นจริงที่: หากทั้งสองกล่องชนกันเส้น A และ B จะทับซ้อนกัน แต่มันก็เป็นความจริงเช่นกัน: ถ้าเส้น A และ B ทับกันทั้งสองกล่องอาจจะใช่หรือไม่ก็ได้
แมตต์ไหม้

7
@floater: ฉันจะบอกว่ามันไม่เพียง แต่ผิด แต่ยังทำให้เข้าใจผิดซึ่งก็ยิ่งเลวร้ายลง
BlueRaja - Danny Pflughoeft

4

คำตอบของ m_pGladiator นั้นถูกต้องและฉันชอบมัน การทดสอบการแยกแกนนั้นง่ายที่สุดและเป็นวิธีมาตรฐานในการตรวจจับการซ้อนทับของสี่เหลี่ยมผืนผ้า บรรทัดที่ช่วงเวลาการฉายไม่ทับซ้อนที่เราเรียกว่าแกนแยก โซลูชันของ Nils Pipenbrinck นั้นกว้างเกินไป มันใช้ผลิตภัณฑ์ dotเพื่อตรวจสอบว่ารูปร่างหนึ่งอยู่ที่ขอบด้านหนึ่งของอีกด้านหนึ่งหรือไม่ การแก้ปัญหานี้เป็นจริงอาจทำให้รูปหลายเหลี่ยมนูน n- edge อย่างไรก็ตามมันไม่ได้ถูกเลือกสำหรับสี่เหลี่ยมสองรูป

จุดวิกฤติของคำตอบ m_pGladiator คือเราควรตรวจสอบการฉายสองรูปสี่เหลี่ยมผืนผ้าบนแกนทั้งสอง (x และ y) หากการฉายสองภาพซ้อนทับกันเราอาจบอกได้ว่าสี่เหลี่ยมสองมุมนี้ซ้อนกัน ดังนั้นความคิดเห็นข้างต้นต่อคำตอบของ m_pGladiator นั้นผิด

สำหรับสถานการณ์ง่าย ๆ ถ้าสองรูปสี่เหลี่ยมผืนผ้าไม่หมุนเรานำเสนอรูปสี่เหลี่ยมผืนผ้าที่มีโครงสร้าง:

struct Rect {
    x, // the center in x axis
    y, // the center in y axis
    width,
    height
}

เราชื่อสี่เหลี่ยมผืนผ้า A, B กับ rectA, rectB

    if Math.abs(rectA.x - rectB.x) < (Math.abs(rectA.width + rectB.width) / 2) 
&& (Math.abs(rectA.y - rectB.y) < (Math.abs(rectA.height + rectB.height) / 2))
    then
        // A and B collide
    end if

ถ้าหนึ่งในสองรูปสี่เหลี่ยมหมุนแล้วมันอาจต้องใช้ความพยายามบางอย่างเพื่อกำหนดโครงร่างของพวกมันบนแกน x และ y กำหนด struct RotatedRect ดังนี้

struct RotatedRect : Rect {
    double angle; // the rotating angle oriented to its center
}

ความแตกต่างคือความกว้างของ 'ตอนนี้แตกต่างกันเล็กน้อย: widthA' สำหรับ rectA: Math.sqrt(rectA.width*rectA.width + rectA.height*rectA.height) * Math.cos(rectA.angle) widthB 'สำหรับ rectB:Math.sqrt(rectB.width*rectB.width + rectB.height*rectB.height) * Math.cos(rectB.angle)

    if Math.abs(rectA.x - rectB.x) < (Math.abs(widthA' + widthB') / 2) 
&& (Math.abs(rectA.y - rectB.y) < (Math.abs(heightA' + heightB') / 2))
    then
        // A and B collide
    end if

อาจอ้างถึง GDC (Game Development Conference 2007) PPT www.realtimecollisiondetection.net/pubs/GDC07_Ericson_Physics_Tutorial_SAT.ppt


ทำไมคุณต้องใช้ Math.abs () ใน "Math.abs (rectA.width + rectB.width)" เพื่อจัดการกับความกว้างติดลบ?
AlexWien

แกนที่แยกไม่จำเป็นต้องเป็นทิศทางของเข็มทิศมันสามารถมีมุมใดก็ได้
Ben Voigt

rectangles แบบไม่หมุน rectA (x = 0, y = 0, width = 1, height = 1) และ rectB (x = 2, y = 0, width = 100, height = 1) ไม่ตัดกัน แต่วิธีการของคุณบอกว่า ตัด. ฉันกำลังทำอะไรผิดหรือเปล่า?
Kagami Sascha Rosylight

4

ในโกโก้คุณสามารถตรวจสอบได้อย่างง่ายดายว่า rectArea ที่เลือกตัดกันการหมุนเฟรมของ NSView ของคุณหรือไม่ คุณไม่จำเป็นต้องคำนวณรูปหลายเหลี่ยม เพียงเพิ่มวิธีการเหล่านี้ในคลาสย่อย NSView ของคุณ ตัวอย่างเช่นผู้ใช้เลือกพื้นที่ในการดูแลของ NSView จากนั้นคุณเรียกวิธีการนี้ว่า API convertRect: จะทำงานดังกล่าว เคล็ดลับเดียวกันนี้ทำงานเมื่อคุณคลิกที่ NSView เพื่อเลือก ในกรณีนั้นเพียงแทนที่วิธี hitTest ดังต่อไปนี้ API convertPoint: จะทำงาน ;-)

- (BOOL)DoesThisRectSelectMe:(NSRect)selectedArea
{
    NSRect localArea = [self convertRect:selectedArea fromView:self.superview];

    return NSIntersectsRect(localArea, self.bounds);
}


- (NSView *)hitTest:(NSPoint)aPoint
{
    NSPoint localPoint = [self convertPoint:aPoint fromView:self.superview];
    return NSPointInRect(localPoint, self.bounds) ? self : nil;
}

2
รหัสนั้นใช้ได้กับสี่เหลี่ยมที่เป็นสี่เหลี่ยมจัตุรัสบนหน้าจอเท่านั้น นั่นเป็นเรื่องเล็กน้อย สมมติฐานคือว่าเรากำลังจัดการกับสี่เหลี่ยมที่ไม่ได้อยู่ที่ 90 องศาของหน้าจอหรือมุมอื่น ๆ
Duncan C

ขณะที่ฉันตรวจสอบและใช้งานในแอปพลิเคชันของฉันรหัสนั้นจะทำงานกับสี่เหลี่ยมหมุนใด ๆ ไม่ว่าระดับการหมุนจะเป็นเท่าไหร่
Leonardo

สิ่งนี้ไม่ได้อธิบายถึงอัลกอริธึม แต่มันก็กล่าวถึงไลบรารีที่ใช้งานแล้ว
Ben Voigt

2

ตรวจสอบว่ามีเส้นใดเส้นหนึ่งจากสี่เหลี่ยมผืนผ้าหนึ่งตัดกับเส้นใดเส้นหนึ่งจากอีกเส้นหนึ่งหรือไม่ การตัดกันของส่วนของเส้นตรงที่ไร้เดียงสานั้นง่ายต่อการเขียนโค้ด

หากคุณต้องการความเร็วมากขึ้นมีอัลกอริธึมขั้นสูงสำหรับการแยกส่วนเซกเมนต์ (การกวาดบรรทัด) ดูhttp://en.wikipedia.org/wiki/Line_segment_intersection


4
ระวัง! อย่าลืมกรณีที่หนึ่งสี่เหลี่ยมผืนผ้าสมบูรณ์ล้อมรอบอีก
Pitarou

2

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

มีการค้นหาไม่พอดีรูปหลายเหลี่ยมบนเว็บและดูว่าคุณสามารถหาอัลกอริทึมสำหรับรูปหลายเหลี่ยมนูน (มันจะซับซ้อนมากขึ้นถ้าคุณมีรูปหลายเหลี่ยมเว้า) หากคุณไม่พบสิ่งใดแล้วส่งอีเมลฉันที่ howard dot J dot อาจ gmail dot com


1

นี่คือสิ่งที่ฉันคิดว่าจะดูแลทุกกรณีที่เป็นไปได้ ทำการทดสอบต่อไปนี้

  1. ตรวจสอบจุดยอดของสี่เหลี่ยม 1 ใด ๆ อยู่ในสี่เหลี่ยม 2 และกลับกัน เมื่อใดก็ตามที่คุณพบจุดสุดยอดที่อยู่ภายในสี่เหลี่ยมอื่น ๆ คุณสามารถสรุปได้ว่าพวกมันตัดกันและหยุดการค้นหา พวกเขาจะดูแลสี่เหลี่ยมผืนผ้าหนึ่งที่พำนักอยู่ในอีกด้านหนึ่งอย่างสมบูรณ์
  2. หากการทดสอบข้างต้นไม่สามารถหาจุดตัดของแต่ละบรรทัดของ 1 สี่เหลี่ยมกับแต่ละบรรทัดของสี่เหลี่ยมอื่น ๆ เมื่อพบจุดตัดตรวจสอบว่าอยู่ระหว่างภายในสี่เหลี่ยมจัตุรัสจินตภาพที่สร้างโดย 4 จุดที่สอดคล้องกันหรือไม่ เมื่อพบจุดเช่นนี้สรุปได้ว่าพวกเขาตัดกันและหยุดการค้นหา

หากการทดสอบ 2 ข้างต้นคืนค่าเท็จสี่เหลี่ยม 2 รูปนี้จะไม่ทับซ้อนกัน


0

หากคุณกำลังใช้ Java การใช้งานทั้งหมดของอินเตอร์เฟซที่รูปร่างมีปริภูมิวิธีการที่จะใช้รูปสี่เหลี่ยมผืนผ้า


น่าเสียดายที่ฉันใช้ C # คลาสสี่เหลี่ยมผืนผ้ามีวิธีการบรรจุ () แต่เป็นเพียงสำหรับสี่เหลี่ยมที่ไม่หมุน
user20493

intersects () method ค่อนข้างไร้ประโยชน์เพราะมันคืนค่าบูลีนแทนที่จะเป็นทางแยกที่ฉันเดา
ZZ 5

0

ทฤษฏีกำลังเดรัจฉานคือการเดินไปตามขอบของสี่เหลี่ยมแนวนอนและตรวจสอบแต่ละจุดตามขอบเพื่อดูว่ามันตกลงบนหรือในสี่เหลี่ยมอื่น ๆ หรือไม่

คำตอบทางคณิตศาสตร์คือการสร้างสมการที่อธิบายถึงขอบของสี่เหลี่ยมทั้งสอง ทีนี้คุณก็สามารถหาได้ว่าเส้นใดในสี่เส้นจากสี่เหลี่ยม A ตัดกับเส้นใด ๆ ของสี่เหลี่ยม B ซึ่งควรเป็นตัวแก้สมการเชิงเส้นอย่างง่าย (เร็ว)

อดัม


2
ปัญหาเกี่ยวกับสมการคือเมื่อคุณมีเส้นแนวตั้งซึ่งมีความชันไม่สิ้นสุด
user20493

มีมุมกล่องสำหรับทุกวิธีแก้ไข
Adam Davis

2
และหนึ่งสี่เหลี่ยมจัตุรัสปิดล้อมอีกอัน
Oliver Hallam

0

คุณสามารถหาจุดตัดของแต่ละด้านของสี่เหลี่ยมมุมฉากกับแต่ละด้านของแนวที่จัดแนวแกน ทำสิ่งนี้โดยการหาสมการของเส้นที่ไม่มีที่สิ้นสุดซึ่งแต่ละด้านอยู่ (เช่น v1 + t (v2-v1) และ v'1 + t '(v'2-v'1) โดยทั่วไปแล้วค้นหาจุดที่ เส้นตรงโดยการหาค่า t เมื่อสมการทั้งสองนั้นเท่ากัน (ถ้าขนานกันคุณสามารถทดสอบได้) แล้วทดสอบว่าจุดนั้นอยู่ในส่วนของเส้นตรงระหว่างจุดยอดทั้งสองหรือไม่นั่นคือความจริงที่ 0 <= t <= 1 และ 0 <= t '<= 1

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


0

นี่คือสิ่งที่ฉันจะทำสำหรับปัญหารุ่น3D :

ทำโมเดลสี่เหลี่ยม 2 รูปเป็นระนาบที่อธิบายโดยสมการ P1 และ P2 จากนั้นเขียน P1 = P2 และได้มาจากเส้นสมการแยกซึ่งจะไม่มีอยู่หากระนาบขนาน (ไม่มีจุดตัด) หรืออยู่ในระนาบเดียวกัน ในกรณีนี้คุณจะได้รับ 0 = 0 ในกรณีนี้คุณจะต้องใช้อัลกอริทึมตัดกันสี่เหลี่ยมผืนผ้า 2D

จากนั้นฉันจะดูว่าเส้นนั้นซึ่งอยู่ในระนาบของสี่เหลี่ยมทั้งสองผ่านทะลุสี่เหลี่ยมทั้งสองหรือไม่ ถ้าเป็นเช่นนั้นคุณมีจุดตัด 2 รูปสี่เหลี่ยมมิฉะนั้นคุณจะไม่ (หรือไม่ควรฉันอาจพลาดมุมตัวเล็กในหัว)

ในการค้นหาว่าเส้นตรงผ่านสี่เหลี่ยมในระนาบเดียวกันฉันจะหาจุดตัดของเส้นตรงและด้านข้างของสี่เหลี่ยม 2 จุด (ทำโมเดลโดยใช้สมการเส้น) แล้วตรวจสอบให้แน่ใจว่ามีจุดใน พิสัย.

นั่นคือคำอธิบายทางคณิตศาสตร์ แต่น่าเสียดายที่ฉันไม่มีรหัสให้ทำข้างต้น


คุณพลาดส่วนที่หากคุณพบเส้นระนาบตัดคุณจะต้องตรวจสอบให้แน่ใจว่ามีบางส่วนอยู่ภายในสี่เหลี่ยมทั้งสอง
Lee Louviere

0

อีกวิธีหนึ่งในการทำแบบทดสอบที่เร็วกว่าการใช้การแยกแกนทดสอบเล็กน้อยคือการใช้อัลกอริธึมตัวเลขที่คดเคี้ยว (บน quadrants เท่านั้น - ไม่ใช่การรวมมุมที่น่ากลัวซึ่งช้าอย่างน่ากลัว) ในแต่ละจุดสุดยอดของสี่เหลี่ยมทั้งสอง หากจุดยอดใดมีจำนวนคดเคี้ยวที่ไม่เป็นศูนย์สี่เหลี่ยมสองรูปนั้นซ้อนทับกัน

อัลกอริธึมนี้ค่อนข้างยืดยาวกว่าการทดสอบแกนที่แยกออก แต่จะเร็วกว่าเพราะต้องการการทดสอบแบบครึ่งระนาบหากขอบข้ามสองควอแดรนท์ (เทียบกับการทดสอบสูงสุด 32 ครั้งโดยใช้วิธีการแยกแกน)

อัลกอริทึมมีข้อได้เปรียบเพิ่มเติมที่สามารถใช้ในการทดสอบการทับซ้อนของรูปหลายเหลี่ยมใด ๆ (นูนหรือเว้า) เท่าที่ฉันรู้อัลกอริทึมทำงานในพื้นที่ 2D เท่านั้น


3
ฉันอาจจะผิด แต่นั่นไม่เพียงตรวจสอบว่าจุดยอดของสี่เหลี่ยมหนึ่งอยู่ภายในอีกหรือไม่ ถ้าใช่มันไม่เพียงพอเพราะสี่เหลี่ยมอาจซ้อนทับกันโดยไม่มีจุดยอดใด ๆ อยู่ภายใน
sinelaw

พวกเขาสามารถกับสี่เหลี่ยม? อย่างไร? สำหรับฉันแล้วเพื่อให้สี่เหลี่ยม 2 รูปตัดกันอย่างน้อยหนึ่งจุดยอดของสี่เหลี่ยมมุมฉากหนึ่งต้องอยู่บนสี่เหลี่ยมอีกมุมหนึ่ง
Duncan C

@DuncanC: ใช่พวกเขาทำได้ ตัวอย่างตัวอย่างเป็นเครื่องหมายกากบาทและแสดงอยู่ในคำถามเดิม
Ben Voigt

@BenVoigt กระทู้นี้เก่ามาก แต่คุณพูดถูก
Duncan C

0

ทำไมฉันถึงทำให้มันซับซ้อน?

ถ้า (x1, y1) และ (X1, Y1) เป็นมุมของรูปสี่เหลี่ยมมุมฉากให้หาจุดตัด:

    xIntersect = false;
    yIntersect = false;
    if (!(Math.min(x1, x2, x3, x4) > Math.max(X1, X2, X3, X4) || Math.max(x1, x2, x3, x4) < Math.min(X1, X2, X3, X4))) xIntersect = true;
    if (!(Math.min(y1, y2, y3, y4) > Math.max(Y1, Y2, Y3, Y4) || Math.max(y1, y2, y3, y4) < Math.min(Y1, Y2, Y3, Y4))) yIntersect = true;
    if (xIntersect && yIntersect) {alert("Intersect");}

3
คุณหายไปว่าเขาต้องการให้มุมหนึ่งหมุนโดยพลการ
Robotbugs

0

ฉันใช้มันเช่นนี้:

bool rectCollision(const CGRect &boundsA, const Matrix3x3 &mB, const CGRect &boundsB)
{
    float Axmin = boundsA.origin.x;
    float Axmax = Axmin + boundsA.size.width;
    float Aymin = boundsA.origin.y;
    float Aymax = Aymin + boundsA.size.height;

    float Bxmin = boundsB.origin.x;
    float Bxmax = Bxmin + boundsB.size.width;
    float Bymin = boundsB.origin.y;
    float Bymax = Bymin + boundsB.size.height;

    // find location of B corners in A space
    float B0x = mB(0,0) * Bxmin + mB(0,1) * Bymin + mB(0,2);
    float B0y = mB(1,0) * Bxmin + mB(1,1) * Bymin + mB(1,2);

    float B1x = mB(0,0) * Bxmax + mB(0,1) * Bymin + mB(0,2);
    float B1y = mB(1,0) * Bxmax + mB(1,1) * Bymin + mB(1,2);

    float B2x = mB(0,0) * Bxmin + mB(0,1) * Bymax + mB(0,2);
    float B2y = mB(1,0) * Bxmin + mB(1,1) * Bymax + mB(1,2);

    float B3x = mB(0,0) * Bxmax + mB(0,1) * Bymax + mB(0,2);
    float B3y = mB(1,0) * Bxmax + mB(1,1) * Bymax + mB(1,2);

    if(B0x<Axmin && B1x<Axmin && B2x<Axmin && B3x<Axmin)
        return false;
    if(B0x>Axmax && B1x>Axmax && B2x>Axmax && B3x>Axmax)
        return false;
    if(B0y<Aymin && B1y<Aymin && B2y<Aymin && B3y<Aymin)
        return false;
    if(B0y>Aymax && B1y>Aymax && B2y>Aymax && B3y>Aymax)
        return false;

    float det = mB(0,0)*mB(1,1) - mB(0,1)*mB(1,0);
    float dx = mB(1,2)*mB(0,1) - mB(0,2)*mB(1,1);
    float dy = mB(0,2)*mB(1,0) - mB(1,2)*mB(0,0);

    // find location of A corners in B space
    float A0x = (mB(1,1) * Axmin - mB(0,1) * Aymin + dx)/det;
    float A0y = (-mB(1,0) * Axmin + mB(0,0) * Aymin + dy)/det;

    float A1x = (mB(1,1) * Axmax - mB(0,1) * Aymin + dx)/det;
    float A1y = (-mB(1,0) * Axmax + mB(0,0) * Aymin + dy)/det;

    float A2x = (mB(1,1) * Axmin - mB(0,1) * Aymax + dx)/det;
    float A2y = (-mB(1,0) * Axmin + mB(0,0) * Aymax + dy)/det;

    float A3x = (mB(1,1) * Axmax - mB(0,1) * Aymax + dx)/det;
    float A3y = (-mB(1,0) * Axmax + mB(0,0) * Aymax + dy)/det;

    if(A0x<Bxmin && A1x<Bxmin && A2x<Bxmin && A3x<Bxmin)
        return false;
    if(A0x>Bxmax && A1x>Bxmax && A2x>Bxmax && A3x>Bxmax)
        return false;
    if(A0y<Bymin && A1y<Bymin && A2y<Bymin && A3y<Bymin)
        return false;
    if(A0y>Bymax && A1y>Bymax && A2y>Bymax && A3y>Bymax)
        return false;

    return true;
}

เมทริกซ์ mB เป็นเมทริกซ์แปลงเลียนแบบใด ๆ ที่แปลงคะแนนในพื้นที่ B เป็นคะแนนในพื้นที่ A ซึ่งรวมถึงการหมุนและการแปลอย่างง่ายการหมุนพร้อมการปรับและการเลียนแบบเต็มรูปแบบ แต่ไม่ใช่มุมมองการบิด

อาจไม่เหมาะสมที่สุด ความเร็วไม่ได้เป็นเรื่องใหญ่ อย่างไรก็ตามดูเหมือนว่าจะใช้ได้สำหรับฉัน


0

นี่คือการใช้ MATLAB ของคำตอบที่ยอมรับได้:

function olap_flag = ol(A,B,sub)

%A and B should be 4 x 2 matrices containing the xy coordinates of the corners in clockwise order

if nargin == 2
  olap_flag = ol(A,B,1) && ol(B,A,1);
  return;
end

urdl = diff(A([1:4 1],:));
s = sum(urdl .* A, 2);
sdiff = B * urdl' - repmat(s,[1 4]);

olap_flag = ~any(max(sdiff)<0);

0

นี่คือวิธีการทั่วไปไปทีละบรรทัดและตรวจสอบว่าเส้นที่ตัดกัน นี่คือรหัสใน MATLAB

C1 = [0, 0];    % Centre of rectangle 1 (x,y)
C2 = [1, 1];    % Centre of rectangle 2 (x,y)
W1 = 5; W2 = 3; % Widths of rectangles 1 and 2
H1 = 2; H2 = 3; % Heights of rectangles 1 and 2
% Define the corner points of the rectangles using the above
R1 = [C1(1) + [W1; W1; -W1; -W1]/2, C1(2) + [H1; -H1; -H1; H1]/2];
R2 = [C2(1) + [W2; W2; -W2; -W2]/2, C2(2) + [H2; -H2; -H2; H2]/2];

R1 = [R1 ; R1(1,:)] ;
R2 = [R2 ; R2(1,:)] ;

plot(R1(:,1),R1(:,2),'r')
hold on
plot(R2(:,1),R2(:,2),'b')


%% lines of Rectangles 
L1 = [R1(1:end-1,:) R1(2:end,:)] ;
L2 = [R2(1:end-1,:) R2(2:end,:)] ;
%% GEt intersection points
P = zeros(2,[]) ;
count = 0 ;
for i = 1:4
    line1 = reshape(L1(i,:),2,2) ;
    for j = 1:4
        line2 = reshape(L2(j,:),2,2) ;
        point = InterX(line1,line2) ;
        if ~isempty(point)
            count = count+1 ;
            P(:,count) = point ;
        end
    end
end
%%
if ~isempty(P)
    fprintf('Given rectangles intersect at %d points:\n',size(P,2))
    plot(P(1,:),P(2,:),'*k')
end

สามารถดาวน์โหลดฟังก์ชัน InterX ได้จาก: https://in.mathworks.com/matlabcentral/fileexchange/22441-curve-intersections?focused=5165138&tab=function


0

ฉันมีวิธีที่ง่ายกว่าของตัวเองถ้าเรามี 2 รูปสี่เหลี่ยมผืนผ้า:

R1 = (min_x1, max_x1, min_y1, max_y1)

R2 = (min_x2, max_x2, min_y2, max_y2)

พวกเขาทับซ้อนกันถ้าหาก:

Overlap = (max_x1> min_x2) และ (max_x2> min_x1) และ (max_y1> min_y2) และ (max_y2> min_y1)

คุณสามารถใช้กับกล่อง 3 มิติได้เช่นกันมันใช้งานได้จริงในทุกมิติ


0

มีคำตอบอื่น ๆ เพียงพอแล้วดังนั้นฉันจะเพิ่ม pseudocode one-liner:

!(a.left > b.right || b.left > a.right || a.top > b.bottom || b.top > a.bottom);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.