Bagaimana cara menghitung sudut dari tiga titik? [Tutup]

120

Katakanlah Anda memiliki ini:

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

Asumsikan itu P1adalah titik pusat sebuah lingkaran. Itu selalu sama. Saya ingin sudut yang dibuat oleh P2dan P3, atau dengan kata lain sudut yang ada di sebelahnya P1. Sudut dalam tepatnya. Itu akan selalu menjadi sudut lancip, jadi kurang dari -90 derajat.

Saya berpikir: Ya ampun, itu matematika geometri sederhana. Tetapi saya telah mencari formula selama sekitar 6 jam sekarang, dan hanya menemukan orang-orang berbicara tentang hal-hal rumit NASA seperti arccos dan hal-hal produk skalar vektor. Kepalaku terasa seperti ada di lemari es.

Beberapa ahli matematika di sini yang menganggap ini masalah sederhana? Saya tidak berpikir bahasa pemrograman penting di sini, tetapi bagi mereka yang menganggapnya penting: java dan objektif-c. Saya membutuhkannya untuk keduanya, tetapi belum memberi tag untuk ini.

Lance Roberts
sumber

Jawaban:

87

Jika yang Anda maksud adalah sudut di mana P1 adalah puncaknya maka gunakan Hukum Cosinus harus bekerja:

arccos((P 12 2 + P 13 2 - P 23 2 ) / (2 * P 12 * P 13 ))

dimana P 12 adalah panjang segmen dari P1 ke P2, dihitung dengan

akar persegi ((P1 x - P2 x ) 2 + (P1 y - P2 y ) 2 )

Lance Roberts
sumber
@Rafa Firenze cos ^ -1 adalah notasi umum untuk acos, tetapi acos kurang ambigu. en.wikipedia.org/wiki/Inverse_trigonometric_functions
geon
Saya akan meninggalkan edit karena tidak ada salahnya, tetapi memiliki gelar Matematika / CS / EE, cos ^ -1 tentu saja merupakan notasi yang paling umum.
Lance Roberts
1
Hanya sedikit bahasa yang menggunakan tanda sisipan untuk 'kekuatan', jadi jika Anda tidak ingin menyebutnya arcos, cukup ketik cos⁻¹. (Jika Anda menggunakan sistem operasi komersial yang mempersulit pengetikan eksponen, saya perkirakan akan ada aplikasi keycaps yang dapat Anda beli, atau mungkin plugin browser yang dapat Anda instal. Atau Anda dapat menelusuri web dan menyalin dan menempel.)
Michael Scheper
1
@MichaelScheper, saya hanya menggunakan tanda sisipan di komentar di mana html dibatasi. Saya pasti hanya akan menggunakan notasi sub / superskrip dalam jawaban yang sebenarnya.
Lance Roberts
47

Ini menjadi sangat sederhana jika Anda menganggapnya sebagai dua vektor, satu dari titik P1 ke P2 dan satu dari P1 ke P3

jadi:
a = (p1.x - p2.x, p1.y - p2.y)
b = (p1.x - p3.x, p1.y - p3.y)

Anda kemudian dapat membalik rumus perkalian titik:
produk titik
untuk mendapatkan sudutnya:
sudut antara dua vektor

Ingat itu produk titikberarti: a1 * b1 + a2 * b2 (hanya 2 dimensi di sini ...)

Andrea Ambu
sumber
1
Ah besarnya vektor
Daniel Little
Periksa solusi atan2.
Luc Boissaye
25

Cara terbaik untuk menangani komputasi sudut adalah dengan menggunakan atan2(y, x)titik tertentu yang x, ymengembalikan sudut dari titik tersebut dan X+sumbu sehubungan dengan asal.

Mengingat bahwa perhitungannya adalah

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

yaitu Anda pada dasarnya menerjemahkan dua poin dengan -P1(dengan kata lain Anda menerjemahkan semuanya sehingga P1berakhir di asal) dan kemudian Anda mempertimbangkan perbedaan sudut absolut dari P3dan dariP2 .

Keuntungan dari atan2adalah bahwa lingkaran penuh terwakili (Anda bisa mendapatkan angka antara -π dan π) di mana sebagai gantinya acosAnda perlu menangani beberapa kasus tergantung pada tanda untuk menghitung hasil yang benar.

Satu-satunya titik tunggal untuk atan2adalah (0, 0)... yang berarti bahwa keduanya P2dan P3harus berbeda dari P1karena dalam kasus itu tidak masuk akal untuk berbicara tentang sudut.

6502
sumber
Terima kasih atas jawaban anda. Itulah yang saya cari. Solusi sederhana dan Anda bisa dengan mudah mendapatkan sudut berlawanan arah jarum jam jika saya hanya menambahkan 2pi ketika nilainya negatif.
Mario
@marcpt: atan2persis seperti yang dibutuhkan untuk masalah ini, tetapi sepertinya kebanyakan orang yang mendapatkan pertanyaan ini tidak dapat membaca atau tidak dapat memahami mengapa acossolusi berbasis itu buruk. Cukup beruntung bagi saya, saya meninggalkan fase "seseorang yang salah di internet" ( xkcd.com/386 ) bertahun-tahun yang lalu dan saya tidak akan memulai pertarungan untuk membela yang sudah jelas :-)
6502
Terima kasih telah menunjukkan hal ini, tetapi dapatkah Anda menangani 3D dengan cara ini?
nicoco
1
@nicoco: dalam tiga dimensi bagaimana Anda mendefinisikan sudut? Lebih spesifiknya, bisakah sudutnya menjadi negatif atau lebih dari pi (180 derajat)? Dua vektor non-paralel dalam 3d mendefinisikan sebuah bidang, tetapi bidang tersebut dapat "dilihat" dari dua sisi: dari satu sisi A akan muncul "ke kiri" dari B dan dari sisi lain akan muncul "ke kanan" .. .
6502
@ 6505 Terima kasih atas jawaban Anda, saya memposting sebelum memikirkan masalah saya secara menyeluruh. Aku mengerti sekarang.
nicoco
19

Izinkan saya memberi contoh dalam JavaScript, saya telah banyak berjuang dengan itu:

/**
 * 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));
}

Bonus: Contoh dengan kanvas HTML5

shaman.sir
sumber
5
Anda dapat membuatnya lebih efisien dengan melakukan lebih sedikit sqrtdan mengkuadratkan. Lihat jawaban saya di sini (ditulis dalam Ruby), atau dalam demo yang diperbarui ini (JavaScript).
Phrogz
Anda dapat menggunakan atan2 untuk solusi yang lebih sederhana.
Luc Boissaye
15

Pada dasarnya yang Anda miliki adalah dua vektor, satu vektor dari P1 ke P2 dan satu lagi dari P1 ke P3. Jadi yang Anda butuhkan hanyalah rumus untuk menghitung sudut antara dua vektor.

Lihat di sini untuk penjelasan yang baik dan rumusnya.

alt teks

Andre Miller
sumber
12

Jika Anda memikirkan P1 sebagai pusat lingkaran, Anda berpikir terlalu rumit. Anda memiliki segitiga sederhana, jadi soal Anda bisa diselesaikan dengan hukum cosinus . Tidak perlu transformasi koordinat kutub atau semacamnya. Misalkan jaraknya adalah P1-P2 = A, P2-P3 = B dan P3-P1 = C:

Sudut = arccos ((B ^ 2-A ^ 2-C ^ 2) / 2AC)

Yang perlu Anda lakukan adalah menghitung panjang jarak A, B, dan C. Itu mudah didapat dari koordinat x dan y titik dan teorema Pythagoras Anda.

Panjang = akar persegi ((X2-X1) ^ 2 + (Y2-Y1) ^ 2)

Treb
sumber
Saya agak bingung bagaimana sebenarnya menerapkan ini karena Anda memperlakukan P1 dll sebagai nilai individual daripada (x, y)
Dominic
@Dominic Tobias: Notasi P1-P2 = Atidak boleh dibaca sebagai "Untuk menghitung A, kurangi P2 dari P1", tetapi sebagai "Saya mendefinisikan A sebagai jarak dari P1 ke P2", yang kemudian dapat dihitung menggunakan persamaan kedua. Saya hanya ingin mendefinisikan singkatan jarak, agar persamaannya lebih mudah dibaca.
Treb
8

Saya mengalami masalah serupa baru-baru ini, hanya saja saya perlu membedakan antara sudut positif dan negatif. Jika ini berguna bagi siapa pun, saya merekomendasikan cuplikan kode yang saya ambil dari milis ini tentang mendeteksi rotasi melalui peristiwa sentuh untuk 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;
 }
Marc
sumber
7

Solusi Geometris Sangat Sederhana dengan Penjelasan

Beberapa hari yang lalu, seorang jatuh ke masalah yang sama & harus duduk dengan buku matematika. Saya memecahkan masalah dengan menggabungkan dan menyederhanakan beberapa rumus dasar.


Mari pertimbangkan angka ini-

sudut

Kita ingin tahu ϴ , jadi kita perlu cari tahu α dan β dulu. Sekarang, untuk setiap garis lurus-

y = m * x + c

Misalkan- A = (ax, ay) , B = (bx, by) , dan O = (ox, oy) . Jadi untuk garis 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)

Dengan cara yang sama, untuk baris OB -

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

Sekarang, kami membutuhkannya ϴ = β - α. Dalam trigonometri kami memiliki rumus-

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

Setelah mengganti nilai tan α(dari eqn-2) dan tan b(dari eqn-3) di eqn-4, dan menerapkan penyederhanaan kita dapatkan-

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

Begitu,

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

Hanya itu saja!


Sekarang, ambil gambar berikut-

sudut

C # atau, metode Java ini menghitung sudut ( ϴ ) -

    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;
    }
Minhas Kamal
sumber
Bagaimana metode ini dapat digunakan untuk segitiga sama sisi?
Vikrant
1
Nah, jawaban Anda berfungsi dengan baik sekarang. Itu adalah beberapa masalah logika di minggu kode saya sebelumnya.
Vikrant
6

Di Objective-C Anda bisa melakukannya dengan

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

Atau baca lebih lanjut di sini

Adeem Maqsood Basraa
sumber
7
Uh, tidak. Ada tiga titik, pusatnya tidak di (0,0), dan ini memberikan sudut segitiga siku-siku, bukan sudut puncak. Dan apa nama "xpoint" untuk suatu sudut?
Jim Balter
4

Anda menyebutkan sudut bertanda (-90). Dalam banyak aplikasi, sudut mungkin memiliki tanda (positif dan negatif, lihat http://en.wikipedia.org/wiki/Angle ). Jika titiknya (katakanlah) P2 (1,0), P1 (0,0), P3 (0,1) maka sudut P3-P1-P2 secara konvensional positif (PI / 2) sedangkan sudut P2-P1- P3 negatif. Menggunakan panjang sisi tidak akan membedakan antara + dan - jadi jika ini penting, Anda perlu menggunakan vektor atau fungsi seperti Math.atan2 (a, b).

Sudut juga dapat melampaui 2 * PI dan sementara ini tidak relevan dengan pertanyaan saat ini, cukup penting bahwa saya menulis kelas Sudut saya sendiri (juga untuk memastikan bahwa derajat dan radian tidak tercampur). Pertanyaan tentang apakah sudut1 lebih kecil dari sudut2 sangat bergantung pada bagaimana sudut didefinisikan. Mungkin juga penting untuk memutuskan apakah baris (-1,0) (0,0) (1,0) direpresentasikan sebagai Math.PI atau -Math.PI

peter.murray.rust
sumber
4

program demo sudut saya

Baru-baru ini, saya juga mengalami masalah yang sama ... Di Delphi Ini sangat mirip dengan 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;
antonio
sumber
2

Berikut adalah metode C # untuk mengembalikan sudut (0-360) berlawanan arah jarum jam dari horizontal untuk sebuah titik pada lingkaran.

    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, Paul

Paul
sumber
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
sumber
0

ADA jawaban sederhana untuk ini menggunakan matematika sekolah menengah ..

Katakanlah Anda memiliki 3 poin

Untuk mendapatkan sudut dari titik A ke B

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

Untuk mendapatkan sudut dari titik B ke C.

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

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

Saya baru saja menggunakan kode ini dalam proyek terakhir yang saya buat, ubah B menjadi P1 .. Anda mungkin juga menghapus "180 +" jika Anda mau

James Penner
sumber
-1

nah, jawaban lain sepertinya mencakup semua yang diperlukan, jadi saya ingin menambahkan ini jika Anda menggunakan JMonkeyEngine:

Vector3f.angleBetween(otherVector)

karena itulah yang saya cari di sini :)

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

}

Daniel Quinn
sumber