En cherchant une solution de calcul de l'angle qui correspond à une certaine longueur d'arc, je suis tombé sur cette question et la réponse actuelle. Malheureusement, ni cette réponse ni aucune autre ressource que j'ai trouvée sur le Web n'ont pu être directement utilisées pour une implémentation.
De toute évidence, le calcul de l'inverse de la fonction de longueur d'arc (qui était également fourni dans la question) est très difficile. Mais une approximation de cet inverse en utilisant la méthode itérative de Newton est possible. Ce qui suit est une classe qui propose principalement deux méthodes:
computeArcLength(double alpha, double angleRad)
: Calcule la longueur d'arc d'un point sur la spirale d'Archimède où alpha
est la distance entre les virages successifs et angleRad
l'angle en radians
computeAngle(double alpha, double arcLength, double epsilon)
: Calcule l'angle auquel le point pour la longueur d'arc donnée est situé sur la spirale d'Archimède, où alpha
est la distance entre les virages successifs, et epsilon
est le seuil d'approximation pour l'itération de Newton
Le code est implémenté ici en Java, mais ces méthodes de base doivent être assez indépendantes du langage:
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
}
}
Un exemple de la façon de l'utiliser pour l'objectif décrit dans la question est donné dans cet extrait: il génère un certain nombre de points sur la spirale, avec une distance souhaitée (longueur d'arc!) Entre les 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++)
{
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;
}
}
}
La distance de longueur d'arc réelle des points calculés est imprimée, et on peut voir qu'ils sont en fait équidistants, avec la distance de longueur d'arc souhaitée.