Mientras buscaba una solución para calcular el ángulo que corresponde a una cierta longitud de arco, me topé con esta pregunta y la respuesta actual. Desafortunadamente, ni esta respuesta ni ningún otro recurso que encontré en la web podrían usarse directamente para una implementación.
Obviamente, calcular la inversa de la función de longitud de arco (que también se proporcionó en la pregunta) es muy difícil. Pero es posible una aproximación de este inverso utilizando el método iterativo de Newton. La siguiente es una clase que ofrece principalmente dos métodos:
computeArcLength(double alpha, double angleRad)
: Calcula la longitud del arco de un punto en la espiral de Arquímedes donde alpha
es la distancia entre giros sucesivos y angleRad
el ángulo en radianes
computeAngle(double alpha, double arcLength, double epsilon)
: Calcula el ángulo en el que el punto para la longitud de arco dada se encuentra en la Espiral de Arquímedes, donde alpha
está la distancia entre giros sucesivos y epsilon
es el umbral de aproximación para la iteración de Newton
El código se implementa aquí en Java, pero estos métodos principales deberían ser bastante independientes del lenguaje:
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)
{
return angleRad;
}
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
}
}
En este fragmento se proporciona un ejemplo de cómo usar esto para el objetivo descrito en la pregunta: genera un cierto número de puntos en la espiral, con una distancia deseada (¡longitud del arco!) Entre los puntos:
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++)
{
double angleRad =
ArchimedeanSpiral.computeAngle(alpha, totalArcLength, epsilon);
Point2D point =
ArchimedeanSpiral.computePoint(alpha, angleRad, null);
totalArcLength += pointArcDistance;
// Compute and print the arc lengths, for validation:
double currentArcLength =
ArchimedeanSpiral.computeArcLength(alpha, angleRad);
double previousArcLength =
ArchimedeanSpiral.computeArcLength(alpha, previousAngleRad);
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);
previousAngleRad = angleRad;
}
}
}
Se imprime la distancia real de la longitud del arco de los puntos calculados, y se puede ver que de hecho son equidistantes, con la distancia deseada de la longitud del arco.