Add support for duals and dual quaternions.

Not everything is unit-tested, but the currently important stuff is.
This commit is contained in:
Bill Currie 2012-05-01 14:19:38 +09:00
parent 3b938592c3
commit f874aeb941
4 changed files with 276 additions and 1 deletions

View file

@ -277,6 +277,157 @@ extern const vec_t *const quat_origin;
//For printf etc
#define QuatExpand(q) (q)[0], (q)[1], (q)[2], (q)[3]
#define DualAdd(a,b,c) \
do { \
(c).r = (a).r + (b).r; \
(c).e = (a).e + (b).e; \
} while (0)
#define DualSubtract(a,b,c) \
do { \
(c).r = (a).r - (b).r; \
(c).e = (a).e - (b).e; \
} while (0)
#define DualNegate(a,b) \
do { \
(b).r = -(a).r; \
(b).e = -(a).e; \
} while (0)
#define DualConj(a,b) \
do { \
(b).r = (a).r; \
(b).e = -(a).e; \
} while (0)
#define DualMult(a,b,c) \
do { \
(c).e = (a).r * (b).e + (a).e * (b).r; \
(c).r = (a).r * (b).r; \
} while (0)
#define DualMultAdd(a,s,b,c) \
do { \
(c).r = (a).r + (s) * (b).r; \
(c).e = (a).e + (s) * (b).e; \
} while (0)
#define DualMultSub(a,s,b,c) \
do { \
(c).r = (a).r - (s) * (b).r; \
(c).e = (a).e - (s) * (b).e; \
} while (0)
#define DualNorm(a) ((a).r)
#define DualScale(a,b,c) \
do { \
(c).r = (a).r * (b); \
(c).e = (a).e * (b); \
} while (0)
#define DualCompCompare(x, op, y) ((x).r op (y).r) && ((x).e op (y).e)
#define DualCompare(x, y) DualCompCompare (x, ==, y)
#define DualIsZero(a) ((a).r == 0 && (a).e == 0)
#define DualIsUnit(a) (((a).r - 1) * ((a).r - 1) < 1e-6 && (a).e * (a).e < 1e-6)
#define DualSet(ar,ae,a) \
do { \
(a).ar = r; \
(a).er = r; \
} while (0)
#define DualZero(a) \
do { \
(a).e = (a).r = 0; \
} while (0)
#define DualBlend(d1,d2,b,d) \
do { \
(d).r = (d1).r * (1 - (b)) + (d2).r * (b); \
(d).e = (d1).e * (1 - (b)) + (d2).e * (b); \
} while (0)
#define DualExpand(d) (d).r, (d).e
#define DualQuatAdd(a,b,c) \
do { \
QuatAdd ((a).q0.q, (b).q0.q, (c).q0.q); \
QuatAdd ((a).qe.q, (b).qe.q, (c).qe.q); \
} while (0)
#define DualQuatSubtract(a,b,c) \
do { \
QuatSub ((a).q0.q, (b).q0.q, (c).q0.q); \
QuatSub ((a).qe.q, (b).qe.q, (c).qe.q); \
} while (0)
#define DualQuatNegate(a,b) \
do { \
QuatNegate ((a).q0.q, (b).q0.q); \
QuatNegate ((a).qe.q, (b).qe.q); \
} while (0)
#define DualQuatConjQ(a,b) \
do { \
QuatConj ((a).q0.q, (b).q0.q); \
QuatConj ((a).qe.q, (b).qe.q); \
} while (0)
#define DualQuatConjE(a,b) \
do { \
(b).q0 = (a).q0; \
QuatNegate ((a).qe.q, (b).qe.q); \
} while (0)
#define DualQuatConjQE(a,b) \
do { \
QuatConj ((a).q0.q, (b).q0.q); \
(b).qe.sv.s = -(a).qe.sv.s; \
VectorCopy ((a).qe.sv.v, (b).qe.sv.v); \
} while (0)
#define DualQuatMult(a,b,c) \
do { \
Quat_t t; \
QuatMult ((a).q0.q, (b).qe.q, t.q); \
QuatMult ((a).qe.q, (b).q0.q, (c).qe.q); \
QuatAdd (t.q, (c).qe.q, (c).qe.q); \
QuatMult ((a).q0.q, (b).q0.q, (c).q0.q); \
} while (0);
#define DualQuatMultAdd(a,s,b,c) \
do { \
QuatMultAdd ((a).q0.q, s, (b).q0.q, (c).q0.q); \
QuatMultAdd ((a).qe.q, s, (b).qe.q, (c).qe.q); \
} while (0)
#define DualQuatMultSub(a,s,b,c) \
do { \
QuatMultSub ((a).q0.q, s, (b).q0.q, (c).q0.q); \
QuatMultSub ((a).qe.q, s, (b).qe.q, (c).qe.q); \
} while (0)
#define DualQuatNorm(a,b) \
do { \
(b).r = QuatLength ((a).q0.q); \
(b).e = QDotProduct ((a).q0.q, (a).qe.q) / (b).r; \
} while (0)
#define DualQuatScale(a,b,c) \
do { \
QuatSub ((a).q0.q, (b), (c).q0.q); \
QuatSub ((a).qe.q, (b), (c).qe.q); \
} while (0)
#define DualQuatCompCompare(x, op, y) \
(QuatCompCompare ((x).q0.q, op, (y).q0.q) \
&&QuatCompCompare ((x).qe.q, op, (y).qe.q))
#define DualQuatCompare(x, y) DualQuatCompCompare (x, ==, y)
#define DualQuatIsZero(a) (QuatIsZero ((a).q0.q) && QuatIsZero ((a).qe.q))
#define DualQuatSetVect(vec, a) \
do { \
(a).q0.sv.s = 1; \
VectorZero ((a).q0.sv.v); \
(a).qe.sv.s = 0; \
VectorCopy (vec, (a).qe.sv.v); \
} while (0)
#define DualQuatRotTrans(rot, trans, dq) \
do { \
QuatCopy (rot, (dq).q0.q); \
(dq).qe.sv.s = 0; \
VectorScale (trans, 0.5, (dq).qe.sv.v); \
QuatMult ((dq).qe.q, (dq).q0.q, (dq).qe.q); \
} while (0)
#define DualQuatZero(a) \
do { \
QuatZero ((a).q0.q); \
QuatZero ((a).qe.q); \
} while (0)
#define DualQuatBlend(dq1,dq2,b,dq) \
do { \
QuatBlend ((dq1).q0.q, (dq2).q0.q, (b), (dq).q0.q); \
QuatBlend ((dq1).qe.q, (dq2).qe.q, (b), (dq).qe.q); \
} while (0)
#define DualQuatExpand(dq) QuatExpand ((dq).q0.q), QuatExpand ((dq).qe.q)
#define Mat4Copy(a, b) \
do { \
QuatCopy ((a) + 0, (b) + 0); \

View file

@ -81,6 +81,14 @@ typedef union {
} sv;
quat_t q;
} Quat_t;
typedef struct {
vec_t r;
vec_t e;
} Dual_t;
typedef struct {
Quat_t q0;
Quat_t qe;
} DualQuat_t;
typedef int fixed4_t;
typedef int fixed8_t;
typedef int fixed16_t;

View file

@ -2,7 +2,11 @@ AUTOMAKE_OPTIONS= foreign
INCLUDES= -I$(top_srcdir)/include
check_PROGRAMS=test-half test-qfs test-quat test-vrect
check_PROGRAMS=test-dq test-half test-qfs test-quat test-vrect
test_dq_SOURCES=test-dq.c
test_dq_LDADD=$(top_builddir)/libs/util/libQFutil.la
test_dq_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la
test_half_SOURCES=test-half.c
test_half_LDADD=$(top_builddir)/libs/util/libQFutil.la

112
libs/util/test/test-dq.c Normal file
View file

@ -0,0 +1,112 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "QF/mathlib.h"
//PITCH YAW ROLL
static vec3_t test_transforms[][2] = {
{{ 0, 0, 0}, { 0, 0, 0}},
{{ 0, 0, 0}, { 1, 2, 3}},
{{45, 0, 0}, { 0, 0, 0}},
{{45, 0, 0}, {-1, 2, 3}},
{{ 0, 45, 0}, { 0, 0, 0}},
{{ 0, 45, 0}, { 1,-2, 3}},
{{ 0, 0, 45}, { 0, 0, 0}},
{{ 0, 0, 45}, { 1, 2,-3}},
{{45, 45, 0}, { 0, 0, 0}},
{{45, 45, 0}, {-1,-2, 3}},
{{ 0, 45, 45}, { 0, 0, 0}},
{{ 0, 45, 45}, {-1, 2,-3}},
{{45, 0, 45}, { 0, 0, 0}},
{{45, 0, 45}, { 1,-2,-3}},
{{45, 45, 45}, { 0, 0, 0}},
{{45, 45, 45}, {-1,-2,-3}},
};
#define num_transform_tests \
(sizeof (test_transforms) / sizeof (test_transforms[0]))
// return true if a and b are close enough (yay, floats)
static int
compare (vec_t a, vec_t b)
{
vec_t diff = a - b;
return diff * diff < 0.001;
}
static int
test_transform (const vec3_t angles, const vec3_t translation)
{
int i;
const vec3_t v = {4,5,6};
vec3_t x;
quat_t rotation;
DualQuat_t transform, conj;
DualQuat_t vd, xd;
Dual_t dual;
VectorZero (x);
DualQuatZero (xd);
AngleQuat (angles, rotation);
DualQuatSetVect (v, vd);
DualQuatRotTrans (rotation, translation, transform);
DualQuatConjQE (transform, conj);
DualQuatNorm (vd, dual);
if (!DualIsUnit (dual)) {
printf ("dual vector not unit: "
"[(%g %g %g %g) (%g %g %g %g)] -> [%g %g]\n",
DualQuatExpand (vd), DualExpand (dual));
goto fail;
}
DualQuatNorm (transform, dual);
if (!DualIsUnit (dual)) {
printf ("dual quat not unit: "
"[(%g %g %g %g) (%g %g %g %g)] -> [%g %g]\n",
DualQuatExpand (transform), DualExpand (dual));
goto fail;
}
QuatMultVec (rotation, v, x);
VectorAdd (x, translation, x);
DualQuatMult (transform, vd, xd);
DualQuatMult (xd, conj, xd);
DualQuatNorm (xd, dual);
if (!DualIsUnit (dual)) {
printf ("dual result not unit: "
"[(%g %g %g %g) (%g %g %g %g)] -> [%g %g]\n",
DualQuatExpand (xd), DualExpand (dual));
goto fail;
}
for (i = 0; i < 3; i++)
if (!compare (xd.qe.sv.v[i], x[i]))
goto fail;
return 1;
fail:
printf ("\n\n(%g %g %g) (%g %g %g)\n",
VectorExpand (angles), VectorExpand (translation));
printf (" [(%g %g %g %g) (%g %g %g %g)]\n", DualQuatExpand (transform));
printf (" [(%g %g %g %g) (%g %g %g %g)]\n", DualQuatExpand (vd));
printf (" [(%g %g %g %g) (%g %g %g %g)]\n", DualQuatExpand (conj));
printf (" (%g %g %g)\n", VectorExpand (x));
printf (" (%g %g %g)\n", VectorExpand (xd.qe.sv.v));
return 0;
}
int
main (int argc, const char **argv)
{
int res = 0;
size_t i;
for (i = 0; i < num_transform_tests; i ++) {
if (!test_transform (test_transforms[i][0], test_transforms[i][1]))
res = 1;
}
return res;
}