2010-04-18 26 views
12

There is a very handy set of 2d geometry utilities here.Come calcolare sia l'angolo positivo che quello negativo tra due linee?

L'angolo tra le linee presenta tuttavia un problema. Il risultato è sempre positivo. Devo rilevare sia gli angoli positivi che quelli negativi, quindi se una linea è 15 gradi "sopra" o "sotto" l'altra linea, la forma ovviamente sembra diversa.

La configurazione che ho è che una linea rimane stazionaria, mentre l'altra linea ruota, e ho bisogno di capire in che direzione sta ruotando, confrontandola con la linea stazionaria.

MODIFICA: in risposta al commento di swestrup di seguito, la situazione è in realtà che ho una singola riga e registro la sua posizione di partenza. La linea quindi ruota dalla sua posizione di partenza, e ho bisogno di calcolare l'angolo dalla sua posizione iniziale alla posizione corrente. E. se è ruotato in senso orario, è una rotazione positiva; se in senso antiorario, quindi negativo. (O viceversa.)

Come migliorare l'algoritmo in modo che restituisca l'angolo come positivo o negativo a seconda di come vengono posizionate le linee?

+0

Dato due segmenti di linea che si intersecano in modo arbitrario, è difficile determinare quale sia "sopra" l'altro e persino quale angolo misurare, poiché di solito formano una forma "X". Stai forse usando sempre due linee con un punto di partenza comune? Ciò lo rende molto più semplice. – swestrup

+0

Spiacente, ho chiarito. In realtà sto parlando di una singola linea e della sua rotazione rispetto alla sua posizione di partenza. – Jaanus

+0

Quale intervallo desideri? Vuoi il pieno -pi per pi o sei felice con solo -pi/2 per pi/2, ti interessa la direzione delle linee o no? – Troubadour

risposta

8

@ di duffymo è corretto, ma se non si desidera implementare cross-prodotto, è possibile utilizzare la funzione atan2. Questo restituisce un angolo tra - π e π, e lo puoi usare su ciascuna delle linee (o più precisamente i vettori che rappresentano le linee).

Se si ottiene un angolo θ per la prima (linea fissa), dovrete per normalizzare l'angolo φ per la seconda linea per essere tra θ-π e θ + π (con l'aggiunta di & plusmn; 2 π). L'angolo tra le due linee sarà quindi φ - θ.

+1

Tutta questa roba vettoriale mi ha fatto esplodere la testa. (Scusa, ho dimenticato molto della mia matematica ...) Ora, ho appena calcolato atan2 per entrambe le linee e confrontarle, funziona bene. Grazie. – Jaanus

+0

Anche l'implementazione di seguito. – Jaanus

0

Tale funzione sta lavorando in RADS

Ci sono RADS 2pi in un cerchio completo (360 gradi)

Così credo che l'answear che stai cercando è semplicemente il valore restituito - 2pi

Se stai chiedendo che una funzione restituisca entrambi i valori nello stesso momento, allora stai chiedendo di interrompere la lingua, una funzione può solo restituire un singolo valore. È possibile passare due puntatori che può essere utilizzato per impostare il valore in modo che la modifica possa persistere dopo che la funzione è terminata e il programma può continuare a funzionare. Ma non è davvero un modo sensato per risolvere questo problema.

Modifica

appena notato che la funzione in realtà converte i Rad per gradi in quanto restituisce il valore. Ma lo stesso principio funzionerà.

7

Questo è un problema facile che coinvolge i vettori 2D. Il seno dell'angolo tra due vettori è correlato al prodotto incrociato tra i due vettori. E "sopra" o "sotto" è determinato dal segno del vettore prodotto dal prodotto incrociato: se si incrociano due vettori A e B e il prodotto incrociato prodotto è positivo, allora A è "sotto" B; se è negativo, A è "sopra" B. Vedi Mathworld per i dettagli.

Ecco come potrei codice in Java:

package cruft; 

import java.text.DecimalFormat; 
import java.text.NumberFormat; 

/** 
* VectorUtils 
* User: Michael 
* Date: Apr 18, 2010 
* Time: 4:12:45 PM 
*/ 
public class VectorUtils 
{ 
    private static final int DEFAULT_DIMENSIONS = 3; 
    private static final NumberFormat DEFAULT_FORMAT = new DecimalFormat("0.###"); 

    public static void main(String[] args) 
    { 
     double [] a = { 1.0, 0.0, 0.0 }; 
     double [] b = { 0.0, 1.0, 0.0 }; 

     double [] c = VectorUtils.crossProduct(a, b); 

     System.out.println(VectorUtils.toString(c)); 
    } 

    public static double [] crossProduct(double [] a, double [] b) 
    { 
     assert ((a != null) && (a.length >= DEFAULT_DIMENSIONS) && (b != null) && (b.length >= DEFAULT_DIMENSIONS)); 

     double [] c = new double[DEFAULT_DIMENSIONS]; 

     c[0] = +a[1]*b[2] - a[2]*b[1]; 
     c[1] = +a[2]*b[0] - a[0]*b[2]; 
     c[2] = +a[0]*b[1] - a[1]*b[0]; 

     return c; 
    } 

    public static String toString(double [] a) 
    { 
     StringBuilder builder = new StringBuilder(128); 

     builder.append("{ "); 

     for (double c : a) 
     { 
      builder.append(DEFAULT_FORMAT.format(c)).append(' '); 
     } 

     builder.append("}"); 

     return builder.toString(); 
    } 
} 

Controllare il segno della terza componente. Se è positivo, A è "sotto" B; se è negativo, A è "sopra" B - purché i due vettori si trovino nei due quadranti a destra dell'asse y. Ovviamente, se sono entrambi nei due quadranti a sinistra dell'asse y, è vero il contrario.

È necessario pensare alle nozioni intuitive di "sopra" e "sotto". Cosa succede se A è nel primo quadrante (0 < = θ < = 90) e B è nel secondo quadrante (90 < = θ < = 180)? "Sopra" e "sotto" perdono il loro significato.

La linea ruota quindi dalla sua posizione iniziale, e devo calcolare l'angolo dal suo avviamento posizione a posizione attuale. Es. Se ha ruotato in senso orario, è rotazione positiva; se in senso antiorario, quindi negativo. (O viceversa.)

Questo è esattamente ciò che il prodotto incrociato è per. Il segno del terzo componente è positivo in senso antiorario e negativo in senso orario (guardando verso il basso il piano di rotazione).

+0

Sembra fantastico, ma la conversione della moltiplicazione del vettore in codice è un po 'oltre me, speravo di essere pigro e ottenerlo in forma di codice: P ie input: cosa ho, coordinate di endpoint di 2 linee; uscita: angolo firmato. – Jaanus

+0

Dai ... la moltiplicazione dei vettori è solo una moltiplicazione. Sai programmare una ricetta? Due vettori in, un vettore fuori. In che lingua lo vuoi? – duffymo

+0

Pseudocodice o C va bene – Jaanus

1

Un metodo 'veloce e sporco' che è possibile utilizzare è quello di introdurre una terza linea di riferimento R. Quindi, date due linee A e B, calcolare gli angoli tra A e R e poi B e R, e sottrarli.

Questo fa circa il doppio del calcolo effettivamente necessario, ma è facile da spiegare e eseguire il debug. risposta

+0

Capisco che atan2 fondamentalmente fa esattamente questo, con l'asse x che è la linea di riferimento. – Jaanus

19

Ecco l'implementazione del suggerimento di brainjam. (Funziona con i miei vincoli che la differenza tra le linee è garantito per essere abbastanza che non c'è bisogno di normalizzare qualcosa di piccolo.)

CGFloat angleBetweenLinesInRad(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) { 
    CGFloat a = line1End.x - line1Start.x; 
    CGFloat b = line1End.y - line1Start.y; 
    CGFloat c = line2End.x - line2Start.x; 
    CGFloat d = line2End.y - line2Start.y; 

    CGFloat atanA = atan2(a, b); 
    CGFloat atanB = atan2(c, d); 

    return atanA - atanB; 
} 

mi piace che è concisa. La versione vettoriale sarebbe più concisa?

+3

Secondo me, questa dovrebbe essere la risposta accettata. – Emil

+1

Ho accettato la risposta di brainjam dato che ho davvero chiesto l'algoritmo, e in generale non mi piace accettare le mie risposte, soprattutto se è un derivato di un'altra risposta, mi piace dare credito. – Jaanus

+0

+1 Questo è geniale! Mi ha salvato ore di valore di calcoli geometrici e debugging! –

Problemi correlati