Как нарисовать стрелку на краю экрана, указывающую на объект, находящийся за пределами экрана?

12

Я желаю сделать то, что описано в этой теме:

http://www.allegro.cc/forums/print-thread/283220

Я пробовал различные методы, упомянутые здесь.

Сначала я попытался использовать метод, описанный Carrus85:

Просто возьмите соотношение двух гипонтенусов треугольника (не имеет значения, какой треугольник вы используете для другого, я предлагаю точку 1 и точку 2 в качестве расстояния, которое вы рассчитываете). Это даст вам процентное соотношение треугольника в углу от большего треугольника. Затем вы просто умножаете deltax на это значение, чтобы получить смещение по x-координате, и deltay на это значение, чтобы получить смещение по y-координате.

Но я не смог найти способ рассчитать, как далеко объект находится от края экрана.

Затем я попытался использовать лучевое литье (чего я никогда раньше не делал), предложенное 23yrold3yrold:

Запустите луч из центра экрана к объекту за пределами экрана. Вычислите, где на прямоугольнике пересекается луч. Там твои координаты.

Сначала я вычислил гипотенузу треугольника, образованную разницей в положениях x и y двух точек. Я использовал это, чтобы создать единичный вектор вдоль этой линии. Я перебирал этот вектор, пока координата x или y не исчезла с экрана. Затем два текущих значения x и y образуют x и y стрелки.

Вот код моего метода приведения лучей (написанный на C ++ и Allegro 5)

void renderArrows(Object* i)
{
    float x1 = i->getX() + (i->getWidth() / 2);
    float y1 = i->getY() + (i->getHeight() / 2);

    float x2 = screenCentreX;
    float y2 = ScreenCentreY;

    float dx = x2 - x1;
    float dy = y2 - y1;
    float hypotSquared = (dx * dx) + (dy * dy);
    float hypot = sqrt(hypotSquared);

    float unitX = dx / hypot;
    float unitY = dy / hypot;

    float rayX = x2 - view->getViewportX();
    float rayY = y2 - view->getViewportY();
    float arrowX = 0;
    float arrowY = 0;

    bool posFound = false;
    while(posFound == false)
    {
        rayX += unitX;
        rayY += unitY;

        if(rayX <= 0 ||
            rayX >= screenWidth ||
            rayY <= 0 ||
            rayY >= screenHeight)
        {
            arrowX = rayX;
            arrowY = rayY;
            posFound = true;
        }               
    }

    al_draw_bitmap(sprite, arrowX - spriteWidth, arrowY - spriteHeight, 0);
}

Это было относительно успешно. Стрелки отображаются в правой нижней части экрана, когда объекты расположены над и слева от экрана, как если бы точки, в которых нарисованы стрелки, были повернуты на 180 градусов вокруг центра экрана.

Я предположил, что это было связано с тем, что, когда я вычислял гипотенузу треугольника, он всегда был бы положительным независимо от того, отрицательна ли разница в x или разность в y.

Размышляя об этом, приведение лучей не кажется хорошим способом решения проблемы (из-за того, что оно включает в себя использование sqrt () и большого цикла for).

Адам Хендерсон
источник

Ответы:

6

Таким образом, у вас есть две координаты или вектора: одна - центр экрана (с этого момента C), а другая - ваш объект (с этого момента P).

Если вы знаете некоторую математику, вы можете знать, что линия может быть выражена как начало координат и вектор направления. Начало координат - ваш экранный центр, в то время как вектор направления можно найти, вычитая C из P. Это уравнение также может быть выражено в параметрической форме, которая по сути такая же:

x = (P.x - C.x)t + C.x;
y = (P.y - C.y)t + C.y;

Видите (P.? - C.?)немного? Это ваш вектор направления (как я уже сказал, вычтите C из P). Последний C.?бит - это начало строки.

tпеременная, которая может варьироваться от 0до 1, 0являясь источником вектора (если вы оперируете, xи yвы бы стали C.xи C.y), 1будучи координатой вашего объекта (опять же, работая, она стала бы P.xи P.y, или «концом» вектора, если хотите) и значения между интерполяцией между обоими концами вашего сегмента. Вы также можете присваивать внешние значения: ниже 0вы будете изменять направление вектора, а выше 1вы будете «расширять» свой вектор в том же направлении.

Как только вы получаете это, это становится довольно легко. Ваша цель состоит в том, чтобы найти точку этого вектора ( xи yдля данного значения t), где X=WIDTHили Y=HEIGHTчто бы ни было первым. Как видите, tваша единственная переменная здесь:

(0)
WIDTH = (P.x - C.x)t + C.x;
and
HEIGHT = (P.y - C.y)t + C.y;

Или повторно выразив это:

(1)
t = (WIDTH - C.x)/(P.x - C.x)
and
t = (HEIGHT - C.y)/(P.y - C.y)

Это позволит получить точку обрезки линии, определенной вашим вектором, на правой и верхней границах. То же самое касается левой и нижней границ вашего экрана, где вы должны проверить 0в обоих случаях, а не WIDTHи HEIGHT.

Так как это в конечном итоге срезает границы, даже за пределами экрана, самое низкое tзначение будет вашей первой точкой контакта. Отмена операции и применение найденного tзначения к уравнениям в (0)(одно и то же значение для обоих!) Принесет новое значение (x,y), которое будет вашими координатами резки.

Для вашей задачи могут быть математические ошибки или различия в реализации, но это основная идея. Я также оставил некоторые детали (всегда четыре случая резки, и вам нужен только один), но немного мыслей поможет вам найти окончательное решение :)

kaoD
источник
Благодарю. Я попробую. РЕДАКТИРОВАТЬ: просто из любопытства, вы думаете, этот метод является тот, который описал Carrus85 (используя соотношение гипотенузы)?
Адам Хендерсон
1
@AdamHenderson Я рад помочь :) Помните, что вы можете сохранить вектор направления, чтобы позже нарисовать стрелку. Вы можете нормализовать его, чтобы получить свой унитарный вектор направления, вычесть его arrow-lengthиз вектора резки «et voila», у вас есть начало и пункт назначения для вашей стрелки.
kaoD
1
@AdamHenderson визуально это то же самое, так как ваша линия - это гипотенуза, о которой он говорит. Практически, это не то же самое, так как его предложение включает в себя углы (и, следовательно, тригонометрию), что я считаю излишним для этого. Это не связано с треугольниками на всех (хотя вы можете думать о своем векторе в виде треугольника , где гипотенуза вектор и обе стороны являются xи yкомпоненты.)
kaoD
Еще раз спасибо! Вы решили мою следующую проблему, указав стрелку в правильном направлении.
Адам Хендерсон
1
@AdamHenderson да, внизу, если ось перевернута. Кстати, я бы предложил разместить ссылку на этот вопрос на форуме Allegro для дальнейшего использования.
kaoD