Auf der Suche nach einer Lösung zur Berechnung des Winkels, der einer bestimmten Bogenlänge entspricht, bin ich auf diese Frage und die aktuelle Antwort gestoßen. Leider konnten weder diese Antwort noch eine andere Ressource, die ich im Web gefunden habe, direkt für eine Implementierung verwendet werden.
Offensichtlich ist die Berechnung der Umkehrung der Bogenlängenfunktion (die auch in der Frage angegeben wurde) sehr schwierig. Eine Annäherung dieser Umkehrung unter Verwendung der Newtonschen iterativen Methode ist jedoch möglich. Die folgende Klasse bietet hauptsächlich zwei Methoden:
computeArcLength(double alpha, double angleRad)
: Berechnet die Bogenlänge eines Punktes auf der archimedischen Spirale, wobei alpha
der Abstand zwischen aufeinanderfolgenden Drehungen und angleRad
der Winkel im Bogenmaß ist
computeAngle(double alpha, double arcLength, double epsilon)
: Berechnet den Winkel, in dem sich der Punkt für die angegebene Bogenlänge auf der archimedischen Spirale befindet, wobei alpha
der Abstand zwischen aufeinanderfolgenden Drehungen und epsilon
die Näherungsschwelle für die Newton-Iteration ist
Der Code ist hier in Java implementiert, aber diese Kernmethoden sollten ziemlich sprachunabhängig sein:
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
}
}
Ein Beispiel dafür, wie dies für das in der Frage beschriebene Ziel verwendet werden kann, finden Sie in diesem Snippet: Es wird eine bestimmte Anzahl von Punkten auf der Spirale mit einem gewünschten Abstand (Bogenlänge!) Zwischen den Punkten generiert:
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;
}
}
}
Der tatsächliche Bogenlängenabstand der berechneten Punkte wird gedruckt, und man kann sehen, dass sie tatsächlich äquidistant mit dem gewünschten Bogenlängenabstand sind.