2010-04-25 18 views
23

NB: Presentaré esta pregunta en grados puramente por simplicidad, radianes, grados, rodamiento de cero diferente, el problema es esencialmente el mismo.Interpolación de rotación

¿Alguien tiene alguna idea sobre el código detrás de la interpolación rotacional? Dada una función de interpolación lineal: Lerp (de, a, cantidad), donde la cantidad es 0 ... 1, que devuelve un valor entre from y to, por cantidad. ¿Cómo podría aplicar esta misma función a una interpolación rotacional entre 0 y 360 grados? Dado que los grados no deben ser devueltos fuera 0 y 360.

Dado este círculo unitario para grados:

Unit Circle

donde a partir de = 45 y = 315, el algoritmo debe tomar el camino más corto a la ángulo, es decir, debe pasar por cero, a 360 y luego a 315 - y no todo el camino alrededor de 90, 180, 270 a 315.

¿Hay una buena manera de lograr esto? ¿O va a ser simplemente un horrible lío de bloques()? ¿Me estoy perdiendo alguna forma estándar bien entendida de hacer esto? Cualquier ayuda sería apreciada.

Respuesta

28

Sé que esto es 2 años de edad, pero he estado recientemente a mirar alrededor para el mismo problema y no veo una solución elegante y sin IFS publicados aquí, así que aquí va:

shortest_angle=((((end - start) % 360) + 540) % 360) - 180; 
    return shortest_angle * amount; 

eso es todo

ps: por supuesto, es el sentido de módulo% y shortest_angle es la variable que contiene todo el ángulo de interpolación

+4

Esto funciona bien. Algunos vudú allí. – Ben

+1

[No todos los modulos se comportan igual] (http://stackoverflow.com/q/1907565/190597), pero en Python, esto se puede simplificar a 'shortest_angle = ((end-start) + 180)% 360 - 180 '. – unutbu

+1

¿Puedes explicar por qué los valores? Por lo tanto, se puede convertir a radianes en lugar de grados. – Ismail

1

Mi forma preferida de manejar el ángulo es usar unidades con una potencia de 2 por revolución. Por ejemplo, si usas enteros con signo de 16 bits para representar -180 a +180 grados, simplemente puedes tomar (de-a)/num_pasos para hacer tu interpolación. Agregar y restar ángulos siempre funciona, ya que los valores binarios se desbordan justo en el punto donde va de 360 ​​a 0.

Lo que probablemente quiera hacer en su caso es el módulo matemático 360. Entonces las diferencias de ángulo se calculan como (desde -to)% 360. Todavía hay algunos problemas con los signos que se han abordado en otras preguntas de SO.

3

Nota: el uso de código C#

Después de algún loco hurgar en mi cerebro, esto es lo que he llegado con. Básicamente, la premisa es realizar la envoltura 0-360 en el último minuto. Trate internamente con valores fuera de 0-360 y luego envuélvalos dentro de 0-360 en el punto en que se solicita un valor de la función.

En el punto en el que elegir un comienzo un punto final, se realizan los siguientes:

 float difference = Math.Abs(end - start); 
     if (difference > 180) 
     { 
      // We need to add on to one of the values. 
      if (end > start) 
      { 
       // We'll add it on to start... 
       start += 360; 
      } 
      else 
      { 
       // Add it on to end. 
       end += 360; 
      } 
     } 

Esto le da los valores finales de comienzo real y que puede estar fuera de 0-360 ...

tenemos una función de envoltura para asegurar un valor entre 0 y 360 ...

public static float Wrap(float value, float lower, float upper) 
{ 
    float rangeZero = upper - lower; 

    if (value >= lower && value <= upper) 
     return value; 

    return (value % rangeZero) + lower; 
} 

a continuación, en el punto que solicitar el valor actual de la función:

return Wrap(Lerp(start, end, amount), 0, 360); 

Es casi seguro que esta no es la mejor solución al problema, sin embargo, parece que funciona de manera consistente. Si alguien tiene alguna forma más óptima de hacer esto, sería genial.

13

Lo siento, fue un poco complicado, aquí es una versión más concisa:

public static float LerpDegrees(float start, float end, float amount) 
    { 
     float difference = Math.Abs(end - start); 
     if (difference > 180) 
     { 
      // We need to add on to one of the values. 
      if (end > start) 
      { 
       // We'll add it on to start... 
       start += 360; 
      } 
      else 
      { 
       // Add it on to end. 
       end += 360; 
      } 
     } 

     // Interpolate it. 
     float value = (start + ((end - start) * amount)); 

     // Wrap it.. 
     float rangeZero = 360; 

     if (value >= 0 && value <= 360) 
      return value; 

     return (value % rangeZero); 
    } 

Alguien tiene una versión más optimizada?

1

que quería volver a escribir mi respuesta para explicar mejor responde a la pregunta. Estoy usando EXCEL para mis fórmulas y grados para mis unidades.

Por simplicidad, B es el mayor de los dos valores, y A es el menor de los dos valores. Puede usar MAX() y MIN() respectivamente en su solución más adelante.

PARTE 1 - ¿QUÉ MANERA DE IR?

Lo que queremos hacer primero es determinar en qué dirección queremos realizar el cálculo, en sentido horario o antihorario. Utilizamos una Declaración IF() para que:

IF((B-A)<=180, (Clockwise_Formula), (AntiClockwise_Formula)) 

Los anteriores comprobaciones de Fórmula si va en sentido antihorario B-A (que es el mismo que ir hacia la derecha A-B) es menor que o igual a 180 grados. Si no, va a ser más corto para ir en la otra dirección.

Para verificar esto: 90 - 45 = 45 (que es menor o igual a 180) hace que la instrucción IF sea TRUE, por lo que la dirección horaria es más corta, pero 315 - 45 = 270 (que es mayor que 180) hace que la sentencia if sea FALSA, por lo que la fórmula en sentido antihorario sería más corta.

PARTE 2 - HACIA LA DERECHA FÓRMULA

Ahora vamos a interpolar N veces entre A y B, ya sea en sentido horario o antihorario. La fórmula a la derecha es relativamente simple.

Clockwise_Formula: ((B-A)/N*S)+A 

Dónde S es un recuento del número de interpolaciones, a partir de 1 y terminando en N-1 (Si S = N, su respuesta será B)

Ejemplo: A = 90, B = 270 , N = 4

S=1:  ((270-90)/4*1)+90 = 135 
S=2:  ((270-90)/4*2)+90 = 180 
S=3:  ((270-90)/4*3)+90 = 225 

PARTE 3 - ANITCLOCKWISE FORMULA

La fórmula antihoraria va a ser un poco más compleja, porque tendremos que cruzar en sentido antihorario en un ángulo de 360 ​​grados. El método más sencillo que puedo pensar es agregar 360 a A, luego modular la respuesta en 360 usando la función MOD(FORMULA,VALUE).

También tendrá que cambiar A y B en la fórmula porque B es ahora el número más pequeño.(Esto puede sonar un poco confuso, pero funciona!)

(Unmodulated) AntiClockwise_Formula: (((A+360)-B)/N*S)+B 

Ejemplo: A = 60, B = 300, N = 4

S=1:  (((60+360)-300)/4*1)+300 = 330 
S=2:  (((60+360)-300)/4*2)+300 = 360 
S=3:  (((60+360)-300)/4*3)+300 = 390 

PARTE 4 - RESTRICCIÓN DE RESPUESTAS A entre 0 y 360

Vea cómo a veces (pero no siempre) las respuestas van a ser mayores que 360? Aquí es donde el envolver su Anticlockwise_formula en una función MOD() entra en juego:

AntiClockwise_Formula: MOD((((A+360)-B)/N*S)+B,360) 

modulante el ejemplo usado en la parte 3 le dará:

S=1:  330 
S=2:  0 
S=3:  30 

PARTE 5 - Poniendo todo junto

Combinando todos los elementos de las Partes 1-4 juntos, la respuesta es:

IF((B-A)<=180,((B-A)/N*S)+A,MOD((((A+360)-B)/N*S)+B,360)) 

Dónde:

A = El más pequeño de los dos valores (se puede sustituir A con MIN())

B = El mayor de los dos valores (se puede sustituir B con MAX())

N = El número de interpolaciones que desea hacer (por ejemplo, 2 es un medio, 3 es en tres partes, etc.)

S = Un conteo incrimental a un máximo de N-1 (véase la Parte 2 para la explicación)

7

creo que un mejor enfoque es interpolar seno y coseno ya ellos no sufren forma siendo definidos de manera múltiple. Sea w = "cantidad" de modo que w = 0 es el ángulo A y w = 1 es el ángulo B. Entonces

CS = (1-w)*cos(A) + w*cos(B); 
SN = (1-w)*sin(A) + w*sin(B); 
C = atan2(SN,CS); 

Uno tiene que convertir a radianes y grados, según sea necesario. Uno también tiene que ajustar la rama. Para atan2 C vuelve en el rango -pi a pi. Si desea 0 a 2pi, simplemente agregue pi a C.

+0

Gven la pregunta es vieja y tiene muchas respuestas, ¿podría explicar por qué esto es mejor? – namezero

+1

Mejor (al menos para mí) es más probable que lo codifique correctamente la primera vez. El problema con la mayoría de las respuestas dadas es que toman múltiples pruebas aritméticas con ramas asociadas. Las condiciones de borde en estas pruebas también agregan un nivel de complejidad en el que me equivoqué más de una vez. La pregunta inicial de "¿a dónde voy?" para interpolar o en qué rama de ángulo estoy estoy solo desde el principio. –

+0

Esta es la única respuesta que funcionó ** sin problemas **. Otras respuestas parecen tartamudear algunas veces en algún caso extraño ocasional. – Zoyt