Как рассчитать угол из трех точек?

Предположим, у вас есть это:

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

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

Я подумал: "Человек, эта простая геометрия. Но я искал формулу около 6 часов и обнаружил, что люди говорят о сложных вещах NASA, таких как arccos и векторный скалярный продукт. Моя голова чувствует себя как в холодильнике.

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

Ответ 1

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

агссоз ((Р <суб > 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 - Р2 <суб > усуб > ) 2)

Ответ 2

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

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

Затем вы можете инвертировать формулу точечного продукта:
dot product
получить угол:
angle between two vectors

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

Ответ 3

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

http://en.wikipedia.org/wiki/Law_of_cosines

Ответ 4

Позвольте мне привести пример в 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-холстом

Ответ 5

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

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

alt text

Ответ 6

Если вы думаете о 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)

Ответ 7

Лучший способ справиться с вычислением углов - использовать 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 вам нужно обрабатывать несколько случаев в зависимости от знаков для вычисления правильного результата.

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

Ответ 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;
 }

Ответ 9

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

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

Или читайте здесь

Ответ 10

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

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


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

angle

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

y = m * x + c

Let-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)) )

Вот и все!


Теперь сделайте следующий рисунок -

angle

Этот метод С# или 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;
    }

Ответ 11

Вы упомянули подписанный угол (-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 (также чтобы убедиться, что градусы и радианы не смешиваются). Вопросы относительно того, является ли угол1 меньше угла2, критически зависят от того, как определяются углы. Также может быть важно решить, представлена ​​ли линия (-1,0) (0,0) (1,0) как Math.PI или -Math.PI

Ответ 12

my angle demo program

В последнее время у меня тоже такая же проблема... В 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;

Ответ 13

Здесь используется метод С# для возврата угла (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;
    }

Cheers, Пол

Ответ 14

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))))

Ответ 15

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

Vector3f.angleBetween(otherVector)

так вот что я здесь искал:)

Ответ 16

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

}