2012-08-10 13 views
13

Come scrivere questa funzione? Qualche esempio apprezzatoCome verificare se un punto si trova su una linea tra altri 2 punti

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

Ci sono alcune buone risposte qui sotto, ma ho pensato di sottolineare che si dovrebbe guardare fuori per il galleggiamento problemi di precisione del punto. Qualunque sia il metodo che utilizzi, probabilmente dovrai consentire una piccola quantità di errori quando, ad esempio, verifichi se due pendenze diverse sono le stesse. –

+0

@Adrian McCarthy: Questo è il problema principale con i metodi basati sulla pendenza. La pendenza cambia in modo non uniforme con l'angolo: più la linea si avvicina alla verticale, più velocemente aumenta la pendenza (nemmeno menzionando il caso speciale con linea verticale e quasi verticale). Semplicemente non esiste una buona strategia basata sulla pendenza.Eviterei i metodi basati sulla pendenza a tutti i costi. – AnT

risposta

34

Supponendo che point1 e point2 siano diversi, in primo luogo si controlla se il punto si trova sulla linea. Per questo è sufficiente un "cross-product" di vettori point1 -> currPoint e 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; 

Il punto si trova sulla linea se e solo se cross è uguale a zero.

if (cross != 0) 
    return false; 

Ora, come lei sa che il punto non si trovano sulla linea, è il momento di verificare se esso si trova tra i punti originali. Questo può essere fatto facilmente confrontando le coordinate x, se la linea è "più orizzontale che verticale", o y coordinate altrimenti

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; 

noti che l'algoritmo precedente se interamente integrale se i dati di input è solidale, cioè non richiede calcoli in virgola mobile per l'input intero. Attenzione al potenziale trabocco quando si calcola lo cross.

P.S. Questo algoritmo è assolutamente preciso, il che significa che rifiuterà punti che si trovano molto vicino alla linea, ma non precisamente sulla linea. A volte questo non è ciò che è necessario. Ma questa è una storia diversa.

+5

È possibile ridurre la precisione dell'algoritmo implementando la soglia nella convalida del prodotto incrociato Quindi se il prodotto incrociato è quasi zero, il punto è quasi sulla linea 'threshold = 0.1; if (abs (cross)> threshold) restituisce false; '. – Matej

+0

Potremmo semplificare questo? Dal momento che sappiamo che è ON the line, perché ci importa se è più orizzontale che verticale? Ci può essere solo un valore y per ogni x data sulla linea, quindi se 'currPoint.x' è tra' point1.x' e 'point2.x', come potrebbe essere ovunque tranne che sulla linea? – mkirk

+1

@mkirk: "Può esserci solo un valore y per ogni x specificata sulla linea" - non vero per una linea verticale. Se il segmento è strettamente verticale, il controllo dell'intervallo per 'x' non produce una risposta significativa. Sì, si può sempre controllare l'intervallo 'x', eccetto che per un segmento strettamente verticale si deve invece controllare l'intervallo' y'. Il mio approccio con "più orizzontale"/"più verticale" è solo una generalizzazione equilibrata di ciò. – AnT

3

Questo è indipendente da Javascript. Provate il seguente algoritmo, con i punti p1 = point1 e p2 = point2, e il vostro terzo punto essere 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 

Se si vuole essere sicuro che sia sul segmento linea tra P1 e P2, nonché :

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

si vuole verificare se la pendenza point1-currPoint è lo stesso che la pendenza da currPoint a point2, quindi:

m1 = (currY - p1Y)/(currX - p1X); 
m2 = (p2Y - currY)/(p2X - currX); 
0.123.516,41 mila

Anche voi volete verificare se currPoint è all'interno della scatola creato da altri due, in modo da:

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

Edit: Questo non è un metodo molto buono; guarda maxim1000's solution per un modo molto più corretto.

+0

Sì, ho appena realizzato il mio errore. Penso di poter aggiungere un altro vincolo per risolverlo. –

+0

Ma i dati di input non dichiarano che 'p1X <= p2X' e' p1Y <= p2Y' (inoltre, semplicemente non può essere vero in tutti i casi). Tuttavia, il controllo finale funzionerà correttamente solo se tali condizioni sono soddisfatte. Inoltre, il tuo algoritmo si basa sul confronto diretto e preciso dei valori in virgola mobile 'm1' e' m2'. Inutile dire che questo semplicemente non funzionerà a causa della natura imprecisa dei calcoli in virgola mobile. – AnT

+0

... senza nemmeno menzionare che il metodo basato su inclinazione non può gestire le linee verticali. Che dire della divisione per zero? – AnT

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

Ma attenzione se si dispone di valori in virgola mobile, le cose sono diverse per loro ...

+4

Molto bello, con valori in virgola mobile (come in JavaScript) si può fare qualcosa come 'return distanceAC + distanceBC - distanceAB mikhail

+1

Questo metodo è più stabile della risposta di AnT sopra con il valore incrociato. – CodePlumber

+0

Ma ha bisogno di molta più potenza di calcolo (3 volte radice quadrata). – Chrisstar

Problemi correlati