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

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

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

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

РЕДАКТИРОВАТЬ: в ответ на комментарий Swestrup ниже, на самом деле ситуация в том, что у меня есть одна строка, и я записываю его стартовую позицию. Затем линия поворачивается из своей начальной позиции, и мне нужно вычислить угол от ее начальной позиции до текущей позиции. Например, если он вращался по часовой стрелке, это положительное вращение; если против часовой стрелки, то отрицательно. (Или наоборот.)

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

Ответы [ 6 ]

20 голосов
/ 19 апреля 2010

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

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

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

8 голосов
/ 18 апреля 2010

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

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

7 голосов
/ 18 апреля 2010

Это простая задача, связанная с 2D векторами. Синус угла между двумя векторами связан с перекрестным произведением между двумя векторами. И «выше» или «ниже» определяется знаком вектора, который получается перекрестным произведением: если вы пересечете два вектора 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)? «Выше» и «ниже» теряют свое значение. </p>

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

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

1 голос
/ 04 октября 2018
// Considering two vectors CA and BA
// Computing angle from CA to BA
// Thanks to code shared by Jaanus, but atan2(y,x) is used wrongly.

float getAngleBetweenVectorsWithSignInDeg(Point2f C, Point2f A, Point2f B)
{      
    float a = A.x - C.x;
    float b = A.y - C.y;
    float c = B.x - C.x;
    float d = B.y - C.y;

    float angleA = atan2(b, a);
    float angleB = atan2(d, c);
    cout << "angleA: " << angleA << "rad, " << angleA * 180 / M_PI << " deg" << endl;
    cout << "angleB: " << angleB << "rad, " << angleB * 180 / M_PI << " deg" << endl;
    float rotationAngleRad = angleB - angleA;
    float thetaDeg = rotationAngleRad * 180.0f / M_PI;
    return thetaDeg;
}
1 голос
/ 18 апреля 2010

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

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

0 голосов
/ 18 апреля 2010

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

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

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

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

Редактировать

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...