Подтвердить что ты не робот

Как рассчитать как положительный, так и отрицательный угол между двумя линиями?

Здесь есть очень удобный набор двухмерных утилит геометрии.

У угла BetweenLines есть проблема. Результат всегда положительный. Мне нужно определить как положительный, так и отрицательный углы, поэтому, если одна строка находится на 15 градусов выше или ниже, другая форма выглядит явно.

Конфигурация, которую я имею, состоит в том, что одна строка остается неподвижной, а другая линия вращается, и мне нужно понять, в каком направлении она вращается, сравнивая ее со стационарной линией.

EDIT: в ответ на комментарий swestrup ниже ситуация на самом деле состоит в том, что у меня есть одна строка, и я записываю ее начальную позицию. Затем линия поворачивается из своего начального положения, и мне нужно рассчитать угол от его исходного положения до текущего положения. Например, если он вращается по часовой стрелке, это положительное вращение; если против часовой стрелки, то отрицательный. (Или наоборот).

Как улучшить алгоритм, чтобы он возвращал угол как положительный, так и отрицательный в зависимости от того, как расположены линии?

4b9b3361

Ответ 1

@duffymo ответ правильный, но если вы не хотите внедрять кросс-продукт, вы можете использовать функцию atan2. Это возвращает угол между - & pi; и & pi;, и вы можете использовать его на каждой из строк (или, точнее, на векторах, представляющих линии).

Если вы получите угол & theta; для первой (стационарной линии) вам придется нормализовать угол & phi; для второй линии между & theta; - & pi; и & thetas; + & pi; (путем добавления & plusmn; 2 & pi;). Тогда угол между двумя линиями будет равен & thetas;

Ответ 2

Здесь реализовано предложение brainjam. (Он работает с моими ограничениями, что разница между строками гарантирована настолько мала, что нет необходимости нормализовать что-либо.)

CGFloat angleBetweenLinesInRad(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {
    CGFloat a = line1End.x - line1Start.x;
    CGFloat b = line1End.y - line1Start.y;
    CGFloat c = line2End.x - line2Start.x;
    CGFloat d = line2End.y - line2Start.y;

    CGFloat atanA = atan2(a, b);
    CGFloat atanB = atan2(c, d);

    return atanA - atanB;
}

Мне нравится, что это лаконично. Будет ли векторная версия более кратким?

Ответ 3

Это простая задача, связанная с двумерными векторами. Синус угла между двумя векторами связан с перекрестным продуктом между двумя векторами. А "выше" или "ниже" определяется знаком вектора, созданного перекрестным произведением: если вы пересекаете два вектора A и B, а полученный перекрестный продукт положителен, то A "ниже" B; если он отрицательный, A "выше" B. Подробнее см. Mathworld.

Вот как я могу его закодировать в Java:

package cruft;

import java.text.DecimalFormat;
import java.text.NumberFormat;

/**
 * VectorUtils
 * User: Michael
 * Date: Apr 18, 2010
 * Time: 4:12:45 PM
 */
public class VectorUtils
{
    private static final int DEFAULT_DIMENSIONS = 3;
    private static final NumberFormat DEFAULT_FORMAT = new DecimalFormat("0.###");

    public static void main(String[] args)
    {
        double [] a = { 1.0, 0.0, 0.0 };
        double [] b = { 0.0, 1.0, 0.0 };

        double [] c = VectorUtils.crossProduct(a, b);

        System.out.println(VectorUtils.toString(c));
    }

    public static double [] crossProduct(double [] a, double [] b)
    {
        assert ((a != null) && (a.length >= DEFAULT_DIMENSIONS ) && (b != null) && (b.length >= DEFAULT_DIMENSIONS));

        double [] c = new double[DEFAULT_DIMENSIONS];

        c[0] = +a[1]*b[2] - a[2]*b[1];
        c[1] = +a[2]*b[0] - a[0]*b[2];
        c[2] = +a[0]*b[1] - a[1]*b[0];

        return c;
    }

    public static String toString(double [] a)
    {
        StringBuilder builder = new StringBuilder(128);

        builder.append("{ ");

        for (double c : a)
        {
            builder.append(DEFAULT_FORMAT.format(c)).append(' ');
        }

        builder.append("}");

        return builder.toString();
    }
}

Проверьте знак 3-го компонента. Если он положительный, A "ниже" B; если он отрицательный, A - "выше" B - до тех пор, пока два вектора находятся в двух квадрантах справа от оси y. Очевидно, что если они оба находятся в двух квадрантах слева от оси y, это верно.

Вам нужно подумать о своих интуитивных представлениях "выше" и "ниже" . Что, если A находится в первом квадранте (0 <= & theta; <= 90), а B находится во втором квадранте (90 <= & theta; <= 180)? "Выше" и "ниже" теряют смысл.

Затем линия поворачивается исходной позиции, и мне нужно вычислить угол от его начала позиция к текущему положению. Например, если он вращается по часовой стрелке, он положительное вращение; если против часовой стрелки, затем отрицательный. (Или наоборот.)

Это именно то, для чего кросс-продукт. Знак 3-й компоненты положителен для против часовой стрелки и отрицателен по часовой стрелке (когда вы смотрите вниз на плоскость вращения).

Ответ 4

Один "быстрый и грязный" метод, который вы можете использовать, - это ввести третью опорную линию R. Итак, учитывая две линии A и B, вычислите углы между A и R, а затем B и R и вычтите их.

Это примерно в два раза больше, чем нужно, но легко объяснить и отлаживать.

Ответ 5

Эта функция работает в RADS

Есть 2pi RADS в полном круге (360 градусов)

Таким образом, я считаю, что ответ, который вы ищете, - это просто возвращаемое значение - 2pi

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

Edit

Только что заметил, что функция фактически преобразует Rads в градусы, когда она возвращает значение. Но тот же принцип будет работать.