Как рассчитать угол по трем точкам? [закрыто]

120

Допустим, у вас есть это:

P1 = (x=2, y=50)
P2 = (x=9, y=40)
P3 = (x=5, y=20)

Предположим, что P1это центральная точка круга. Всегда одно и то же. Мне нужен угол, образованный P2и P3, или, другими словами, угол, который находится рядом P1. Внутренний угол, если быть точным. Это всегда будет острый угол, поэтому меньше -90 градусов.

Я подумал: «Боже, это же простая геометрическая математика». Но я искал формулу около 6 часов и обнаружил, что люди говорят только о сложных вещах НАСА, таких как arccos и векторные скалярные произведения. Голова словно в холодильнике.

Некоторые математические гуру думают, что это простая проблема? Я не думаю, что язык программирования имеет здесь значение, но для тех, кто думает, что это имеет значение: java и objective-c. Мне он нужен для обоих, но я не пометил его для них.

Лэнс Робертс
источник

Ответы:

87

Если вы имеете в виду угол, вершиной которого является P1, тогда использование Закона косинусов должно работать:

агссоз((P 12 2 + P 13 2 - P 23 2 ) / (2 * P 12 * P 13 ))

где P 12 - длина отрезка от P1 до P2, рассчитанная по формуле

sqrt ((P1 x - P2 x ) 2 + (P1 y - P2 y ) 2 )

Лэнс Робертс
источник
@Rafa Firenze cos ^ -1 - обычное обозначение acos, но acos менее двусмысленно. en.wikipedia.org/wiki/Inverse_trigonometric_functions
geon
Я оставлю правку, так как это ничего не повредит, но, имея степени Math / CS / EE, cos ^ -1, безусловно, является наиболее распространенным обозначением.
Лэнс Робертс,
1
Лишь в некоторых языках используется символ «сила», поэтому, если вы не хотите называть его arcos, просто введите cos⁻¹. (Если вы используете коммерческую операционную систему, которая затрудняет ввод показателей, я ожидаю, что вы можете купить приложения для клавишных колпачков или, может быть, подключаемый модуль браузера, который вы можете установить. Или вы можете поискать в Интернете, скопировать и вставить.)
Майкл Шепер, 03
1
@MichaelScheper, я использовал только курсор в комментариях, где html ограничен. Я бы, конечно, просто использовал подстрочные / надстрочные обозначения в любом фактическом ответе.
Лэнс Робертс,
47

Это становится очень просто, если вы думаете, что это два вектора, один от точки P1 до P2, а другой от P1 до P3.

так:
a = (p1.x - p2.x, p1.y - p2.y)
b = (p1.x - p3.x, p1.y - p3.y)

Затем вы можете инвертировать формулу скалярного произведения:
скалярное произведение
чтобы получить угол:
угол между двумя векторами

Помните, что это скалярное произведениепросто означает: a1 * b1 + a2 * b2 (здесь всего 2 измерения ...)

Андреа Амбу
источник
1
Ах, величина вектора
Дэниел Литтл
Проверьте решение atan2.
Люк Буассе 07
25

Лучший способ справиться с вычислением угла - использовать atan2(y, x)то, что заданная точка x, yвозвращает угол от этой точки и X+ось по отношению к началу координат.

Учитывая, что вычисление

double result = atan2(P3.y - P1.y, P3.x - P1.x) -
                atan2(P2.y - P1.y, P2.x - P1.x);

то есть вы в основном переводите две точки на -P1(другими словами, вы переводите все, что P1попадает в начало координат), а затем вы рассматриваете разницу абсолютных углов P3и P2.

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

Единственная особенность для atan2is (0, 0)... означает, что оба P2и P3должны отличаться от P1as в этом случае не имеет смысла говорить об угле.

6502
источник
Спасибо за Ваш ответ. Это было именно то, что я искал. Простое решение, и вы можете легко получить угол против часовой стрелки, если я просто добавлю 2pi, когда значение отрицательное.
Марио
@marcpt: atan2это именно то, что нужно для этой проблемы, но похоже, что большинство людей, которые задаются этим вопросом, просто не могут прочитать или не могут понять, почему acosрешения на основе плохой. К счастью для меня, я оставил фазу «кто-то не прав в Интернете» ( xkcd.com/386 ) много лет назад, и я не собираюсь начинать борьбу за защиту очевидного :-)
6502
Спасибо, что указали на это, но сможете ли вы таким образом справиться с 3D?
nicoco
1
@nicoco: как определить угол в трех измерениях? Более конкретно, может ли угол быть отрицательным или быть больше пи (180 градусов)? Два непараллельных вектора в 3d определяют плоскость, но плоскость можно «увидеть» с двух сторон: если смотреть с одной стороны, A будет отображаться «слева» от B, а с другой - «справа». .
6502
@ 6505 Спасибо за ответ, я написал, прежде чем обдумать свою проблему. Но теперь я понял это.
nicoco
19

Приведу пример на JavaScript, я много боролся с этим:

/**
 * Calculates the angle (in radians) between two vectors pointing outward from one center
 *
 * @param p0 first point
 * @param p1 second point
 * @param c center point
 */
function find_angle(p0,p1,c) {
    var p0c = Math.sqrt(Math.pow(c.x-p0.x,2)+
                        Math.pow(c.y-p0.y,2)); // p0->c (b)   
    var p1c = Math.sqrt(Math.pow(c.x-p1.x,2)+
                        Math.pow(c.y-p1.y,2)); // p1->c (a)
    var p0p1 = Math.sqrt(Math.pow(p1.x-p0.x,2)+
                         Math.pow(p1.y-p0.y,2)); // p0->p1 (c)
    return Math.acos((p1c*p1c+p0c*p0c-p0p1*p0p1)/(2*p1c*p0c));
}

Бонус: пример с HTML5-канвой

shaman.sir
источник
5
Вы можете сделать это более эффективным, делая меньше sqrtи возводя в квадрат. См. Мой ответ здесь (написанный на Ruby) или в этой обновленной демонстрации (JavaScript).
Phrogz
Вы можете использовать atan2 для более простого решения.
Люк Буассе 07
15

По сути, у вас есть два вектора: один от P1 до P2, а другой от P1 до P3. Итак, все, что вам нужно, это формула для вычисления угла между двумя векторами.

Посмотрите здесь хорошее объяснение и формулу.

альтернативный текст

Андре Миллер
источник
12

Если вы думаете о P1 как о центре круга, вы думаете слишком сложно. У вас простой треугольник, поэтому ваша задача решается с помощью закона косинусов . Нет необходимости в преобразовании полярных координат или в чем-то подобном. Скажем, расстояния P1-P2 = A, P2-P3 = B и P3-P1 = C:

Угол = arccos ((B ^ 2-A ^ 2-C ^ 2) / 2AC)

Все, что вам нужно сделать, это вычислить длину расстояний A, B и C. Их легко получить из координат x и y ваших точек и теоремы Пифагора.

Длина = sqrt ((X2-X1) ^ 2 + (Y2-Y1) ^ 2)

треб
источник
Я немного сбит с толку, как это реализовать, поскольку вы рассматриваете P1 и т. Д. Как отдельные значения, а не (x, y)
Доминик
@Dominic Tobias: Обозначение P1-P2 = Aне следует читать как «Чтобы вычислить A, вычтите P2 из P1», а как «Я определяю A как расстояние от P1 до P2», которое затем можно вычислить с использованием второго уравнения. Я просто хотел дать сокращенное обозначение расстояний, чтобы уравнения были более удобочитаемыми.
Treb
8

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

 @Override
 public boolean onTouchEvent(MotionEvent e) {
    float x = e.getX();
    float y = e.getY();
    switch (e.getAction()) {
    case MotionEvent.ACTION_MOVE:
       //find an approximate angle between them.

       float dx = x-cx;
       float dy = y-cy;
       double a=Math.atan2(dy,dx);

       float dpx= mPreviousX-cx;
       float dpy= mPreviousY-cy;
       double b=Math.atan2(dpy, dpx);

       double diff  = a-b;
       this.bearing -= Math.toDegrees(diff);
       this.invalidate();
    }
    mPreviousX = x;
    mPreviousY = y;
    return true;
 }
Марк
источник
7

Очень простое геометрическое решение с пояснением

Несколько дней назад я столкнулся с той же проблемой и вынужден был сидеть с учебником по математике. Я решил проблему, объединив и упростив некоторые основные формулы.


Давайте рассмотрим эту цифру -

угол

Мы хотим знать ϴ , поэтому нам нужно сначала узнать α и β . Теперь для любой прямой -

y = m * x + c

Пусть- A = (ax, ay) , B = (bx, by) и O = (ox, oy) . Итак, для линии OA -

oy = m1 * ox + c   ⇒ c = oy - m1 * ox   ...(eqn-1)

ay = m1 * ax + c   ⇒ ay = m1 * ax + oy - m1 * ox   [from eqn-1]
                   ⇒ ay = m1 * ax + oy - m1 * ox
                   ⇒ m1 = (ay - oy) / (ax - ox)
                   ⇒ tan α = (ay - oy) / (ax - ox)   [m = slope = tan ϴ]   ...(eqn-2)

Таким же образом для линии OB -

tan β = (by - oy) / (bx - ox)   ...(eqn-3)

Теперь нам нужно ϴ = β - α. В тригонометрии у нас есть формула:

tan (β-α) = (tan β + tan α) / (1 - tan β * tan α)   ...(eqn-4)

После замены значения tan α(из уравнения-2) и tan b(из уравнения-3) в уравнении-4 и применения упрощения мы получаем:

tan (β-α) = ( (ax-ox)*(by-oy)+(ay-oy)*(bx-ox) ) / ( (ax-ox)*(bx-ox)-(ay-oy)*(by-oy) )

Так,

ϴ = β-α = tan^(-1) ( ((ax-ox)*(by-oy)+(ay-oy)*(bx-ox)) / ((ax-ox)*(bx-ox)-(ay-oy)*(by-oy)) )

Вот и все!


Теперь возьмем следующий рисунок -

угол

Этот метод C # или Java вычисляет угол ( ϴ ) -

    private double calculateAngle(double P1X, double P1Y, double P2X, double P2Y,
            double P3X, double P3Y){

        double numerator = P2Y*(P1X-P3X) + P1Y*(P3X-P2X) + P3Y*(P2X-P1X);
        double denominator = (P2X-P1X)*(P1X-P3X) + (P2Y-P1Y)*(P1Y-P3Y);
        double ratio = numerator/denominator;

        double angleRad = Math.Atan(ratio);
        double angleDeg = (angleRad*180)/Math.PI;

        if(angleDeg<0){
            angleDeg = 180+angleDeg;
        }

        return angleDeg;
    }
Минхас Камаль
источник
Как этот метод можно использовать для равностороннего треугольника?
Викрант
1
Что ж, теперь ваш ответ работает нормально. Неделей ранее в моем коде была некоторая логическая проблема.
Викрант
6

В Objective-C это можно сделать с помощью

float xpoint = (((atan2((newPoint.x - oldPoint.x) , (newPoint.y - oldPoint.y)))*180)/M_PI);

Или читайте подробнее здесь

Адим Максуд Басраа
источник
7
Нет. Есть три точки, центр не в (0,0), и это дает угол прямоугольного треугольника, а не угол вершины. А какое название «xpoint» для угла?
Джим Балтер,
4

Вы упомянули угол со знаком (-90). Во многих приложениях углы могут иметь знаки (положительные и отрицательные, см. Http://en.wikipedia.org/wiki/Angle ). Если точки (скажем) P2 (1,0), P1 (0,0), P3 (0,1), то угол P3-P1-P2 условно положительный (PI / 2), тогда как угол P2-P1- P3 отрицательный. Использование длин сторон не приведет к различию между + и -, поэтому, если это имеет значение, вам нужно будет использовать векторы или функцию, такую ​​как Math.atan2 (a, b).

Углы также могут выходить за пределы 2 * PI, и хотя это не имеет отношения к текущему вопросу, было достаточно важно, чтобы я написал свой собственный класс Angle (также чтобы убедиться, что градусы и радианы не перепутались). Вопрос о том, меньше ли angle1, чем angle2, во многом зависит от того, как определяются углы. Также может быть важно решить, будет ли линия (-1,0) (0,0) (1,0) представлена ​​как Math.PI или -Math.PI.

peter.murray.rust
источник
4

моя демонстрационная программа угла

Недавно у меня тоже возникла такая же проблема ... В Delphi это очень похоже на Objective-C.

procedure TForm1.FormPaint(Sender: TObject);
var ARect: TRect;
    AWidth, AHeight: Integer;
    ABasePoint: TPoint;
    AAngle: Extended;
begin
  FCenter := Point(Width div 2, Height div 2);
  AWidth := Width div 4;
  AHeight := Height div 4;
  ABasePoint := Point(FCenter.X+AWidth, FCenter.Y);
  ARect := Rect(Point(FCenter.X - AWidth, FCenter.Y - AHeight),
    Point(FCenter.X + AWidth, FCenter.Y + AHeight));
  AAngle := ArcTan2(ClickPoint.Y-Center.Y, ClickPoint.X-Center.X) * 180 / pi;
  AngleLabel.Caption := Format('Angle is %5.2f', [AAngle]);
  Canvas.Ellipse(ARect);
  Canvas.MoveTo(FCenter.X, FCenter.Y);
  Canvas.LineTo(FClickPoint.X, FClickPoint.Y);
  Canvas.MoveTo(FCenter.X, FCenter.Y);
  Canvas.LineTo(ABasePoint.X, ABasePoint.Y);
end;
антонио
источник
2

Вот метод C # для возврата угла (0-360) против часовой стрелки от горизонтали для точки на окружности.

    public static double GetAngle(Point centre, Point point1)
    {
        // Thanks to Dave Hill
        // Turn into a vector (from the origin)
        double x = point1.X - centre.X;
        double y = point1.Y - centre.Y;
        // Dot product u dot v = mag u * mag v * cos theta
        // Therefore theta = cos -1 ((u dot v) / (mag u * mag v))
        // Horizontal v = (1, 0)
        // therefore theta = cos -1 (u.x / mag u)
        // nb, there are 2 possible angles and if u.y is positive then angle is in first quadrant, negative then second quadrant
        double magnitude = Math.Sqrt(x * x + y * y);
        double angle = 0;
        if(magnitude > 0)
            angle = Math.Acos(x / magnitude);

        angle = angle * 180 / Math.PI;
        if (y < 0)
            angle = 360 - angle;

        return angle;
    }

Привет, Пол

Павел
источник
2

function p(x, y) {return {x,y}}

function normaliseToInteriorAngle(angle) {
	if (angle < 0) {
		angle += (2*Math.PI)
	}
	if (angle > Math.PI) {
		angle = 2*Math.PI - angle
	}
	return angle
}

function angle(p1, center, p2) {
	const transformedP1 = p(p1.x - center.x, p1.y - center.y)
	const transformedP2 = p(p2.x - center.x, p2.y - center.y)

	const angleToP1 = Math.atan2(transformedP1.y, transformedP1.x)
	const angleToP2 = Math.atan2(transformedP2.y, transformedP2.x)

	return normaliseToInteriorAngle(angleToP2 - angleToP1)
}

function toDegrees(radians) {
	return 360 * radians / (2 * Math.PI)
}

console.log(toDegrees(angle(p(-10, 0), p(0, 0), p(0, -10))))

Dominic
источник
0

есть простой ответ на этот вопрос, используя математику средней школы.

Допустим, у вас 3 очка

Чтобы получить угол от точки A до B

angle = atan2(A.x - B.x, B.y - A.y)

Чтобы получить угол от точки B до C

angle2 = atan2(B.x - C.x, C.y - B.y)

Answer = 180 + angle2 - angle
If (answer < 0){
    return answer + 360
}else{
    return answer
}

Я просто использовал этот код в недавнем проекте, который я сделал, измените B на P1 .. вы можете также удалить "180 +", если хотите

Джеймс Пеннер
источник
-1

ну, другие ответы, похоже, охватывают все необходимое, поэтому я хотел бы просто добавить это, если вы используете JMonkeyEngine:

Vector3f.angleBetween(otherVector)

так как это то, что я сюда искал :)

VeganEye
источник
-2
      Atan2        output in degrees
       PI/2              +90
         |                | 
         |                |    
   PI ---.--- 0   +180 ---.--- 0       
         |                |
         |                |
       -PI/2             +270

public static double CalculateAngleFromHorizontal(double startX, double startY, double endX, double endY)
{
    var atan = Math.Atan2(endY - startY, endX - startX); // Angle in radians
    var angleDegrees = atan * (180 / Math.PI);  // Angle in degrees (can be +/-)
    if (angleDegrees < 0.0)
    {
        angleDegrees = 360.0 + angleDegrees;
    }
    return angleDegrees;
}

// Angle from point2 to point 3 counter clockwise
public static double CalculateAngle0To360(double centerX, double centerY, double x2, double y2, double x3, double y3)
{
    var angle2 = CalculateAngleFromHorizontal(centerX, centerY, x2, y2);
    var angle3 = CalculateAngleFromHorizontal(centerX, centerY, x3, y3);
    return (360.0 + angle3 - angle2)%360;
}

// Smaller angle from point2 to point 3
public static double CalculateAngle0To180(double centerX, double centerY, double x2, double y2, double x3, double y3)
{
    var angle = CalculateAngle0To360(centerX, centerY, x2, y2, x3, y3);
    if (angle > 180.0)
    {
        angle = 360 - angle;
    }
    return angle;
}

}

Дэниел Куинн
источник