5

Problem jest Polygon::FindAxisLeastPenetration:Oddzielenie twierdzenie osi Obrót wokół środka ciężkości

double Polygon::FindAxisLeastPenetration(unsigned int *faceIndex, const Polygon &polygonA, const Polygon &polygonB) const { 
    double bestDistance = -std::numeric_limits<double>::infinity(); 
    unsigned int bestIndex; 

    for (unsigned int i = 0; i < polygonA.points.size(); i++) { 
    Vector2D n = polygonA.normals[i]; 
    Vector2D nw = polygonA.rotationMatrix * n; //ROTATION 
    Matrix22 buT = polygonB.rotationMatrix.Transposed(); 
    n = buT * nw; //ROTATION 

    Vector2D support = polygonB.points[polygonB.GetSupport(-n)]; 

    Vector2D vertex = polygonA.points[i]; 
    vertex = polygonA.rotationMatrix * vertex; //ROTATION 
    vertex.Add(polygonA.body->GetPosition()); 
    vertex.Subtract(polygonB.body->GetPosition()); 
    vertex = buT * vertex; // ROTATION 
    double distance = n.DotProduct(support - vertex); 
    if (distance > bestDistance) { 
     bestDistance = distance; 
     bestIndex = i; 
    } 
    } 
    *faceIndex = bestIndex; 

    return bestDistance; 
} 

unsigned int Polygon::GetSupport(const Vector2D &dir) const { 
    double bestProjection = -std::numeric_limits<double>::infinity(); 
    unsigned int bestIndex = 0; 

    for (unsigned int i = 0; i < points.size(); i++) { 
    Vector2D vertex = points[i]; 
    double projection = vertex.DotProduct(dir); 

    if (projection > bestProjection) { 
     bestProjection = projection; 
     bestIndex = i; 
    } 
    } 

    return bestIndex; 
} 

Manifold Polygon::CheckCollision(const Polygon &polygonA, const Polygon &polygonB) const { 
    Manifold result; 
    result.objectA = polygonA.body; 
    result.objectB = polygonB.body; 
    unsigned int indexA; 
    double penetrationA = Polygon::FindAxisLeastPenetration(&indexA, polygonA, polygonB); 
    if (penetrationA >= 0.0) { 
    result.intersects = false; 
    return result; 
    } 

    unsigned int indexB; 
    double penetrationB = Polygon::FindAxisLeastPenetration(&indexB, polygonB, polygonA); 

    if (penetrationB >= 0.0) { 
    result.intersects = false; 
    return result; 
    } 

    result.intersects = true; 
    //... 
    return result; 

Rectangle::Rectangle(double width, double height) : Polygon() { 
    double hw = width/2.0; 
    double hh = height/2.0; 
    points.push_back(Vector2D(-hw, -hh)); 
    points.push_back(Vector2D(hw, -hh)); 
    points.push_back(Vector2D(hw, hh)); 
    points.push_back(Vector2D(-hw, hh)); 

    // points.push_back(Vector2D(0, 0)); 
    // points.push_back(Vector2D(width, 0)); 
    // points.push_back(Vector2D(width, height)); 
    // points.push_back(Vector2D(0, height)); 

    normals.push_back(Vector2D(0.0, -1.0)); 
    normals.push_back(Vector2D(1.0, 0.0)); 
    normals.push_back(Vector2D(0.0, 1.0)); 
    normals.push_back(Vector2D(-1.0, 0.0)); 

    center.x = 0; 
    center.y = 0; 

}

polygon.rotationMatrix jest obiektem typu Matrix22 która jest macierzą 2x2.
polygon.points jest std::vector<Vector2D> wypełniona wektorami.
polygon.body to wskaźnik do instancji Object. W tym przypadku służy tylko do zdobycia pozycji.
polygon.body->position to instancja Vector2D zawierająca współrzędne X i Y.
Vector2D polygon.body->GetPosition() zwraca wektor pozycji ciała.

Działa dobrze, z tym że obrót odbywa się wokół [0, 0] punktu , ale powinien obracać się wokół środka masy.

wiem, że obrót wokół punktu można zrobić tak:

rotationMatrix * (vertex - point) + point 

i działa dobrze podczas renderowania wielokątów. Ale nie w wykryciu kolizji.

Jak obrócić wektory wokół określonego punktu w tym przypadku?

EDIT: Oto, co mam tak daleko

double Polygon::FindAxisLeastPenetration(unsigned int *faceIndex, const Polygon &polygonA, const Polygon &polygonB) const { 
    double bestDistance = -std::numeric_limits<double>::infinity(); 
    unsigned int bestIndex; 

    for (unsigned int i = 0; i < polygonA.points.size(); i++) { 
    // Calculate normal 
    unsigned int j = i == points.size() ? 0 : i + 1; 
    Vector2D n; 
    // Rotate points 
    Vector2D p1 = polygonA.rotationMatrix * (polygonA.points[i] - polygonA.Center()) + polygonA.Center(); 
    Vector2D p2 = polygonA.rotationMatrix * (polygonA.points[j] - polygonA.Center()) + polygonA.Center(); 
    n.x = p2.y - p1.y; 
    n.y = -(p2.x - p1.x); 
    n.Normalize(); 

    Vector2D support = polygonB.points[polygonB.GetSupport(-n)]; 
    support = polygonB.rotationMatrix * (support - polygonB.Center()) + polygonB.Center(); 
    support.Add(polygonB.body->GetPosition()); 

    Vector2D vertex = polygonA.points[i]; 
    vertex = polygonA.rotationMatrix * (vertex - polygonA.Center()) + polygonA.Center(); //ROTATION 
    vertex.Add(polygonA.body->GetPosition()); 

    double distance = n.DotProduct(support - vertex); 
    if (distance > bestDistance) { 
     bestDistance = distance; 
     bestIndex = i; 
    } 
    } 
    *faceIndex = bestIndex; 

    return bestDistance; 
} 

unsigned int Polygon::GetSupport(const Vector2D &dir) const { 
    double bestProjection = -std::numeric_limits<double>::infinity(); 
    unsigned int bestIndex = 0; 

    for (unsigned int i = 0; i < points.size(); i++) { 
    Vector2D vertex = rotationMatrix * (points[i] - center) + center; 
    double projection = vertex.DotProduct(dir); 

    if (projection > bestProjection) { 
     bestProjection = projection; 
     bestIndex = i; 
    } 
    } 

    return bestIndex; 
} 

Teraz ja nie troszczą się o optymalizacje. Jest taki sam problem. Podczas obracania się wokół środka kolizje nie są wykrywane prawidłowo. Jeśli jednak środek jest [0, 0] lub nie jest używany, to wykrywanie kolizji działa poprawnie, ale znowu obrót jest wykonywany nieprawidłowo.

Edycja: Nawet podczas obracania się przed wykryciem kolizji pojawia się ten sam problem. Do tej pory najlepszym sposobem było przetłumaczenie wielokąta tak, aby jego środek znajdował się pod numerem [0, 0], ale pod pewnymi kątami nie wykryto kolizji. Nie mam pojęcia, co teraz zrobić.

EDIT: Zrzuty ekranu (wielokąty są tłumaczone tak, że ich centra masy są zawsze na [0, 0], wielokąty są prostokąty w tym przypadku) Wykrywanie kolizji nie działa dobrze tutaj Collision detection didn't work well here

Wykrywanie kolizji nie działa dobrze tutaj zbyt Collision detection didn't work well here too

wykrywanie kolizji działa dobrze tutaj Collision detection worked well here

Edycja: dodano klasę Rectangle.

+0

jakich bibliotek używasz? – uitty400

+0

@ uitty400 none. Wszystko jest niestandardowe. Szukasz jakiejś funkcji lub klasy? – ivknv

+0

Musisz wyjaśnić, co robisz. Nie wygląda jak kod przecięcia dwóch wielokątów. Czym jest "punkt" i skąd się bierze? Co to jest 'polygon.GetSupport()'? Co to jest 'polygon.body' i jego' .position'? –

Odpowiedz

1

Powinno to działać niezależnie od tego, czy pochodzenie wielokąta jest wyrównane do środka ciężkości. Zacznę od najważniejszych rzeczy i zakończę metodami, które się zmieniły.

Edytuj: Zmieniona implementacja.

struct Response { 
     Response() 
      : overlap(std::numeric_limits<double>::max()) {} 
     Vector2D axis; 
     double overlap; 
}; 

bool FindAxisLeastPenetration(const Polygon& a, const Polygon& b, 
     Response* response) 
{ 
     for (unsigned long i = 0; i < a.points.size(); i++) 
     { 
      Vector2D axis = a.normals[i]; 
      Vector2D support = b.GetSupport(-axis); 

      double overlap = axis.DotProduct(a.points[i] - support); 
      if (overlap <= 0.0) 
       return false; 
      if (overlap < response->overlap) 
      { 
       response->overlap = overlap; 
       response->axis = axis; 
      } 
     } 
     return true; 
} 

bool CheckCollisionLocal(const Polygon& a, const Polygon& b, 
     Vector2D* min_translation) 
// @note assumes untransformed polygons. 
{ 
     Polygon worldA = a.ToWorld(); 
     Polygon worldB = b.ToWorld(); 

     Response responseA; 
     Response responseB; 

     if (!FindAxisLeastPenetration(worldA, worldB, &responseA)) 
      return false; 
     if (!FindAxisLeastPenetration(worldB, worldA, &responseB)) 
      return false; 

     if (responseA.overlap <= responseB.overlap) 
      *min_translation = responseA.axis * responseA.overlap; 
     else 
      *min_translation = -responseB.axis * responseB.overlap; 
     return true; 
} 

przypadków użycia,

bool HandleCollisionLocal(Polygon& a, Polygon& b) 
{ 
     Vector2D translation; 
     if (!CheckCollisionLocal(a, b, &translation)) 
      return false; 
     if (MOVE_POLYGON_A) 
      a.body.SetPosition(a.body.GetPosition() - translation); 
     else 
      b.body.SetPosition(b.body.GetPosition() + translation); 
     return true; 
} 

Wsparcie,

Polygon Polygon::ToWorld() const 
{ 
     Polygon result = *this; 
     for (auto& point : result.points) 
     { 
      point = result.rotationMatrix * point; 
      point.Add(result.body.GetPosition()); 
     } 
     for (auto& normal : result.normals) 
      normal = result.rotationMatrix * normal; 
     return result; 
} 

Vector2D Polygon::GetSupport(const Vector2D& direction) const 
{ 
     double best_projection = -std::numeric_limits<double>::max(); 
     Vector2D best_point; 

     for (auto point : points) 
     { 
      double projection = point.DotProduct(direction); 
      if (projection > best_projection) 
      { 
       best_projection = projection; 
       best_point = point; 
      } 
     } 
     return best_point; 
} 

Uwaga: Wersja ta odwraca wektora przesunięcia, aby odpowiadać - co wydaje się być - standard.