나는 이것을 벤치마킹 할 시간이 없었지만, 직사각형을 x와 y 범위에서 축 정렬 된 사각형으로 변환하는 변환 행렬을 0에서 1까지 저장하는 것이 좋습니다. 사각형의 한 모서리를 (0,0)으로, 반대쪽 모서리를 (1,1)로 변환합니다.
사각형이 많이 움직이고 충돌이 거의 확인되지 않으면 비용이 많이 들지만, 사각형 업데이트보다 검사가 훨씬 많으면 적어도 두 개의 삼각형에 대한 테스트 방법보다 빠릅니다. 6 개의 내적은 하나의 행렬 곱셈으로 대체되기 때문입니다
그러나 항상이 알고리즘의 속도는 수행 할 검사 종류에 따라 크게 달라집니다. 대부분의 포인트가 간단한 거리 확인 (예 : (point.x-firstCorner.x)> aLargeDistance)을 수행하는 사각형에 가깝지 않은 경우 속도가 크게 증가 할 수 있지만 거의 모든 경우에 속도가 느려질 수 있습니다. 점은 사각형 안에 있습니다.
편집 : 이것은 내 Rectangle 클래스의 모습입니다.
class Rectangle
{
public:
Matrix3x3 _transform;
Rectangle()
{}
void setCorners(Vector2 p_a, Vector2 p_b, Vector2 p_c)
{
// create a matrix from the two edges of the rectangle
Vector2 edgeX = p_b - p_a;
Vector2 edgeY = p_c - p_a;
// and then create the inverse of that matrix because we want to
// transform points from world coordinates into "rectangle coordinates".
float scaling = 1/(edgeX._x*edgeY._y - edgeY._x*edgeX._y);
_transform._columns[0]._x = scaling * edgeY._y;
_transform._columns[0]._y = - scaling * edgeX._y;
_transform._columns[1]._x = - scaling * edgeY._x;
_transform._columns[1]._y = scaling * edgeX._x;
// the third column is the translation, which also has to be transformed into "rectangle space"
_transform._columns[2]._x = -p_a._x * _transform._columns[0]._x - p_a._y * _transform._columns[1]._x;
_transform._columns[2]._y = -p_a._x * _transform._columns[0]._y - p_a._y * _transform._columns[1]._y;
}
bool isInside(Vector2 p_point)
{
Vector2 test = _transform.transform(p_point);
return (test._x>=0)
&& (test._x<=1)
&& (test._y>=0)
&& (test._y<=1);
}
};
이것은 내 벤치 마크의 전체 목록입니다.
#include <cstdlib>
#include <math.h>
#include <iostream>
#include <sys/time.h>
using namespace std;
class Vector2
{
public:
float _x;
float _y;
Vector2()
:_x(0)
,_y(0)
{}
Vector2(float p_x, float p_y)
: _x (p_x)
, _y (p_y)
{}
Vector2 operator-(const Vector2& p_other) const
{
return Vector2(_x-p_other._x, _y-p_other._y);
}
Vector2 operator+(const Vector2& p_other) const
{
return Vector2(_x+p_other._x, _y+p_other._y);
}
Vector2 operator*(float p_factor) const
{
return Vector2(_x*p_factor, _y*p_factor);
}
static float Dot(Vector2 p_a, Vector2 p_b)
{
return (p_a._x*p_b._x + p_a._y*p_b._y);
}
};
bool PointInTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P)
{
// Compute vectors
Vector2 v0 = C - A;
Vector2 v1 = B - A;
Vector2 v2 = P - A;
// Compute dot products
float dot00 = Vector2::Dot(v0, v0);
float dot01 = Vector2::Dot(v0, v1);
float dot02 = Vector2::Dot(v0, v2);
float dot11 = Vector2::Dot(v1, v1);
float dot12 = Vector2::Dot(v1, v2);
// Compute barycentric coordinates
float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// Check if point is in triangle
if(u >= 0 && v >= 0 && (u + v) < 1)
{ return true; } else { return false; }
}
bool PointInRectangle(Vector2 X, Vector2 Y, Vector2 Z, Vector2 W, Vector2 P)
{
if(PointInTriangle(X,Y,Z,P)) return true;
if(PointInTriangle(X,Z,W,P)) return true;
return false;
}
class Matrix3x3
{
public:
Vector2 _columns[3];
Vector2 transform(Vector2 p_in)
{
return _columns[0] * p_in._x + _columns[1] * p_in._y + _columns[2];
}
};
class Rectangle
{
public:
Matrix3x3 _transform;
Rectangle()
{}
void setCorners(Vector2 p_a, Vector2 p_b, Vector2 p_c)
{
// create a matrix from the two edges of the rectangle
Vector2 edgeX = p_b - p_a;
Vector2 edgeY = p_c - p_a;
// and then create the inverse of that matrix because we want to
// transform points from world coordinates into "rectangle coordinates".
float scaling = 1/(edgeX._x*edgeY._y - edgeY._x*edgeX._y);
_transform._columns[0]._x = scaling * edgeY._y;
_transform._columns[0]._y = - scaling * edgeX._y;
_transform._columns[1]._x = - scaling * edgeY._x;
_transform._columns[1]._y = scaling * edgeX._x;
// the third column is the translation, which also has to be transformed into "rectangle space"
_transform._columns[2]._x = -p_a._x * _transform._columns[0]._x - p_a._y * _transform._columns[1]._x;
_transform._columns[2]._y = -p_a._x * _transform._columns[0]._y - p_a._y * _transform._columns[1]._y;
}
bool isInside(Vector2 p_point)
{
Vector2 test = _transform.transform(p_point);
return (test._x>=0)
&& (test._x<=1)
&& (test._y>=0)
&& (test._y<=1);
}
};
void runTest(float& outA, float& outB)
{
Rectangle r;
r.setCorners(Vector2(0,0.5), Vector2(0.5,1), Vector2(0.5,0));
int numTests = 10000;
Vector2 points[numTests];
Vector2 cornerA[numTests];
Vector2 cornerB[numTests];
Vector2 cornerC[numTests];
Vector2 cornerD[numTests];
bool results[numTests];
bool resultsB[numTests];
for (int i=0; i<numTests; ++i)
{
points[i]._x = rand() / ((float)RAND_MAX);
points[i]._y = rand() / ((float)RAND_MAX);
cornerA[i]._x = rand() / ((float)RAND_MAX);
cornerA[i]._y = rand() / ((float)RAND_MAX);
Vector2 edgeA;
edgeA._x = rand() / ((float)RAND_MAX);
edgeA._y = rand() / ((float)RAND_MAX);
Vector2 edgeB;
edgeB._x = rand() / ((float)RAND_MAX);
edgeB._y = rand() / ((float)RAND_MAX);
cornerB[i] = cornerA[i] + edgeA;
cornerC[i] = cornerA[i] + edgeB;
cornerD[i] = cornerA[i] + edgeA + edgeB;
}
struct timeval start, end;
gettimeofday(&start, NULL);
for (int i=0; i<numTests; ++i)
{
r.setCorners(cornerA[i], cornerB[i], cornerC[i]);
results[i] = r.isInside(points[i]);
}
gettimeofday(&end, NULL);
float elapsed = (end.tv_sec - start.tv_sec)*1000;
elapsed += (end.tv_usec - start.tv_usec)*0.001;
outA += elapsed;
gettimeofday(&start, NULL);
for (int i=0; i<numTests; ++i)
{
resultsB[i] = PointInRectangle(cornerA[i], cornerB[i], cornerC[i], cornerD[i], points[i]);
}
gettimeofday(&end, NULL);
elapsed = (end.tv_sec - start.tv_sec)*1000;
elapsed += (end.tv_usec - start.tv_usec)*0.001;
outB += elapsed;
}
/*
*
*/
int main(int argc, char** argv)
{
float a = 0;
float b = 0;
for (int i=0; i<5000; i++)
{
runTest(a, b);
}
std::cout << "Result: " << a << " / " << b << std::endl;
return 0;
}
코드는 확실히 아름답지는 않지만 주요 버그는 즉시 보지 않습니다. 해당 코드를 사용하면 사각형이 각 검사 사이에서 이동하면 솔루션이 약 두 배 빠릅니다. 움직이지 않으면 코드가 5 배 이상 빠릅니다.
코드가 어떻게 사용되는지 알고 있다면 변환과 수표를 두 차원으로 분리하여 속도를 조금 높일 수도 있습니다. 예를 들어 레이싱 게임에서는 많은 장애물이 차 앞이나 뒤에있을 것이기 때문에 운전 방향을 가리키는 좌표를 먼저 확인하는 것이 더 빠를 것입니다.