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

Существует очень удобный набор 2-х утилит геометрии здесь.

angleBetweenLines имеет проблему, все же. Результат всегда положителен. Я должен обнаружить и положительные и отрицательные углы, поэтому если одна строка "на 15 градусов выше" или "ниже" другой строки, форма, очевидно, выглядит по-другому.

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

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

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

13
задан Jaanus 18 April 2010 в 19:30
поделиться

5 ответов

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

Если вы получили угол θ для первой (стационарной линии), вам придется нормализовать угол φ для второй линии, чтобы он находился между θ-π и θ + π (добавляя ± 2π). Тогда угол между двумя линиями будет φ-θ.

8
ответ дан 1 December 2019 в 19:14
поделиться

Вот реализация предложения мозгового джема. (С моими ограничениями работает то, что разница между строками гарантированно достаточно мала, чтобы не нужно ничего нормализовать.)

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

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

20
ответ дан 1 December 2019 в 19:14
поделиться

Это простая задача, связанная с двумерными векторами. Синус угла между двумя векторами связан с перекрестным произведением между двумя векторами. А «вверху» или «внизу» определяется знаком вектора, полученного в результате перекрестного произведения: если вы скрещиваете два вектора 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 <= θ <= 90), а B находится во втором квадранте (90 <= θ <= 180)? «Вверху» и «внизу» теряют смысл.

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

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

7
ответ дан 1 December 2019 в 19:14
поделиться

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

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

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

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

Править

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

0
ответ дан 1 December 2019 в 19:14
поделиться

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

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

1
ответ дан 1 December 2019 в 19:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: