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