ปัญหาการตรวจจับการชนวงกลม -line


11

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

ส่วนใหญ่แล้วการทดสอบแบบ Circle-Line นั้นทำงานได้อย่างถูกต้องและจุดของการชนนั้นได้รับการแก้ไขอย่างถูกต้อง

การตรวจจับการชนกันทำงานอย่างถูกต้อง

อย่างไรก็ตามในบางครั้งรหัสตรวจจับการชนของฉันคืนเท็จเนื่องจากการเลือกปฏิบัติเชิงลบเมื่อลูกบอลตัดกับก้อนอิฐ

การตรวจหาการชนกันล้มเหลว

ฉันตระหนักถึงความไร้ประสิทธิภาพด้วยวิธีนี้และฉันใช้กล่องที่จัดเรียงตามแนวแกนเพื่อลดจำนวนอิฐที่ทดสอบ ความกังวลหลักของฉันคือถ้ามีข้อบกพร่องทางคณิตศาสตร์ในรหัสของฉันด้านล่าง

/* 
 * from and to are points at the start and end of the convex polygons edge.
 * This function is called for every edge in the convex polygon until a
 * collision is detected. 
 */

bool circleLineCollision(Vec2f from, Vec2f to)
{
    Vec2f lFrom, lTo, lLine;
    Vec2f line, normal;
    Vec2f intersectPt1, intersectPt2;
    float a, b, c, disc, sqrt_disc, u, v, nn, vn;
    bool one = false, two = false;

    // set line vectors
    lFrom = from - ball.circle.centre;      // localised
    lTo = to - ball.circle.centre;          // localised
    lLine = lFrom - lTo;                    // localised
    line = from - to;

    // calculate a, b & c values
    a = lLine.dot(lLine);
    b = 2 * (lLine.dot(lFrom));
    c = (lFrom.dot(lFrom)) - (ball.circle.radius * ball.circle.radius);

    // discriminant
    disc = (b * b) - (4 * a * c);

    if (disc < 0.0f)
    {
        // no intersections
        return false;
    }
    else if (disc == 0.0f)
    {
        // one intersection
        u = -b / (2 * a);

        intersectPt1 = from + (lLine.scale(u));
        one = pointOnLine(intersectPt1, from, to);

        if (!one)
            return false;
        return true;
    }
    else
    {
        // two intersections
        sqrt_disc = sqrt(disc);
        u = (-b + sqrt_disc) / (2 * a);
        v = (-b - sqrt_disc) / (2 * a);
        intersectPt1 = from + (lLine.scale(u));
        intersectPt2 = from + (lLine.scale(v));

        one = pointOnLine(intersectPt1, from, to);
        two = pointOnLine(intersectPt2, from, to);

        if (!one && !two)
            return false;
        return true;
    }
}

bool pointOnLine(Vec2f p, Vec2f from, Vec2f to)
{
    if (p.x >= min(from.x, to.x) && p.x <= max(from.x, to.x) && 
        p.y >= min(from.y, to.y) && p.y <= max(from.y, to.y))
        return true;
    return false;
}

ฉันไม่พบความแตกต่างระหว่าง lLine กับเส้น ...
FxIII

การทดสอบ pointOnLine นั้นสามารถทำให้ง่ายขึ้นและทำได้ก่อนการคำนวณจุดที่แท้จริง
FxIII

วิธีคำนวณ sqrt_disc
FxIII

ขออภัย FxIII ฉันต้องสับสนเล็กน้อยเมื่อฉันทำการแปลเวกเตอร์ของฉันฉันไม่ทราบว่าเวกเตอร์จะเท่ากันเมื่อพวกมันถูกลบออกจากกัน ฉันทำความสะอาดโค้ดก่อนที่จะโพสต์และฉันลืมใส่sqrt_disc = sqrt(disc);กลับขอบคุณมากสำหรับคำตอบของคุณด้านล่างมันช่วยฉันได้มาก
jazzdawg

คำตอบ:


20

ส่วนที่ทำงานจากAถึงBสามารถคำนวณได้ดังนี้

P (t) = A + D · t โดยที่DคือB - Aและt วิ่งจาก 0 ถึง 1

ตอนนี้วงกลมเป็นศูนย์กลางในการกำเนิด (เลื่อนและBถ้าจำเป็นที่จะนำศูนย์ในแหล่งกำเนิด) และมีรัศมีR

คุณมีทางแยกถ้าคุณรู้ว่าPมีความยาวrเท่ากับหรือเท่ากับความยาวของPกำลังสองเท่ากับ

ความยาวกำลังสองของเวกเตอร์นั้นได้มาจากการทำดอทโปรดัคท์ของเวกเตอร์ด้วยตัวเอง (นี่เป็นความจริงที่ว่าหากพบว่าการใช้งานที่เหมาะสมสำหรับผลิตภัณฑ์ดอทนั้นเขาสามารถกำหนดแนวคิดใหม่ของความยาว

P · P = ( A + D · t) · ( A + D · t) =

A · A + 2 A · D t + D · D

เราต้องการค้นหาสิ่งที่เราได้รับP · P = r²ดังนั้นเราจึงถามตัวเองเมื่อใด

A · A + 2 A · D t + D · D t² = r²

หรือเมื่อ

D · D t² + 2 A · D t + A · A -r² = 0

นี่คือสมการกำลังสองที่โด่งดังมาก

at² + bt + c = 0

กับ

a = D · D ; b = 2 A · Dและ c = A · A -r²

เราต้องตรวจสอบว่าดีเทอร์มีแนนต์b² - 4acนั้นเป็นค่าบวกหรือไม่ดังนั้นเราพบ 2 ค่าของ t ที่ให้จุดตัดกันP (t)

t ต้องอยู่ระหว่าง 0 ถึง 1มิฉะนั้นเราจะพบวิธีแก้ปัญหาที่อยู่บนบรรทัดที่ผ่านAและBแต่นั่นคือก่อนหน้าAหรือหลังB

[แก้ไข]

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

ส่วนที่เรียกใช้จาก A ถึง B

Dคือเวกเตอร์ที่เคลื่อนที่ A ไป B ดังนั้นหาก t อยู่ระหว่าง 0 และ 1 D · t คือ "เศษส่วนที่เหมาะสม" ของ D ดังนั้นจุดA + D · t อยู่ในเซ็กเมนต์A_B : จุดสีน้ำตาลเกิดขึ้นเมื่อ t คือ ระหว่าง 0 ถึง 1 และสีเขียวเข้มคือเมื่อ t> 1

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

วงกลมย้ายไปที่ศูนย์

ตอนนี้เรามีวิธีง่ายๆในการคำนวณความยาวของPเมื่อ t แตกต่างกันและบอกว่า t Pข้ามขอบเขตของวงกลมอย่างไร

ตัวอย่าง

ตามที่คุณเห็นP 'มีความยาวมากกว่า r ในขณะที่P "น้อยกว่า r เนื่องจากทั้งเวกเตอร์ความยาวและ r เป็นตัวเลขบวก กำลังสองและรัศมีกำลังสองP * 1และP * 2เป็นจุดที่ทำให้| P | ²เท่ากับr²

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

จำแนกถูกนำมาใช้ในการแยกแยะสภาพก่อนหน้านี้และการทดสอบความถูกต้องที่จะทำบนเสื้อเพื่อดูว่ามันแยกที่ถูกต้อง แต่อยู่นอกส่วนของเรา - คือการแก้ปัญหาทีจะต้องมีจริงและระหว่าง 0 และ 1 ได้รับการพิจารณาแยกที่เหมาะสมที่ฤดูใบไม้ร่วง ในกลุ่มA_B


3
นี่เป็นอัลกอริทึมที่เหมาะสมที่จะใช้ คำอธิบายที่ดีมากเกี่ยวกับวิธีการทำงานสามารถพบได้ใน Real Time Rendering Third Edition , หน้า 787 ถึง 791 หากคุณสามารถหามันได้ในห้องสมุด
Darcy Rayner

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

แขวนบนบัญชีนี้สำหรับสองมุมกรณีถูกต้องหรือไม่ ตัวอย่างเช่นวงกลมอาจข้ามระนาบที่กำหนดโดยบรรทัดนอก t0 <= t <= t1 แต่กดจุดสิ้นสุดของส่วนของเส้นในภายหลัง คุณต้องตรวจสอบระยะห่างขั้นต่ำระหว่างจุดสิ้นสุดของเส้นและเส้นทางวงกลม หากระยะทางนั้นน้อยกว่ารัศมีวงกลมแสดงว่าเส้นนั้นชน
Darcy Rayner

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