# How can I draw an arrow at the edge of the screen pointing to an object that is off screen?

I am wishing to do what is described in this topic:

I have attempted a variety of the methods mentioned here.

First I tried to use the method described by Carrus85:

Just take the ratio of the two triangle hypontenuses (doesn't matter which triagle you use for the other, I suggest point 1 and point 2 as the distance you calculate). This will give you the aspect ratio percentage of the triangle in the corner from the larger triangle. Then you simply multiply deltax by that value to get the x-coordinate offset, and deltay by that value to get the y-coordinate offset.

But I could not find a way to calculate how far the object is away from the edge of the screen.

I then tried using ray casting (which I have never done before) suggested by 23yrold3yrold:

Fire a ray from the center of the screen to the offscreen object. Calculate where on the rectangle the ray intersects. There's your coordinates.

I first calculated the hypotenuse of the triangle formed by the difference in x and y positions of the two points. I used this to create a unit vector along that line. I looped through that vector until either the x coordinate or the y coordinate was off the screen. The two current x and y values then form the x and y of the arrow.

Here is the code for my ray casting method (written in C++ and 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);
}


This was relatively successful. Arrows are displayed in the bottom right section of the screen when objects are located above and left of the screen as if the locations of the where the arrows are drawn have been rotated 180 degrees around the center of the screen.

I assumed this was due to the fact that when I was calculating the hypotenuse of the triangle, it would always be positive regardless of whether or not the difference in x or difference in y is negative.

Thinking about it, ray casting does not seem like a good way of solving the problem (due to the fact that it involves using sqrt() and a large for loop).

So you have two coordinates or vectors, one is the center of the screen (C from now on) and the other is your object (P from now on.)

If you know some math, you might know that a line can be expressed as an origin and a direction vector. The origin is your screen-center, while the direction vector can be found subtracting C from P. This equation can also be expressed in parametric form, which is essentially the same:

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


See the (P.? - C.?) bit? That's your direction vector (as I said, subtract C from P). The last C.? bit is the origin of the line.

t is a variable which can vary from 0 to 1, 0 being the origin of the vector (if you operate, x and y woulld become C.x and C.y), 1 being your object coordinate (again, operating, it'd become P.x and P.y, or the "end" of the vector, if you wish) and values in between interpolating between both ends of your segment. You can also assign outer values: below 0 you'll get your vector direction reversed and above 1 you'll "extend" your vector further in the same direction.

Once you get this, it gets pretty easy. Your objective is to find the point of this vector (x and y for a given value of t) where X=WIDTH or Y=HEIGHT, whatever comes first. As you can see, t is your only variable here:

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


Or re-expressing it:

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


This will get the cutting point of the line defined by your vector on your right and top borders. The same goes for the left and bottom borders of your screen, where you need to check against 0 for both cases, not WIDTH and HEIGHT.

Since it will eventually cut the borders, even off-screen, the lowest t value will be your first point of contact. Reversing the operation and applying your found t value on the equations at (0) (the same value for both!) will bring a new (x,y), which will be your cutting coordinates.

There might be some math errors or implementation differences for your problem, but that's the basic idea. I left some parts out too (there are always four cases of cutting, and you need just one) but a bit of thought will get you to a final solution :)

• Thanks. I'll give it a go. EDIT: Just out of curiosity, do you think this method is the one that Carrus85 described (using the ratio of the hypotenuse)? Apr 11, 2012 at 18:12
• @AdamHenderson I'm glad to help :) Remember you can keep your direction vector so you can draw your arrow later. You can normalize it to get your unitary direction vector, subtract it arrow-length times from the cutting vector "et voila", you have an origin and destination for your arrow.
– kaoD
Apr 11, 2012 at 18:15
• @AdamHenderson visually it's the same thing, since your line is the hypotenuse he's talking about. Practically, it's not the same, since his suggestion involves angles (and trigonometry therefore) which I think is overkill for this. This doesn't involve triangles at all (although you can think of your vector as a triangle where the hypotenuse is the vector and both sides are the x and y components.)
– kaoD
Apr 11, 2012 at 18:17
• Thanks again! You solved my next problem of pointing the arrow in the right direction. Apr 11, 2012 at 18:20
• @AdamHenderson yep, bottom if axis is flipped. BTW, I'd suggest posting a link to this question in Allegro's forum for future reference.
– kaoD
Apr 11, 2012 at 19:11