diff options
Diffstat (limited to 'source/blender/blenlib/intern/math_rotation.c')
-rw-r--r-- | source/blender/blenlib/intern/math_rotation.c | 337 |
1 files changed, 238 insertions, 99 deletions
diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c index f72269f6d7b..1160ec9fc3a 100644 --- a/source/blender/blenlib/intern/math_rotation.c +++ b/source/blender/blenlib/intern/math_rotation.c @@ -1,4 +1,4 @@ -/** +/* * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** @@ -26,11 +26,25 @@ * */ +#include <assert.h> #include "BLI_math.h" /******************************** Quaternions ********************************/ -void unit_qt(float *q) +/* used to test is a quat is not normalized */ +#define QUAT_EPSILON 0.0001 + +/* convenience, avoids setting Y axis everywhere */ +void unit_axis_angle(float axis[3], float *angle) +{ + axis[0]= 0.0f; + axis[1]= 1.0f; + axis[2]= 0.0f; + *angle= 0.0f; +} + + +void unit_qt(float q[4]) { q[0]= 1.0f; q[1]= q[2]= q[3]= 0.0f; @@ -88,7 +102,7 @@ void conjugate_qt(float *q) q[3] = -q[3]; } -float dot_qtqt(float *q1, float *q2) +float dot_qtqt(const float q1[4], const float q2[4]) { return q1[0]*q2[0] + q1[1]*q2[1] + q1[2]*q2[2] + q1[3]*q2[3]; } @@ -119,11 +133,16 @@ void mul_qt_fl(float *q, const float f) q[3] *= f; } -void sub_qt_qtqt(float *q, float *q1, float *q2) +void sub_qt_qtqt(float q[4], const float q1[4], const float q2[4]) { - q2[0]= -q2[0]; - mul_qt_qtqt(q, q1, q2); - q2[0]= -q2[0]; + float nq2[4]; + + nq2[0]= -q2[0]; + nq2[1]= q2[1]; + nq2[2]= q2[2]; + nq2[3]= q2[3]; + + mul_qt_qtqt(q, q1, nq2); } /* angular mult factor */ @@ -138,7 +157,8 @@ void mul_fac_qt_fl(float *q, const float fac) mul_v3_fl(q+1, si); } -void quat_to_mat3(float m[][3], float *q) +/* skip error check, currently only needed by mat3_to_quat_is_ok */ +static void quat_to_mat3_no_error(float m[][3], const float q[4]) { double q0, q1, q2, q3, qda,qdb,qdc,qaa,qab,qac,qbb,qbc,qcc; @@ -170,10 +190,29 @@ void quat_to_mat3(float m[][3], float *q) m[2][2]= (float)(1.0-qaa-qbb); } -void quat_to_mat4(float m[][4], float *q) + +void quat_to_mat3(float m[][3], const float q[4]) +{ +#ifdef DEBUG + float f; + if(!((f=dot_qtqt(q, q))==0.0 || (fabs(f-1.0) < QUAT_EPSILON))) { + fprintf(stderr, "Warning! quat_to_mat3() called with non-normalized: size %.8f *** report a bug ***\n", f); + } +#endif + + quat_to_mat3_no_error(m, q); +} + +void quat_to_mat4(float m[][4], const float q[4]) { double q0, q1, q2, q3, qda,qdb,qdc,qaa,qab,qac,qbb,qbc,qcc; +#ifdef DEBUG + if(!((q0=dot_qtqt(q, q))==0.0 || (fabs(q0-1.0) < QUAT_EPSILON))) { + fprintf(stderr, "Warning! quat_to_mat4() called with non-normalized: size %.8f *** report a bug ***\n", (float)q0); + } +#endif + q0= M_SQRT2 * q[0]; q1= M_SQRT2 * q[1]; q2= M_SQRT2 * q[2]; @@ -294,7 +333,7 @@ void mat3_to_quat_is_ok(float q[4], float wmat[3][3]) q1[3]= -nor[2]*si; /* rotate back x-axis from mat, using inverse q1 */ - quat_to_mat3( matr,q1); + quat_to_mat3_no_error( matr,q1); invert_m3_m3(matn, matr); mul_m3_v3(matn, mat[0]); @@ -312,7 +351,7 @@ void mat3_to_quat_is_ok(float q[4], float wmat[3][3]) } -void normalize_qt(float *q) +float normalize_qt(float *q) { float len; @@ -324,6 +363,14 @@ void normalize_qt(float *q) q[1]= 1.0f; q[0]= q[2]= q[3]= 0.0f; } + + return len; +} + +float normalize_qt_qt(float r[4], const float q[4]) +{ + copy_qt_qt(r, q); + return normalize_qt(r); } /* note: expects vectors to be normalized */ @@ -356,10 +403,13 @@ void rotation_between_quats_to_quat(float *q, const float q1[4], const float q2[ } -void vec_to_quat(float *q,float *vec, short axis, short upflag) +void vec_to_quat(float q[4], const float vec[3], short axis, const short upflag) { float q2[4], nor[3], *fp, mat[3][3], angle, si, co, x2, y2, z2, len1; + assert(axis >= 0 && axis <= 5); + assert(upflag >= 0 && upflag <= 2); + /* first rotate to axis */ if(axis>2) { x2= vec[0] ; y2= vec[1] ; z2= vec[2]; @@ -491,7 +541,7 @@ void QuatInterpolW(float *result, float *quat1, float *quat2, float t) } #endif -void interp_qt_qtqt(float *result, float *quat1, float *quat2, float t) +void interp_qt_qtqt(float result[4], const float quat1[4], const float quat2[4], const float t) { float quat[4], omega, cosom, sinom, sc1, sc2; @@ -528,7 +578,7 @@ void interp_qt_qtqt(float *result, float *quat1, float *quat2, float t) result[3] = sc1 * quat[3] + sc2 * quat2[3]; } -void add_qt_qtqt(float *result, float *quat1, float *quat2, float t) +void add_qt_qtqt(float result[4], const float quat1[4], const float quat2[4], const float t) { result[0]= quat1[0] + t*quat2[0]; result[1]= quat1[1] + t*quat2[1]; @@ -536,7 +586,7 @@ void add_qt_qtqt(float *result, float *quat1, float *quat2, float t) result[3]= quat1[3] + t*quat2[3]; } -void tri_to_quat(float *quat, float *v1, float *v2, float *v3) +void tri_to_quat(float quat[4], const float v1[3], const float v2[3], const float v3[3]) { /* imaginary x-axis, y-axis triangle is being rotated */ float vec[3], q1[4], q2[4], n[3], si, co, angle, mat[3][3], imat[3][3]; @@ -588,13 +638,16 @@ void print_qt(char *str, float q[4]) /******************************** Axis Angle *********************************/ /* Axis angle to Quaternions */ -void axis_angle_to_quat(float q[4], float axis[3], float angle) +void axis_angle_to_quat(float q[4], const float axis[3], float angle) { float nor[3]; float si; - normalize_v3_v3(nor, axis); - + if(normalize_v3_v3(nor, axis) == 0.0f) { + unit_qt(q); + return; + } + angle /= 2; si = (float)sin(angle); q[0] = (float)cos(angle); @@ -604,10 +657,16 @@ void axis_angle_to_quat(float q[4], float axis[3], float angle) } /* Quaternions to Axis Angle */ -void quat_to_axis_angle(float axis[3], float *angle,float q[4]) +void quat_to_axis_angle(float axis[3], float *angle, const float q[4]) { float ha, si; - + +#ifdef DEBUG + if(!((ha=dot_qtqt(q, q))==0.0 || (fabs(ha-1.0) < QUAT_EPSILON))) { + fprintf(stderr, "Warning! quat_to_axis_angle() called with non-normalized: size %.8f *** report a bug ***\n", ha); + } +#endif + /* calculate angle/2, and sin(angle/2) */ ha= (float)acos(q[0]); si= (float)sin(ha); @@ -625,7 +684,7 @@ void quat_to_axis_angle(float axis[3], float *angle,float q[4]) } /* Axis Angle to Euler Rotation */ -void axis_angle_to_eulO(float eul[3], short order,float axis[3], float angle) +void axis_angle_to_eulO(float eul[3], const short order, const float axis[3], const float angle) { float q[4]; @@ -635,7 +694,7 @@ void axis_angle_to_eulO(float eul[3], short order,float axis[3], float angle) } /* Euler Rotation to Axis Angle */ -void eulO_to_axis_angle(float axis[3], float *angle,float eul[3], short order) +void eulO_to_axis_angle(float axis[3], float *angle, const float eul[3], const short order) { float q[4]; @@ -645,12 +704,15 @@ void eulO_to_axis_angle(float axis[3], float *angle,float eul[3], short order) } /* axis angle to 3x3 matrix - safer version (normalisation of axis performed) */ -void axis_angle_to_mat3(float mat[3][3],float axis[3], float angle) +void axis_angle_to_mat3(float mat[3][3], const float axis[3], const float angle) { float nor[3], nsi[3], co, si, ico; /* normalise the axis first (to remove unwanted scaling) */ - normalize_v3_v3(nor, axis); + if(normalize_v3_v3(nor, axis) == 0.0f) { + unit_m3(mat); + return; + } /* now convert this to a 3x3 matrix */ co= (float)cos(angle); @@ -673,7 +735,7 @@ void axis_angle_to_mat3(float mat[3][3],float axis[3], float angle) } /* axis angle to 4x4 matrix - safer version (normalisation of axis performed) */ -void axis_angle_to_mat4(float mat[4][4],float axis[3], float angle) +void axis_angle_to_mat4(float mat[4][4], const float axis[3], const float angle) { float tmat[3][3]; @@ -730,7 +792,7 @@ void mat4_to_vec_rot(float axis[3], float *angle,float mat[4][4]) } /* axis angle to 3x3 matrix */ -void vec_rot_to_mat3(float mat[][3],float *vec, float phi) +void vec_rot_to_mat3(float mat[][3], const float vec[3], const float phi) { /* rotation of phi radials around vec */ float vx, vx2, vy, vy2, vz, vz2, co, si; @@ -756,7 +818,7 @@ void vec_rot_to_mat3(float mat[][3],float *vec, float phi) } /* axis angle to 4x4 matrix */ -void vec_rot_to_mat4(float mat[][4],float *vec, float phi) +void vec_rot_to_mat4(float mat[][4], const float vec[3], const float phi) { float tmat[3][3]; @@ -766,7 +828,7 @@ void vec_rot_to_mat4(float mat[][4],float *vec, float phi) } /* axis angle to quaternion */ -void vec_rot_to_quat(float *quat,float *vec, float phi) +void vec_rot_to_quat(float *quat, const float vec[3], const float phi) { /* rotation of phi radials around vec */ float si; @@ -790,7 +852,7 @@ void vec_rot_to_quat(float *quat,float *vec, float phi) /******************************** XYZ Eulers *********************************/ /* XYZ order */ -void eul_to_mat3(float mat[][3], float *eul) +void eul_to_mat3(float mat[][3], const float eul[3]) { double ci, cj, ch, si, sj, sh, cc, cs, sc, ss; @@ -818,7 +880,7 @@ void eul_to_mat3(float mat[][3], float *eul) } /* XYZ order */ -void eul_to_mat4(float mat[][4], float *eul) +void eul_to_mat4(float mat[][4], const float eul[3]) { double ci, cj, ch, si, sj, sh, cc, cs, sc, ss; @@ -850,7 +912,7 @@ void eul_to_mat4(float mat[][4], float *eul) /* returns two euler calculation methods, so we can pick the best */ /* XYZ order */ -static void mat3_to_eul2(float tmat[][3], float *eul1, float *eul2) +static void mat3_to_eul2(float tmat[][3], float eul1[3], float eul2[3]) { float cy, quat[4], mat[3][3]; @@ -907,16 +969,16 @@ void mat4_to_eul(float *eul,float tmat[][4]) } /* XYZ order */ -void quat_to_eul(float *eul,float *quat) +void quat_to_eul(float *eul, const float quat[4]) { float mat[3][3]; - + quat_to_mat3(mat,quat); mat3_to_eul(eul,mat); } /* XYZ order */ -void eul_to_quat(float *quat,float *eul) +void eul_to_quat(float *quat, const float eul[3]) { float ti, tj, th, ci, cj, ch, si, sj, sh, cc, cs, sc, ss; @@ -932,10 +994,12 @@ void eul_to_quat(float *quat,float *eul) } /* XYZ order */ -void rotate_eul(float *beul, char axis, float ang) +void rotate_eul(float *beul, const char axis, const float ang) { float eul[3], mat1[3][3], mat2[3][3], totmat[3][3]; - + + assert(axis >= 'X' && axis <= 'Z'); + eul[0]= eul[1]= eul[2]= 0.0f; if(axis=='X') eul[0]= ang; else if(axis=='Y') eul[1]= ang; @@ -952,7 +1016,7 @@ void rotate_eul(float *beul, char axis, float ang) /* exported to transform.c */ /* order independent! */ -void compatible_eul(float *eul, float *oldrot) +void compatible_eul(float eul[3], const float oldrot[3]) { float dx, dy, dz; @@ -1016,7 +1080,7 @@ void compatible_eul(float *eul, float *oldrot) /* uses 2 methods to retrieve eulers, and picks the closest */ /* XYZ order */ -void mat3_to_compatible_eul(float *eul, float *oldrot,float mat[][3]) +void mat3_to_compatible_eul(float eul[3], const float oldrot[3], float mat[][3]) { float eul1[3], eul2[3]; float d1, d2; @@ -1073,20 +1137,20 @@ static RotOrderInfo rotOrders[]= { * NOTE: since we start at 1 for the values, but arrays index from 0, * there is -1 factor involved in this process... */ -#define GET_ROTATIONORDER_INFO(order) (((order)>=1) ? &rotOrders[(order)-1] : &rotOrders[0]) +#define GET_ROTATIONORDER_INFO(order) (assert(order>=0 && order<=6), (order < 1) ? &rotOrders[0] : &rotOrders[(order)-1]) /* Construct quaternion from Euler angles (in radians). */ -void eulO_to_quat(float q[4],float e[3], short order) +void eulO_to_quat(float q[4], const float e[3], const short order) { RotOrderInfo *R= GET_ROTATIONORDER_INFO(order); short i=R->axis[0], j=R->axis[1], k=R->axis[2]; double ti, tj, th, ci, cj, ch, si, sj, sh, cc, cs, sc, ss; double a[3]; - ti = e[i]/2; tj = e[j]/2; th = e[k]/2; - - if (R->parity) e[j] = -e[j]; - + ti = e[i] * 0.5f; + tj = e[j] * (R->parity ? -0.5f : 0.5f); + th = e[k] * 0.5f; + ci = cos(ti); cj = cos(tj); ch = cos(th); si = sin(ti); sj = sin(tj); sh = sin(th); @@ -1102,11 +1166,11 @@ void eulO_to_quat(float q[4],float e[3], short order) q[2] = a[1]; q[3] = a[2]; - if (R->parity) q[j] = -q[j]; + if (R->parity) q[j+1] = -q[j+1]; } /* Convert quaternion to Euler angles (in radians). */ -void quat_to_eulO(float e[3], short order,float q[4]) +void quat_to_eulO(float e[3], short const order, const float q[4]) { float M[3][3]; @@ -1115,7 +1179,7 @@ void quat_to_eulO(float e[3], short order,float q[4]) } /* Construct 3x3 matrix from Euler angles (in radians). */ -void eulO_to_mat3(float M[3][3],float e[3], short order) +void eulO_to_mat3(float M[3][3], const float e[3], const short order) { RotOrderInfo *R= GET_ROTATIONORDER_INFO(order); short i=R->axis[0], j=R->axis[1], k=R->axis[2]; @@ -1139,53 +1203,6 @@ void eulO_to_mat3(float M[3][3],float e[3], short order) M[i][k] = -sj; M[j][k] = cj*si; M[k][k] = cj*ci; } -/* Construct 4x4 matrix from Euler angles (in radians). */ -void eulO_to_mat4(float M[4][4],float e[3], short order) -{ - float m[3][3]; - - /* for now, we'll just do this the slow way (i.e. copying matrices) */ - normalize_m3(m); - eulO_to_mat3(m,e, order); - copy_m4_m3(M, m); -} - -/* Convert 3x3 matrix to Euler angles (in radians). */ -void mat3_to_eulO(float e[3], short order,float M[3][3]) -{ - RotOrderInfo *R= GET_ROTATIONORDER_INFO(order); - short i=R->axis[0], j=R->axis[1], k=R->axis[2]; - double cy = sqrt(M[i][i]*M[i][i] + M[i][j]*M[i][j]); - - if (cy > 16*FLT_EPSILON) { - e[i] = atan2(M[j][k], M[k][k]); - e[j] = atan2(-M[i][k], cy); - e[k] = atan2(M[i][j], M[i][i]); - } - else { - e[i] = atan2(-M[k][j], M[j][j]); - e[j] = atan2(-M[i][k], cy); - e[k] = 0; - } - - if (R->parity) { - e[0] = -e[0]; - e[1] = -e[1]; - e[2] = -e[2]; - } -} - -/* Convert 4x4 matrix to Euler angles (in radians). */ -void mat4_to_eulO(float e[3], short order,float M[4][4]) -{ - float m[3][3]; - - /* for now, we'll just do this the slow way (i.e. copying matrices) */ - copy_m3_m4(m, M); - normalize_m3(m); - mat3_to_eulO(e, order,m); -} - /* returns two euler calculation methods, so we can pick the best */ static void mat3_to_eulo2(float M[3][3], float *e1, float *e2, short order) { @@ -1228,6 +1245,45 @@ static void mat3_to_eulo2(float M[3][3], float *e1, float *e2, short order) } } +/* Construct 4x4 matrix from Euler angles (in radians). */ +void eulO_to_mat4(float M[4][4], const float e[3], const short order) +{ + float m[3][3]; + + /* for now, we'll just do this the slow way (i.e. copying matrices) */ + normalize_m3(m); + eulO_to_mat3(m,e, order); + copy_m4_m3(M, m); +} + + +/* Convert 3x3 matrix to Euler angles (in radians). */ +void mat3_to_eulO(float eul[3], const short order,float M[3][3]) +{ + float eul1[3], eul2[3]; + + mat3_to_eulo2(M, eul1, eul2, order); + + /* return best, which is just the one with lowest values it in */ + if(fabs(eul1[0])+fabs(eul1[1])+fabs(eul1[2]) > fabs(eul2[0])+fabs(eul2[1])+fabs(eul2[2])) { + copy_v3_v3(eul, eul2); + } + else { + copy_v3_v3(eul, eul1); + } +} + +/* Convert 4x4 matrix to Euler angles (in radians). */ +void mat4_to_eulO(float e[3], const short order,float M[4][4]) +{ + float m[3][3]; + + /* for now, we'll just do this the slow way (i.e. copying matrices) */ + copy_m3_m4(m, M); + normalize_m3(m); + mat3_to_eulO(e, order,m); +} + /* uses 2 methods to retrieve eulers, and picks the closest */ void mat3_to_compatible_eulO(float eul[3], float oldrot[3], short order,float mat[3][3]) { @@ -1249,12 +1305,23 @@ void mat3_to_compatible_eulO(float eul[3], float oldrot[3], short order,float ma copy_v3_v3(eul, eul1); } +void mat4_to_compatible_eulO(float eul[3], float oldrot[3], short order,float M[4][4]) +{ + float m[3][3]; + + /* for now, we'll just do this the slow way (i.e. copying matrices) */ + copy_m3_m4(m, M); + normalize_m3(m); + mat3_to_compatible_eulO(eul, oldrot, order, m); +} /* rotate the given euler by the given angle on the specified axis */ // NOTE: is this safe to do with different axis orders? void rotate_eulO(float beul[3], short order, char axis, float ang) { float eul[3], mat1[3][3], mat2[3][3], totmat[3][3]; - + + assert(axis >= 'X' && axis <= 'Z'); + eul[0]= eul[1]= eul[2]= 0.0f; if (axis=='X') eul[0]= ang; @@ -1272,7 +1339,7 @@ void rotate_eulO(float beul[3], short order, char axis, float ang) } /* the matrix is written to as 3 axis vectors */ -void eulO_to_gimbal_axis(float gmat[][3], float *eul, short order) +void eulO_to_gimbal_axis(float gmat[][3], const float eul[3], const short order) { RotOrderInfo *R= GET_ROTATIONORDER_INFO(order); @@ -1364,7 +1431,7 @@ void mat4_to_dquat(DualQuat *dq,float basemat[][4], float mat[][4]) mul_m4_m4m4(S, baseRS, baseRinv); /* set scaling part */ - mul_serie_m4(dq->scale, basemat, S, baseinv, 0, 0, 0, 0, 0); + mul_serie_m4(dq->scale, basemat, S, baseinv, NULL, NULL, NULL, NULL, NULL); dq->scale_weight= 1.0f; } else { @@ -1385,7 +1452,7 @@ void mat4_to_dquat(DualQuat *dq,float basemat[][4], float mat[][4]) dq->trans[3]= 0.5f*(t[0]*q[2] - t[1]*q[1] + t[2]*q[0]); } -void dquat_to_mat4(float mat[][4],DualQuat *dq) +void dquat_to_mat4(float mat[][4], DualQuat *dq) { float len, *t, q0[4]; @@ -1521,7 +1588,79 @@ void copy_dq_dq(DualQuat *dq1, DualQuat *dq2) memcpy(dq1, dq2, sizeof(DualQuat)); } -/* lense/angle conversion (radians) */ +/* axis matches eTrackToAxis_Modes */ +void quat_apply_track(float quat[4], short axis, short upflag) +{ + /* rotations are hard coded to match vec_to_quat */ + const float quat_track[][4]= {{0.70710676908493, 0.0, -0.70710676908493, 0.0}, /* pos-y90 */ + {0.5, 0.5, 0.5, 0.5}, /* Quaternion((1,0,0), radians(90)) * Quaternion((0,1,0), radians(90)) */ + {0.70710676908493, 0.0, 0.0, 0.70710676908493}, /* pos-z90 */ + {0.70710676908493, 0.0, 0.70710676908493, 0.0}, /* neg-y90 */ + {0.5, -0.5, -0.5, 0.5}, /* Quaternion((1,0,0), radians(-90)) * Quaternion((0,1,0), radians(-90)) */ + {-3.0908619663705394e-08, 0.70710676908493, 0.70710676908493, 3.0908619663705394e-08}}; /* no rotation */ + + assert(axis >= 0 && axis <= 5); + assert(upflag >= 0 && upflag <= 2); + + mul_qt_qtqt(quat, quat, quat_track[axis]); + + if(axis>2) + axis= axis-3; + + /* there are 2 possible up-axis for each axis used, the 'quat_track' applies so the first + * up axis is used X->Y, Y->X, Z->X, if this first up axis isn used then rotate 90d + * the strange bit shift below just find the low axis {X:Y, Y:X, Z:X} */ + if(upflag != (2-axis)>>1) { + float q[4]= {0.70710676908493, 0.0, 0.0, 0.0}; /* assign 90d rotation axis */ + q[axis+1] = ((axis==1)) ? 0.70710676908493 : -0.70710676908493; /* flip non Y axis */ + mul_qt_qtqt(quat, quat, q); + } +} + + +void vec_apply_track(float vec[3], short axis) +{ + float tvec[3]; + + assert(axis >= 0 && axis <= 5); + + copy_v3_v3(tvec, vec); + + switch(axis) { + case 0: /* pos-x */ + /* vec[0]= 0.0; */ + vec[1]= tvec[2]; + vec[2]= -tvec[1]; + break; + case 1: /* pos-y */ + /* vec[0]= tvec[0]; */ + /* vec[1]= 0.0; */ + /* vec[2]= tvec[2]; */ + break; + case 2: /* pos-z */ + /* vec[0]= tvec[0]; */ + /* vec[1]= tvec[1]; */ + // vec[2]= 0.0; */ + break; + case 3: /* neg-x */ + /* vec[0]= 0.0; */ + vec[1]= tvec[2]; + vec[2]= -tvec[1]; + break; + case 4: /* neg-y */ + vec[0]= -tvec[2]; + /* vec[1]= 0.0; */ + vec[2]= tvec[0]; + break; + case 5: /* neg-z */ + vec[0]= -tvec[0]; + vec[1]= -tvec[1]; + /* vec[2]= 0.0; */ + break; + } +} + +/* lens/angle conversion (radians) */ float lens_to_angle(float lens) { return 2.0f * atan(16.0f/lens); |