Log In  


How can you move something along an outward spiral with consistent speed?

The method I have been using to make stuff move in circles doesn't quiete work with spirals because the change in angle stays the same while the circumference gets bigger.
This causes the object to accellerate as it moves outwards.
Is there any way around this?
I tried changing the amound by which "t" changes each frame to act as a speed correction but I get weird results, because it either goes below zero and changes direction or I run into the fixed point limit because all values are so small. Not that any values I tried to dercrease "spd" by each frame actually fixed the accellaration issue before acting up in the first place.
This gets even worse at 60 fps of course.
I don't feel like I am coming at it from the right angle.
Am I missing some completely different way to make spirals?

out=10
t=0
spd=0.01

function _draw()
	cls()
	out+=.2
	spd-=.0001
	t+=spd

	circfill(sin(t)*out+64,cos(t)*out+64,1,5)
	print(t)
	print(spd)
end
1


2

Hey @Doriencey, I think this does what you're looking for. I'll try to explain the math below if you're interested but if not just skip it.

r0 = 15      -- initial radius
w0 = 0.5     -- initial angular velocity
r = r0       -- actual radius
dr = 0.05    -- amount to change radius per frame
a = 0        -- initial angle

pts = {}     -- for drawing the spiral
p2i = 0      -- for drawing the 2nd point

function _update()
   w = w0*r0/r  -- update the angular velocity
   a += w/30    -- update the angle. w/60 if using 60 fps.
   r += dr      -- update the radius
   -- calculate x and y
   x = r*cos(a) + 64
   y = r*sin(a) + 64

   -- this is used to draw the spiral
   add(pts, {x, y})
end

function _draw()
   cls()
   -- draw the spiral
   for p in all(pts) do
      line(p[1], p[2], 7)
   end
   line()

   -- draw the first point
   circfill(x, y, 2, 8)

   -- wait 5 seconds and then start drawing the 2nd point. the 2nd
   -- point just follows the path calculated by the 1st point.
   if t() > 5 then
      p2i += 1
   end
   if p2i > 0 then
      local pt = pts[p2i]
      circfill(pt[1], pt[2], 2, 8)
   end
end

It helps to start with an easier problem. So take two circles one with radius 1 and the other with radius 2. If we put a point on each one and they both have the same angular velocity then they'll both do one full revolution in the same amount of time. But since the circumference of the second circle is larger, the linear velocity of the second point is faster than the linear velocity of the first.

Just in case it's not clear: angular velocity determines how quickly the angle changes while linear velocity determines how the actual distance traveled changes. Both points have the same angular velocity but the second has a higher linear velocity.

Okay, so how much faster is it traveling? A circle's circumference is given by c = 2πr so the first circle has circumference c₁ = 2π and the second is c₂ = 4π. Or more simply c₂ = 2c₁. So the second point is traveling twice as far in the same amount of time or twice as fast.

The linear and angular velocities are related to each other so if we change one, we'll change the other. So divide the second point's angular velocity by 2 and its linear velocity will also be divided by two and will match the other point.

So we know that we want to change the angular velocity depending on the radius and it gets smaller as the radius gets bigger. We can write that as an equation ω = k/r, where ω is the angular velocity and k is some constant that we don't know the value of yet.

To find k assume that the point starts at some initial radius r₀ with some initial velocity ω₀. Then by the equation above we have ω₀ = k/r₀ and solving for k gives k = r₀ω₀.

So your angular velocity is given by:
ω = r₀ω₀/r

So each time you update r you also have to update ω. Then multiply ω by Δt (the time step) to get the change to the angle. At 30 fps the time step is 1/30 of a second which is why I'm dividing w by 30 to get the change to a. From there you just convert to x- and y-coordinates and plot the point.

You might still get weird behaviour depending on your choices for r0 and w0. And if dr is too high the point just ends up traveling in basically a straight line. So you might have to play with those values to get exactly what you're after.


1

@jasondelaat
Something like this is exactly what I was looking for. Thanks for answering the question. I think I can work with this.
One thing I noticed though, was that even though we change the radius by a constant value each frame, the gaps between sections of the spiral itself get bigger the further out you go. I don't think it will be a problem for what i was working on, but it did get me thinking.
I analyzed it as follows:

In my orininal code each full revolution completes at regular intervals regardless of the distance from the center.
Say one revolution every 30 frames or so.
Consequently, the radius will have increased by the same amount at the end of each revulotion.

With your method the time it takes to complete a revolution increases with distance so the radius will have had more time to increase between each completed revolution.
Am I right to assume the increase in gap size is caused by this?

P.S. Are you from the Netherlands?


1

> Am I right to assume the increase in gap size is caused by this?

Yep, that's correct. Doing one full revolution every 30 frames means that your point has the same angular velocity regardless of radius so it speeds up as it gets farther away. With my code the point has different angular velocity depending on the radius so it has more time to expand during a single rotation.

It should be possible to stabilize that if you want though. Instead of increasing the radius by a fixed amount every frame you could increase by a fixed amount every full rotation and then interpolate between. That would mean your dr would change each revolution as well as r and w but it would keep your spacing consistent. I won't have time to play around with it tonight but I'll try and get something working tomorrow unless you figure it out before then.

> Are you from the Netherlands?

I'm not but that is indeed where my family—my Dad's half anyway—originates. Sadly, I've never been.


2

Okay, apparently I lied. Here you go.

r0 = 5       -- initial radius
w0 = 1.5     -- initial angular velocity
r = r0       -- actual radius
dr = 10      -- amount to change radius per rotation
a = 0        -- initial angle

pts = {}     -- for drawing the spiral
p2i = 0      -- for drawing the 2nd point

function lerp(a, b, t)
   return a + t*(b - a)
end

function _update()
   if a > 1 then
      a -= 1
      r += dr
   end
   local d = lerp(r, r + dr, a)
   w = w0*r0/d
   a += w/30
   x = d*cos(a) + 64
   y = d*sin(a) + 64

   -- this is used to draw the spiral
   add(pts, {x, y})
end

function _draw()
   cls()
   -- draw the spiral
   for p in all(pts) do
      line(p[1], p[2], 7)
   end
   line()

   -- draw the first point
   circfill(x, y, 2, 8)

   -- wait 5 seconds and then start drawing the 2nd point. the 2nd
   -- point just follows the points calculated by the 1st point.
   if t() > 5 then
      p2i += 1
   end
   if p2i > 0 then
      local pt = pts[p2i]
      circfill(pt[1], pt[2], 2, 8)
   end
end

1

@jasondelaat
It took me some time, but it finally clicked how that last one works.
I didn't recognize the math in the lerp function at first, until I realized I sometimes do this at my job without the time component to estimate terrain elevations in surveys between two elevation points.

What part of the previous post was the "Okay, apparently I lied." about?
Some inaccuracy in our assumptions regarding the math or The Netherlands?


1

No, it was the part where I said I wouldn't have time to work on it and then posted the soltion like 30 minutes later. I realized it was actually easier than I thought.


1

@jasondelaat
Ohh. I must have missed that part.
I was sleeping when you posted that second spiral so I didn't catch you referred to the timing.
I don't know what time zone you are in, but over here when I posted about the bigger gaps in the second spiral, it was right around bedtime for me.

Anyways, I learned a lot from this thread.
Thanks a lot for all of the clear explanations.


1

> Ohh. I must have missed that part.

Yeah, it made sense to me when I posted it because it was still fresh in my mind but, in hindsight, there's no reason why you should have realized what I was talking about. Anyway, glad I could help and I hope the code does what you need it to do.


2

@jasondelaat
I finally have slightly more annoying enemies now.
Unfortunatly I didn't end up using the Lerped version, because not doing it ended up working better in this context, but I still learned some usefull concepts along the way.
So thanks a lot for taking the time to answer.



[Please log in to post a comment]