2012-08-10 2 views
13

Wie würde ich diese Funktion schreiben? Beliebige Beispiele geschätztWie überprüfe ich, ob ein Punkt auf einer Linie zwischen zwei anderen Punkten liegt?

function isPointBetweenPoints(currPoint, point1, point2):Boolean { 

    var currX = currPoint.x; 
    var currY = currPoint.y; 

    var p1X = point1.x; 
    var p1y = point1.y; 

    var p2X = point2.x; 
    var p2y = point2.y; 

    //here I'm stuck 
} 
+1

Es gibt einige gute Antworten unten, aber ich dachte, dass ich darauf hinweisen würde, dass Sie achten sollten für Schwimm punktgenaue Probleme. Unabhängig davon, welche Methode Sie verwenden, müssen Sie wahrscheinlich eine kleine Fehlermenge zulassen, wenn Sie zum Beispiel testen, ob zwei verschiedene Steigungen gleich sind. –

+0

@Adrian McCarthy: Das ist das Hauptproblem bei den pistenbasierten Methoden. Die Steigung ändert sich ungleichmäßig mit dem Winkel: Je näher die Linie an der Vertikalen liegt, desto schneller wächst die Steigung (nicht einmal der Spezialfall mit vertikaler und fast vertikaler Linie). Es gibt einfach keine gute pistenbasierte Strategie.Ich würde Pisten-basierte Methoden um jeden Preis vermeiden. – AnT

Antwort

34

Angenommen, point1 und point2 sind unterschiedlich, prüfen Sie zuerst, ob der Punkt auf der Linie liegt. Dazu benötigen Sie einfach ein "Cross-Produkt" der Vektoren point1 -> currPoint und point1 -> point2.

dxc = currPoint.x - point1.x; 
dyc = currPoint.y - point1.y; 

dxl = point2.x - point1.x; 
dyl = point2.y - point1.y; 

cross = dxc * dyl - dyc * dxl; 

Ihr Punkt liegt auf der Linie, wenn und nur wenn cross gleich Null ist.

if (cross != 0) 
    return false; 

Nun, wie Sie wissen, dass der Punkt auf der Linie nicht liegt, ist es an der Zeit zu prüfen, ob es zwischen den ursprünglichen Punkten liegt. Dies kann leicht durch einen Vergleich der x Koordinaten durchgeführt werden, wenn die Leitung „mehr horizontal als vertikal“, oder y Koordinaten sonst

if (abs(dxl) >= abs(dyl)) 
    return dxl > 0 ? 
    point1.x <= currPoint.x && currPoint.x <= point2.x : 
    point2.x <= currPoint.x && currPoint.x <= point1.x; 
else 
    return dyl > 0 ? 
    point1.y <= currPoint.y && currPoint.y <= point2.y : 
    point2.y <= currPoint.y && currPoint.y <= point1.y; 

anzumerken, dass der obige Algorithmus, wenn vollständig integral wenn die Eingangsdaten integriert ist, ist es nämlich erfordert keine Gleitkommaberechnungen für ganzzahlige Eingaben. Vorsicht vor einem möglichen Überlauf bei der Berechnung cross obwohl.

P.S. Dieser Algorithmus ist absolut präzise, ​​dh Punkte, die sehr nahe an der Linie, aber nicht genau an der Linie liegen, werden verworfen. Manchmal ist das nicht nötig. Aber das ist eine andere Geschichte.

+5

Sie können die Genauigkeit des Algorithmus verringern, indem Sie den Schwellenwert bei der Validierung des Kreuzprodukts implementieren. Wenn also das Kreuzprodukt fast Null ist, befindet sich der Punkt fast auf der Linie "threshold = 0.1; if (abs (cross)> threshold) gibt false zurück; '. – Matej

+0

Könnten wir das vereinfachen? Da wir wissen, dass es auf der Linie ist, warum kümmern wir uns, wenn es mehr horizontal als vertikal ist? Es kann nur einen y-Wert für ein beliebiges x auf der Linie geben, also, wenn 'currPoint.x' zwischen' point1.x' und 'point2.x' liegt, wie könnte es irgendwo anders als auf der Linie sein? – mkirk

+1

@mkirk: "Es kann nur einen y-Wert für jedes gegebene x auf der Linie geben" - gilt nicht für eine vertikale Linie. Wenn das Segment streng vertikal ist, ergibt die Bereichsüberprüfung für "x" keine sinnvolle Antwort. Ja, man kann immer den 'x' Bereich überprüfen, außer dass man für ein streng vertikales Segment stattdessen den' y' Bereich überprüfen muss. Mein Ansatz mit "mehr horizontal"/"mehr vertikal" ist nur eine ausgewogene Verallgemeinerung davon. – AnT

3

Dies ist unabhängig von Javascript. Versuchen Sie, den folgenden Algorithmus, mit Punkten p1 = Punkt 1 und p2 = point2 und Ihre dritte Punkt ist p3 = currPoint:

v1 = p2 - p1 
v2 = p3 - p1 
v3 = p3 - p2 
if (dot(v2,v1)>0 and dot(v3,v1)<0) return between 
else return not between 

Wenn Sie es auf der Liniensegment zwischen P1 und P2 als auch sicher sein wollen :

v1 = normalize(p2 - p1) 
v2 = normalize(p3 - p1) 
v3 = p3 - p2 
if (fabs(dot(v2,v1)-1.0)<EPS and dot(v3,v1)<0) return between 
else return not between 
3

Sie wollen, ob die Steigung so von currPoint zu point2, ist das gleiche wie die Steigung point1-currPoint überprüfen:

m1 = (currY - p1Y)/(currX - p1X); 
m2 = (p2Y - currY)/(p2X - currX); 

Sie wollen auch, ob currPoint überprüfen, ist im Strafraum von den beiden anderen erstellt, so:

return (m1 == m2) && (p1Y <= currY && currY <= p2Y) && (p1X <= currX && currX <= p2X); 

Edit: Das ist nicht eine sehr gute Methode; Blick auf maxim1000's solution für eine viel korrekte Art und Weise.

+0

Ja, ich habe gerade meinen Fehler erkannt. Ich denke, ich kann eine weitere Einschränkung hinzufügen, um das zu beheben. –

+0

Aber die Eingabedaten sagen nicht, dass 'p1X <= p2X' und' p1Y <= p2Y' (außerdem kann es einfach nicht in allen Fällen wahr sein). Ihre abschließende Prüfung funktioniert jedoch nur dann ordnungsgemäß, wenn diese Bedingungen erfüllt sind. Darüber hinaus benötigt Ihr Algorithmus einen direkten präzisen Vergleich der Gleitkommawerte 'm1' und' m2'. Es ist unnötig zu sagen, dass dies aufgrund der ungenauen Natur der Gleitkommaberechnungen einfach nicht funktioniert. – AnT

+0

... nicht einmal erwähnen, dass die slope-basierte Methode nicht mit vertikalen Linien umgehen kann. Was ist mit Division durch Null? – AnT

19
Distance(point1,currPoint)+Distance(currPoint,point2)==Distance(point1,point2) 

Aber Vorsicht, wenn Sie Gleitkommazahlen haben, sind die Dinge anders für sie ...

+4

Sehr schön, mit Fließkommawerten (wie in JavaScript) können Sie etwas tun wie 'return distanceAC + distanceBC - distanceAB mikhail

+1

Diese Methode ist stabiler als die obige Antwort mit dem cross-Wert. – CodePlumber

+0

Aber es braucht viel mehr Rechenleistung (3 mal Quadratwurzel). – Chrisstar