4
\$\begingroup\$

I wanna draw a line between two moving objects

I can't use:

cDrawLine(startPointCG, endPointCG);

because the line must have a length. So I'm trying to measure the angle between those two points and then trace a line from start point towards that angle.

Something like this:

CGPoint endPoint = CGPointMake(startX + cos(direction) * 100, startY + sin(direction) * 100);    

However, I can't manage to get it done properly. How do I calculate the angle direction?

\$\endgroup\$
2
  • \$\begingroup\$ I'v managed to solve, doing this: float offX = startX - endX; float offY = startY - endY; float direction = atan2f(offY, offX); CGPoint p1 = CGPointMake(startX, startY); CGPoint p2 = CGPointMake(startX + cos(direction) * 100 * -1, startY + sin(direction) * 100 * -1); However, i have no idea how it is working. Can someone explain me please? Thank you! \$\endgroup\$
    – gmemario
    Nov 4, 2011 at 13:00
  • \$\begingroup\$ I answered the question directly, explaining how to calculate the angle. However as others are noting, are you sure you actually need the angle? You say you need to know the line's length; if you look up some simple vector math, you don't need to know the angle in order to calculate the length. \$\endgroup\$
    – jhocking
    Nov 4, 2011 at 14:23

4 Answers 4

3
\$\begingroup\$

Your solution is:

float offX = startX - endX;
float offY = startY - endY;
float direction = atan2f(offY, offX);
CGPoint p1 = CGPointMake(startX, startY);
CGPoint p2 = CGPointMake(startX + cos(direction) * 100 * -1, startY + sin(direction) * 100 * -1); 

The reason for the -1 is because you calculate the angle from end point to the start point. If startX was 5 and endX was 10, the offX is -5. Which is counter-intuitive. Swapping start and end in the calculation of off would remove the need for the -1 in the calculation of p2.

But then, why do you need the angle. Knowing a bit of vector maths would be really useful:

float dx = endX - startX;
float dy = endY - startY;
float scale = 100 / sqrt (dx * dx + dy * dy);
CGPoint p1 = CGPointMake (startX, startY);
CGPoint p2 = CGPointMake (startX + dx * scale, startY + dy * scale); 

Even with the sqrt this should be more optimal than the atan2 solution and if you've got it, an inverse square root will be quicker still, like this:

float scale = 100 * inv_sqrt (dx * dx + dy * dy);

This works because the sqrt gives the length of the vector from start to end and dividing the vector start->end by the length normalises the vector (makes it length one unit) and then multiplying by 100 gives a vector of length 100.

\$\endgroup\$
2
\$\begingroup\$

I'm not sure I understand the requirement for a line length, but your implementation (while valid) shouldn't require angles..

I'll replicate your solution from the comments and explain:

Explanation

float offX = startX - endX;    // <--- take note of this for later
float offY = startY - endY;
// atan2 calculates the angle in radians given the height and width of a triangle
// we calculate the delta so that startPoint is effectively at 0, 0
// tl;dr: atan2 converts from cartesian offsets to the angle needed to reach them
float direction = atan2(offY, offX);
CGPoint p1 = CGPointMake(startX, startY);
// here you're converting back from the angle to cartesian coordinates
// cos() calculates the x offset based on direction, sin() does the y
CGPoint p2 = CGPointMake(startX + cos(direction) * 100 * - 1, startY + sin(direction) * 100 * -1);

Cleaning up a little

it's worth noting that you're multiplying by 100 (your scaling factor) but also by -1 which is unnecessary: Ideally offX and offY should be how far you have to travel to get from start to finish, but you've got that backwards, so:

float deltaX = endX - startX;    // the distance from start to end on the horizontal
float deltaY = endY - startY;    // in case it's not immediately apparent, I've switched the start and end
// ... SNIP ...
CGPoint p2 = CGPointMake(startX + cos(direction) * 100, startY + sin(direction) * 100);

Get to the point already

but even that is unnecessary - you're converting one way and then back. If you really want just the length, use Pythagoras' Theorem:

float deltaX = endX - startX;
float deltaY = endY - startY;
float distance = sqrt( deltaX*deltaX + deltaY*deltaY );

But I really must say: I still don't follow why you can't just put endX and endY into the CGPointMake function?

// this should work given your code in the comments, unless that `100` is a scaling factor for a weird coordinate system?
CGPoint p2 = CGPointMake(endX, endY);

ooh and another thing:

// if it _is_ a scaling factor, just normalise the point and then multiply by the desired scale:
float deltaX = endX - startX;
float deltaY = endY - startY;

// take away the minus sign and then return the maximum value
float maximum = max( abs(deltaX), abs(deltaY) );    

deltaX /= maximum;
deltaY /= maximum;

CGPoint p2 = CGPointMake( startX + (deltaX * 100), startY + (deltaY * 100) );

I highly recommend having a google party for trigonometry tutorials, a lot of the straight math texts can be a little dry and unsatisfying, but have try adding "for programmers" in the query :) (alas I'm not at my usual machine so I can't recommend any right now - I shall return with resources).

(Sorry to put this in an answer; I can't reply in comments yet)

\$\endgroup\$
1
\$\begingroup\$

You do not need the angle itself, just the direction, which is given by vector End - Start. For convenience it's good to normalise that vector, ie. divide it by its length. You will then extrapolate or interpolate the position of the second point.

Let L be the length of the segment you wish to draw.

The distance between Start and End is:

D = sqrt((End.X - Start.X)^2 + (End.Y - Start.Y)^2)

If D = 0 you cannot draw the line. Just abort, or draw it in an arbitrary direction.

If D < L you may wish to plot only until the endpoint. If you really want a segment of length L, go to next step.

If D > 0 the desired point is:

Mid.X = Start.X + L / D * (End.X - Start.X);
Mid.Y = Start.Y + L / D * (End.Y - Start.Y);
\$\endgroup\$
1
  • \$\begingroup\$ +1. And for the record, the same works in any number of dimensions, since it relies on position difference and distance, not direction. \$\endgroup\$ Nov 4, 2011 at 14:03
0
\$\begingroup\$

Calculating the angle between two points is done using atan2()

If you think about simple trigonometry, you can use tangent to relate an angle in a right triangle to the opposite and adjacent sides. You can easily determine the lengths of the sides by subtracting the start point from the end point.

Well the reverse of tangent is arc-tangent, so you can calculate the angle that way. However that won't take into account which quadrant the triangle is facing (ie. the angle returned will always be between 0 and 90) so the special "arc-tangent 2" function was created to return angle values in a full 360 circle.

\$\endgroup\$
3
  • \$\begingroup\$ Tip: Skip atan2() and similar stuff, use Complex numbers for angles in 2D, Quaternions for angles in 3D. \$\endgroup\$ Nov 4, 2011 at 14:31
  • \$\begingroup\$ @MartinSojka: Quaternions are a solution in search of a problem. Most applications don’t need them. \$\endgroup\$
    – Jon Purdy
    Nov 4, 2011 at 21:39
  • \$\begingroup\$ Well quaternions are really useful for interpolating rotations (eg. lerp/slerp). However I don't see what relevance they have in this case, or how they replace atan2(). I mean, quaternions are a data type not a function. \$\endgroup\$
    – jhocking
    Nov 4, 2011 at 23:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .