2013-08-01 18 views
7

Muszę obliczyć kąt między 3 punktami. W tym celu, to należy wykonać następujące czynności:Nieprawidłowy kąt, zła strona obliczona

  1. chwycić 3 punkty (poprzednie, prądu i następne, to jest w pętli)
  2. obliczenia odległości pomiędzy punktami z Pitagorasa
  3. obliczyć kąt w Math.acos

Wydaje się, że działa to dobrze w przypadku kształtów bez aniołów powyżej 180 stopni, jednak jeśli kształt ma taki kąt, oblicza krótszy bok. Oto ilustracja, aby pokazać o co mi chodzi (czerwone wartości są źle):

A scatch illustrating what goes wrong with the calculations

Jest to kod, który wykonuje obliczenia:

// Pythagoras for calculating distance between two points (2D) 
pointDistance = function (p1x, p1y, p2x, p2y) { 
    return Math.sqrt((p1x - p2x)*(p1x - p2x) + (p1y - p2y)*(p1y - p2y)); 
}; 

// Get the distance between the previous, current and next points 
// vprev, vcur and vnext are objects that look like this: 
//  { x:float, y:float, z:float } 
lcn = pointDistance(vcur.x, vcur.z, vnext.x, vnext.z); 
lnp = pointDistance(vnext.x, vnext.z, vprev.x, vprev.z); 
lpc = pointDistance(vprev.x, vprev.z, vcur.x, vcur.z); 

// Calculate and print the angle 
Math.acos((lcn*lcn + lpc*lpc - lnp*lnp)/(2*lcn*lpc))*180/Math.PI 

Czy jest coś złego w kodzie, czy Zapominam o zrobieniu czegoś, czy też powinienem to zrobić zupełnie inaczej?

+2

Można użyć [ 'Math.atan2()'] (https://developer.mozilla.org/en-US/docs/Web/ JavaScript/Reference/Global_Objects/Math/atan2) w celu obliczenia kątów od współrzędnych, ułatwi obliczenia. Możesz zobaczyć działający przykład [w tym skrzypcach] (http://jsfiddle.net/92jWG/6/). – Teemu

+0

W jaki sposób trójkąt może mieć kąt większy niż 180 stopni?Suma kątów wynosi 180 stopni ..... – Jiminion

+1

Problem polega na tym, że 'cos (90)' i 'cos (270)' są zarówno 0, więc przy wykonywaniu acos (0) będzie musiał wybrać, co dać i najwyraźniej wybiera 90 jako preferencję. Jednym ze sposobów rozwiązania tego problemu może być użycie atan2, jak sugeruje Teemu i wykreślenie kątów każdego segmentu linii i odjęcie ich. Myślę, że jeśli zrobisz to we właściwej kolejności, powinno ci to dostarczyć pełnych informacji (chociaż może potrzebować normalizacji, ponieważ może to być od -360 do 360). – Chris

Odpowiedz

4

Oto twoja matematyka i obliczenia są idealne. Twój problem dotyczy większości kalkulatorów, czyli orientacji. Co chciałbym zrobić, to dowiedzieć się, czy punkt leży na lewo lub na prawo od wektora wykonane przez pierwsze dwa punkty za pomocą tego kodu, który znalazłem z

Determine which side of a line a point lies

isLeft = function(ax,ay,bx,by,cx,cy){ 
return ((bx - ax)*(cy - ay) - (by - ay)*(cx - ax)) > 0; 
} 

Gdzie ax i ay makijażu Twój pierwszy punkt bx twój drugi i cx twój trzeci.

jeśli jest w lewo wystarczy dodać 180 do kąta

+1

Myślę, że masz na myśli 'angle =! IfLeft (....)? 360 - kąt: kąt; "zamiast" kąt + 180 ". – Broxzier

+0

Sądzę, że nie przeszedłem dokładnie tego pytania, ale ustalenie, czy jest po lewej, czy na prawo od linii, jest kluczem. Mam nadzieję, że odrobina kodu pomogła powodzenia! – ZJS

0

Musisz sprawdzić warunki brzegowe (najwyraźniej, jeśli punkty są współliniowe) i zastosować odpowiednie obliczenia, aby znaleźć kąt. Ponadto trójkąt nie może mieć żadnego (wewnętrznego) kąta większego niż 180 stopni. Suma kąta trójkąta wynosi 180 stopni.

+0

Dobrym początkiem może być ustalenie, która odległość jest najdłuższa. – Jiminion

+0

Granica jest zawsze po lewej stronie, jeśli przechodzisz od poprzedniego przez bieżący do następnego, ale jak mogę tego użyć, jakie obliczenia muszę wykonać? – Broxzier

+1

@Jim: Nie jestem pewien, czy znalezienie najdłuższego jest istotne? – Chris

1

Mam pracę, ale niekoniecznie krótki przykład jak to może działać:

var point1x = 0, point1y = 0, 
    point2x = 10, point2y = 10, 
    point3x = 20, point3y = 10, 
    point4x = 10, point4y = 20; 

var slope1 = Math.atan2(point2y-point1y,point2x-point1x)*180/Math.PI; 
var slope2 = Math.atan2(point3y-point2y,point3x-point2x)*180/Math.PI; 
var slope3 = Math.atan2(point4y-point3y,point4x-point3x)*180/Math.PI; 
alert(slope1); 
alert(slope2); 
alert(slope3); 
var Angle1 = slope1-slope2; 
var Angle2 = slope2-slope3; 
alert(180-Angle1); 
alert(180-Angle2); 

(patrz http://jsfiddle.net/ZUESt/1/)

Aby wyjaśnić wielu krokach slopeN zmienne są stoki osoby Segmenty linii. AngleN to kwota obrócona na każdym skrzyżowaniu (tj. Punkt N + 1). Dodatni kąt to skręt w prawo, a kąt ujemny to skręt w lewo.

Następnie można odjąć ten kąt od 180, aby uzyskać rzeczywisty kąt wewnętrzny, który chcesz.

Należy zauważyć, że kod ten można oczywiście skompresować, a pięć linii to jedynie wyprowadzanie zmiennych, aby zobaczyć, co się dzieje. Pozwolę ci się martwić o optymalizację na własny użytek, ponieważ jest to dowód koncepcji.

+0

A teraz zastanawiam się, czy używanie produktu dot może być lepszym sposobem na zrobienie tego tak czy inaczej ... – Chris

+1

Nie. Dot Produkt ma taki sam problem, skutecznie tylko daje ci cosinus kąta, który następnie cierpi na ten sam problem, co wcześniej. – Chris