Circle-Line Çarpışma Tespit Sorunu


11

Şu anda bir koparma klonu geliştiriyorum ve düzgün çalışan bir top (daire) ve bir tuğla (dışbükey çokgen) arasındaki çarpışma tespitinde bir engel oluşturdum. Her hattın dışbükey çokgen tuğlayı temsil ettiği ve kenarladığı bir Circle-Line çarpışma algılama testi kullanıyorum.

Çoğu zaman Circle-Line testi düzgün çalışır ve çarpışma noktaları doğru şekilde çözülür.

Çarpışma algılama düzgün çalışıyor.

Ancak, top gerçekten tuğla ile kesiştiğinde, çarpışma tespit kodum bazen negatif bir ayrımcıdan dolayı yanlış döner.

Çarpışma algılama başarısız.

Bu yöntemdeki verimsizliğin farkındayım ve test edilen tuğla sayısını azaltmak için eksene hizalanmış sınırlayıcı kutuları kullanıyorum. Temel kaygım, aşağıdaki kodumda herhangi bir matematiksel hata olup olmadığıdır.

/* 
 * 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 ve line arasında herhangi bir fark bulamıyorum ...
FxIII

PointOnLine testi, gerçek nokta hesaplanmadan önce basitleştirilebilir ve yapılabilir.
FxIII

sqrt_disc nasıl hesaplanır?
FxIII

Üzgünüm FxIII Vektörlerimi yerelleştirirken biraz kafam karışmış olmalı, vektörlerin birbirlerinden çıkarıldıklarında aynı olacağını fark etmedim. Göndermeden önce kodumu biraz temizliyordum ve sqrt_disc = sqrt(disc);tekrar koymayı unuttum . Aşağıdaki cevabınız için çok teşekkürler bana çok yardımcı oldu.
jazzdawg

Yanıtlar:


20

Çalışan kademeli bir A için B aşağıdaki gibi hesaplanabilir:

P (t) = A + D • t D olan B - A ve 0 ile 1 t çalışır

Şimdi daire başlangıç ​​noktasına ortalanmıştır ( merkezi başlangıç ​​noktasına yerleştirmek için gerekirse A ve B'yi hareket ettirin ) ve r yarıçapına sahiptir .

Bazı t için P'nin aynı r uzunluğuna sahip olduğunu veya eşit olarak P kare uzunluğunun r²'ye eşit olduğunu düşünüyorsanız bir kesişiminiz vardır.

Bir vektörün kare uzunluğu, bir vektörün nokta ürününü kendisi yaparak elde edilir (bu nokta ürün için uygun bir işlem bulursa yeni ve tutarlı bir uzunluk kavramı tanımlayabileceği kadar doğrudur)

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

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

Hangi t için P · P = r² elde ettiğimizi bulmak istiyoruz.

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

ya da ne zaman

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

bu çok ünlü İkinci dereceden denklem

at² + bt + c = 0

ile

a = D · D ; b = 2 A · D ve c = A · A -r²

Belirleyici b² - 4ac'ın pozitif olup olmadığını kontrol etmeliyiz ve bu yüzden bize P (t) kesişim noktalarını veren 2 t değeri buluyoruz .

t, 0 ile 1 arasında olmalıdır , aksi takdirde satırındaki yalan geçen bu çözüm bulunan A ve B ama daha önce olduğu A veya daha sonra B

[DÜZENLE]

Diğer sorular bu yanıta yardımcı olabileceğinden, bazı düzenlemeleri kullanarak bu düzenlemenin nedenini basitleştirmeye karar verdim. başlama koşulu Bu başlangıç ​​koşulu. Şimdi A_B segmentine odaklanın

Segment A'dan B'ye doğru çalışıyor

D , A'yı B'ye hareket ettiren vektördür, bu nedenle t, 0 ile 1 arasındaysa, D · t , D' nin "uygun bir kısmıdır ", bu nedenle A + D · t noktası A_B segmentinde bulunur: kahverengi noktalar, t olduğunda 0 ile 1 arasında ve koyu yeşil ise t> 1 olduğunda.

Şimdi, dairenin merkezini Kökeni'ne taşırsak işleri basitleştirebiliriz. Bu her zaman yapılabilir çünkü geometriyi, açıları, kesişimi, ölçüleri vb. Koruyan basit bir koordinat sistemi değişikliği.

merkeze hareket eden daire

Şimdi , t değiştiğinde P uzunluğunu hesaplamanın basit bir yoluna sahibiz ve bunun için hangi P'nin çemberin sınırlarını aştığını söyleyelim .

Örnekler

Gördüğünüz gibi P " r'den daha uzunken P" r'den küçüktür. Hem vektör uzunluğu hem de r pozitif sayılar olduğundan, korunandan daha büyük veya daha küçük olma sırası ilişkisi, uzunluklar arasındaki ilişkiyi hesaplamamızdır. kare ve yarıçap kare P * 1 ve P * 2 | P | ² değerini r²'ye eşit yapan noktadır

Ön düzenleme bölümünde belirtildiği gibi, t ​​değişkenimiz olduğu ikinci dereceden bir denklem elde etmeye geldik. Bilindiği gibi t'nin çözelti değerleri, t'nin birkaç karmaşık sayı olduğu durumdan değişir - bu, kesişme olmadığı anlamına gelir; t'nin iki eşit çözüm olduğu durumda - bu bir kavşak olduğu anlamına gelir; iki ayrı çözümün olduğu durum- iki kavşak olduğu anlamına gelir.

Diskriminant önceki durumunu ayırt etmek için kullanılır ve bir geçerlilik testi eğer o geçerli bir kavşak ama bizim segmentinde dışında görmek t yapılır - yani çözüm t gerçek olmalı ve 0 ile 1 arasında uygun bir kavşak sonbahar olduğu dikkate alınacak A_B segmentinde


3
Bu, kullanılacak doğru algoritmadır. Nasıl çalıştığına dair gerçekten iyi bir açıklama, Real Time Rendering Third Edition , sayfa 787 ila 791'de bulunabilir. Bir kütüphanede bulabilirseniz, bir göz atmaya değer.
Darcy Rayner

4
Bu cevabın 8'inci oyu ile 2k itibar puanına ulaştım. Bana verdiğin güven için çok minnettarım. Bu hem çabalarımın tanınması hem de mümkün olan en yüksek kalitede cevap üretmek için elimden gelenin en iyisini yapmaya devam etmenin bir teşvikidir. Teşekkür ederim
FxIII

Bekle, bu iki köşe vakası doğru mu? Örneğin, bir daire t0 <= t <= t1 dışındaki çizgi ile tanımlanan düzlemden geçebilir, ancak çizgi segmentinin bitiş noktalarına biraz sonra vurabilir. Çizgi uç noktaları ile daire yolu arasındaki minimum mesafeyi kontrol etmeniz gerekir. Bu mesafe daire yarıçapından daha küçükse, çizgi vuruldu.
Darcy Rayner

@DarcyRayner, her iki nokta da daire alanının içindeyken mi kastediyorsunuz?
FxIII
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.