1 module polyplex.math.simplemath.quaternion; 2 import polyplex.math.simplemath.matrix4x4; 3 import polyplex.math.simplemath.vectors; 4 5 public struct Quaternion { 6 private float[4] data; 7 8 /** 9 Creates a new Quaternion, in which initial values are X, Y, Z and W. 10 */ 11 public this(float x, float y, float z, float w) { 12 data[0] = x; 13 data[1] = y; 14 data[2] = z; 15 data[3] = w; 16 } 17 18 /** 19 Creates a new Quaternion, in which initial values are X, Y, Z and 0. 20 */ 21 public this(float x, float y, float z) { 22 data[0] = x; 23 data[1] = y; 24 data[2] = z; 25 data[3] = 0; 26 } 27 28 /** 29 Creates a new Quaternion, in which initial values are X, Y, 0 and 0. 30 */ 31 public this(float x, float y) { 32 data[0] = x; 33 data[1] = y; 34 data[2] = 0; 35 data[3] = 0; 36 } 37 38 /** 39 Creates a new Quaternion, in which all initial values are X. 40 */ 41 public this(float x) { 42 data[0] = x; 43 data[1] = x; 44 data[2] = x; 45 data[3] = x; 46 } 47 48 /** 49 The X component. 50 */ 51 public float X() { return data[0]; } 52 public void X(float value) { data[0] = value; } 53 54 /** 55 The Y component. 56 */ 57 public float Y() { return data[1]; } 58 public void Y(float value) { data[1] = value; } 59 60 /** 61 The Z component. 62 */ 63 public float Z() { return data[2]; } 64 public void Z(float value) { data[2] = value; } 65 66 /** 67 The W component. 68 */ 69 public float W() { return data[3]; } 70 public void W(float value) { data[3] = value; } 71 72 public void Reorient(Vector3 axis, float angle) { 73 this.X = axis.X; 74 this.Y = axis.Y; 75 this.Z = axis.Z; 76 this.W = angle; 77 } 78 79 public Vector3 ForwardDirection() { 80 return Vector3( 81 2 * (X*Z + W*Y), 82 2 * (Y*Z - W*X), 83 1 - 2 * (X*X + Y*Y)); 84 } 85 86 public Vector3 UpDirection() { 87 return Vector3( 88 2 * (X*Y + W*Z), 89 1 - 2 * (X*X + Z*Z), 90 2 * (Y*Z - W*X)); 91 } 92 93 public Vector3 LeftDirection() { 94 return Vector3( 95 1 - 2 * (Y*Y + Z*Z), 96 2 * (X*Y + W*Z), 97 2 * (X*Z - W*Y)); 98 } 99 100 public void Rotate(T)(Vector3 axis, T theta) { 101 X = Mathf.Sin(cast(real)theta/2) * axis.X; 102 Y = Mathf.Sin(cast(real)theta/2) * axis.Y; 103 Z = Mathf.Sin(cast(real)theta/2) * axis.Z; 104 W = Mathf.Cos(cast(real)theta/2); 105 } 106 107 public static Quaternion Rotated(T)(Vector3 axis, T theta) { 108 Quaternion q; 109 q.Rotate(axis, theta); 110 return q; 111 } 112 113 /*public Vector3 Rotate(T)(Vector3 axis, T angle) { 114 Vector3 vec; 115 vec.X = X; 116 vec.Y = Y; 117 vec.Z = Z; 118 119 float vd = 2.0f * vec.Dot(axis); 120 Vector3 vdv = Vector3(vec.X * vd, vec.Y * vd, vec.Z * vd); 121 122 float ad = W*W - vec.Dot(vec); 123 Vector3 adv = Vector3(axis.X * ad, axis.Y * ad, axis.Z * ad); 124 125 float vc = 2.0f * W; 126 Vector3 vcv = vec.Cross(axis); 127 vcv.X *= vc; 128 vcv.Y *= vc; 129 vcv.Z *= vc; 130 131 return vdv * adv + vcv ; 132 }*/ 133 134 public static Quaternion EulerToQuaternion(Vector3 euler) { 135 Quaternion quat; 136 137 // pitch 138 double cp = Mathf.Cos(euler.X * 0.5); 139 double sp = Mathf.Cos(euler.X * 0.5); 140 141 // roll 142 double cr = Mathf.Cos(euler.Y * 0.5); 143 double sr = Mathf.Cos(euler.Y * 0.5); 144 145 // yaw 146 double cy = Mathf.Cos(euler.Z * 0.5); 147 double sy = Mathf.Cos(euler.Z * 0.5); 148 149 150 // convert to quaternion. 151 quat.X = cy * sr * cp - sy * cr * sp; 152 quat.Y = cy * cr * sp + sy * sr * cp; 153 quat.Z = sy * cr * cp - cy * sr * sp; 154 quat.W = cy * cr * cp + sy * sr * sp; 155 return quat; 156 } 157 158 public static Quaternion FromMatrix(Matrix4x4 mat) { 159 Quaternion qt = Quaternion.Identity; 160 float tr = mat[0,0] + mat[1,1] + mat[2,2]; 161 162 if (tr > 0) { 163 float S = Mathf.Sqrt(tr+1.0) * 2; 164 qt.X = (mat[2,1] - mat[1,2]) / S; 165 qt.Y = (mat[0,2] - mat[2,0]) / S; 166 qt.Z = (mat[1,0] - mat[0,1]) / S; 167 qt.W = 0.25 * S; 168 return qt; 169 } 170 171 if ((mat[0,0] > mat[1,1])&(mat[0,0] > mat[2,2])) { 172 float S = Mathf.Sqrt(1.0 + mat[0,0] - mat[1,1] - mat[2,2]) * 2; 173 qt.X = 0.25 * S; 174 qt.Y = (mat[0,1] + mat[1,0]) / S; 175 qt.Z = (mat[0,2] + mat[2,0]) / S; 176 qt.W = (mat[2,1] - mat[1,2]) / S; 177 return qt; 178 } 179 180 if (mat[1,1] > mat[2,2]) { 181 float S = Mathf.Sqrt(1.0 + mat[1,1] - mat[0,0] - mat[2,2]) * 2; 182 qt.X = (mat[0,1] + mat[1,0]) / S; 183 qt.Y = 0.25 * S; 184 qt.Z = (mat[1,2] - mat[2,1]) / S; 185 qt.W = (mat[0,2] + mat[2,0]) / S; 186 return qt; 187 } 188 189 float S = Mathf.Sqrt(1.0 + mat[2,2] - mat[0,0] - mat[1,1]) * 2; 190 qt.X = (mat[0,2] + mat[2,0]) / S; 191 qt.Y = (mat[1,2] - mat[2,1]) / S; 192 qt.Z = 0.25 * S; 193 qt.W = (mat[1,0] + mat[0,1]) / S; 194 return qt; 195 } 196 197 // Binary actions. 198 public @trusted nothrow Quaternion opBinary(string op, T2)(T2 other) if (is (T2 : Quaternion)) { 199 // Operation on these, (due to being smallest size) can be done faster this way. 200 mixin(q{ 201 return Quaternion( 202 this.X * other.W + this.Y * other.Z + this.Z * other.Y - this.W * other.X, 203 -this.X * this.Z - this.Y * other.W + this.Z * other.X + this.W * other.Y, 204 this.X * this.Y - this.Y * other.X + this.Z * other.W + this.W * other.Z, 205 -this.X * this.X - this.Y * other.Y + this.Z * other.Z + this.W * other.W); 206 }.Format(op) 207 ); 208 } 209 210 public @trusted Quaternion Normalized() { 211 import polyplex.math; 212 double n = Mathf.Sqrt(X*X+Y*Y+Z*Z+W*W); 213 return Quaternion(X/n, Y/n, Z/n, W/n); 214 } 215 216 public static Quaternion Identity() { 217 return Quaternion(0f, 0f, 0f, 0f); 218 } 219 220 public string toString() { 221 import polyplex.utils.strutils; 222 return "<{0}, {1}, {2}, {3}>".Format(X, Y, Z, W); 223 } 224 }