Calculating x and y positions with distance and angle info

How can I get the x and y positions (for starting a transition.to) by having the startpoint coordinates and the angle and the distance to another point on screen? What is the best solution to do this with high performance (needed for a lot of objects)?

Any help welcome!

Ok, so here is the high performance solution utilizing predefined values and localized functions:

local math\_sin = math.sin local math\_cos = math.cos local degToRadFactor = math.pi/180 local function angle2Vec(startX, startY, angle, distance) angle = angle\*degToRadFactor return startX + math\_cos(angle)\*distance, startY + math\_sin(angle)\*distance end --testing values local x, y = 100, 300 local angle = 45 --in degrees locla distance = 300 local newX, newY = angle2Vec(x, y, angle, distance)

This is a very specified use. If the angle is the same for all objects, you may optimise further.

You are the MAN! Thank you!!! That’s really great :slight_smile:

I noticed a little problem with the newX and newY values. When trying to move to this points the objects move to the mirrored side of the range circle… means they first move to the start values startX, startY for example, then continue to move to the mirrored side seen from point newX,newY with startX and startY in the center.

What can I change in the code above to move the objects correctly?

I’m not sure if I understand you right.

If you want to move in the reverse direction just multiply the values -1 or add 180 to the angle.

Otherwise please explain the problem in more detail.

@c.noeth,

If your angles are whole numbers (not floating point),  you can get a huge speed up by caching the results of that calculation

(modification of torbenratzlaff’s answer)

local math\_sin = math.sin local math\_cos = math.cos local degToRadFactor = math.pi/180 local sinCache = {} -- ONLY USE IF ANGLES ARE WHOLE NUMBERS (1,2,3,4) local cosCache = {} -- NOT for floating point (1.2, 1.223, etc) local sv, cv local function angle2Vec(startX, startY, angle, distance) -- You should really clean the angles before calling angle2Vec, but -- This ensures you only calculate on values [0, 360 ) while(angle\<0) do angle = angle + 360 end while(angle\>=360) do angle = angle - 360 end angle = angle\*degToRadFactor sv = sinCache[angle] or math\_sin(angle) cv = cosCache[angle] or math\_cos(angle) sinCache[angle] = sv -- repetitive, but cheap and straightfoward code cosCache[angle] = cv -- repetitive, but cheap and straightfoward code return startX + cv\*distance, startY + sv\*distance end --testing values local x, y = 100, 300 local angle = 45 --in degrees locla distance = 300 local newX, newY = angle2Vec(x, y, angle, distance)

There was a debate in the LUA forums some time ago, if using a table call for the angles is faster than a simple math.sin and math.cos call. And they came to the conclusion, that there is no performance benifite in using a table. Furthermore it even restricts you to whole numbers. This all comes down the fact, that math.sin and math.cos are direct low level calls and are super efficent (compared to other LUA stuff) and indexing tables takes performance as well (not to mention all the if checks you have to do.

Nuts!  Sound reasoning though, and I’m ashamed to say I didn’t actually measure this time…  :blink:

I’m probably doing something stupid, but I seem to be getting a pretty substantial improvement using a table:

local numRuns = 100000 local sin = math.sin local sinCache = {} for i = 1, 360 do sinCache[i] = math.sin(i) end local startTime = system.getTimer() for i = 1, numRuns do for j = 1, 360 do local x = math.sin(j) end end print("unlocalised math.sin required a time of:", system.getTimer() - startTime) startTime = system.getTimer() for i = 1, numRuns do for j = 1, 360 do local x = sin(j) end end print("localised math.sin required a time of:", system.getTimer() - startTime) startTime = system.getTimer() for i = 1, numRuns do for j = 1, 360 do local x = sinCache[i] end end print("table call for sin required a time of:", system.getTimer() - startTime)

unlocalised math.sin required a time of:        7899
localised math.sin required a time of:  5620
table call for sin required a time of:  3574

Edit: Forgot degToRadFactor, but it makes little difference, and actually slows the first two a tad.

        Also, I’ve tried a solution that would allow more precision using the table method, but it uses math.floor which slows it down  to over 8700 ms.

Hey hasty,

what you did is mostly correct, but you’ve missed a point here.

Your example works, because you are using whole numbers and the numbers are from 1 to 360 only. If that’s the case in your game, than it’s great and you can go with your solution. Otherwise you would need to take in account the steps roaming gamer already incorporated into his solution. First you would have to use math.floor (or round, ceil) to convert to whole numbers, and second you need to use modulo (%) so the angles do not exeed the range 0 to 359 (not 1 to 360).

Making these changes shrinks the performance benifite, so it becomes questionable if the small (if existend) performance increase is worth the flexibility loss. (e.g. someone suggested this performance improvement for spineand it worked, but looked horrible on small animations)

Some additional thoughts.

If all your angles are equally spaced, you can actually get away with a single (cosine, sine) pair:

local cosa, sina = 1, 0 -- can be R, 0 for a fixed radius; otherwise, you'll scale these -- intermediate results -- Angle increment of 360 / N local cosda, sinda = math.cos(2 \* math.pi / N), math.sin(2 \* math.pi / N) for i = 1, N do -- do something with (cosa, sina) -- Rotate by angle delta. cosa, sina = cosa \* cosda - sina \* sinda, sina \* cosda + cosa \* sinda end

Another possibility is that your angles come from something like a math.atan2(). In that case, you can probably take a more direct approach, assuming nothing else needs the result.

The angles could all be randomly generated, of course. Then there really won’t be any structure to exploit. That said, you might be able to stagger your transitions by only launching a few (say every second or third one) per frame, some more on the next, etc. Naturally, you would only bother with the computations for those you actually fired. If it matters, you can interpolate the stragglers’ positions and adjust their transition times to keep everything approximately in line.

Ok, so here is the high performance solution utilizing predefined values and localized functions:

local math\_sin = math.sin local math\_cos = math.cos local degToRadFactor = math.pi/180 local function angle2Vec(startX, startY, angle, distance) angle = angle\*degToRadFactor return startX + math\_cos(angle)\*distance, startY + math\_sin(angle)\*distance end --testing values local x, y = 100, 300 local angle = 45 --in degrees locla distance = 300 local newX, newY = angle2Vec(x, y, angle, distance)

This is a very specified use. If the angle is the same for all objects, you may optimise further.

You are the MAN! Thank you!!! That’s really great :slight_smile:

I noticed a little problem with the newX and newY values. When trying to move to this points the objects move to the mirrored side of the range circle… means they first move to the start values startX, startY for example, then continue to move to the mirrored side seen from point newX,newY with startX and startY in the center.

What can I change in the code above to move the objects correctly?

I’m not sure if I understand you right.

If you want to move in the reverse direction just multiply the values -1 or add 180 to the angle.

Otherwise please explain the problem in more detail.

@c.noeth,

If your angles are whole numbers (not floating point),  you can get a huge speed up by caching the results of that calculation

(modification of torbenratzlaff’s answer)

local math\_sin = math.sin local math\_cos = math.cos local degToRadFactor = math.pi/180 local sinCache = {} -- ONLY USE IF ANGLES ARE WHOLE NUMBERS (1,2,3,4) local cosCache = {} -- NOT for floating point (1.2, 1.223, etc) local sv, cv local function angle2Vec(startX, startY, angle, distance) -- You should really clean the angles before calling angle2Vec, but -- This ensures you only calculate on values [0, 360 ) while(angle\<0) do angle = angle + 360 end while(angle\>=360) do angle = angle - 360 end angle = angle\*degToRadFactor sv = sinCache[angle] or math\_sin(angle) cv = cosCache[angle] or math\_cos(angle) sinCache[angle] = sv -- repetitive, but cheap and straightfoward code cosCache[angle] = cv -- repetitive, but cheap and straightfoward code return startX + cv\*distance, startY + sv\*distance end --testing values local x, y = 100, 300 local angle = 45 --in degrees locla distance = 300 local newX, newY = angle2Vec(x, y, angle, distance)

There was a debate in the LUA forums some time ago, if using a table call for the angles is faster than a simple math.sin and math.cos call. And they came to the conclusion, that there is no performance benifite in using a table. Furthermore it even restricts you to whole numbers. This all comes down the fact, that math.sin and math.cos are direct low level calls and are super efficent (compared to other LUA stuff) and indexing tables takes performance as well (not to mention all the if checks you have to do.

Nuts!  Sound reasoning though, and I’m ashamed to say I didn’t actually measure this time…  :blink:

I’m probably doing something stupid, but I seem to be getting a pretty substantial improvement using a table:

local numRuns = 100000 local sin = math.sin local sinCache = {} for i = 1, 360 do sinCache[i] = math.sin(i) end local startTime = system.getTimer() for i = 1, numRuns do for j = 1, 360 do local x = math.sin(j) end end print("unlocalised math.sin required a time of:", system.getTimer() - startTime) startTime = system.getTimer() for i = 1, numRuns do for j = 1, 360 do local x = sin(j) end end print("localised math.sin required a time of:", system.getTimer() - startTime) startTime = system.getTimer() for i = 1, numRuns do for j = 1, 360 do local x = sinCache[i] end end print("table call for sin required a time of:", system.getTimer() - startTime)

unlocalised math.sin required a time of:        7899
localised math.sin required a time of:  5620
table call for sin required a time of:  3574

Edit: Forgot degToRadFactor, but it makes little difference, and actually slows the first two a tad.

        Also, I’ve tried a solution that would allow more precision using the table method, but it uses math.floor which slows it down  to over 8700 ms.

Hey hasty,

what you did is mostly correct, but you’ve missed a point here.

Your example works, because you are using whole numbers and the numbers are from 1 to 360 only. If that’s the case in your game, than it’s great and you can go with your solution. Otherwise you would need to take in account the steps roaming gamer already incorporated into his solution. First you would have to use math.floor (or round, ceil) to convert to whole numbers, and second you need to use modulo (%) so the angles do not exeed the range 0 to 359 (not 1 to 360).

Making these changes shrinks the performance benifite, so it becomes questionable if the small (if existend) performance increase is worth the flexibility loss. (e.g. someone suggested this performance improvement for spineand it worked, but looked horrible on small animations)