ประการแรกในกรณีของสี่เหลี่ยมที่เรียงตามแนวแกนคำตอบของ Kevin Reid นั้นดีที่สุดและอัลกอริทึมนั้นเร็วที่สุด
ประการที่สองสำหรับรูปร่างที่เรียบง่ายให้ใช้ความเร็วสัมพัทธ์ (ดังที่เห็นด้านล่าง) และทฤษฎีการแยกแกนสำหรับการตรวจจับการชนกัน มันจะบอกคุณว่ามีการชนกันในกรณีของการเคลื่อนที่เชิงเส้น (ไม่มีการหมุน) และหากมีการหมุนคุณต้องมีการประทับเวลาเล็กน้อยเพื่อให้แม่นยำ ตอนนี้เพื่อตอบคำถาม:
จะบอกได้อย่างไรในกรณีทั่วไปว่ารูปร่างนูนสองจุดตัดกันหรือไม่
ฉันจะให้อัลกอริทึมที่เหมาะกับรูปร่างนูนทั้งหมดไม่ใช่แค่รูปหกเหลี่ยม
สมมติว่าXและYเป็นสองรูปร่างนูน พวกเขาตัดและถ้าหากพวกเขามีจุดที่เหมือนกันคือมีจุดx ∈ XและจุดY ∈ Yเช่นว่าx y หากคุณคิดว่าพื้นที่ที่เป็นพื้นที่เวกเตอร์แล้วจำนวนนี้เพื่อบอกx - Y = 0 และตอนนี้เราไปถึงธุรกิจ Minkowski นี้:
รวมคอฟสกีของXและYคือชุดของทุกx + y ที่สำหรับx ∈ XและY ∈ Y
ตัวอย่างสำหรับ X และ Y
X, Y และผลรวม Minkowski, X + Y
เผื่อว่า(-Y)เป็นชุดของทุก-yสำหรับปี∈ Y , ได้รับแล้วย่อหน้าก่อนหน้า, XและYตัดถ้าหากว่าX + (-Y)มี0นั่นคือจุดเริ่มต้น
หมายเหตุด้านข้าง: ทำไมผมเขียนX + (-Y)แทนX - Y ? เพราะในวิชาคณิตศาสตร์มีการดำเนินการที่เรียกว่าความแตกต่างของ Minkowski ของAและBซึ่งบางครั้งเขียนX - Yแต่ไม่มีอะไรเกี่ยวข้องกับเซตของx - y ทั้งหมดสำหรับx ∈ Xและy ∈ Y (Minkowski จริง ความแตกต่างนั้นซับซ้อนกว่าเล็กน้อย)
ดังนั้นเราจึงต้องการคำนวณผลรวมของ Minkowski ของXและ-Yและเพื่อค้นหาว่ามันมีต้นกำเนิดอยู่หรือไม่ ที่มาไม่ได้พิเศษเมื่อเทียบกับจุดอื่น ๆ เพื่อที่จะค้นหาว่าต้นกำเนิดอยู่ในโดเมนที่แน่นอนเราใช้อัลกอริทึมที่สามารถบอกเราว่าจุดใดก็ตามที่เป็นของโดเมนนั้น
ผลรวมของ Minkowski ของXและYมีคุณสมบัติเจ๋ง ๆ ซึ่งก็คือถ้าXและYเป็นส่วนนูนดังนั้นX + Yก็เช่นกัน และพบว่าจุดที่เป็นชุดนูนเป็นมากง่ายกว่าถ้าชุดที่ไม่ได้ (ที่รู้จักกันจะ) นูน
เราไม่สามารถคำนวณx - y ทั้งหมดสำหรับx ∈ Xและy ∈ Y ได้เพราะมีจุดอนันต์เช่นxและyดังนั้นหวังว่าเนื่องจากX , YและX + Yเป็นรูปโค้งเราจึงสามารถใช้ จุด "นอกสุด" กำหนดรูปร่างXและYซึ่งเป็นจุดยอดของพวกเขาและเราจะได้รับจุดนอกสุดของX + Yและอีกมากมาย
จุดเพิ่มเติมเหล่านี้คือ "ล้อมรอบ" โดยจุดนอกสุดของX + Yเพื่อไม่ให้มีส่วนช่วยในการกำหนดรูปร่างนูนที่ได้รับใหม่ เราบอกว่าพวกเขาไม่ได้นิยาม " ตัวเรือนูน " ของชุดคะแนน ดังนั้นสิ่งที่เราทำคือเรากำจัดพวกมันเพื่อเตรียมอัลกอริธึมสุดท้ายที่บอกเราว่าต้นกำเนิดนั้นอยู่ภายในตัวเรือนูนหรือไม่
ฮัลล์นูนของ X + Y เราได้ลบจุดยอด "ภายใน"
เราจึงได้
อัลกอริทึมไร้เดียงสาแรก
boolean intersect(Shape X, Shape Y) {
SetOfVertices minkowski = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
minkowski.addVertice(x-y);
}
}
return contains(convexHull(minkowski), Vector2D(0,0));
}
เห็นได้ชัดว่าลูปมีความซับซ้อนO (mn)โดยที่mและnเป็นจำนวนจุดยอดของแต่ละรูปร่าง minkoswki
ชุดประกอบด้วยล้านองค์ประกอบที่มากที่สุด convexHull
อัลกอริทึมที่มีความซับซ้อนที่ขึ้นอยู่กับขั้นตอนวิธีการที่คุณใช้และคุณสามารถมีจุดมุ่งหมายเพื่อO (บันทึก k (k))ที่kคือขนาดของชุดของจุดดังนั้นในกรณีของเราเราได้รับO (ล้านบาทเข้าสู่ระบบ (ล้านบาท) ) . contains
อัลกอริทึมที่มีความซับซ้อนที่เป็นเชิงเส้นที่มีจำนวนขอบ (ในแบบ 2D) หรือใบหน้า (3 มิติ) ของเปลือกนูนจึงจริงๆขึ้นอยู่กับรูปทรงเริ่มต้นของคุณ แต่มันจะไม่เป็นมากกว่าO (ล้านบาท)
ฉันจะให้คุณ google สำหรับcontains
อัลกอริทึมสำหรับรูปร่างนูนมันเป็นเรื่องธรรมดา ฉันอาจวางไว้ที่นี่ถ้าฉันมีเวลา
แต่เป็นการตรวจจับการชนกันของข้อมูลที่เรากำลังทำอยู่
ตอนแรกเรามีสองร่างAและBเคลื่อนไหวโดยไม่มีการหมุนในช่วงเวลาdt (จากสิ่งที่ฉันสามารถบอกได้โดยดูที่รูปของคุณ) โทร Let 's วีและวีBความเร็วที่เกี่ยวข้องของและBซึ่งเป็นค่าคงที่ในช่วง timestep ของระยะเวลาของเราdt เราได้รับดังต่อไปนี้:
และในขณะที่คุณชี้ให้เห็นในรูปภาพร่างกายเหล่านี้จะกวาดผ่านพื้นที่ (หรือระดับเสียงในแบบ 3 มิติ) ขณะที่เคลื่อนไหว:
และพวกเขาก็จบลงด้วยการ 'AและB'หลังจากเวลาที่กำหนด
เมื่อต้องการใช้อัลกอริทึมไร้เดียงสาของเราที่นี่เราจะต้องคำนวณปริมาณที่กวาด แต่เราไม่ได้ทำสิ่งนี้
ในกรอบอ้างอิงของB , Bจะไม่ย้าย (duh!) และAมีความเร็วบางอย่าง เทียบกับ Bที่คุณได้จากการคำนวณv A - v B (คุณสามารถทำการสนทนา, คำนวณความเร็วสัมพัทธ์ของBในกรอบอ้างอิงของA )
จากซ้ายไปขวา: ความเร็วในกรอบอ้างอิงฐาน ความเร็วสัมพัทธ์ การคำนวณความเร็วสัมพัทธ์
โดยเกี่ยวกับBเป็นเคลื่อนที่ในกรอบอ้างอิงของตัวเองคุณจะต้องคำนวณปริมาณที่เรตติ้งผ่านขณะที่มันเคลื่อนในช่วงdtกับญาติความเร็ววีv - B
สิ่งนี้จะลดจำนวนของจุดยอดที่จะใช้ในการคำนวณผลรวมของ Minkowski (บางครั้งก็มาก)
การปรับให้เหมาะสมที่เป็นไปได้อีกอย่างคือจุดที่คุณคำนวณปริมาตรที่พัดผ่านโดยวัตถุตัวใดตัวหนึ่งสมมติว่า A. คุณไม่จำเป็นต้องแปลจุดยอดทั้งหมดที่ประกอบเป็น A. เฉพาะสิ่งที่อยู่ในขอบ (ใบหน้าใน 3D) "ใบหน้า" ด้านนอกปกติทิศทางของการกวาด แน่นอนคุณสังเกตเห็นแล้วว่าเมื่อคุณคำนวณพื้นที่กวาดของคุณสำหรับสี่เหลี่ยม คุณสามารถบอกได้ว่าปกติเป็นไปสู่ทิศทางการกวาดโดยใช้ผลิตภัณฑ์ดอทกับทิศทางการกวาดซึ่งจะต้องเป็นบวก
การเพิ่มประสิทธิภาพครั้งล่าสุดซึ่งไม่เกี่ยวข้องกับคำถามของคุณเกี่ยวกับทางแยกมีประโยชน์จริง ๆ ในกรณีของเรา มันใช้ความเร็วสัมพัทธ์ที่เรากล่าวถึงและวิธีการแยกแกนที่เรียกว่า แน่นอนคุณรู้เกี่ยวกับมันแล้ว
สมมติว่าคุณรู้รัศมีของAและBเกี่ยวกับศูนย์กลางมวลของมัน (กล่าวคือระยะห่างระหว่างจุดศูนย์กลางมวลและจุดสุดยอดมากที่สุดจากจุดนี้):
ชนสามารถเกิดขึ้นได้ แต่ถ้ามันเป็นไปได้ว่าวงกลมวิ่งของพบว่าB เราดูที่นี่ว่ามันจะไม่ได้และวิธีที่จะบอกคอมพิวเตอร์ที่อยู่ในการคำนวณระยะทางจากC Bไปฉันตามในภาพต่อไปนี้และให้แน่ใจว่ามันมีขนาดใหญ่กว่าผลรวมของรัศมีของและB ถ้ามันใหญ่กว่าก็ไม่ชนกัน หากมีขนาดเล็กก็ให้ชนกัน
นี้ไม่ได้ทำงานเป็นอย่างดีกับรูปร่างที่ค่อนข้างยาว แต่ในกรณีของสี่เหลี่ยมหรือรูปทรงอื่น ๆ เช่นมันเป็นแก้ปัญหาที่ดีมากที่จะออกกฎการปะทะกัน
ทฤษฎีการแยกแกนนำไปใช้กับBและปริมาตรที่พัดผ่านโดยAจะบอกคุณได้หรือไม่ว่าการชนเกิดขึ้น ความซับซ้อนของอัลกอริธึมที่เกี่ยวข้องนั้นเป็นเส้นตรงกับผลรวมของจำนวนจุดยอดของรูปร่างนูนแต่ละอัน แต่มันวิเศษน้อยลงเมื่อถึงเวลาที่ต้องจัดการกับการชน
อัลกอริทึมใหม่ที่ดีกว่าของเราที่ใช้การแยกเพื่อช่วยตรวจจับการชน แต่ก็ยังไม่ดีเท่าทฤษฎีแกนการแยกที่บอกว่าการชนเกิดขึ้นจริงหรือไม่
boolean mayCollide(Body A, Body B) {
Vector2D relativeVelocity = A.velocity - B.velocity;
if (radiiHeuristic(A, B, relativeVelocity)) {
return false; // there is a separating axis between them
}
Volume sweptA = sweptVolume(A, relativeVelocity);
return contains(convexHull(minkowskiMinus(sweptA, B)), Vector2D(0,0));
}
boolean radiiHeuristic(A, B, relativeVelocity)) {
// the code here
}
Volume convexHull(SetOfVertices s) {
// the code here
}
boolean contains(Volume v, Vector2D p) {
// the code here
}
SetOfVertices minkowskiMinus(Body X, Body Y) {
SetOfVertices result = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
result.addVertice(x-y);
}
}
return result;
}