Проблема виявлення колізійних ліній в колі


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

Відрізок, що працює від А до В можна обчислити як

P (t) = A + D · t, де D є B - A і t працює від 0 до 1

Тепер коло орієнтоване на початок (перемістіть A і B, якщо необхідно, щоб покласти центр у походження) і має радіус r .

У вас є перетин, якщо для деякого t виходить, що P має однакову довжину r або, що еквівалентно, що довжина P у квадраті еквівалентна

Довжина квадрата вектора виходить, роблячи крапковий добуток вектора самостійно (це так правдиво, що якщо людина знайде відповідну операцію для крапкового продукту, він може визначити нову і послідовну концепцію довжини)

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

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

Ми хочемо знайти, для якого t отримаємо 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

[EDIT]

Оскільки інші питання можуть знайти допомогу в цій відповіді, я вирішив спробувати спростити міркування в цій редакції за допомогою деяких зображень. стартовий стан Це початкова умова. Тепер зосередимось на сегменті 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
Це правильний алгоритм для використання. Дійсно хороший опис того, як це працює, можна знайти в Третьому виданні рендерингу в реальному часі , сторінки від 787 до 791. Якщо ви можете знайти його в бібліотеці, варто переглянути.
Дарсі Рейнер

4
З восьмим підсумком цієї відповіді я досяг 2-х балів репутації. Я дуже ціную довіру, яку ви мені висловили. Це і визнання моїх зусиль, і стимул продовжувати робити все можливе, щоб отримати відповідь найвищої якості. Дякую
FxIII

Почекайте, чи правильно цей рахунок для двох кутових випадків? Наприклад, коло може перетинати площину, визначену лінією поза t0 <= t <= t1, але торкнутися кінцевих точок відрізка лінії трохи пізніше. Потрібно перевірити мінімальну відстань між кінцевими точками рядка та колом шляху. Якщо ця відстань менша за радіус кола, то лінія потрапила.
Дарсі Рейнер

@DarcyRayner Ви маєте на увазі випадок, коли обидві точки знаходяться всередині кола кола?
FxIII
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.