การตรวจจับการชนกันของรูปสี่เหลี่ยมผืนผ้าวงกลม (ทางแยก)


192

ฉันจะบอกได้อย่างไรว่าวงกลมและสี่เหลี่ยมตัดกันในพื้นที่แบบยุคลิดแบบ 2D หรือไม่? (เช่นเรขาคณิต 2D แบบคลาสสิก)


1
สี่เหลี่ยมผืนผ้าอยู่ในแนวเดียวกันกับแกนหรือไม่หรือมันสามารถหมุนโดยมุมที่กำหนดเองได้หรือไม่?
e.James

11
@eJames: มันสำคัญยังไง? คุณกำลังตรวจสอบสี่เหลี่ยมสำหรับจุดตัดกับวงกลม ; คุณสามารถเปลี่ยนระบบพิกัดของคุณเพื่อให้สี่เหลี่ยมผืนผ้าเป็นแนวขนานโดยไม่มีการเปลี่ยนแปลงในวงกลม :-)
ShreevatsaR

คุณควรเพิ่มนั่นเป็นคำตอบหมุนผ่าน-Θและทั้งหมด ...
aib

2
@ShreevatsaR: มันสำคัญในแง่ของอากาศหรือไม่ฉันจำเป็นต้องกังวลเกี่ยวกับการแปลพิกัดที่หรือไม่ @aib: โอ้ที่รัก!
e.James

คำตอบ:


191

มีเพียงสองกรณีเมื่อวงกลมตัดกับสี่เหลี่ยม:

  • ศูนย์กลางของวงกลมนั้นอยู่ภายในสี่เหลี่ยมหรือ
  • หนึ่งในขอบของสี่เหลี่ยมมีจุดในวงกลม

โปรดทราบว่าสิ่งนี้ไม่จำเป็นต้องให้สี่เหลี่ยมผืนผ้าเป็นเส้นขนาน

วิธีต่าง ๆ ที่วงกลมและสี่เหลี่ยมอาจตัดกัน

(วิธีหนึ่งที่จะเห็นสิ่งนี้: ถ้าไม่มีขอบใด ๆ ที่มีจุดอยู่ในวงกลม (ถ้าขอบทั้งหมด "นอก" วงกลมทั้งหมด) ดังนั้นวิธีเดียวที่วงกลมยังคงสามารถตัดรูปหลายเหลี่ยมได้ถ้ามันอยู่ข้างใน รูปหลายเหลี่ยม.)

มีความเข้าใจว่าสิ่งที่ต้องการต่อไปนี้จะทำงานที่วงกลมมีศูนย์PและรัศมีRและสี่เหลี่ยมผืนผ้ามีจุดA, B, C, Dในลำดับที่ (รหัสไม่เสร็จสมบูรณ์):

def intersect(Circle(P, R), Rectangle(A, B, C, D)):
    S = Circle(P, R)
    return (pointInRectangle(P, Rectangle(A, B, C, D)) or
            intersectCircle(S, (A, B)) or
            intersectCircle(S, (B, C)) or
            intersectCircle(S, (C, D)) or
            intersectCircle(S, (D, A)))

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

0 ≤ AP·AB ≤ AB·AB and 0 ≤ AP·AD ≤ AD·AD

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

สิ่งที่เจ๋งคือความคิดเดียวกันนั้นใช้ไม่ได้กับสี่เหลี่ยม แต่สำหรับจุดตัดของวงกลมที่มีรูปหลายเหลี่ยมแบบง่าย ๆ - ไม่จำเป็นต้องนูนออกมา!


25
สำหรับสิ่งที่คุ้มค่าฉันคิดว่าคำตอบนี้ดีกว่าของฉัน สองเหตุผลหลัก: 1: มันไม่จำเป็นต้องมีการหมุนถ้าสี่เหลี่ยมผืนผ้าไม่ขนานกับแกนและ 2: แนวคิดนี้ขยายไปถึงรูปหลายเหลี่ยมทั้งหมดได้อย่างง่ายดาย
e.James

2
@paniq: ทั้งคู่เป็นเวลาคงที่ :-) แต่ใช่นี้มีประโยชน์มากกว่าในฐานะโซลูชันทั่วไปซึ่งครอบคลุมสี่เหลี่ยมในทุกทิศทางและในความเป็นจริงแล้วรูปหลายเหลี่ยมแบบง่าย ๆ
ShreevatsaR

7
สิ่งที่เกี่ยวกับกรณีที่เป็นรูปสี่เหลี่ยมผืนผ้าภายในวงกลม แต่ศูนย์กลางของวงกลมไม่ได้อยู่ในรูปสี่เหลี่ยมผืนผ้า?
ericsoco

2
@ericsoco: สังเกตดี :-) ฉันคิดว่าฉันควรจะพูดว่า "ตัดแผ่นดิสก์" ใน "หนึ่งในขอบของสี่เหลี่ยมตัดกับวงกลม" เพราะฉันหมายถึงมันหมายความว่ามันใช้จุดร่วมกับวงกลมนั้นเองไม่จำเป็นต้องเป็นขอบเขตของวงกลม อย่างไรก็ตามคำอธิบายข้างต้น "ตรวจสอบว่าเท้าของตั้งฉากจาก P [ศูนย์กลางของวงกลม] ไปยังบรรทัดนั้นอยู่ใกล้พอและระหว่างจุดสิ้นสุดและตรวจสอบจุดสิ้นสุดมิฉะนั้น" จะยังคงทำงาน - เช่นจุดสิ้นสุดอยู่ภายในวงกลม ( แผ่นดิสก์)
ShreevatsaR

2
@ DexD.Hunter ถ้าจุดศูนย์กลางของวงกลมอยู่นอกสี่เหลี่ยม แต่ส่วนหนึ่งของวงกลมนั้นอยู่ในสี่เหลี่ยมแล้วจำเป็นต้องมีขอบด้านใดด้านหนึ่งของสี่เหลี่ยมตัดกันวงกลม
ShreevatsaR

289

นี่คือวิธีที่ฉันจะทำ:

bool intersects(CircleType circle, RectType rect)
{
    circleDistance.x = abs(circle.x - rect.x);
    circleDistance.y = abs(circle.y - rect.y);

    if (circleDistance.x > (rect.width/2 + circle.r)) { return false; }
    if (circleDistance.y > (rect.height/2 + circle.r)) { return false; }

    if (circleDistance.x <= (rect.width/2)) { return true; } 
    if (circleDistance.y <= (rect.height/2)) { return true; }

    cornerDistance_sq = (circleDistance.x - rect.width/2)^2 +
                         (circleDistance.y - rect.height/2)^2;

    return (cornerDistance_sq <= (circle.r^2));
}

นี่คือวิธีการทำงาน:

illusration

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

  2. คู่ที่สองของเส้นขจัดกรณีง่าย ๆ ที่วงกลมอยู่ห่างจากสี่เหลี่ยมผืนผ้า (ทั้งสองทิศทาง) ซึ่งไม่สามารถแยกได้ สิ่งนี้สอดคล้องกับพื้นที่สีเขียวในภาพ

  3. เส้นคู่ที่สามนั้นจัดการกรณีง่าย ๆ ที่วงกลมนั้นอยู่ใกล้กับสี่เหลี่ยม (ในทิศทางใดทิศทางหนึ่ง) ที่รับประกันจุดตัด สิ่งนี้สอดคล้องกับส่วนสีส้มและสีเทาในภาพ โปรดทราบว่าขั้นตอนนี้จะต้องทำหลังจากขั้นตอนที่ 2 เพื่อให้ตรรกะมีเหตุผล

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


4
ดีมาก! หมายเหตุ: เห็นได้ชัดที่นี่ rect.x / y อยู่ที่มุมขวาบนของสี่เหลี่ยมผืนผ้า นอกจากนี้คุณสามารถกำจัดสแควร์รูทที่มีราคาแพงโดยเปรียบเทียบกับสแควร์ของรัศมีแทน
luqui

2
โอ้ไม่ดีของฉัน rect.x / y อยู่ที่ด้านล่างซ้ายของสี่เหลี่ยมผืนผ้า ฉันจะได้เขียน: circleDistance.x = abs (circle.x - (rect.x + rect.width / 2));
luqui

2
@ แทนเนอร์: ไปแล้ว ไชโยสำหรับการสำรองข้อมูลและ OCD;)
e.James

11
เพื่อชี้แจง - คำตอบนี้ใช้ได้เฉพาะกับสี่เหลี่ยมที่เรียงแนวแกนเท่านั้น ที่ชัดเจนจากการอ่านผ่านความคิดเห็นเกี่ยวกับคำตอบอื่น ๆ แต่ไม่ชัดเจนจากคำตอบนี้ + ความคิดเห็นเพียงอย่างเดียว (คำตอบที่ดีสำหรับ rects จัดแนวแกนสรรพสินค้าใหญ่)
ericsoco

3
ที่ดี! เป็นสิ่งสำคัญสำหรับผู้อ่านที่จะรู้ว่าที่นี่ฉันเชื่อว่าคำนิยามของ rect คือ rect.x & rect.y เป็นศูนย์กลางของ rect ในโลกของฉัน xy ของ rect คือด้านบน / ซ้ายของ rect และ 0,0 คือด้านบน / ซ้ายของหน้าจอดังนั้นฉันจึงใช้:circleDistance_x = abs(circle.x - (rect.x-rect.w/2)); circleDistance_y = abs(circle.y - (rect.y-rect.h/2));
erco

123

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

// clamp(value, min, max) - limits value to the range min..max

// Find the closest point to the circle within the rectangle
float closestX = clamp(circle.X, rectangle.Left, rectangle.Right);
float closestY = clamp(circle.Y, rectangle.Top, rectangle.Bottom);

// Calculate the distance between the circle's center and this closest point
float distanceX = circle.X - closestX;
float distanceY = circle.Y - closestY;

// If the distance is less than the circle's radius, an intersection occurs
float distanceSquared = (distanceX * distanceX) + (distanceY * distanceY);
return distanceSquared < (circle.Radius * circle.Radius);

ด้วยห้องสมุดคณิตศาสตร์ที่ดีใด ๆ ที่สามารถย่อให้เหลือ 3 หรือ 4 บรรทัด


3
คุณมีจุดบกพร่องอยู่ตรงนั้นคุณค้นหา Y ที่ใกล้เคียงที่สุดด้วยซ้ายและขวาไม่ใช่บนและล่างโซลูชันที่น่ารัก
manveru

8
ฉันชอบคำตอบนี้ดีที่สุด สั้นเข้าใจง่ายและรวดเร็ว
John Kurlak

2
ฉันคิดว่าวิธีแก้ปัญหาของคุณล้มเหลวถ้าสี่เหลี่ยมผืนผ้านั้นเอียงไปทางแกน x และ y
Leo

3
@Leo ฉันคิดว่ามันไม่ยากที่จะแก้ไขอัลกอริธึมนี้เพื่อรองรับกรณีนั้นเราควรใช้การแปลงพิกัดโดยที่จุดกำเนิดอยู่ตรงกลางสี่เหลี่ยมผืนผ้าและสี่เหลี่ยมผืนผ้าไม่เอียงอีกต่อไป คุณต้องใช้การแปลงกับศูนย์กลางวงกลมเท่านั้น
enobayram

1
นี่เป็นพื้นเดียวกันกับรหัสที่พบที่migapro.com/circle-and-rotated-rectangle-collision-detectionซึ่งฉันได้ส่งไปยัง Objective-C ด้วย ทำงานได้ดีมาก มันเป็นทางออกที่ดีในการแก้ไขปัญหา
PKCLsoft

10

ทรงกลมและจุดตัดของคุณตัดกัน IIF
ระยะห่างระหว่างจุดศูนย์กลางวงกลมกับจุดยอดหนึ่งจุดของคุณเล็กกว่ารัศมีของทรงกลมของคุณ
หรือ
ระยะห่างระหว่างจุดศูนย์กลางวงกลมและขอบด้านหนึ่งของสี่เหลี่ยมจัตุรัสของคุณเล็กกว่ารัศมีของทรงกลมของคุณ ( [ ระยะห่างจากจุดเส้น ])
หรือ
จุดศูนย์กลางวงกลมอยู่ใน

ระยะทางจุดจุดที่ถูกต้อง:

P1 = [x1, y1]
P2 = [x2, y2]
ระยะทาง = sqrt (abs (x1 - x2) + abs (y1-y2))

ระยะทางจุดเส้น:

L1 = [x1, y1], L2 = [x2, y2] (สองจุดในบรรทัดของคุณคือจุดยอด)
P1 = [px, py] บางจุด

ระยะทาง d = abs ((x2-x1) (y1-py) - (x1-px) (y2-y1)) / ระยะทาง (L1, L2)


ศูนย์กลางวงกลมด้านใน rect:
นำ aproach แกน aproach: ถ้ามีการฉายภาพลงบนเส้นที่แยกสี่เหลี่ยมผืนผ้าออกจากจุดพวกมันจะไม่ตัดกัน

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

คุณเพียงต้องการผลิตภัณฑ์ชั้นใน (x = [x1, x2], y = [y1, y2], x * y = x1 * y1 + x2 * y2)

การทดสอบของคุณจะเป็นเช่นนั้น:

// ขอบสี่เหลี่ยมผืนผ้า: TL (ซ้ายบน), TR (ขวาบน), BL (ซ้ายล่าง), BR (ขวาล่าง)
// ชี้ไปที่ทดสอบ: POI

seperated = false
สำหรับ egde ใน {{TL, TR}, {BL, BR}, {TL, BL}, {TR-BR}}: // the edge
    D = edge [0] - edge [1]
    innerProd = D * POI
    Interval_min = นาที (D * edge [0], D * edge [1])
    Interval_max = สูงสุด (D * edge [0], D * edge [1])
    ถ้าไม่ (Interval_min ≤ innerProd ≤ Interval_max) 
           seperated = จริง
           break // end สำหรับลูป 
    จบถ้า
จบสิ้น
ถ้า (แยกเป็นจริง)    
      ส่งคืน "ไม่มีทางแยก"
อื่น 
      ส่งคืน "ทางแยก"
จบถ้า

สิ่งนี้ไม่ถือว่าเป็นรูปสี่เหลี่ยมผืนผ้าที่จัดแนวแกนและสามารถขยายได้อย่างง่ายดายสำหรับการทดสอบทางแยกระหว่างชุดนูน


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

6

นี่คือทางออกที่เร็วที่สุด:

public static boolean intersect(Rectangle r, Circle c)
{
    float cx = Math.abs(c.x - r.x - r.halfWidth);
    float xDist = r.halfWidth + c.radius;
    if (cx > xDist)
        return false;
    float cy = Math.abs(c.y - r.y - r.halfHeight);
    float yDist = r.halfHeight + c.radius;
    if (cy > yDist)
        return false;
    if (cx <= r.halfWidth || cy <= r.halfHeight)
        return true;
    float xCornerDist = cx - r.halfWidth;
    float yCornerDist = cy - r.halfHeight;
    float xCornerDistSq = xCornerDist * xCornerDist;
    float yCornerDistSq = yCornerDist * yCornerDist;
    float maxCornerDistSq = c.radius * c.radius;
    return xCornerDistSq + yCornerDistSq <= maxCornerDistSq;
}

หมายเหตุลำดับของการดำเนินการและครึ่งหนึ่งของความกว้าง / ความสูงถูกคำนวณล่วงหน้า การตั้งกำลังทำก็ทำได้ด้วยตนเองเพื่อบันทึกรอบสัญญาณนาฬิกา


3
ฉันไม่คิดว่าคุณสามารถอ้างได้ว่าการทดสอบ / การเปรียบเทียบห้ารายการในเส้นทางของรหัสที่แพงที่สุดคือ "ทางออกที่เร็วที่สุด" โดยไม่มีข้อพิสูจน์
sam hocevar


1
จากประสบการณ์ของฉันด้วยวิธีนี้การปะทะกันไม่ได้เกิดขึ้นบ่อยครั้ง ดังนั้นการทดสอบจะทำให้เกิดการออกก่อนก่อนที่รหัสส่วนใหญ่จะถูกดำเนินการ
intrepidis

6

ทางออกที่ง่ายที่สุดที่ฉันคิดไว้คือความตรงไปตรงมา

มันทำงานได้โดยการหาจุดในสี่เหลี่ยมที่อยู่ใกล้กับวงกลมที่สุดแล้วเปรียบเทียบระยะทาง

คุณสามารถทำสิ่งนี้ได้ด้วยการใช้งานเพียงไม่กี่อย่างและยังสามารถหลีกเลี่ยงฟังก์ชัน sqrt ได้อีกด้วย

public boolean intersects(float cx, float cy, float radius, float left, float top, float right, float bottom)
{
   float closestX = (cx < left ? left : (cx > right ? right : cx));
   float closestY = (cy < top ? top : (cy > bottom ? bottom : cy));
   float dx = closestX - cx;
   float dy = closestY - cy;

   return ( dx * dx + dy * dy ) <= radius * radius;
}

และนั่นมัน! วิธีการแก้ปัญหาข้างต้นถือว่าเป็นจุดกำเนิดในมุมซ้ายบนของโลกโดยมีแกน x ชี้ลง

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


สิ่งนี้จะล้มเหลวในการตรวจจับทางแยกถ้ารัศมีของวงกลมเล็กเกินไปและศูนย์กลางอยู่ภายในสี่เหลี่ยมผืนผ้า!
Yoav

2
คุณสามารถให้ข้อมูลจริงที่ทำให้สิ่งนี้ล้มเหลวได้หรือไม่? เมื่อวงกลมอยู่ข้างในส่วนซ้ายของการทดสอบคือ 0.0 เว้นเสียแต่ว่ารัศมีจะเป็นศูนย์ส่วนด้านขวาของการทดสอบควร> 0.0
ClickerMonkey

มันจะใช้กับสี่เหลี่ยมหมุนได้หรือไม่? ถ้าไม่โปรดให้คำแนะนำเกี่ยวกับเรื่องนั้นให้ฉัน .....
M อับดุลเซ

4

จริงๆแล้วมันง่ายกว่านี้มาก คุณต้องการเพียงสองสิ่ง

ก่อนอื่นคุณต้องหาระยะทางสี่มุมฉากจากศูนย์กลางวงกลมไปยังแต่ละบรรทัดของสี่เหลี่ยมผืนผ้า จากนั้นวงกลมของคุณจะไม่ตัดกันสี่เหลี่ยมผืนผ้าถ้าสามในนั้นมีขนาดใหญ่กว่ารัศมีของวงกลม

ประการที่สองคุณต้องค้นหาระยะห่างระหว่างศูนย์กลางวงกลมกับศูนย์กลางสี่เหลี่ยมผืนผ้าจากนั้นคุณวงกลมจะไม่อยู่ภายในสี่เหลี่ยมถ้าระยะทางนั้นมีขนาดใหญ่กว่าครึ่งหนึ่งของความยาวเส้นทแยงมุมสี่เหลี่ยมผืนผ้า

โชคดี!


3

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

float physicsProcessCollisionBetweenSelfAndActorRect(SPhysics *self, SPhysics *actor)
{
    float diff = 99999;

    SVector relative_position_of_circle = getDifference2DBetweenVectors(&self->worldPosition, &actor->worldPosition);
    rotateVector2DBy(&relative_position_of_circle, -actor->axis.angleZ); // This aligns the coord system so the rect becomes an AABB

    float x_clamped_within_rectangle = relative_position_of_circle.x;
    float y_clamped_within_rectangle = relative_position_of_circle.y;
    LIMIT(x_clamped_within_rectangle, actor->physicsRect.l, actor->physicsRect.r);
    LIMIT(y_clamped_within_rectangle, actor->physicsRect.b, actor->physicsRect.t);

    // Calculate the distance between the circle's center and this closest point
    float distance_to_nearest_edge_x = relative_position_of_circle.x - x_clamped_within_rectangle;
    float distance_to_nearest_edge_y = relative_position_of_circle.y - y_clamped_within_rectangle;

    // If the distance is less than the circle's radius, an intersection occurs
    float distance_sq_x = SQUARE(distance_to_nearest_edge_x);
    float distance_sq_y = SQUARE(distance_to_nearest_edge_y);
    float radius_sq = SQUARE(self->physicsRadius);
    if(distance_sq_x + distance_sq_y < radius_sq)   
    {
        float half_rect_w = (actor->physicsRect.r - actor->physicsRect.l) * 0.5f;
        float half_rect_h = (actor->physicsRect.t - actor->physicsRect.b) * 0.5f;

        CREATE_VECTOR(push_vector);         

        // If we're at one of the corners of this object, treat this as a circular/circular collision
        if(fabs(relative_position_of_circle.x) > half_rect_w && fabs(relative_position_of_circle.y) > half_rect_h)
        {
            SVector edges;
            if(relative_position_of_circle.x > 0) edges.x = half_rect_w; else edges.x = -half_rect_w;
            if(relative_position_of_circle.y > 0) edges.y = half_rect_h; else edges.y = -half_rect_h;   

            push_vector = relative_position_of_circle;
            moveVectorByInverseVector2D(&push_vector, &edges);

            // We now have the vector from the corner of the rect to the point.
            float delta_length = getVector2DMagnitude(&push_vector);
            float diff = self->physicsRadius - delta_length; // Find out how far away we are from our ideal distance

            // Normalise the vector
            push_vector.x /= delta_length;
            push_vector.y /= delta_length;
            scaleVector2DBy(&push_vector, diff); // Now multiply it by the difference
            push_vector.z = 0;
        }
        else // Nope - just bouncing against one of the edges
        {
            if(relative_position_of_circle.x > 0) // Ball is to the right
                push_vector.x = (half_rect_w + self->physicsRadius) - relative_position_of_circle.x;
            else
                push_vector.x = -((half_rect_w + self->physicsRadius) + relative_position_of_circle.x);

            if(relative_position_of_circle.y > 0) // Ball is above
                push_vector.y = (half_rect_h + self->physicsRadius) - relative_position_of_circle.y;
            else
                push_vector.y = -((half_rect_h + self->physicsRadius) + relative_position_of_circle.y);

            if(fabs(push_vector.x) < fabs(push_vector.y))
                push_vector.y = 0;
            else
                push_vector.x = 0;
        }

        diff = 0; // Cheat, since we don't do anything with the value anyway
        rotateVector2DBy(&push_vector, actor->axis.angleZ);
        SVector *from = &self->worldPosition;       
        moveVectorBy2D(from, push_vector.x, push_vector.y);
    }   
    return diff;
}

2

ในการมองเห็นให้ใช้แป้นตัวเลขของคุณ หากคีย์ '5' แทนสี่เหลี่ยมของคุณคีย์ทั้งหมด 1-9 จะแทนพื้นที่สี่เหลี่ยมจัตุรัส 9 อันหารด้วยเส้นที่ประกอบสี่เหลี่ยมผืนผ้าของคุณ (โดยมี 5 อยู่ด้านใน)

1) ถ้าจุดศูนย์กลางของวงกลมอยู่ในจตุภาคที่ 5 (เช่นภายในสี่เหลี่ยม) ทั้งสองจะตัดกัน

เมื่อออกนอกเส้นทางมีสองกรณีที่เป็นไปได้: a) วงกลมตัดกับขอบด้านข้างของสี่เหลี่ยมสองหรือมากกว่านั้น b) วงกลมตัดกับขอบด้านหนึ่งของสี่เหลี่ยมผืนผ้า

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

2) หากมุม A, B, C, D ของสี่เหลี่ยมอยู่ภายในวงกลมทั้งสองนั้นจะตัดกัน

กรณีที่สองคือเล่ห์เหลี่ยม เราควรสังเกตว่ามันอาจเกิดขึ้นเมื่อศูนย์กลางของวงกลมอยู่ในหนึ่งในจตุภาค 2, 4, 6 หรือ 8 (อันที่จริงถ้าศูนย์กลางอยู่ในจตุภาคใด ๆ ของ 1, 3, 7, 8 มุมที่เกี่ยวข้องจะเป็นจุดที่ใกล้ที่สุด)

ตอนนี้เรามีกรณีที่ศูนย์กลางของวงกลมอยู่ในจตุภาค 'edge' และมันตัดกับขอบที่สอดคล้องกันเท่านั้น จากนั้นจุดบนขอบที่ใกล้กับศูนย์กลางของวงกลมจะต้องอยู่ในวงกลม

3) สำหรับแต่ละบรรทัด AB, BC, CD, DA, สร้างเส้นตั้งฉาก p (AB, P), p (BC, P), p (CD, P), p (DA, P) ผ่านศูนย์กลางของวงกลม P สำหรับ แต่ละเส้นตั้งฉากถ้าการตัดกับขอบเดิมอยู่ภายในวงกลมแล้วทั้งสองตัดกัน

มีทางลัดสำหรับขั้นตอนสุดท้ายนี้ ถ้าจุดศูนย์กลางของวงกลมอยู่ในจตุภาคที่ 8 และขอบ AB เป็นขอบด้านบนจุดตัดจะมีพิกัด y ของ A และ B และพิกัด x ของจุดศูนย์กลาง P

คุณสามารถสร้างสี่แยกบรรทัดและตรวจสอบว่าพวกเขาอยู่บนขอบที่สอดคล้องกันหรือค้นหาว่า P Quadrant ใดที่อยู่ในและตรวจสอบแยกที่สอดคล้องกัน ทั้งคู่ควรทำให้สมการบูลีนง่ายขึ้น ระวังให้ดีว่าขั้นตอนที่ 2 ข้างต้นไม่ได้แยกแยะว่า P อยู่ในหนึ่งในกลุ่ม 'มุม' มันแค่มองหาจุดตัด

แก้ไข: ปรากฎว่าฉันได้มองข้ามความจริงง่ายๆที่ # 2 เป็น subcase ของ # 3 ข้างต้น ท้ายที่สุดแล้วมุมก็เป็นจุดที่ขอบ ดูคำตอบของ @ ShreevatsaR ด้านล่างสำหรับคำอธิบายที่ดี และในขณะเดียวกันให้ลืม # 2 ข้างต้นเว้นแต่คุณต้องการเช็คที่รวดเร็ว แต่ซ้ำซ้อน


2

ฟังก์ชั่นนี้ตรวจจับการชนกัน (ทางแยก) ระหว่างวงกลมและสี่เหลี่ยมผืนผ้า เขาทำงานเหมือนวิธี e.James ในคำตอบของเขา แต่อันนี้ตรวจจับการชนกันของทุกมุมสี่เหลี่ยมผืนผ้า (ไม่เฉพาะมุมขวา)

บันทึก:

aRect.origin.xและaRect.origin.yเป็นพิกัดของมุมล่างซ้ายของรูปสี่เหลี่ยมผืนผ้า!

aCircle.xและaCircle.yเป็นพิกัดของ Circle Center!

static inline BOOL RectIntersectsCircle(CGRect aRect, Circle aCircle) {

    float testX = aCircle.x;
    float testY = aCircle.y;

    if (testX < aRect.origin.x)
        testX = aRect.origin.x;
    if (testX > (aRect.origin.x + aRect.size.width))
        testX = (aRect.origin.x + aRect.size.width);
    if (testY < aRect.origin.y)
        testY = aRect.origin.y;
    if (testY > (aRect.origin.y + aRect.size.height))
        testY = (aRect.origin.y + aRect.size.height);

    return ((aCircle.x - testX) * (aCircle.x - testX) + (aCircle.y - testY) * (aCircle.y - testY)) < aCircle.radius * aCircle.radius;
}

1

ฉันมีวิธีที่หลีกเลี่ยงพีทาโกรัสราคาแพงถ้าไม่จำเป็น - เช่น เมื่อกล่องสี่เหลี่ยมของวงกลมและวงกลมไม่ตัดกัน

และมันก็ใช้ได้กับคนที่ไม่ใช่ยูคลิดด้วยเช่นกัน:

class Circle {
 // create the bounding box of the circle only once
 BBox bbox;

 public boolean intersect(BBox b) {
    // test top intersect
    if (lat > b.maxLat) {
        if (lon < b.minLon)
            return normDist(b.maxLat, b.minLon) <= normedDist;
        if (lon > b.maxLon)
            return normDist(b.maxLat, b.maxLon) <= normedDist;
        return b.maxLat - bbox.minLat > 0;
    }

    // test bottom intersect
    if (lat < b.minLat) {
        if (lon < b.minLon)
            return normDist(b.minLat, b.minLon) <= normedDist;
        if (lon > b.maxLon)
            return normDist(b.minLat, b.maxLon) <= normedDist;
        return bbox.maxLat - b.minLat > 0;
    }

    // test middle intersect
    if (lon < b.minLon)
        return bbox.maxLon - b.minLon > 0;
    if (lon > b.maxLon)
        return b.maxLon - bbox.minLon > 0;
    return true;
  }
}
  • minLat, maxLat สามารถแทนที่ด้วย minY, maxY และเหมือนกันสำหรับ minLon, maxLon: แทนที่ด้วย minX, maxX
  • normDist เป็นเพียงวิธีที่เร็วขึ้นเล็กน้อยแล้วการคำนวณระยะทางเต็มรูปแบบ เช่นโดยไม่มีรากในพื้นที่ Euclidean (หรือโดยไม่ต้องมากของสิ่งอื่น ๆ สำหรับ haversine dLat=(lat-circleY); dLon=(lon-circleX); normed=dLat*dLat+dLon*dLona): แน่นอนถ้าคุณใช้วิธี normDist นั้นคุณจะต้องสร้างnormedDist = dist*dist;วงกลม

ดูรหัสBBoxและCircleเต็มรูปแบบของโครงการGraphHopperของฉัน


1

ฉันสร้างคลาสเพื่อทำงานกับรูปร่างหวังว่าคุณจะสนุก

public class Geomethry {
  public static boolean intersectionCircleAndRectangle(int circleX, int circleY, int circleR, int rectangleX, int rectangleY, int rectangleWidth, int rectangleHeight){
    boolean result = false;

    float rectHalfWidth = rectangleWidth/2.0f;
    float rectHalfHeight = rectangleHeight/2.0f;

    float rectCenterX = rectangleX + rectHalfWidth;
    float rectCenterY = rectangleY + rectHalfHeight;

    float deltax = Math.abs(rectCenterX - circleX);
    float deltay = Math.abs(rectCenterY - circleY);

    float lengthHypotenuseSqure = deltax*deltax + deltay*deltay;

    do{
        // check that distance between the centerse is more than the distance between the circumcircle of rectangle and circle
        if(lengthHypotenuseSqure > ((rectHalfWidth+circleR)*(rectHalfWidth+circleR) + (rectHalfHeight+circleR)*(rectHalfHeight+circleR))){
            //System.out.println("distance between the centerse is more than the distance between the circumcircle of rectangle and circle");
            break;
        }

        // check that distance between the centerse is less than the distance between the inscribed circle
        float rectMinHalfSide = Math.min(rectHalfWidth, rectHalfHeight);
        if(lengthHypotenuseSqure < ((rectMinHalfSide+circleR)*(rectMinHalfSide+circleR))){
            //System.out.println("distance between the centerse is less than the distance between the inscribed circle");
            result=true;
            break;
        }

        // check that the squares relate to angles
        if((deltax > (rectHalfWidth+circleR)*0.9) && (deltay > (rectHalfHeight+circleR)*0.9)){
            //System.out.println("squares relate to angles");
            result=true;
        }
    }while(false);

    return result;
}

public static boolean intersectionRectangleAndRectangle(int rectangleX, int rectangleY, int rectangleWidth, int rectangleHeight, int rectangleX2, int rectangleY2, int rectangleWidth2, int rectangleHeight2){
    boolean result = false;

    float rectHalfWidth = rectangleWidth/2.0f;
    float rectHalfHeight = rectangleHeight/2.0f;
    float rectHalfWidth2 = rectangleWidth2/2.0f;
    float rectHalfHeight2 = rectangleHeight2/2.0f;

    float deltax = Math.abs((rectangleX + rectHalfWidth) - (rectangleX2 + rectHalfWidth2));
    float deltay = Math.abs((rectangleY + rectHalfHeight) - (rectangleY2 + rectHalfHeight2));

    float lengthHypotenuseSqure = deltax*deltax + deltay*deltay;

    do{
        // check that distance between the centerse is more than the distance between the circumcircle
        if(lengthHypotenuseSqure > ((rectHalfWidth+rectHalfWidth2)*(rectHalfWidth+rectHalfWidth2) + (rectHalfHeight+rectHalfHeight2)*(rectHalfHeight+rectHalfHeight2))){
            //System.out.println("distance between the centerse is more than the distance between the circumcircle");
            break;
        }

        // check that distance between the centerse is less than the distance between the inscribed circle
        float rectMinHalfSide = Math.min(rectHalfWidth, rectHalfHeight);
        float rectMinHalfSide2 = Math.min(rectHalfWidth2, rectHalfHeight2);
        if(lengthHypotenuseSqure < ((rectMinHalfSide+rectMinHalfSide2)*(rectMinHalfSide+rectMinHalfSide2))){
            //System.out.println("distance between the centerse is less than the distance between the inscribed circle");
            result=true;
            break;
        }

        // check that the squares relate to angles
        if((deltax > (rectHalfWidth+rectHalfWidth2)*0.9) && (deltay > (rectHalfHeight+rectHalfHeight2)*0.9)){
            //System.out.println("squares relate to angles");
            result=true;
        }
    }while(false);

    return result;
  } 
}

1

นี่คือรหัส modfied ทำงาน 100%:

public static bool IsIntersected(PointF circle, float radius, RectangleF rectangle)
{
    var rectangleCenter = new PointF((rectangle.X +  rectangle.Width / 2),
                                     (rectangle.Y + rectangle.Height / 2));

    var w = rectangle.Width  / 2;
    var h = rectangle.Height / 2;

    var dx = Math.Abs(circle.X - rectangleCenter.X);
    var dy = Math.Abs(circle.Y - rectangleCenter.Y);

    if (dx > (radius + w) || dy > (radius + h)) return false;

    var circleDistance = new PointF
                             {
                                 X = Math.Abs(circle.X - rectangle.X - w),
                                 Y = Math.Abs(circle.Y - rectangle.Y - h)
                             };

    if (circleDistance.X <= (w))
    {
        return true;
    }

    if (circleDistance.Y <= (h))
    {
        return true;
    }

    var cornerDistanceSq = Math.Pow(circleDistance.X - w, 2) + 
                                    Math.Pow(circleDistance.Y - h, 2);

    return (cornerDistanceSq <= (Math.Pow(radius, 2)));
}

Bassam Alugili


1

นี่คือการทดสอบหนึ่งบรรทัดที่รวดเร็วสำหรับสิ่งนี้:

if (length(max(abs(center - rect_mid) - rect_halves, 0)) <= radius ) {
  // They intersect.
}

นี่คือกรณีที่จัดแนวแกนซึ่งrect_halvesเป็นเวกเตอร์บวกที่ชี้จากสี่เหลี่ยมกลางถึงมุม การแสดงออกภายในlength()เป็นเวกเตอร์ของเดลต้าจากcenterไปยังจุดที่ใกล้ที่สุดในสี่เหลี่ยมผืนผ้า สิ่งนี้ใช้ได้ในทุกมิติ


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

มันมีประสิทธิภาพเพราะ:

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

1

ทำงานได้สำหรับฉัน (ทำงานเฉพาะเมื่อมุมของรูปสี่เหลี่ยมผืนผ้าคือ 180)

function intersects(circle, rect) {
  let left = rect.x + rect.width > circle.x - circle.radius;
  let right = rect.x < circle.x + circle.radius;
  let top = rect.y < circle.y + circle.radius;
  let bottom = rect.y + rect.height > circle.y - circle.radius;
  return left && right && bottom && top;
}

อืม ... ฉันโหวตสิ่งนี้แล้ว แต่ผ่านการทดสอบอย่างถูกต้องและฉันคิดว่ามันไม่สามารถใช้งานได้เช่นกัน มันจะใช้ได้กับสี่เหลี่ยมสองอัน
Dan Zen

1

การปรับปรุงคำตอบของ e.James เล็กน้อย :

double dx = abs(circle.x - rect.x) - rect.w / 2,
       dy = abs(circle.y - rect.y) - rect.h / 2;

if (dx > circle.r || dy > circle.r) { return false; }
if (dx <= 0 || dy <= 0) { return true; }

return (dx * dx + dy * dy <= circle.r * circle.r);

การลบนี้rect.w / 2และrect.h / 2ครั้งเดียวแทนที่จะสูงถึงสามครั้ง


ฉันสงสัยอย่างยิ่งว่าคอมไพเลอร์สมัยใหม่ส่วนใหญ่จะ (หรืออย่างน้อยสามารถทำได้) ปรับการคำนวณซ้ำซ้อนให้คุณโดยอัตโนมัติ
martineau

0

สำหรับผู้ที่มีการคำนวณการชนกันของวงกลม / สี่เหลี่ยมผืนผ้าในพิกัดทางภูมิศาสตร์กับ SQL
นี่คือการดำเนินการของฉันใน Oracle 11 ของe.James แนะนำขั้นตอนวิธีแนะนำขั้นตอนวิธีการ

ในอินพุตมันต้องใช้พิกัดวงกลมรัศมีวงกลมในกม. และสองจุดยอดพิกัดของสี่เหลี่ยมผืนผ้า:

CREATE OR REPLACE FUNCTION "DETECT_CIRC_RECT_COLLISION"
(
    circleCenterLat     IN NUMBER,      -- circle Center Latitude
    circleCenterLon     IN NUMBER,      -- circle Center Longitude
    circleRadius        IN NUMBER,      -- circle Radius in KM
    rectSWLat           IN NUMBER,      -- rectangle South West Latitude
    rectSWLon           IN NUMBER,      -- rectangle South West Longitude
    rectNELat           IN NUMBER,      -- rectangle North Est Latitude
    rectNELon           IN NUMBER       -- rectangle North Est Longitude
)
RETURN NUMBER
AS
    -- converts km to degrees (use 69 if miles)
    kmToDegreeConst     NUMBER := 111.045;

    -- Remaining rectangle vertices 
    rectNWLat   NUMBER;
    rectNWLon   NUMBER;
    rectSELat   NUMBER;
    rectSELon   NUMBER;

    rectHeight  NUMBER;
    rectWIdth   NUMBER;

    circleDistanceLat   NUMBER;
    circleDistanceLon   NUMBER;
    cornerDistanceSQ    NUMBER;

BEGIN
    -- Initialization of remaining rectangle vertices  
    rectNWLat := rectNELat;
    rectNWLon := rectSWLon;
    rectSELat := rectSWLat;
    rectSELon := rectNELon;

    -- Rectangle sides length calculation
    rectHeight := calc_distance(rectSWLat, rectSWLon, rectNWLat, rectNWLon);
    rectWidth := calc_distance(rectSWLat, rectSWLon, rectSELat, rectSELon);

    circleDistanceLat := abs( (circleCenterLat * kmToDegreeConst) - ((rectSWLat * kmToDegreeConst) + (rectHeight/2)) );
    circleDistanceLon := abs( (circleCenterLon * kmToDegreeConst) - ((rectSWLon * kmToDegreeConst) + (rectWidth/2)) );

    IF circleDistanceLon > ((rectWidth/2) + circleRadius) THEN
        RETURN -1;   --  -1 => NO Collision ; 0 => Collision Detected
    END IF;

    IF circleDistanceLat > ((rectHeight/2) + circleRadius) THEN
        RETURN -1;   --  -1 => NO Collision ; 0 => Collision Detected
    END IF;

    IF circleDistanceLon <= (rectWidth/2) THEN
        RETURN 0;   --  -1 => NO Collision ; 0 => Collision Detected
    END IF;

    IF circleDistanceLat <= (rectHeight/2) THEN
        RETURN 0;   --  -1 => NO Collision ; 0 => Collision Detected
    END IF;


    cornerDistanceSQ := POWER(circleDistanceLon - (rectWidth/2), 2) + POWER(circleDistanceLat - (rectHeight/2), 2);

    IF cornerDistanceSQ <=  POWER(circleRadius, 2) THEN
        RETURN 0;  --  -1 => NO Collision ; 0 => Collision Detected
    ELSE
        RETURN -1;  --  -1 => NO Collision ; 0 => Collision Detected
    END IF;

    RETURN -1;  --  -1 => NO Collision ; 0 => Collision Detected
END;    

0

ใช้งานได้เพียงคิดเมื่อสัปดาห์ที่แล้วและตอนนี้ก็ต้องทดสอบ

double theta = Math.atan2(cir.getX()-sqr.getX()*1.0,
                          cir.getY()-sqr.getY()*1.0); //radians of the angle
double dBox; //distance from box to edge of box in direction of the circle

if((theta >  Math.PI/4 && theta <  3*Math.PI / 4) ||
   (theta < -Math.PI/4 && theta > -3*Math.PI / 4)) {
    dBox = sqr.getS() / (2*Math.sin(theta));
} else {
    dBox = sqr.getS() / (2*Math.cos(theta));
}
boolean touching = (Math.abs(dBox) >=
                    Math.sqrt(Math.pow(sqr.getX()-cir.getX(), 2) +
                              Math.pow(sqr.getY()-cir.getY(), 2)));

อาจใช้ได้กับ Circle-Square แต่คำถามเกี่ยวกับ Circle-สี่เหลี่ยมผืนผ้า
martineau

0
def colision(rect, circle):
dx = rect.x - circle.x
dy = rect.y - circle.y
distance = (dy**2 + dx**2)**0.5
angle_to = (rect.angle + math.atan2(dx, dy)/3.1415*180.0) % 360
if((angle_to>135 and angle_to<225) or (angle_to>0 and angle_to<45) or (angle_to>315 and angle_to<360)):
    if distance <= circle.rad/2.+((rect.height/2.0)*(1.+0.5*abs(math.sin(angle_to*math.pi/180.)))):
        return True
else:
    if distance <= circle.rad/2.+((rect.width/2.0)*(1.+0.5*abs(math.cos(angle_to*math.pi/180.)))):
        return True
return False

-2

สมมติว่าคุณมีสี่ขอบของสี่เหลี่ยมตรวจสอบระยะห่างจากขอบถึงศูนย์กลางของวงกลมถ้าน้อยกว่ารัศมีแล้วรูปร่างจะถูกตัดกัน

if sqrt((rectangleRight.x - circleCenter.x)^2 +
        (rectangleBottom.y - circleCenter.y)^2) < radius
// then they intersect

if sqrt((rectangleRight.x - circleCenter.x)^2 +
        (rectangleTop.y - circleCenter.y)^2) < radius
// then they intersect

if sqrt((rectangleLeft.x - circleCenter.x)^2 +
        (rectangleTop.y - circleCenter.y)^2) < radius
// then they intersect

if sqrt((rectangleLeft.x - circleCenter.x)^2 +
        (rectangleBottom.y - circleCenter.y)^2) < radius
// then they intersect

สิ่งที่เกี่ยวกับกรณีที่วงกลมขนาดเล็กล้อมรอบด้วยสี่เหลี่ยมขนาดใหญ่ทั้งหมด? แน่นอนว่าเป็นจุดตัดและจะไม่ผ่านการทดสอบในคำตอบนี้
Ken Paul

อ่าใช่ฉันไม่คิดอย่างนั้น คุณสามารถเพิ่มการตรวจสอบเพิ่มเติมได้เช่นถ้า sqrt ((rectangleRight.x / 2 - circleCenter.x) ^ 2 + (rectangleBottom.y / 2 - circleCenter.y) ^ 2) <รัศมีจากนั้นพวกเขาตัดกันซึ่งจะยาวและช้า แต่ออกมาจากหัวของฉันนั่นเป็นสิ่งที่ดีที่สุดที่ฉันสามารถทำได้
ForYourOwnGood

พวกเขาสามารถตัดกันที่จุดใดจุดหนึ่งบนขอบใดก็ได้ คุณควรค้นหาระยะกึ่งกลางขอบเช่นกัน (โอ้และเรียกมุม "มุม" ของคุณ :)
aib

ดูเหมือนว่าจะตรวจจับเฉพาะเมื่อมุมอยู่ภายในวงกลม
สิ้นเชิง

-2

ถ้าสี่เหลี่ยมตัดกับวงกลมหนึ่งหรือหลายมุมของสี่เหลี่ยมควรอยู่ในวงกลม สมมติว่าจุดสี่จุดของสี่เหลี่ยมคือ A, B, C, D อย่างน้อยหนึ่งคนควรตัดกันวงกลม ดังนั้นหากระยะทางจากจุดหนึ่งไปยังศูนย์กลางของวงกลมนั้นน้อยกว่ารัศมีของวงกลมมันควรตัดกันวงกลม เพื่อให้ได้ระยะทางคุณสามารถใช้ทฤษฎีบทพีทาโกรัส

H^2 = A^2 + B^2

เทคนิคนี้มีข้อ จำกัด แต่มันจะทำงานได้ดีขึ้นสำหรับผู้พัฒนาเกม การตรวจจับการชนโดยเฉพาะอย่างยิ่ง

เป็นการปรับปรุงอัลกอริทึมที่ดีของ Arvo


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