1 /**
2 dlsl.dual_quaternion
3 
4 
5 Authors: Peter Particle
6 License: MIT
7 
8 Based on: Ben Kenwright: A Beginners Guide to Dual-Quaternions
9 */
10 
11 module dlsl.dual_quaternion;
12 
13 public import dlsl.quaternion;
14 
15 
16 
17 /// Pre-defined quaternion of type float.
18 alias DualQuaternion!( float ) dualQ;
19 
20 
21 /// If T is a quaternion, this evaluates to true, otherwise false.
22 template isDualQuaternion( T )  {  enum isDualQuaternion = is( typeof( isDualQuaternionImpl( T.init )));  }
23 private void isDualQuaternionImpl( T )( Quaternion!( T ) q )  {}
24 alias isDualQ = isDualQuaternion;
25 
26 
27 /// Base template for all quaternion-types.
28 /// Params:
29 ///  type = all values get stored as this type
30 struct DualQuaternion( type ) {
31 
32     nothrow @nogc:
33 
34     /// Returns the current matrix formatted as flat string.
35     char[] toString( char[] buffer ) {
36         import core.stdc.stdio : sprintf;
37 
38         size_t s = 0;
39         s += sprintf( buffer.ptr + s, "[ ");
40         s += r.toString( buffer[ s .. $ ] ).length;
41 
42         s += sprintf( buffer.ptr + s, ", ");
43         s += d.toString( buffer[ s .. $ ] ).length;
44 
45         s += sprintf( buffer.ptr + s, " ]");
46 
47         return buffer[ 0 .. s ];
48     }
49 
50 
51     pure nothrow @nogc:
52 
53     /// Returns the pointer to the stored values as OpenGL requires it.
54     /// Note this will return a pointer to a $( RED column - major ) matrix,
55     /// $( RED this is the OpneGL convention and expected in programs via Uniforms or UBOs ).
56     auto ptr() {
57         return data.ptr;
58     }
59 
60 
61     pure nothrow @nogc @safe:
62 
63     union {
64         type[8] data;
65         struct {
66             Quaternion!type r;
67             Quaternion!type d;
68         }
69     }
70 
71     alias type valueType;   /// Holds the internal type of the vector.
72     alias Quaternion!( type ) quaternionType;
73 
74 
75     /// Returns an identity vector ( x=0, y=0, z=0, w=1 ).
76     static DualQuaternion identity() {
77         return DualQuaternion(
78             cast( type )0, cast( type )0, cast( type )0, cast( type )1,
79             cast( type )0, cast( type )0, cast( type )0, cast( type )0
80         );
81     }
82 
83 
84     /// Construct from eight values of type $( I valueType )
85     /// first four the real part, rotation quaternion with last value being the w value
86     /// last four being the dual, translation part
87     this()(
88         valueType rx, valueType ry, valueType rz, valueType rw,
89         valueType dx, valueType dy, valueType dz, valueType dw
90         ) {
91         data[0] = rx; data[1] = ry; data[2] = rz; data[3] = rw;
92         data[4] = dx; data[5] = dy; data[6] = dz; data[7] = dw;
93     }
94 
95 
96     /// Construct from two Quaternions
97     this()( Quaternion!valueType r, Quaternion!valueType d ) {
98         this.r = r.normalize;
99         this.d = d;
100     }
101 
102     this()( Quaternion!valueType r, Vector!( valueType, 3 ) v ) {
103         this.r = r.normalize;
104         this.d = Quaternion!valueType( v, 0 ) * this.r * 0.5;
105         // Todo(pp): confirm that the above is an associative operation and the same as bellow
106         // this.d = ( Quaternion!valueType( v, 0 ) * this.r ) * 0.5;
107     }
108 
109 
110 
111     ///////////////
112     // Operators //
113     ///////////////
114 
115     /// DualQuternion add or subtract DUalQuaternion
116     DualQuaternion opBinary( string op )( DualQuaternion dq ) const if(( op == "+" ) || ( op == "-" )) {
117         DualQuaternion result;
118         mixin( "result[] = data[] " ~ op ~ " dq[];" );
119         return result;
120     }
121 
122     // Multiplication order - left to right
123     //static DualQuaternion operator * ( DualQuaternion lhs, DualQuaternion rhs ) {
124     //    return DualQuaternion( rhs.m_real * lhs.m_real, rhs.m_dual * lhs.m_real + rhs.m_real * lhs.m_dual );
125     //}
126 
127     DualQuaternion opBinary( string op : "*" )( DualQuaternion dq ) const {
128         return DualQuternion( dq.r * r, dq.d * r + dq.r * d );
129     }
130 
131 
132     DualQuaternion opBinary( string op : "*" )( valueType s ) const {
133         auto result = this;
134         result[] *= s;
135         return result;
136         //return DualQuaternion(
137         //  s * r.x, s * r.y, s * r.z, s * r.w,
138         //  s * d.x, s * d.y, s * d.z, s * d.w
139         //);
140     }
141 
142 
143     Quaternion!valueType rotation() {
144         return r;
145     }
146 
147 
148     Vector!( valueType, 3 ) translation() {
149         //Quaternion t = dq.m_dual;
150         //t.scaleIt( 2.0 );
151         //t *= dq.m_real.conjugate();
152         //return MVector( t.x, t.y, t.z );
153         return Vector!( valueType, 3 )( d * 2 * dlsl.quaternion.invert( r ));
154     }
155 }
156 
157 
158 pure nothrow @nogc @safe:
159 
160 /// Convenience functions returning the vector as corresponding matrices
161 /// Params:
162 ///     q = convert quternion
163 /// Returns: mat4 / mat4x4, mat4x3
164 import dlsl.matrix;
165 auto toMat4(   DQ )( DQ dq ) if( isDualQuaternion!DQ && is( DQ.valueType : float )) { return mat4(   dq ); }
166 auto toMat4x3( DQ )( DQ dq ) if( isDualQuaternion!DQ && is( DQ.valueType : float )) { return mat4x3( dq ); }
167 auto toMat4x4( DQ )( DQ dq ) if( isDualQuaternion!DQ && is( DQ.valueType : float )) { return mat4x4( dq ); }
168 
169 
170 /// Dual Quaternion dot product
171 DQ.valueType dot( DQ )( in DQ a, in DQ b ) if( isDualQuaternion!DQ ) {
172     return dot( a.r, b.r );
173 }
174 
175 
176 /// Dual Quaternion normalize
177 DQ normalize( DQ )( in DQ dq ) if( isDualQuaternion!DQ ) {
178     auto l = dq.r.lengthSquared;
179     import dlsl.math : almostEqual;
180     if( l == 0 || almostEqual( 1.0f, l, 0.00000001f ))
181         return dq;
182     auto recipLength = 1.0 / l.sqrt;
183     DQ result = dq;
184     result.r *= recipLength;
185     result.d *= recipLength;
186     return result;
187 }
188 
189 DQ invert( DQ )( in DQ dq ) if( isDualQuaternion!DQ ) {
190     return DQ(
191         dlsl.quaternion.invert( dq.r ),
192         dlsl.quaternion.invert( dq.d )
193     );
194 } alias conjugate = invert;
195