Moving a particle around an Archimedean spiral at a constant speed

I want to move a particle in a spiral at a constant speed. Note that that is not a constant angular speed. This is proving rather difficult, and I'll go through my method so far below.

The spiral in question is a classic Archimedean spiral with the polar equation r = ϑ, and the parametric equations x = t*cos(t), y = t*sin(t). This looks like this: I want to move a particle around the spiral, so naively, I can just give the particle position as the value of t, and the speed as the increase in t. That way the particle moves round the spiral at a constant angular speed. However, this means that the further out from the centre it gets, the faster its (non angular) speed becomes.

So, instead of having my speed in the increase in t, I want my speed as the increase in arc length. Getting the arc length of a spiral is the first challenge, but due to the fact that the Archimedean spiral I am using isn't too insane, the arc length function is , where a = 1. This allows me to convert theta values to the arc length, but that is the precise opposite of what I need. So I need to find the inverse of the arc length function, and at that hurdle, Wolfram-Alpha has failed me.

So is it possible to find the inverse of the arc length function? The function is a one to one mapping, if you exclude negative values of theta.

Thanks,

Laurie

• I think you'll get an answer quicker on math overflow. It is relevant to GameDev though. – deceleratedcaviar Sep 2 '11 at 3:35
• This would be easier if it wasn't parametric - does it have to be? – CiscoIPPhone Sep 2 '11 at 7:31
• Does the spiral have to be Archimedean? – Engineer Sep 2 '11 at 7:41
• @Cisco Well I gave the polar equation, and they are pretty much interchangable – Blue Peppers Sep 2 '11 at 11:46
• @Nick Yes :P Logarithmic and/or lituus is not what I want – Blue Peppers Sep 2 '11 at 11:47

Let's complicate your spiral:

be in your case f(t) := t, in mine f(t) := 1 (so i pay back my complications with simplifications :)

If you want to go at a certain speed in this degenerate spiral (a circle) you have to know how long is your spiral in a round so you can say how much rounds per second do to be sure that your point travels with the desired speed.

Now we know that each complete round in a circle is 2·π·r long: 2·π·1 in our case; if ω is the revolution speed (in rounds per seconds) the speed V will be V = 2·π·1·ω or in a more general way: if r is the general radius; this tell us that: if r is a function of t we can say: in my "complicated" case this can be rewrite as follow: in your "simplificated" case the answer is: You know your desidered constant speed V, you know: 2, π and t is your variable: you know everything and you are ready to go! the circle approximation for the infinitesimal neighbourhood of the spiral in t

[DISCLAIMER]

This is not intended to be a rigorous mathematical treatment: it does not take into account the contribution of the differential of f nor say what types of function can not be used.

• So using the last equation you solve for w(t) and then put that into the original parametric equation to get the position of the particle, is that correct? – CiscoIPPhone Sep 2 '11 at 8:51
• Oh this really is an excellent answer. And due to the use of f(t) instead of t, we can modify our spiral with this solution still working. Thank you very much. – Blue Peppers Sep 2 '11 at 11:50
• @CiscoIPPhone w(t) is the rotational speed, it tell you how much add to your t as the time pass, then use t to get position. – FxIII Sep 3 '11 at 11:46
• @Blue Peppers as I said in the disclaimer is not true for every f(t), it works if f(t) moves slowly (and is differentiable) – FxIII Sep 3 '11 at 11:46

If you don't mind an assumption that gets fairly accurate quite quickly, this simple solution works quite well:

theta = r = sqrt(2) . sqrt({time})

This is parametric in time, which is quite handy. However, to get this I needed to assume that the motion is roughly circular -- ie. the instantaneous linear velocity is proportional to the radius times the angular velocity:

{velocity} ~= {radius} . d{theta} / d{time}

To show that the solution works, plug it into d{theta} / d{time}:

d{theta} / d{time} = d(sqrt(2).{time}^(1/2)) / d{time}
= (sqrt(2) / 2) . {time}^(-1/2))
= 1 / {theta}
= 1 / {radius}
=> {velocity} = {radius} / {radius} = 1, as required.

At {time}=1, this places a point at distance sqrt(2) from the origin. After this, the approximation improves significantly: the separation (linear, not along the path) between subsequent points is 1.13, 1.08, 1.06. After 100 points the separation is less than 1.0023.

While looking for a solution of computing the angle that corresponds to a certain arc length, I stumbled upon this question and the current answer. Unfortunately, neither this answer nor any other resource that I found on the web could directly be used for an implementation.

Obviously, computing the inverse of the arc length function (that was also provided in the question) is very difficult. But an approximation of this inverse using Newtons Iterative Method is possible. The following is a class that mainly offers two methods:

• computeArcLength(double alpha, double angleRad): Computes the arc length of a point on the Archimedean Spiral where alpha is the distance between successive turnings, and angleRad is the angle in radians
• computeAngle(double alpha, double arcLength, double epsilon): Computes the angle at which the point for the given arc length is located on the Archimedean Spiral, where alpha is the distance between successive turnings, and epsilon is the approximation threshold for the Newton Iteration

The code is implemented here in Java, but these core methods should be fairly language-agnostic:

import java.awt.geom.Point2D;

/**
* A class for computations related to an Archimedean Spiral
*/
class ArchimedeanSpiral
{
/**
* Computes an approximation of the angle at which an Archimedean Spiral
* with the given distance between successive turnings has the given
* arc length.<br>
* <br>
* Note that the result is computed using an approximation, and not
* analytically.
*
* @param alpha The distance between successive turnings
* @param arcLength The desired arc length
* @param epsilon A value greater than 0 indicating the precision
* of the approximation
* @return The angle at which the desired arc length is achieved
* @throws IllegalArgumentException If the given arc length is negative
* or the given epsilon is not positive
*/
static double computeAngle(
double alpha, double arcLength, double epsilon)
{
if (arcLength < 0)
{
throw new IllegalArgumentException(
"Arc length may not be negative, but is "+arcLength);
}
if (epsilon <= 0)
{
throw new IllegalArgumentException(
"Epsilon must be positive, but is "+epsilon);
}
double angleRad = Math.PI + Math.PI;
while (true)
{
double d = computeArcLength(alpha, angleRad) - arcLength;
if (Math.abs(d) <= epsilon)
{
}
double da = alpha * Math.sqrt(angleRad * angleRad + 1);
angleRad -= d / da;
}
}

/**
* Computes the arc length of an Archimedean Spiral with the given
* parameters
*
* @param alpha The distance between successive turnings
* @param angleRad The angle, in radians
* @return The arc length
* @throws IllegalArgumentException If the given alpha is negative
*/
static double computeArcLength(
double alpha, double angleRad)
{
if (alpha < 0)
{
throw new IllegalArgumentException(
"Alpha may not be negative, but is "+alpha);
}
double u = Math.sqrt(1 + angleRad * angleRad);
double v = Math.log(angleRad + u);
return 0.5 * alpha * (angleRad * u + v);
}

/**
* Compute the point on the Archimedean Spiral for the given parameters.<br>
* <br>
* If the given result point is <code>null</code>, then a new point will
* be created and returned.
*
* @param alpha The distance between successive turnings
* @param angleRad The angle, in radians
* @param result The result point
* @return The result point
* @throws IllegalArgumentException If the given alpha is negative
*/
static Point2D computePoint(
double alpha, double angleRad, Point2D result)
{
if (alpha < 0)
{
throw new IllegalArgumentException(
"Alpha may not be negative, but is "+alpha);
}
double distance = angleRad * alpha;
double x = Math.sin(angleRad) * distance;
double y = Math.cos(angleRad) * distance;
if (result == null)
{
result = new Point2D.Double();
}
result.setLocation(x, y);
return result;
}

/**
* Private constructor to prevent instantiation
*/
private ArchimedeanSpiral()
{
// Private constructor to prevent instantiation
}
}

An example of how to use this for the goal described in the question is given in this snippet: It generates a certain number of points on the spiral, with a desired (arc length!) distance between the points:

import java.awt.geom.Point2D;
import java.util.Locale;

public class ArchimedeanSpiralExample
{
public static void main(String[] args)
{
final int numPoints = 50;
final double pointArcDistance = 0.1;
final double alpha = 0.5;
final double epsilon = 1e-5;

double totalArcLength = 0.0;
double previousAngleRad = 0.0;
for (int i=0; i<numPoints; i++)
{
ArchimedeanSpiral.computeAngle(alpha, totalArcLength, epsilon);
Point2D point =
totalArcLength += pointArcDistance;

// Compute and print the arc lengths, for validation:
double currentArcLength =
double previousArcLength =
double arcDistance = (currentArcLength - previousArcLength);
System.out.printf(Locale.ENGLISH,
"Point (%6.2f, %6.2f  distance in arc "
+ "length from previous is %6.2f\n",
point.getX(), point.getY(), arcDistance);

}
}
}

The actual arc length distance of the computed points is printed, and one can see that they are in fact equidistant, with the desired arc length distance.

I'm struggling with this too.

What I'm doing is keeping the velocity constant, and changing the direction of the object.

If I make is so the angle (in degrees) is equal to the distance from the origin, times a constant, i get a nice perfect archimedean spiral. larger constants get less space between the lines. Only issue is if the speed is too high, then it jumps the track and messes up. so tighter spirals require a slower speed to reliably trace.

direction = ((spiral_factor*(current_distance) mod 360);

Where current_distance is the radius drawn from the location to the spawn point in pixels, grabbed by an engine function that gives it to me.

What's driving me up the wall is the inverse. placing the object at the OUTSIDE and having it trace the Archimedean spiral INWARDS. Moving the particle the opposite way doesn't work. that just rotates the spiral 180 degrees. inverting the direction gives a clockwise one.