В поисках решения для вычисления угла, который соответствует определенной длине дуги, я наткнулся на этот вопрос и текущий ответ. К сожалению, ни этот ответ, ни какой-либо другой ресурс, который я нашел в Интернете, не могли быть непосредственно использованы для реализации.
Очевидно, что вычисление обратной функции длины дуги (которая также была представлена в вопросе) очень сложно. Но аппроксимация этого обратного с использованием итерационного метода Ньютона возможна. Ниже приведен класс, который в основном предлагает два метода:
computeArcLength(double alpha, double angleRad)
: Вычисляет длину дуги точки на архимедовой спирали, где alpha
расстояние между последовательными поворотами и angleRad
угол в радианах
computeAngle(double alpha, double arcLength, double epsilon)
: Вычисляет угол, под которым точка для данной длины дуги находится на архимедовой спирали, где alpha
расстояние между последовательными витками и epsilon
порог аппроксимации для итерации Ньютона
Код реализован здесь на Java, но эти основные методы должны быть достаточно независимыми от языка:
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
}
}
Пример того, как использовать это для цели, описанной в вопросе, приведен в следующем фрагменте: он генерирует определенное количество точек на спирали с желаемым (длина дуги!) Расстоянием между точками:
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;
}
}
}
Фактическая длина дуги расстояние вычисленных точек печатаются, и можно увидеть , что они на самом деле эквидистант, с требуемым расстоянием длиной дуги.