Внутренний угол между двумя линиями

У меня две строки: Line1 и Line2. Каждая строка определяется двумя точками (P1L1(x1, y1), P2L1(x2, y2) и P1L1(x1, y1), P2L3(x2, y3)). Я хочу знать внутренний угол, определяемый этими двумя строками.

Для этого я вычисляю угол каждой линии с абсциссой:

double theta1 = atan(m1) * (180.0 / PI);
double theta2 = atan(m2) * (180.0 / PI);

После того, как узнать угол, вычислим следующее:

double angle = abs(theta2 - theta1);

Проблема или сомнения в том, что у меня есть: иногда я получаю правильный угол, но иногда я получаю дополнительный угол (для меня внешний). Как узнать, когда вычесть 180º, чтобы узнать внутренний угол? Есть ли какой-нибудь алгоритм? Потому что я пробовал некоторые методы: dot product, следующая формула:

result = (m1 - m2) / (1.0 + (m1 * m2));

Но всегда у меня такая же проблема; Я никогда не знал, когда у меня есть внешний угол или внутренний угол!

Ответ 1

Я думаю, что вы ищете внутренний продукт (вы также можете просмотреть dot product) двух углов. В вашем случае это означает:

float dx21 = x2-x1;
float dx31 = x3-x1;
float dy21 = y2-y1;
float dy31 = y3-y1;
float m12 = sqrt( dx21*dx21 + dy21*dy21 );
float m13 = sqrt( dx31*dx31 + dy31*dy31 );
float theta = acos( (dx21*dx31 + dy21*dy31) / (m12 * m13) );

Ответ в радианах.

EDIT: Здесь полная реализация. Замените проблематичные значения в p1, p2 и p3 и сообщите мне, что вы получаете. Точка p1 - это вершина, где две линии пересекаются в соответствии с вашим определением двух линий.

#include <math.h>
#include <iostream>

template <typename T> class Vector2D
{
private:
    T x;
    T y;

public:
    explicit Vector2D(const T& x=0, const T& y=0) : x(x), y(y) {}
    Vector2D(const Vector2D&ltT>& src) : x(src.x), y(src.y) {}
    virtual ~Vector2D() {}

    // Accessors
    inline T X() const { return x; }
    inline T Y() const { return y; }
    inline T X(const T& x) { this->x = x; }
    inline T Y(const T& y) { this->y = y; }

    // Vector arithmetic
    inline Vector2D<T> operator-() const
        { return Vector2D<T>(-x, -y); }

    inline Vector2D<T> operator+() const
        { return Vector2D<T>(+x, +y); }

    inline Vector2D<T> operator+(const Vector2D<T>& v) const
        { return Vector2D<T>(x+v.x, y+v.y); }

    inline Vector2D<T> operator-(const Vector2D<T>& v) const
        { return Vector2D<T>(x-v.x, y-v.y); }

    inline Vector2D<T> operator*(const T& s) const
        { return Vector2D<T>(x*s, y*s); }

    // Dot product
    inline T operator*(const Vector2D<T>& v) const
        { return x*v.x + y*v.y; }

    // l-2 norm
    inline T norm() const { return sqrt(x*x + y*y); }

    // inner angle (radians)
    static T angle(const Vector2D<T>& v1, const Vector2D<T>& v2)
    {
        return acos( (v1 * v2) / (v1.norm() * v2.norm()) );
    }
};

int main()
{
    Vector2D<double> p1(215, 294);
    Vector2D<double> p2(174, 228);
    Vector2D<double> p3(303, 294);

    double rad = Vector2D<double>::angle(p2-p1, p3-p1);
    double deg = rad * 180.0 / M_PI;

    std::cout << "rad = " << rad << "\tdeg = " << deg << std::endl;

    p1 = Vector2D<double>(153, 457);
    p2 = Vector2D<double>(19, 457);
    p3 = Vector2D<double>(15, 470);

    rad = Vector2D<double>::angle(p2-p1, p3-p1);
    deg = rad * 180.0 / M_PI;

    std::cout << "rad = " << rad << "\tdeg = " << deg << std::endl;

    return 0;
}

Приведенный выше код дает:

rad = 2.12667   deg = 121.849
rad = 0.0939257 deg = 5.38155

Ответ 2

if (result > 180)
{
     result = 360 - result;
}

Таким образом, это всегда будет внутренний угол. Просто добавьте его после получения результата.

Ответ 3

Если вы хотите, чтобы между углом в 0 градусов до 360 градусов, используйте следующий код; Его полностью протестировано и функционально:

static inline CGFloat angleBetweenLinesInRadians(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {
double angle1 = atan2(line1Start.y-line1End.y, line1Start.x-line1End.x);
double angle2 = atan2(line2Start.y-line2End.y, line2Start.x-line2End.x);
double result = (angle2-angle1) * 180 / 3.14;
if (result<0) {
    result+=360;
}
return result;

}

Примечание: Вращение будет по часовой стрелке;

Ответ 4

Внутренний угол между двумя векторами (v1, v2) = arc cos (внутреннее произведение (v1, v2)/(модуль (v1) * module (v2))).

Где внутреннее произведение (v1, v2) = xv1 * xv2 + yv1 * yv2

модуль (v) = sqrt (pow (xv, 2) + pow (yv, 2))

Итак, ответ на ваш вопрос реализован в следующем примере:

#define PI   3.14159258

int main()
{
    double x1,y1,x2,y2,y3;
    double m1, m2;
    double mod1, mod2, innerp, angle;

    cout << "x1 :";
    cin >> x1;
    cout << "y1 :";
    cin >> y1;
    cout << "x2 :";
    cin >> x2;
    cout << "y2 :";
    cin >> y2;
    cout << "y3 :";
    cin >> y3;

    m1 = atan((y2-y1)/(x2-x1)) * 180 / PI;
    m2 = atan((y3-y1)/(x2-x1)) * 180 / PI;

    mod1   = sqrt(pow(y2-y1,2)+pow(x2-x1,2));
    mod2   = sqrt(pow(y3-y1,2)+pow(x2-x1,2));
    innerp = (x2-x1)*(x2-x1) + (y2-y1)*(y3-y1);
    angle  = acos(innerp / (mod1 * mod2)) * 180 / PI;

    cout << "m1 : " << m1 << endl;
    cout << "m2 : " << m2 << endl;
    cout << "angle : " << angle << endl;
}

Ответ 5

Весь смысл намного проще, чем заданные ответы:

Когда вы используете atan (slope), вы теряете (буквально) один бит информации, то есть есть ровно два угла (тета) и (theta + PI) в диапазоне (0..2 * PI), которые дают то же значение для функции tan().

Просто используйте atan2 (deltax, deltay), и вы получите правильный угол. Например,

atan2(1,1) == PI/4
atan2(-1,-1) == 5*PI/4

Затем вычитайте, возьмите абсолютное значение, и если больше, чем PI вычесть из 2 * PI.

Ответ 6

Если вы используете абсолютное значение, вы всегда получите острый угол. Это касательная величина theta = abs m1-m2 над (1 + m1 * m2). Если вы возьмете обратную касательную, ваш ответ будет в радианах или градусах, но калькулятор будет установлен. Извините, это не программирование lingo, я учитель математики, а не программист...

Ответ 7

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

Ответ 8

Надеюсь, я правильно понимаю ваш вопрос, так как желаю острого угла, а не тупого угла пересечения двух линий. Правильно ли я?

Острые и тупые углы пересечения являются 180-градусными дополнениями друг от друга. то есть.

 acute + obtuse = PI.

http://www.mathworks.com/access/helpdesk/help/techdoc/ref/atan.html показывает, что атан асимптотичен при +/- pi/2.

Следовательно, максимальная разница между двумя результатами атана равна pi или 180 град, независимо от того, используете ли вы обозначение +/- или положительную 0 to pi обозначение градиента.

Рассмотрим следующий псевдокод:

acuteAngle(m1, m2){
  a = atan(m1) - atan(m2);

  // if obtuse get the complementary acute angle:
  if (a>PI/2) 
    a = PI - a;
  return a;
} 

Функция acuteAngle иллюстрирует, что вам нужно сделать математически.

Однако он не может использоваться для значений углов в окрестности PI/2, потому что бинарные сравнения углов с результатами в этой окрестности сомнительны, представляется ли тупой или острый угол.

Поэтому нам нужно сравнить координаты точек двух линий. Мы выясним, является ли 3-я линия, образованная из [(x2,y2)(x3,y3)] короче, равна или длиннее гипотетической гипотенузы.

В силу теоремы Пифагора, Гипотенуза образуется, если угол равен PI/2 или 90 град. Позвольте называть его гипотетическую линию гипотенузы L3Hypo.

По геометрической визуализации в вашем уме,

  • Если 3-я строка длиннее L3Hypo, угол тупой.
  • Если короче, угол острый.
  • В противном случае, совершенный 90.

Таким образом,

L1.lengthSquared = sq(x2-x1) + sq(y2-y1)
L2.lengthSquared = sq(x3-x1) + sq(y3-y1)
L3Hypo.lengthSquared = L1.lengthSquared + L2.lengthSquared
L3.lengthSquared = sq(x3-x2) + sq(y3-y2)

Следовательно, следующий псевдокод,

struct Point{
  double x, y;
}

// no need to struct, for clarity only
struct Line{
  double lengthSquared;
}

#define sq(n) (n*n)
int isObtuse(Point P1, P2, P3){
  Line L1, L2, L3, L3Hypo;

  L1.lengthSquared = sq(P2.x-P1.x) + sq(P2.y-P1.y);
  L2.lengthSquared = sq(P3.x-P1.x) + sq(P3.y-P1.y);
  L3Hypo.lengthSquared = L1.lengthSquared + L2.lengthSquared;
  L3.lengthSquared = sq(P3.x-P2.x) + sq(P3.y-P2.y);

  if (L3>L3Hypo) return 1; //obtuse
  else if (L3<L3Hypo) return -1; //acute
  else return 0;
}

Предполагая, что у вас уже есть функция getGradient (точка P, Q):

double m1m2 = getGradient(P1,P2);
double m1m3 = getGradient(P1,P3);
double a = Abs(atan(m1m2) - atan(m1m3));
if (isObtuse(P1, P2, P3)>0)
  a = PI - a;

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

Далее Однако после размышления над ним я обнаружил, что борьба за точность поворачивается на самом слабом звене из-за директивы

#define PI 3.14159blah..blah..blah.

Итак, мы могли бы сэкономить все проблемы и просто сделать это:

double m1m2 = getGradient(P1,P2);
double m1m3 = getGradient(P1,P3);
double a = Abs(atan(m1m2) - atan(m1m3));
double b = PI - a;
return min(a, b);//the smaller of the two is the acute