Як я ефективно перевіряю, чи знаходиться точка всередині обертового прямокутника?


11

Частина заради оптимізації, частина для цілей навчання, я наважусь запитати: Як я можу найбільш ефективно перевірити, чи є 2D точка Pвсередині 2D обертового прямокутника XYZW, використовуючи C # або C ++?

Наразі те, що я роблю, - це використовувати алгоритм «точка в трикутнику», знайдений у книзі Виявлення зіткнень у реальному часі , та запускати його двічі (для двох трикутників, що складають прямокутник, скажімо, XYZ та XZW):

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;
}

Однак у мене є відчуття, що може бути чистіший і швидший спосіб. Зокрема, для зменшення кількості математичних операцій.


У вас багато точок, або у вас багато прямокутників? Це перше питання, яке ви повинні задати собі, перш ніж спробувати оптимізувати таке невелике завдання.
sam hocevar

Гарна думка. У мене буде дуже велика кількість балів, але ще більше прямокутників для перевірки.
Луї15

Супутнє питання про знаходження відстані точки до обертового прямокутника . Це вироджений випадок цього (перевірка лише тоді, коли відстань дорівнює 0). Звичайно, тут будуть застосовуватися оптимізації, які там відсутні.
Анко

Чи розглядали ви обертання точки в рамці відліку прямокутника?
Річард Тінгл

@RichardTingle Насправді я не був на початку. Пізніше я це зробив, тому що я думаю, що це стосується однієї з відповідей, поданих нижче. Але лише для уточнення: у тому, що ви пропонуєте, після обертання точки до опорного кадру прямокутників, тоді слід перевірити на включення лише логічними порівняннями між max.x, min.x тощо?
Луї15,

Відповіді:


2

Легкою і простою оптимізацією було б змінити остаточну умову в PointInTriangle:

bool PointInRectangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P) {
  ...
  if(u >= 0 && v >= 0 && u <= 1 && v <= 1)
      { return true; } else { return false; }
  }
}

коду вже було багато PointInRectangle, умова (u + v) < 1була там, щоб перевірити, чи немає у "другому" трикутнику прямокутника.

Крім того, ви також можете зробити isLeft(перший приклад коду на сторінці, також чудово пояснений) тест чотири рази і перевірити, чи всі вони повертають результати з однаковим знаком (який залежить від того, чи були бали задані за годинниковою або проти годинникової стрілки) для справа бути всередині. Це працює і для будь-якого іншого опуклого багатокутника.

float isLeft( Point P0, Point P1, Point P2 )
{
    return ( (P1.x - P0.x) * (P2.y - P0.y) - (P2.x - P0.x) * (P1.y - P0.y) );
}
bool PointInRectangle(Vector2 X, Vector2 Y, Vector2 Z, Vector2 W, Vector2 P)
{
    return (isLeft(X, Y, P) > 0 && isLeft(Y, Z, P) > 0 && isLeft(Z, W, P) > 0 && isLeft(W, X, p) > 0);
}

Прекрасно. Я не знаю, чи більше мені подобається ваша пропозиція, яка справді швидша і набагато елегантніша, ніж моя, або якщо мені подобається більше, що ви помітили, що мій код PointInTri легко може стати PointInRec! Спасибі
Луї15

+1 для isLeftметоду. Для цього не потрібні триггерні функції (як Vector2.Dotце робиться), що значно прискорює роботу.
Анко

Btw, не вдалося змінити код (не тестував; не маю, як у цьому комп’ютері), включивши isLeft безпосередньо в основну функцію та замінивши операторів "&&" на "||" через зворотну логіку? public static bool PointInRectangle(Vector2 P, Vector2 X, Vector2 Y, Vector2 Z, Vector2 W) { return !(( (Y.x - X.x) * (P.y - X.y) - (P.x - X.x) * (Y.y - X.y) ) < 0 || ( (Z.x - Y.x) * (P.y - Y.y) - (P.x - Y.x) * (Z.y - Y.y) ) < 0 || ( (W.x - Z.x) * (P.y - Z.y) - (P.x - Z.x) * (W.y - Z.y) ) < 0 || ( (X.x - W.x) * (P.y - W.y) - (P.x - W.x) * (X.y - W.y) ) < 0 ); }
Луї15,

1
@ Louis15 Я не думаю, що вам потрібно - і &&, і || перестане виконувати подальші твердження, якщо знайдено одне негативне / позитивне (чи була інша причина?). Визначивши isLeftкомпілятор як вбудований, він зробить щось подібне для вас (і, мабуть, краще, ніж ви могли б, тому що інженери, які пишуть компілятор, найкраще знали процесори, вибираючи, який варіант є найшвидшим), зробивши ваш код більш читабельним з таким же чи кращим ефектом.
wondra

8

Редагувати: коментар ОП скептично ставився до ефективності запропонованої негативної кругової перевірки для вдосконалення алгоритму, щоб перевірити, чи лежить довільна точка 2D у межах обертового та / або рухомого прямокутника. Трохи обертаючись моїм 2D ігровим механізмом (OpenGL / C ++), я доповнюю свою відповідь, надаючи орієнтир продуктивності мого алгоритму проти поточних алгоритмів (і варіацій перевірки ОП) в поточному прямокутнику.

Спочатку я запропонував залишити алгоритм на місці (як це майже оптимально), але спростити за допомогою простої логічної гри: (1) за допомогою попередньо обробленого кола навколо початкового прямокутника; (2) зробити перевірку відстані, і якщо точка лежить у межах даного кола; (3) використовувати ОП або інший прямий алгоритм (я рекомендую алгоритм isLeft, як це передбачено в іншій відповіді). Логіка моєї пропозиції полягає в тому, що перевірка того, чи знаходиться точка в колі, є значно ефективнішою, ніж гранична перевірка повернутого прямокутника чи будь-якого іншого багатокутника.

Мій початковий сценарій тесту на тест - запустити велику кількість точок, що з’являються і зникають (положення яких змінюється в кожному циклі гри) у обмеженому просторі, який заповниться приблизно 20 квадратами, що обертаються / рухаються. Для ілюстрації я опублікував відео ( посилання на youtube ). Зверніть увагу на параметри: кількість випадково з’являються крапок, число або прямокутники. Я буду орієнтуватись на такі параметри:

OFF : Прямий алгоритм, передбачений ОП, без негативної перевірки меж кола

ON : використання перероблених (граничних) кіл навколо прямокутників у якості першої перевірки виключення

ON + стек : створення меж кола під час виконання циклу в циклі стека

ON + Квадратне відстань : Використання квадратних відстаней як додаткової оптимізації, щоб уникнути використання більш дорогого алгоритму квадратного кореня (Pieter Geerkens).

Ось підсумок різних виконання різних алгоритмів, показуючи час, необхідний для перегляду циклу.

введіть тут опис зображення

Вісь x показує підвищену складність, додаючи більше крапок (і тим самим сповільнюючи цикл). (Наприклад, при 1000 перевірки точок, що з'являються випадковим чином, у конфіденційному просторі з 20 прямокутниками, цикл повторюється і викликає алгоритм 20000 разів.) Вісь y показує час, який потрібно (мс), щоб пройти весь цикл, використовуючи високу роздільну здатність таймер продуктивності. Більше 20 мс було б проблематично для гідної гри, оскільки вона не скористалася б високою швидкістю кадрів в секунду, щоб інтерполювати гладку анімацію, і гра може виглядати таким чином "міцною" часом.

Результат 1 : Попередньо оброблений круговий зв'язаний алгоритм з швидкою негативною перевіркою в циклі покращує продуктивність на 1900% порівняно зі звичайним алгоритмом (5% від початкового часу циклу без перевірки). Результат відповідає приблизно пропорційному кількості ітерацій в циклі, тому не має значення, перевіряємо 10 або 10000 випадково з'являються точок. Таким чином, на цій ілюстрації можна безпечно збільшити кількість об'єктів до 10 к, не відчуваючи втрати продуктивності.

Результат 2 : Попереднім коментарем було запропоновано сказати, що алгоритм може бути швидшим, але об'єм пам'яті. Однак зауважте, що зберігання поплавця для попередньо обробленого розміру кола займає лише 4 байти. Це не повинно створювати реальних проблем, якщо ОП не планує одночасно запускати 100000+ об'єктів. Альтернативний і ефективний підхід в пам’яті полягає в тому, щоб обчислити максимальний розмір кола на стеку в циклі і випустити його за межі кожної ітерації і, таким чином, практично не використовувати пам'ять за деяку невідому ціну швидкості. Дійсно, результат показує, що цей підхід дійсно повільніше, ніж використання розміру попередньо обробленого кола, але він все ще показує значне поліпшення продуктивності приблизно на 1150% (тобто 8% від початкового часу обробки).

Результат 3 : Я додатково вдосконалюю алгоритм результату 1, використовуючи відстані в квадраті замість фактичних відстаней і, таким чином, приймаючи обчислювально дорогу операцію квадратного кореня. Це лише плавно підвищує продуктивність (2400%). (Примітка. Я також пробую хеш-таблиці для попередньо оброблених масивів для наближень квадратних коренів з аналогічним, але трохи гіршим результатом)

Результат 4 : я додатково перевіряю переміщення / стикання прямокутників навколо; однак це не змінює основних результатів (як очікувалося), оскільки логічна перевірка залишається по суті однаковою.

Результат 5 : Я змінюю кількість прямокутників і виявляю, що алгоритм стає ще більш ефективним, тим менш зайнятим є простір (не показаний у демонстрації). Результат також дещо очікуваний, оскільки зменшується ймовірність появи точки в межах невеликого простору між колом та межами об'єкта. З іншого боку, я намагаюся збільшити кількість прямокутників теж на 100 в межах одного обмеженого крихітного простору І динамічно змінювати їх за розмірами під час виконання в межах циклу (sin (ітератор)). Це як і раніше надзвичайно добре із збільшенням продуктивності на 570% (або на 15% від початкового часу циклу).

Результат 6 : Я тестую альтернативні алгоритми, запропоновані тут, і виявляю дуже незначну, але не суттєву різницю в продуктивності (2%). Цікавий і простіший алгоритм IsLeft працює дуже добре з підвищенням продуктивності на 17% (85% від початкового часу розрахунку), але ніде не досягає ефективності алгоритму швидкої негативної перевірки.

Моя суть - спершу розглянути мізерний дизайн та логіку гри, особливо коли йдеться про межі та події зіткнення. Поточний алгоритм ОП вже досить ефективний, і подальша оптимізація не є настільки важливою, як оптимізація самої основної концепції. Більше того, добре повідомити обсяг і мету гри, оскільки ефективність алгоритму критично залежить від них.

Я пропоную завжди намагатися орієнтувати будь-який складний алгоритм на етапі проектування гри, оскільки просто перегляд простого коду може не виявити правду про фактичну продуктивність роботи. Запропонований алгоритм може не бути тут навіть необхідним, якщо, наприклад, потрібно просто перевірити, чи знаходиться курсор миші у прямокутнику чи ні, або коли більшість об'єктів вже торкаються. Якщо більшість балів перевіряються в прямокутнику, алгоритм буде менш ефективним. (Однак тоді можна було б встановити межу "внутрішнього кола" як вторинну негативну перевірку.) Перевірка меж кола / сфери є дуже корисною для будь-якого пристойного виявлення зіткнення великої кількості об'єктів, які мають природний простір між ними .

Rec Points  Iter    OFF     ON     ON_Stack     ON_SqrDist  Ileft Algorithm (Wondra)
            (ms)    (ms)    (ms)    (ms)        (ms)        (ms)
20  10      200     0.29    0.02    0.04        0.02        0.17
20  100     2000    2.23    0.10    0.20        0.09        1.69
20  1000    20000   24.48   1.25    1.99        1.05        16.95
20  10000   200000  243.85  12.54   19.61       10.85       160.58

Хоча мені подобався незвичний підхід і любив посилання на Да Вінчі, я не думаю, що спілкування з колами, не кажучи вже про радіус, було б таким ефективним. Крім того, це рішення розумне лише в тому випадку, якщо всі прямокутники зафіксовані та відомі заздалегідь
Louis15

Положення прямокутника не потрібно фіксувати. Використовуйте відносні координати. Подумайте також про це так. Цей радіус залишається колишнім, незалежно від обертання.
Майте

Це чудова відповідь; ще краще, бо я не думав про це. Ви можете зауважити, що достатньо використовувати відстані у квадраті замість фактичних відстаней, економлячи необхідність колись обчислювати квадратний корінь.
Пітер Геркенс

Цікавий алгоритм швидкого позитивного / негативного тестування! Проблемою може бути додаткова пам'ять для збереження попередньо оброблених обмежувальних кіл (і ширини), це може бути добре евристичним, але також зауважте, що це обмежене використання - переважно для випадків, коли пам'ять не має великого значення (прямокутники статичного розміру на більших об'єктах = спрайти ігрових об'єктів) і встигнути попередньо обробити.
wondra

Відредаговано + додано тестовий показник.
Майте

2

Визначення прямокутника з 4 точками дає можливість зробити трапецію. Якщо ви визначите це за допомогою x, y, ширини, висоти та обертання навколо його середини, ви можете просто повернути точку, яку ви перевіряєте, шляхом обертання прямокутника (навколо того самого початку), а потім перевірити, чи це в оригінальному прямокутнику.


Хм, спасибі за пропозицію, але обертання та отримання зворотного обертання не здається таким ефективним. Насправді це буде навряд чи настільки ефективно, як моє рішення - не кажучи вже про Wondra's
Louis15

Ви можете зауважити, що обертання 3D-точки з матрицею - це 6 множень і 3 доповнення та один виклик функції. @ вирішення wondra в кращому випадку еквівалентне, але набагато менш чітке за наміром; і більш чутливі до помилок у технічному обслуговуванні через порушення DRY
Pieter Geerkens

@ Pieter Geerkens, що перетинається твердженням, як будь-яке з моїх рішень порушує DRY (і DRY є одним із ключових принципів програмування? Ніколи про це не чув досі)? І найголовніше, які помилки мають ці рішення? Завжди готовий вчитися.
wondra

@wondra: DRY = Не повторюй себе. Ваш фрагмент коду пропонує кодування деталей матриці шляхом множення вектора скрізь, коли функціональність відображається в коді замість виклику стандартного методу матриця-додаток-вектор.
Пітер Геркенс

@PieterGeerkens, звичайно, пропонують лише її частину - 1) у вас немає явної матриці (виділення нової матриці для кожного запиту буде важко вражати продуктивність) 2) я використовую лише конкретний випадок множення, оптимізований для цього випадку, скидаючи потік загальних один. Це низький рівень роботи, і він повинен залишатися капсульованим, щоб запобігти несподіваній поведінці.
wondra

1

У мене не було часу на тестування цього еталону, але моя пропозиція полягала б у тому, щоб зберігати матрицю перетворення, яка перетворює прямокутник у квадрат, вирівняний по осі, у діапазоні x- та y від 0 до 1. Іншими словами, зберігайте матрицю, яка перетворює один кут прямокутника на (0,0), а протилежний - на (1,1).

Це, звичайно, буде дорожче, якщо прямокутник сильно переміщується і зіткнення перевіряється досить рідко, але якщо на перевірку прямокутника набагато більше перевірок, ніж оновлення, це, принаймні, буде швидше, ніж оригінальний підхід тестування на два трикутники, оскільки шість точкових добутків буде замінено одним матричним множенням.

Але, як завжди, швидкість цього алгоритму багато в чому залежить від того, який тип перевірок ви очікуєте виконати. Якщо більшість точок навіть не наближається до прямокутника, виконуючи просту перевірку відстані (наприклад (point.x - firstCorner.x)> aLargeDistance), це може призвести до значного прискорення, в той час як це може навіть уповільнити ситуацію, якщо майже всі точки знаходяться всередині прямокутника.

EDIT: Ось як виглядав би мій клас Прямокутник:

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;
}

Код, звичайно, не прекрасний, але я не бачу відразу жодних великих помилок. За допомогою цього коду я отримую результати, які вказують на те, що моє рішення приблизно вдвічі швидше, якщо прямокутник переміщується між кожною перевіркою. Якщо він не рухається, то мій код здається більш ніж у п’ять разів швидшим.

Якщо ви знаєте, як буде використовуватися код, ви можете навіть трохи прискорити його, розділивши перетворення та перевірки на два виміри. Наприклад, у гоночній грі, мабуть, було б швидше перевірити координату спочатку, яка вказує у напрямку водіння, тому що багато перешкод буде перед або позаду машини, але навряд чи будь-яка справа і зліва від неї.


Цікаво, але не забувайте, що вам також потрібно застосувати обертання матриць на точки. Я маю операцію гниття матриці в моєму генпідроботі і можу потім порівняти ваш алгоритм. Що стосується вашого останнього коментаря. Тоді ви можете також визначити "внутрішнє коло" і зробити подвійну негативну перевірку, чи крапка лежить поза внутрішнім колом і всередині зовнішнього кола, як описано вище.
Майте

Так, це допоможе, якщо ви очікуєте, що більшість точок буде близько до середини трикутника. Я уявляв таку ситуацію, як прямокутна гоночна доріжка, де ви, наприклад, визначаєте прямокутний шлях, використовуючи зовнішній прямокутник, якому персонаж повинен залишатися всередині, і менший внутрішній прямокутник, у якому він повинен залишатися поза. У такому випадку кожна перевірка була б близькою до межі прямокутника, і ці перевірки кола, ймовірно, лише погіршать продуктивність. Звичайно, це сконструйований приклад, але я б сказав, що це щось насправді може статися.
Ларс Кокемохр

Такі речі можуть статися, так. Цікаво, яке солодке місце повернути проти алгоритму. Зрештою, це зводиться до вашої мети. Якщо у вас є час, чи можете ви опублікувати свій код, використовуючи пост ОП, і я можу порівняти ваш алгоритм? Давайте подивимось, чи правильна ваша інтуїція. Мені цікаво виконання вашої ідеї проти алгоритму IsLeft.
Майте
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.