Simular la rotación simultánea de un objeto sobre un origen fijo con recursos limitados.

Lo siento si el título es un poco críptico. Es lo mejor que se me ocurrió.

En primer lugar, esta pregunta está relacionada con otra pregunta que publiqué aquí , pero esa pregunta no se planteó correctamente y terminó generando respuestas que pueden ser útiles para algunas personas que se topan con la pregunta, pero no abordan mi problema. soportes

Además, un descargo de responsabilidad: voy a publicar un código aquí. Lo estaré anotando para tratar de dejar lo más claro posible lo que estoy haciendo.

Bien, entonces estoy usando el lenguaje de programación Processing para mover una caja al azar (de acuerdo con el ruido de Perlin). A medida que la caja se mueve, me gustaría que girara para que pareciera que está rodando. No busco simular la física exacta aquí, solo busco una solución visualmente agradable.

Las reglas generales para la rotación son que, si la caja solo se mueve hacia la derecha, debe girar alrededor del eje Y de manera que gire hacia la derecha y si la caja solo se mueve hacia arriba, debe girar alrededor del eje Y. X -eje tal que parece que está rodando directamente hacia arriba.

En otras palabras, si así es como se ve normalmente la caja:

ingrese la descripción de la imagen aquí

Entonces, si digo: rotate2D(45,0)debería obtener:

ingrese la descripción de la imagen aquí

y si digo rotate2D(0,45)que debo obtener:

ingrese la descripción de la imagen aquí

(para mayor claridad, la declaración de la función para rotar2D parece rotate2D(horizontal, vertical))

Processing hace que esto sea fácil de hacer con las funciones de rotación que proporciona, que son:

rotateX();
rotateY();
rotatez();

El problema surge cuando quieres rotar en 2 dimensiones simultáneamente. Verá, en Procesamiento, si llamo rotateX()para rotar un objeto sobre el X -eje, en realidad es el sistema de coordenadas en sí mismo el que se desplaza y todos los demás ejes lo acompañan, por lo que cuando voy a hacer rotateY()después de hacer un rotateX()no obtendré el resultado que quiero porque el Y -El eje alrededor del cual estoy rotando ahora está sesgado. Puedes ver esto en las imágenes de arriba.

Entonces, si lo hago:

rotateX(radians(45));
rotateY(radians(45));

Yo obtengo:

ingrese la descripción de la imagen aquí

y si lo hago:

rotateY(radians(45));
rotateX(radians(45));

Yo obtengo:

ingrese la descripción de la imagen aquí

Pero lo que quiero es algo como:

ingrese la descripción de la imagen aquí

que dibujé llamando a mi funciónrotate2D()

El problema es que los resultados que produce mi función son un poco contrarios a la intuición. No es evidente en el ejemplo anterior, pero digamos que quiero hacer: rotate2D(90,90);. Creo que eso produciría una caja que parece que no se ha girado en absoluto, ¿verdad? Sin embargo, en realidad produce:

ingrese la descripción de la imagen aquí

Esta es mi implementación actual derotate2D()


La función:

void rotate2D(float horizontal, float vertical)
{  
  float[] rotations ={horizontal, vertical};
  if(rotations[0]>rotations[1])
  {
    float tmp = rotations[1];
    rotations[1]=rotations[0];
    rotations[0]=tmp;
  } 

  if(rotations[1]==vertical) rotateX(radians(rotations[1]-rotations[0]));
  else rotateY(radians(rotations[1]-rotations[0]));

  float hIncr= (horizontal<0) ? -0.1 : 0.1;
  float vIncr= (vertical<0) ? -0.1 : 0.1;
  for(float i=0; i<=abs(rotations[0]);i+=0.1)
  {    
    rotateY(radians(hIncr));
    rotateX(radians(vIncr));
  }   

}

Explicación de la función: lo que hace la función es esencialmente lo siguiente (y esto es todo lo que realmente necesita para abordar la pregunta desde un punto de vista matemático).

La función realiza lentamente un montón de X y Y rotaciones del eje uno tras otro en una pequeña cantidad cada vez (0,1 grados) hasta que alcanza un límite superior. Ese límite superior es el menor de los valores que se pasó la función. La caja ya se habrá girado sobre el eje adecuado por la diferencia entre el valor mayor y el menor antes de que tenga lugar esta secuencia.

Para aclarar aún más, estoy buscando una explicación matemática de por qué este proceso produce resultados contrarios a la intuición, que con suerte arrojarán luz sobre una solución.

El objetivo final ideal aquí es encontrar una fórmula que pueda usar para simular la rotación sobre un eje fijo en múltiples dimensiones cuando todo lo que puedo hacer es rotar 'el mundo' un eje a la vez como se describe arriba (que es esencialmente equivalente a rotar los ejes relativos de los objetos)

por favor, no dude en pedirme que aclare más. Gracias de antemano.

EDITAR

Después de leer los comentarios, ahora entiendo que las rotaciones sobre 2 ejes no conmutan y no debería esperar los resultados que esperaba anteriormente.

Lo que estoy calculando y pasando a mi función es la rotación neta en cada dirección.

Entonces, ¿es posible editar mi fórmula para que produzca esta rotación neta? Por ejemplo, en el ejemplo 90/90, si continúo la secuencia durante ~37 pasos adicionales (127 en total) obtengo el resultado que intuitivamente quiero obtener). Sin embargo, esto debería funcionar para todas las combinaciones de entradas.

No veo por qué la rotación (90,90) debería producir un resultado que no parezca girado. Alternaría la rotación en pequeñas cantidades en x e y, de modo que cada uno totalice 90. El resultado debería ser equivalente a una rotación alrededor de un eje a medio camino entre las rotaciones X e Y, pero no de 180 grados. La rotación neta está en algún lugar entre 90 grados. Considere viajar desde (0,0) en el plano hasta (90,90); incluso si lo hace en pequeños pasos x e y, aún habrá viajado alrededor de 127 a lo largo de la diagonal cuando haya terminado. El ángulo neto puede no ser 127 aquí, ya que las rotaciones no se suman de la misma manera.
No he revisado cuidadosamente para ver si esto explica completamente la discrepancia que está viendo con rotate2D(90,90), pero las rotaciones sobre ejes distintos (incluso perpendiculares) no conmutan. Romper 90 grados en pequeños pasos y girando alternativamente sobre el X - y y -ejes por una cantidad "total" de 90 grados sobre cada eje no es lo mismo que rotar 90 grados sobre cada eje por separado. (¿Su intuición puede ser engañada por las coordenadas geográficas?) Incluso 90 rotaciones de grados sobre el X - y y -Los ejes no conmutan.
Entonces, no estoy seguro de lo que estás preguntando. Una rotación general se puede expresar como "girar el ángulo A alrededor del eje v", donde v es un vector unitario. No es sencillo convertir eso en rotaciones x/y/z, pero se puede hacer... ¿es eso lo que estás buscando?
Bien, entonces voy a tratar de reformular mi pregunta porque, en última instancia, lo que estoy buscando es una forma de simular la rotación sobre un sistema de coordenadas fijo, pero estoy buscando una fórmula que pueda usar en lugar de esta solución porque es ineficiente y también es glitchy.
@greggo tal vez, aunque no estoy seguro de cuál sería el vector unitario
El vector unitario define el eje de rotación. entonces (1,0,0) para X, o (0,1,0) para Y, o (0.7071,0.7071,0) para el punto medio.
He editado mi pregunta, revisa la parte inferior
@LukeP: en sus ediciones, el punto más profundo es que la "rotación neta sobre cada eje" no es suficiente para especificar una rotación espacial . Es decir, no está pasando suficientes datos (matemáticamente hablando) para especificar una rotación única. Desafortunadamente, esto es un hecho de la vida sobre las rotaciones espaciales, no un error rectificable en su código. Sin embargo, si especifica una dirección en el plano y un ángulo de rotación numérico (proporcional a la distancia que ha rodado el cubo), es posible especificar una rotación única; ¿Es eso lo que buscas?
@ user86418 ¿Esto no podría resolverse con el teorema de Pitágoras? Si piensa que la condición de prueba en el ciclo for especifica la magnitud de una hipotenusa de un triángulo rectángulo para el cual conozco la longitud de los otros 2 lados (parámetros de función horizontal y vertical), entonces creo que esto produciría el resultado I ¡desear! al menos lo probé con 90/90 y funcionó. Voy a implementar e informar.
@LukeP: ¿Es una descripción justa decir que estas funciones de rotación no rotan el cubo sino que rotan el marco global? ¿Tal que una vez que haces "rotar X", un "rotar Y" posterior gira sobre la nueva dirección Y, no la original?
@Muphrid sí, exactamente. De hecho, acabo de publicar mi solución, por lo que es posible que desee actualizar la página.

Respuestas (2)

Lo siento, Luke, no creo que tu solución funcione. Debe tener una solución rigurosa que se base en las matemáticas de las rotaciones. Esto se hace más fácilmente usando cuaterniones, o mejor aún, sus análogos del álgebra de Clifford llamados rotores .

Explicaré brevemente los rotores de álgebra de Clifford y cómo se pueden usar para convertir rotaciones secuenciales en una rotación neta.


Álgebra de Clifford

El álgebra de Clifford introduce un producto de vectores que no es conmutativo pero sigue siendo asociativo. Si está familiarizado con los productos punto y cruz, incorpora propiedades de ambos. Dada una base ortogonal mi 1 , mi 2 , mi 3 , tenemos lo siguiente:

mi i mi j = { + 1 i = j mi j mi i i j

De nuevo, también es asociativo, así que mi 1 mi 2 mi 3 = ( mi 1 mi 2 ) mi 3 = mi 1 ( mi 2 mi 3 ) por ejemplo. Esto crea un espacio vectorial de 8 dimensiones, con los siguientes elementos básicos:

1 mi 1 , mi 2 , mi 3 mi 1 mi 2 , mi 2 mi 3 , mi 3 mi 1 mi 1 mi 2 mi 3

múltiplos escalares de 1 son escalares. combinaciones lineales de mi 1 , mi 2 , mi 3 son vectores. combinaciones lineales de mi 1 mi 2 , mi 2 mi 3 , mi 3 mi 1 se denominan bivectores y son de interés para las rotaciones. Las rotaciones tienen lugar en planos (solo en 3D podemos decir que son alrededor de un eje, de manera equivalente), y los bivectores describen directamente los planos en los que tienen lugar las rotaciones.


Rotores y rotaciones

Declararé lo siguiente sin pruebas, aunque debería ser familiar para cualquier persona con una comprensión básica de los cuaterniones.

Defina un rotor como la exponencial de un bivector (generalmente definido a través de una serie de potencias). Dejar B sea ​​algún bivector unitario. Entonces el rotor q = Exp ( B t ) para algún escalar t es

q = Exp ( B t ) = porque t + B pecado t

Un mapa de rotación R _ en el plano correspondiente a B por el ángulo θ toma la forma

R _ ( a ) = mi B θ / 2 a mi B θ / 2

dónde a es cualquier vector.

(Nota: para aquellos con antecedentes de cuaterniones, i = mi 2 mi 3 , j = mi 3 mi 1 , y k = mi 1 mi 2 , por lo que no hay discrepancia de signos con esta definición).


Aplicación: encontrar una rotación neta

El uso de rotores simplifica muchas de las matemáticas del análisis de rotaciones. Por ejemplo, consideremos el problema que tiene: sus funciones de rotación rotan el marco global y ellas mismas usan el marco global como el marco de referencia para estas rotaciones. Esto hace que secuenciar las rotaciones no sea algo trivial, pero los rotores nos permiten encontrar una forma más sencilla de hacer las cosas.

Dejar mi X , mi y , mi z ser el marco original. Si desea rotar sobre el original mi X y luego sobre el original mi y , entonces sus rotores se verían así:

q neto = Exp ( mi z mi X ϕ / 2 ) Exp ( mi y mi z θ / 2 ) = q y q X

Luego podemos multiplicar esto para encontrar el plano de rotación neto y el ángulo neto.

q neto = porque ϕ 2 porque θ 2 mi z mi X pecado ϕ 2 porque θ 2 mi y mi z porque ϕ 2 pecado θ 2 + mi X mi y pecado θ 2 pecado ϕ 2

Podemos encontrar el ángulo de rotación neto α por

porque α 2 = porque ϕ 2 porque θ 2

Consideremos el caso como usted sugirió, ϕ = θ = π / 2 . Entonces deberíamos tener

porque α 2 = ( porque π 4 ) 2 = 1 2 α 2 = π 3

o α = 2 π / 3 = 120 .

El plano de rotación neta es ( mi y mi z + mi z mi X mi X mi y ) / 3 , o una rotación sobre el eje ( mi X + mi y mi z ) / 3 .

Editar: con esto en mente, esto es lo que debe hacer.

  • Para cada rotación en una secuencia de rotaciones, identifique el ángulo de rotación θ y el plano unitario B ^ . Por ejemplo, si desea rotar sobre el mi X dirección, entonces el plano de rotación es mi y mi z .
  • Escriba el rotor correspondiente para cada una de esas rotaciones como q = porque θ / 2 B ^ pecado θ / 2 .
  • Escribe una función para multiplicar rotores. Cada rotor tiene sólo cuatro componentes y es una combinación lineal de 1 , mi X mi y , mi y mi z , mi z mi X . Usando las reglas que describí en la segunda sección, calcula la tabla de multiplicar para estos elementos básicos. (O hacer trampa y buscar una tabla de multiplicar de cuaterniones).
  • Multiplique los rotores individuales para encontrar el rotor neto de la rotación combinada.
  • Dado ese rotor, encuentre el ángulo ϕ y el plano de rotacion b ^ para la rotación neta. Nuevamente, recuerde que el rotor se puede escribir como q = porque ϕ / 2 b ^ pecado ϕ / 2 . Esto significa que puedes tomar la parte escalar y usar un coseno inverso para encontrar ϕ . Puede tomar los componentes restantes y normalizarlos como un bivector (de la misma manera que lo haría con un vector) para encontrar el plano unitario de rotación.
¡Impresionante! muchas de estas matemáticas están sobre mi cabeza en este momento, pero (una vez que las entiendo) probablemente pueda usarlas para hacer un algoritmo más eficiente, pero creo que mi solución funciona. Lo acabo de implementar en mi aplicación y se ve muy bien. ¿Estabas hablando de mi implementación de la idea (si es así, verifica mi código actualizado en mi respuesta)?
De todos modos, gracias, voté a favor, pero esperaré a ver si más personas tienen opiniones sobre mi solución frente a la suya antes de elegir una para "aceptar". Aunque, independientemente, el suyo sería técnicamente más preciso, ya que tendría que ralentizar mi algoritmo considerablemente para superar la precisión de un solo decimal.
Estaba hablando específicamente de tu respuesta. Por ejemplo, invoca el teorema de Pitágoras para concluir que el ángulo debe ser 127 . Sin embargo, el teorema de Pitágoras no está involucrado en este cálculo, y es por eso que concluí que el ángulo de rotación es 120 y no 127 .
hmm, una de las razones por las que tenía confianza en mi método es que coincidía con lo que dijo @greggo en su comentario. ¿Tu método realmente resultará en una rotación neta de 90 grados a la derecha y 90 grados hacia arriba?
Sí. Tomé los rotores para cada uno de esos 90 rotaciones, las multipliqué para realizar esas rotaciones en secuencia, y luego simplemente analicé el rotor neto para encontrar su ángulo y plano de rotación.
Pido disculpas, de hecho revisé de nuevo y mi solución no funciona, tienes razón. Aunque produce rotaciones visualmente agradables, funcionará muy bien como sustituto mientras trato de descubrir cómo se puede implementar esto de manera pragmática.
Sí, anticipo que puede tener problemas para convertir esta rotación en una secuencia de rotaciones usando las funciones enlatadas que se le dan. Tomé una grieta en eso y encontré el resultado algo desordenado. Si está interesado en eso, hágamelo saber, y si está interesado en hacer esto como una secuencia de pequeñas rotaciones (para fines de animación suave), también puedo abordarlo.
@Mumphrid Entonces, resulta que Processing tiene una función de rotación que no está en la documentación estándar que le permite rotar sobre un ángulo y un eje que especifique, ¿podría ayudarme a descubrir cómo traducir la información rotate(angle,x,y,z)? función en un vector unitario y un ángulo? (en el chat)

Mi solución

¡A todos en la sección de comentarios, me gustaría agradecerles por su ayuda ya que me han guiado a una solución!

Entonces, como dijiste, mi intuición era incorrecta y las rotaciones sobre múltiples ejes no conmutan.

Para que quede completamente claro con qué estoy trabajando, quiero reiterar que los valores que se pasan a mi función representan la rotación neta deseada en las direcciones horizontal y vertical, respectivamente.

entonces, cuando probé la rotación 2D (90,90) no obtuve una rotación de 90 grados en cada dirección, sino un compromiso entre los dos. En otras palabras, ¡estaba tratando de usar la longitud de uno de los lados pequeños del triángulo como hipotenusa!

Como tengo la magnitud de cada lado pequeño cuando se llama a la función, puedo usar el teorema de Pitágoras para determinar cuál debe ser la longitud de la hipotenusa. Después de leer los comentarios, creo que esta hipotenusa es equivalente al vector unitario de la rotación resultante. (Por favor, corríjame si me equivoco en ese punto).

ingrese la descripción de la imagen aquí

Entonces, al usar el teorema de Pitágoras para determinar cuánto tiempo continuar la secuencia de rotaciones de 0,1 grados en función de las rotaciones netas deseadas en las direcciones horizontal y vertical, ¡puedo lograr los resultados que quiero!

Aquí está el nuevo código:

void rotate2D(float horizontal, float vertical)
{ 
  float[] rotations ={horizontal, vertical};
  if(rotations[0]>rotations[1])
  {
    float tmp = rotations[1];
    rotations[1]=rotations[0];
    rotations[0]=tmp;
  } 

  float c= sqrt(pow(rotations[0],2)+pow(rotations[1],2));
  float cIncr=0.1;
  float hIncr=(horizontal/(c/cIncr));
  float vIncr=(vertical/(c/cIncr));
  for(float i=0; i<c; i+=cIncr)
  {    
     rotateY(radians(hIncr));
     rotateX(radians(vIncr));
  }   
}

EDITAR: resulta que este método no funciona, hubo una falla en mi método de prueba, pero SÍ produce rotaciones visualmente agradables para mis propósitos.