Перемещение частицы вокруг архимедовой спирали с постоянной скоростью


8

Я хочу перемещать частицу по спирали с постоянной скоростью. Обратите внимание, что это не постоянная угловая скорость. Это довольно сложно, и я покажу свой метод ниже.

Спираль, о которой идет речь, - это классическая архимедова спираль с полярным уравнением r = ϑи параметрическими уравнениями x = t*cos(t), y = t*sin(t). Это выглядит так:введите описание изображения здесь

Я хочу переместить частицу по спирали, поэтому наивно я могу просто указать положение частицы как значение t, а скорость - как увеличение t. Таким образом, частица движется по спирали с постоянной угловой скоростью. Однако это означает, что чем дальше от центра он становится, тем выше его (не угловая) скорость.

Таким образом, вместо того, чтобы иметь мою скорость в увеличении t, я хочу мою скорость как увеличение длины дуги. Получение длины дуги спирали является первой проблемой, но из-за того, что архимедова спираль, которую я использую, не слишком безумна, функция длины дуги есть где a = 1. Это позволяет мне преобразовывать значения тета в длину дуги, но это полная противоположность того, что мне нужно. Поэтому мне нужно найти обратную функцию длины дуги, и на этом препятствии Wolfram-Alpha подвел меня.

Так можно ли найти обратную функцию длины дуги? Функция является взаимно-однозначным отображением, если исключить отрицательные значения тета.

Спасибо,

Laurie


1
Я думаю, вы получите ответ быстрее по переполнению математики. Это относится к GameDev, хотя.
замедленная

Это было бы проще, если бы оно не было параметрическим - это должно быть?
CiscoIPPhone

Есть ли спираль должна быть архимедовым?
инженер

@Cisco Ну, я дал полярное уравнение, и они в значительной степени взаимозаменяемы
Blue Peppers

@ Ник Да: P Логарифмический и / или lituus - это не то, что я хочу
Blue Peppers

Ответы:


12

Давайте усложним вашу спираль:

быть p (t): = (cos (t) · f (t), sin (t) · f (t))

в вашем случае f (t): = t, в моем f (t): = 1 (так что я заплачу за свои сложности упрощениями :)

Если вы хотите двигаться с определенной скоростью в этой вырожденной спирали (круг), вы должны знать, какова длина вашей спирали в раунде, чтобы вы могли сказать, сколько кругов в секунду нужно делать, чтобы быть уверенным, что ваша точка движется с желаемой скоростью. ,

Теперь мы знаем, что каждый полный раунд в окружности имеет длину 2 · π · r : 2 · π · 1 в нашем случае; если ω - скорость вращения (в раундах в секунду), скорость V будет V = 2 · π · 1 · ω или в более общем виде:

V = 2 · π · r · ω

если r - общий радиус; это говорит нам, что:

V / (2 · π · r) = ω

если r является функцией от t, мы можем сказать:

ω (t) = V / (2 · π · r (t))

в моем «сложном» случае это можно переписать следующим образом:

ω (t) = V / (2 · π · f (t))

в вашем «упрощенном» случае ответ таков:

ω (t) = V / (2 · π · t)

Вы знаете свою желаемую постоянную скорость V, вы знаете: 2, π и t - ваша переменная: вы знаете все и готовы к работе!

аппроксимация окружности для бесконечно малой окрестности спирали по t

окружное приближение для бесконечно малой окрестности спирали по t

[ОТКАЗ]

Это не предназначено для строгой математической обработки: оно не учитывает вклад дифференциала f и не говорит о том, какие типы функций нельзя использовать.


Итак, используя последнее уравнение, которое вы решаете для w (t), и затем помещаете его в исходное параметрическое уравнение, чтобы получить положение частицы, это правильно?
CiscoIPPhone

О, это действительно отличный ответ. И благодаря использованию f (t) вместо t мы можем модифицировать нашу спираль, пока это решение все еще работает. Большое спасибо.
Blue Peppers

@CiscoIPPhone w (t) - скорость вращения, она сообщает вам, сколько прибавит к вашему t за время, затем используйте t, чтобы получить позицию.
FxIII

@Blue Peppers, как я уже говорил в заявлении об отказе от ответственности, не является верным для каждого f (t), он работает, если f (t) движется медленно (и дифференцируемо)
FxIII

2

Если вы не возражаете против предположения, которое становится довольно точным довольно быстро, это простое решение работает довольно хорошо:

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

Это параметрическое время, что очень удобно. Однако, чтобы получить это, мне нужно было предположить, что движение примерно круговое, т.е. мгновенная линейная скорость пропорциональна радиусу, умноженному на угловую скорость:

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

Чтобы показать, что решение работает, подключите его к 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.

В {time}=1это помещает точку на расстоянии sqrt(2)от начала координат. После этого аппроксимация значительно улучшается: расстояние (линейное, а не вдоль пути) между последующими точками составляет 1,13, 1,08, 1,06. После 100 баллов разделение составляет менее 1,0023.


0

В поисках решения для вычисления угла, который соответствует определенной длине дуги, я наткнулся на этот вопрос и текущий ответ. К сожалению, ни этот ответ, ни какой-либо другой ресурс, который я нашел в Интернете, не могли быть непосредственно использованы для реализации.

Очевидно, что вычисление обратной функции длины дуги (которая также была представлена ​​в вопросе) очень сложно. Но аппроксимация этого обратного с использованием итерационного метода Ньютона возможна. Ниже приведен класс, который в основном предлагает два метода:

  • 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;
        }
    }
}

Фактическая длина дуги расстояние вычисленных точек печатаются, и можно увидеть , что они на самом деле эквидистант, с требуемым расстоянием длиной дуги.


0

Я тоже борюсь с этим.

Что я делаю, так это поддерживаю постоянную скорость и меняю направление движения объекта.

Если я сделаю так, чтобы угол (в градусах) равнялся расстоянию от начала координат, умноженному на постоянную, я получаю прекрасную идеальную архимедову спираль. большие константы занимают меньше места между строками. Единственная проблема заключается в том, что если скорость слишком высока, то она скачет по дорожке и испортится. поэтому более надежные спирали требуют более медленной скорости для надежного отслеживания.

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

Где current_distance - это радиус, оттянутый от местоположения до точки появления в пикселях, захваченный функцией двигателя, которая дает его мне.

То, что ведет меня вверх по стене - это обратное. поместив объект снаружи и проследив за ним архимедова спираль внутрь. Перемещение частицы в обратном направлении не работает. это просто поворачивает спираль на 180 градусов. инвертирование направления дает направление по часовой стрелке.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.