আমার কাছে এটি বেঞ্চমার্ক করার সময় ছিল না, তবে আমার পরামর্শটি রূপান্তর ম্যাট্রিক্স সংরক্ষণ করবে যা আয়তক্ষেত্রটি অক্ষরেখাযুক্ত স্কোয়ারে x- এবং y- রেঞ্জের মধ্যে 0 থেকে 1 পর্যন্ত সংরক্ষণ করবে। অন্য কথায় ম্যাট্রিক্স সংরক্ষণ করুন যা আয়তক্ষেত্রের একটি কোণকে (0,0) এবং বিপরীতটি একটি (1,1) তে রূপান্তর করে।
আয়তক্ষেত্রটি অনেক বেশি স্থানান্তরিত হয় এবং সংঘাতের পরিবর্তে খুব কমই পরীক্ষা করা হয় তবে এটি অবশ্যই আরও ব্যয়বহুল হবে, তবে যদি আয়তক্ষেত্রের আপডেটের চেয়ে আরও অনেক বেশি চেক থাকে তবে এটি দুটি ত্রিভুজের বিরুদ্ধে পরীক্ষার মূল পদ্ধতির চেয়ে কমপক্ষে দ্রুততর হবে, যেহেতু ছয়টি ডট পণ্য একটি ম্যাট্রিক্সের গুণ দ্বারা প্রতিস্থাপিত হবে।
তবে সর্বদা হিসাবে এই অ্যালগরিদমের গতি আপনি যে ধরণের চেক সম্পাদন করবেন বলে আশা করছেন তার উপর অনেক বেশি নির্ভর করে। বেশিরভাগ পয়েন্ট যদি কোনও সাধারণ দূরত্ব পরীক্ষা করে আয়তক্ষেত্রের খুব কাছাকাছি না হয় (যেমন (পয়েন্ট। এক্স - ফার্স্টকর্নার.এক্স)> এ লার্জ ডিসটেনস) একটি বড় গতিবেগের কারণ হতে পারে, তবে এটি প্রায় সমস্তগুলি যদি ধীরে ধীরে ধীরে ধীরে কমিয়ে দেয় তবে পয়েন্টগুলি আয়তক্ষেত্রের অভ্যন্তরে।
সম্পাদনা: এটি আমার আয়তক্ষেত্র-শ্রেণীর মতো দেখাবে:
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;
}
কোডটি অবশ্যই সুন্দর নয়, তবে আমি অবিলম্বে কোনও বড় বাগ দেখতে পাচ্ছি না। সেই কোডটি দিয়ে আমি ফলাফল পেয়েছি যা ইঙ্গিত করে যে আমার সমাধানটি প্রায় দ্বিগুণ দ্রুত হয় যদি প্রতিটি চেকের মধ্যে আয়তক্ষেত্র স্থানান্তরিত হয়। যদি এটি সরে না যায় তবে আমার কোডটি পাঁচ গুণ বেশি দ্রুত বলে মনে হচ্ছে।
কোডটি কীভাবে ব্যবহার করা হচ্ছে তা আপনি যদি জানেন তবে আপনি রূপান্তর এবং চেকগুলি দুটি মাত্রায় পৃথক করে আরও কিছুটা গতি বাড়িয়ে নিতে পারেন। উদাহরণস্বরূপ, একটি রেসিং গেমের ক্ষেত্রে প্রথমে স্থানাঙ্কটি পরীক্ষা করা দ্রুততর হবে যা ড্রাইভিংয়ের দিক নির্দেশ করে, কারণ অনেকগুলি বাধা গাড়ির সামনে বা পিছনে থাকবে তবে খুব কমই এর ডান বা বাম দিক থাকবে।