Як обчислити кут з трьох точок? [зачинено]


120

Скажімо, у вас це є:

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

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

Я подумав: Людина, це проста математика з геометрії. Але я шукав формулу вже близько 6 годин, і знаходжу лише людей, які розмовляють про складні речі NASA, такі як аркоси та векторні скалярні вироби. Моя голова відчуває, що це в холодильнику.

Деякі гуру математики, які думають, що це проста проблема? Я не думаю, що мова програмування тут має значення, але для тих, хто думає, що це робить: java та aim-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
Лише жменька мов використовує довідку про "владу", тому якщо ви не хочете називати це аркосом, просто введіть cos⁻¹. (Якщо ви використовуєте комерційну операційну систему, яка ускладнює набір експонентів, я думаю, що у вас є додатки для набору ключових слів, які ви можете придбати, або, можливо, ви зможете встановити плагін браузера. Або ви можете веб-пошук, копіювання та вставлення.)
Майкл Шепер

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.
Люк Боассей

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

Єдиним особливим моментом atan2є (0, 0)... це означає, що і те, P2і інше P3повинно бути відмінним, P1оскільки в цьому випадку немає сенсу говорити про кут.


Дякую за вашу відповідь. Саме це я і шукав. Просте рішення, і ви можете легко отримати кут проти годинникової стрілки, якщо я просто додаю 2пі, коли значення негативне.
Маріо

@marcpt: atan2це саме те, що потрібно для цієї проблеми, але схоже, що більшість людей, які переходять до цього питання, просто не можуть прочитати або не можуть зрозуміти, чому acosрішення на базі погано. На щастя для мене, я залишив фазу "хтось помиляється в Інтернеті" ( xkcd.com/386 ) багато років тому, і я не збираюся розпочати бійку за захист очевидного :-)
6502

Дякуємо, що вказали на це, але чи можете ви обробити 3D таким чином?
nicoco

1
@nicoco: у трьох вимірах як визначити кут? Більш конкретно, чи може бути кут від'ємним чи більшим за pi (180 градусів)? Два не паралельних вектора в 3d визначають площину, але площину можна "побачити" з двох сторін: дивиться з одного боку А з'явиться "зліва" від 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-полотном


5
Ви можете зробити це більш ефективним, роблячи менше sqrtі займаючи квадрати. Дивіться мою відповідь тут (написана в Ruby) або в цьому оновленому демонстраційному режимі (JavaScript).
Фрогз

Ви можете використовувати atan2 для більш простого рішення.
Люк Боассаї

15

В основному у вас є два вектори, один вектор від P1 до P2 і інший від P1 до P3. Отже, все, що вам потрібно, - це формула для обчислення кута між двома векторами.

Подивіться тут на гарне пояснення та формулу.

alt текст


12

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

Кут = дуги ((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", яке потім можна обчислити за допомогою другого рівняння. Я просто хотів визначити скорочення для відстаней, щоб зробити рівняння більш читабельними.
Треб

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 α(від eqn-2) і tan b(від eqn-3) в eqn-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;
    }

Як можна використовувати цей метод для рівностороннього трикутника?
Vikrant

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- Р3 негативний. Використання довжин сторін не буде розрізняти + і - тож якщо це має значення, вам потрібно буде використовувати вектори або функцію, таку як Math.atan2 (a, b).

Кути можуть також виходити за межі 2 * PI, і хоча це не стосується поточного питання, було досить важливо, щоб я написав власний клас Angle (також щоб переконатися, що градуси і радіани не змішалися). Питання про те, чи кут1 менший від кута2, критично залежить від того, як визначаються кути. Також може бути важливим рішення, чи лінія (-1,0) (0,0) (1,0) представлена ​​як Math.PI або -Math.PI


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


0

Є проста відповідь на це, використовуючи математику середньої школи.

Скажімо, у вас є 3 бали

Щоб отримати кут від точки А до В

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

Щоб отримати кут від точки В до С

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)

як це те, що я прийшов сюди шукати :)


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

}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.