2010-04-18 21 views
12

There is a very handy set of 2d geometry utilities here.¿Cómo se calcula el ángulo positivo y el negativo entre dos líneas?

El angleBetweenLines tiene un problema, sin embargo. El resultado es siempre positivo. Necesito detectar ángulos positivos y negativos, así que si una línea está 15 grados "arriba" o "debajo" de la otra línea, la forma obviamente se ve diferente.

La configuración que tengo es que una línea permanece estacionaria, mientras que la otra línea gira, y necesito entender en qué dirección está girando, al compararla con la línea estacionaria.

EDITAR: en respuesta al comentario de Swestrup a continuación, la situación es que tengo una sola línea, y grabo su posición inicial. La línea luego gira desde su posición inicial, y necesito calcular el ángulo desde su posición inicial hasta la posición actual. Por ejemplo, si ha girado en el sentido de las agujas del reloj, es una rotación positiva; si es en sentido antihorario, entonces negativo. (O viceversa).

¿Cómo mejorar el algoritmo para que devuelva el ángulo como positivo o negativo dependiendo de cómo se colocan las líneas?

+0

Dados dos segmentos de línea que se intersectan arbitrariamente, es difícil determinar cuál está 'por encima' del otro e incluso qué ángulo medir, porque típicamente forman una 'X'. ¿Estás utilizando quizás dos líneas con un punto de partida común? Eso lo hace mucho más directo. – swestrup

+0

Lo siento, lo aclaré. De hecho, estoy hablando de una sola línea y su rotación relativa a su posición inicial. – Jaanus

+0

¿Qué rango quieres? ¿Desea obtener el pi completo o está satisfecho con solo -pi/2 a pi/2, es decir, si le importa la dirección de las líneas o no? – Troubadour

Respuesta

8

@ La respuesta de duffymo es correcta, pero si no desea implementar el producto cruzado, puede usar la función atan2. Esto devuelve un ángulo entre - π y π, y puede usarlo en cada una de las líneas (o más precisamente, los vectores que representan las líneas).

Si obtiene un ángulo θ por primera (línea fija), que tendrá que normalizar el ángulo φ para la segunda línea a estar entre θ - π y θ + π (añadiendo y plusmn; 2 π). El ángulo entre las dos líneas será entonces φ - θ.

+1

Todo este vector explotó mi cabeza. (Lo siento, olvidé muchas de mis matemáticas ...) Ahora, simplemente calculo atan2 para ambas líneas y las comparo, funciona bien. Gracias. – Jaanus

+0

También implementación a continuación. – Jaanus

0

Esa función está trabajando en RADS

Hay RADS 2pi en un círculo completo (360 grados)

Por lo tanto creo que el answear que busca es simplemente el valor devuelto - 2 pi

Si está pidiendo que esa función devuelva ambos valores al mismo tiempo, entonces está pidiendo que se rompa el idioma, una función solo puede devolver un solo valor. Podría pasarle dos punteros que puede usar para establecer el valor de modo que el cambio pueda persistir después de que la función finalice y su programa pueda seguir funcionando. Pero no es realmente una forma sensata de resolver este problema.

Editar

di cuenta de que la función se convierte en realidad los RAD Grados a medida que retorna el valor. Pero el mismo principio funcionará.

7

Este es un problema fácil que involucra vectores 2D. El seno del ángulo entre dos vectores está relacionado con el producto cruzado entre los dos vectores. Y "arriba" o "abajo" está determinado por el signo del vector producido por el producto cruzado: si cruza dos vectores A y B, y el producto cruzado producido es positivo, entonces A está "debajo" B; si es negativo, A está "arriba" B. Ver Mathworld para más detalles.

Así es como podría codificar en 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(); 
    } 
} 

Comprobar la señal de la tercera componente. Si es positivo, A está "debajo" B; si es negativo, A está "arriba" B - siempre y cuando los dos vectores estén en los dos cuadrantes a la derecha del eje y. Obviamente, si ambos están en los dos cuadrantes a la izquierda del eje y, el reverso es verdadero.

Tiene que pensar en sus nociones intuitivas de "arriba" y "abajo". ¿Qué sucede si A está en el primer cuadrante (0 < = θ < = 90) y B está en el segundo cuadrante (90 < = θ < = 180)? "Arriba" y "abajo" pierden su significado.

La línea a continuación, gira desde su posición de partida , y necesito calcular el ángulo de su partida posición a la posición actual. Por ejemplo, si ha girado en el sentido de las agujas del reloj, es rotación positiva; si en sentido antihorario, entonces negativo. (O viceversa).

Esto es exactamente para lo que es el producto cruzado. El signo del 3er componente es positivo para el sentido contrario a las agujas del reloj y negativo para el sentido horario (mientras miras hacia abajo al plano de rotación).

+0

Esto suena genial, pero la conversión de multiplicación de vectores a código está un poco más allá de mí, estaba esperando ser flojo y conseguirlo en forma de código: P ie entrada: lo que tengo, coordenadas de los puntos finales de 2 líneas; salida: ángulo firmado. – Jaanus

+0

C'mon ... la multiplicación de vectores es solo una multiplicación. ¿Puedes codificar una receta? Dos vectores en, un vector fuera. ¿En qué idioma lo quieres? – duffymo

+0

Pseudocódigo o C está bien – Jaanus

1

Un método "rápido y sucio" que puede utilizar es introducir una tercera línea de referencia R. Entonces, dos líneas A y B, calcule los ángulos entre A y R y luego B y R, y restelas.

Esto hace aproximadamente el doble de cálculo de lo que realmente es necesario, pero es fácil de explicar y depurar.

+0

Entiendo que atan2 básicamente hace exactamente eso, con el eje x siendo la línea de referencia. – Jaanus

19

Aquí está la implementación de la sugerencia de brainjam. (Funciona con mis limitaciones que la diferencia entre las líneas se garantiza que sea suficiente con que no hay necesidad de normalizar cualquier cosa pequeña.)

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

me gusta que sea concisa. ¿La versión del vector sería más conciso?

+3

En mi opinión, esta debería ser la respuesta aceptada. – Emil

+1

Acepté la respuesta de brainjam ya que realmente pedí el algoritmo, y en general no me gusta aceptar mis propias respuestas, especialmente si es una derivación de otra respuesta, me gusta dar crédito. – Jaanus

+0

+1 ¡Esto es ingenioso! Me ahorró horas de cálculos geométricos y depuración! –

Cuestiones relacionadas