Skip to content

Commit 55a0cab

Browse files
committed
I think quaternions are finished now
1 parent c4e7017 commit 55a0cab

File tree

7 files changed

+150
-160
lines changed

7 files changed

+150
-160
lines changed

src/main/scala/io/github/hexagonnico/vecmatlib/quaternion/Quaternion.scala

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package io.github.hexagonnico.vecmatlib.quaternion
22

3-
import io.github.hexagonnico.vecmatlib.vector.Vec3d
3+
import io.github.hexagonnico.vecmatlib.vector.VecAbstract
44

55
/**
66
* Abstract class with quaternion operations.
77
*
88
* @tparam Q The quaternion class extending this one
99
*/
10-
abstract class Quaternion[Q <: Quaternion[Q]] {
10+
abstract class Quaternion[Q <: Quaternion[Q, V], V <: VecAbstract[V]] {
1111

1212
/**
1313
* Returns the sum between this quaternion and the given one.
@@ -103,29 +103,29 @@ abstract class Quaternion[Q <: Quaternion[Q]] {
103103
def normalized: Q
104104

105105
/**
106-
* Returns the multiplicative inverse (or the reciprocal) of this quaternion.
106+
* Returns the multiplicative inverse of this quaternion.
107107
*
108108
* The same quaternion can be obtained with `1.0 / q`.
109109
*
110-
* @return The reciprocal of this quaternion
110+
* @return The inverse of this quaternion
111111
*/
112-
def reciprocal: Q
112+
def inverse: Q
113113

114114
/**
115-
* Returns the product of this quaternion by the [[reciprocal]] of the given one.
115+
* Returns the product of this quaternion by the [[inverse]] of the given one.
116116
*
117117
* @param q The second operand of the division
118-
* @return The product of this quaternion by the reciprocal of the given one
118+
* @return The product of this quaternion by the inverse of the given one
119119
*/
120-
def /(q: Q): Q = this * q.reciprocal
120+
def /(q: Q): Q = this * q.inverse
121121

122122
/**
123-
* Returns the product of this quaternion by the [[reciprocal]] of the given one.
123+
* Returns the product of this quaternion by the [[inverse]] of the given one.
124124
*
125125
* This method can be used in place of the '/' operator for better interoperability with Java.
126126
*
127127
* @param q The second operand of the division
128-
* @return The product of this quaternion by the reciprocal of the given one
128+
* @return The product of this quaternion by the inverse of the given one
129129
*/
130130
def divide(q: Q): Q = this / q
131131

@@ -150,7 +150,7 @@ abstract class Quaternion[Q <: Quaternion[Q]] {
150150
*
151151
* @return This quaternion's rotation in the form of euler angles
152152
*/
153-
def euler: Vec3d
153+
def euler: V
154154

155155
/**
156156
* Returns the angle of the rotation represented by this unit quaternion.
@@ -162,11 +162,14 @@ abstract class Quaternion[Q <: Quaternion[Q]] {
162162
def angle: Double
163163

164164
/**
165-
* Returns the axis this quaternion is rotating around.
165+
* Returns the vector part of this quaternion.
166+
* The vector part of a quaternion `w + xi + yj + zk` is the vector `(x, y, z)`.
166167
*
167-
* @return The axis this quaternion is rotating around
168+
* Normalize this vector to get the rotation axis of the quaternion.
169+
*
170+
* @return
168171
*/
169-
def axis: Vec3d
172+
def vector: V
170173

171174
/**
172175
* Returns that is, this quaternion divided by its norm or [[length]].

src/main/scala/io/github/hexagonnico/vecmatlib/quaternion/QuaternionD.scala

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import io.github.hexagonnico.vecmatlib.vector.Vec3d
1212
* @param y The second component of the vector part (imaginary 'j' axis)
1313
* @param z The third component of the vector part (imaginary 'k' axis)
1414
*/
15-
case class QuaternionD(w: Double, x: Double, y: Double, z: Double) extends Quaternion[QuaternionD] with Double4 {
15+
case class QuaternionD(w: Double, x: Double, y: Double, z: Double) extends Quaternion[QuaternionD, Vec3d] with Double4 {
1616

1717
/**
1818
* Constructs a quaternion from the given scalar part and the given vector part
@@ -208,35 +208,35 @@ case class QuaternionD(w: Double, x: Double, y: Double, z: Double) extends Quate
208208
override def normalized: QuaternionD = this / this.length
209209

210210
/**
211-
* Returns the multiplicative inverse (or the reciprocal) of this quaternion.
211+
* Returns the multiplicative inverse of this quaternion.
212212
*
213213
* The same quaternion can be obtained with `1.0 / q`.
214214
*
215-
* @return The reciprocal of this quaternion
215+
* @return The inverse of this quaternion
216216
*/
217-
override def reciprocal: QuaternionD = this.conjugate / this.lengthSquared
217+
override def inverse: QuaternionD = this.conjugate / this.lengthSquared
218218

219219
/**
220-
* Returns the product of this quaternion by the [[reciprocal]] of the quaternion `w + xi + yj + zk`.
220+
* Returns the product of this quaternion by the [[inverse]] of the quaternion `w + xi + yj + zk`.
221221
*
222222
* @param w The real/scalar part of the second operand of the division
223223
* @param x The first component of the vector part of the second operand of the division
224224
* @param y The second component of the vector part of the second operand of the division
225225
* @param z The third component of the vector part of the second operand of the division
226-
* @return The product of this quaternion by the reciprocal of the quaternion `w + xi + yj + zk`
226+
* @return The product of this quaternion by the inverse of the quaternion `w + xi + yj + zk`
227227
*/
228228
def /(w: Double, x: Double, y: Double, z: Double): QuaternionD = this / QuaternionD(w, x, y, z)
229229

230230
/**
231-
* Returns the product of this quaternion by the [[reciprocal]] of the quaternion `w + xi + yj + zk`.
231+
* Returns the product of this quaternion by the [[inverse]] of the quaternion `w + xi + yj + zk`.
232232
*
233233
* This method can be used in place of the '/' operator for better interoperability with Java.
234234
*
235235
* @param w The real/scalar part of the second operand of the division
236236
* @param x The first component of the vector part of the second operand of the division
237237
* @param y The second component of the vector part of the second operand of the division
238238
* @param z The third component of the vector part of the second operand of the division
239-
* @return The product of this quaternion by the reciprocal of the quaternion `w + xi + yj + zk`
239+
* @return The product of this quaternion by the inverse of the quaternion `w + xi + yj + zk`
240240
*/
241241
def divide(w: Double, x: Double, y: Double, z: Double): QuaternionD = this / (w, x, y, z)
242242

@@ -285,14 +285,14 @@ case class QuaternionD(w: Double, x: Double, y: Double, z: Double) extends Quate
285285
override def angle: Double = 2.0 * math.acos(this.w)
286286

287287
/**
288-
* Returns the axis this quaternion is rotating around.
288+
* Returns the vector part of this quaternion.
289+
* The vector part of a quaternion `w + xi + yj + zk` is the vector `(x, y, z)`.
289290
*
290-
* @return The axis this quaternion is rotating around
291+
* Normalize this vector to get the rotation axis of the quaternion.
292+
*
293+
* @return
291294
*/
292-
override def axis: Vec3d = {
293-
val r = 1.0 / math.sqrt(1.0 - w * w)
294-
Vec3d(x * r, y * r, z * r)
295-
}
295+
override def vector: Vec3d = Vec3d(this.x, this.y, this.z)
296296

297297
/**
298298
* Returns that is, this quaternion divided by its norm or [[length]].
@@ -447,11 +447,11 @@ object QuaternionD {
447447
def *(q: QuaternionD): QuaternionD = q * l
448448

449449
/**
450-
* Returns the product between this scalar and the [[Quaternion.reciprocal]] of the given quaternion.
450+
* Returns the product between this scalar and the [[Quaternion.inverse]] of the given quaternion.
451451
*
452452
* @param q The second operand of the division
453-
* @return The product of this scalar by the reciprocal of the given quaternion
453+
* @return The product of this scalar by the inverse of the given quaternion
454454
*/
455-
def /(q: QuaternionD): QuaternionD = l * q.reciprocal
455+
def /(q: QuaternionD): QuaternionD = l * q.inverse
456456
}
457457
}

src/main/scala/io/github/hexagonnico/vecmatlib/quaternion/QuaternionF.scala

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import io.github.hexagonnico.vecmatlib.vector.{Vec3d, Vec3f}
1212
* @param y The second component of the vector part (imaginary 'j' axis)
1313
* @param z The third component of the vector part (imaginary 'k' axis)
1414
*/
15-
case class QuaternionF(w: Float, x: Float, y: Float, z: Float) extends Quaternion[QuaternionF] with Float4 {
15+
case class QuaternionF(w: Float, x: Float, y: Float, z: Float) extends Quaternion[QuaternionF, Vec3f] with Float4 {
1616

1717
/**
1818
* Constructs a quaternion from the given scalar part and the given vector part
@@ -208,35 +208,35 @@ case class QuaternionF(w: Float, x: Float, y: Float, z: Float) extends Quaternio
208208
override def normalized: QuaternionF = this / this.length.toFloat
209209

210210
/**
211-
* Returns the multiplicative inverse (or the reciprocal) of this quaternion.
211+
* Returns the multiplicative inverse of this quaternion.
212212
*
213213
* The same quaternion can be obtained with `1.0 / q`.
214214
*
215-
* @return The reciprocal of this quaternion
215+
* @return The inverse of this quaternion
216216
*/
217-
override def reciprocal: QuaternionF = this.conjugate / this.lengthSquared
217+
override def inverse: QuaternionF = this.conjugate / this.lengthSquared
218218

219219
/**
220-
* Returns the product of this quaternion by the [[reciprocal]] of the quaternion `w + xi + yj + zk`.
220+
* Returns the product of this quaternion by the [[inverse]] of the quaternion `w + xi + yj + zk`.
221221
*
222222
* @param w The real/scalar part of the second operand of the division
223223
* @param x The first component of the vector part of the second operand of the division
224224
* @param y The second component of the vector part of the second operand of the division
225225
* @param z The third component of the vector part of the second operand of the division
226-
* @return The product of this quaternion by the reciprocal of the quaternion `w + xi + yj + zk`
226+
* @return The product of this quaternion by the inverse of the quaternion `w + xi + yj + zk`
227227
*/
228228
def /(w: Float, x: Float, y: Float, z: Float): QuaternionF = this / QuaternionF(w, x, y, z)
229229

230230
/**
231-
* Returns the product of this quaternion by the [[reciprocal]] of the quaternion `w + xi + yj + zk`.
231+
* Returns the product of this quaternion by the [[inverse]] of the quaternion `w + xi + yj + zk`.
232232
*
233233
* This method can be used in place of the '/' operator for better interoperability with Java.
234234
*
235235
* @param w The real/scalar part of the second operand of the division
236236
* @param x The first component of the vector part of the second operand of the division
237237
* @param y The second component of the vector part of the second operand of the division
238238
* @param z The third component of the vector part of the second operand of the division
239-
* @return The product of this quaternion by the reciprocal of the quaternion `w + xi + yj + zk`
239+
* @return The product of this quaternion by the inverse of the quaternion `w + xi + yj + zk`
240240
*/
241241
def divide(w: Float, x: Float, y: Float, z: Float): QuaternionF = this / (w, x, y, z)
242242

@@ -245,22 +245,14 @@ case class QuaternionF(w: Float, x: Float, y: Float, z: Float) extends Quaternio
245245
*
246246
* @return The exponential of this quaternion
247247
*/
248-
override def exp: QuaternionF = {
249-
val v = Vec3d(this.x, this.y, this.z)
250-
val length = v.length
251-
(QuaternionD(math.cos(length), v / v.length * math.sin(length)) * math.exp(this.w)).toFloat
252-
}
248+
override def exp: QuaternionF = this.toDouble.exp.toFloat
253249

254250
/**
255251
* Returns the logarithm of this quaternion.
256252
*
257253
* @return The logarithm of this quaternion
258254
*/
259-
override def log: QuaternionF = {
260-
val v = Vec3d(this.x, this.y, this.z)
261-
val length = this.length
262-
QuaternionD(math.log(length), v.normalized * math.acos(this.w / length)).toFloat
263-
}
255+
override def log: QuaternionF = this.toDouble.log.toFloat
264256

265257
/**
266258
* Returns this quaternion's rotation in the form of euler angles.
@@ -269,11 +261,7 @@ case class QuaternionF(w: Float, x: Float, y: Float, z: Float) extends Quaternio
269261
*
270262
* @return This quaternion's rotation in the form of euler angles
271263
*/
272-
override def euler: Vec3d = Vec3d(
273-
math.atan2(2.0 * (w * x + y * z), 1.0 - 2.0 * (x * x + y * y)),
274-
2.0 * math.atan2(1.0 + 2.0 * (w * y - x * z), 1.0 - 2.0 * (w * y - x * z)) - math.Pi / 2.0,
275-
math.atan2(2.0 * (w * z + x * y), 1.0 - 2.0 * (y * y + z * z))
276-
)
264+
override def euler: Vec3f = this.toDouble.euler.toFloat
277265

278266
/**
279267
* Returns the angle of the rotation represented by this unit quaternion.
@@ -285,25 +273,21 @@ case class QuaternionF(w: Float, x: Float, y: Float, z: Float) extends Quaternio
285273
override def angle: Double = 2.0 * math.acos(this.w)
286274

287275
/**
288-
* Returns the axis this quaternion is rotating around.
276+
* Returns the vector part of this quaternion.
277+
* The vector part of a quaternion `w + xi + yj + zk` is the vector `(x, y, z)`.
289278
*
290-
* @return The axis this quaternion is rotating around
279+
* Normalize this vector to get the rotation axis of the quaternion.
280+
*
281+
* @return
291282
*/
292-
override def axis: Vec3d = {
293-
val r = 1.0f / math.sqrt(1.0 - w * w)
294-
Vec3d(x * r, y * r, z * r)
295-
}
283+
override def vector: Vec3f = Vec3f(this.x, this.y, this.z)
296284

297285
/**
298286
* Returns that is, this quaternion divided by its norm or [[length]].
299287
*
300288
* @return A unit quaternion
301289
*/
302-
override def slerp(to: QuaternionF, weight: Double): QuaternionF = {
303-
val omega = math.acos(this dot to)
304-
val sinOmega = math.sin(omega)
305-
(this.toDouble * (math.sin((1.0 - weight) * omega) / sinOmega) + to.toDouble * (math.sin(weight * omega) / sinOmega)).toFloat
306-
}
290+
override def slerp(to: QuaternionF, weight: Double): QuaternionF = this.toDouble.slerp(to.toDouble, weight).toFloat
307291

308292
/**
309293
* Converts this quaternion to a double quaternion.
@@ -475,11 +459,11 @@ object QuaternionF {
475459
def *(q: QuaternionF): QuaternionF = q * l
476460

477461
/**
478-
* Returns the product between this scalar and the [[Quaternion.reciprocal]] of the given quaternion.
462+
* Returns the product between this scalar and the [[Quaternion.inverse]] of the given quaternion.
479463
*
480464
* @param q The second operand of the division
481-
* @return The product of this scalar by the reciprocal of the given quaternion
465+
* @return The product of this scalar by the inverse of the given quaternion
482466
*/
483-
def /(q: QuaternionF): QuaternionF = l * q.reciprocal
467+
def /(q: QuaternionF): QuaternionF = l * q.inverse
484468
}
485469
}

src/test/java/io/github/hexagonnico/vecmatlib/quaternion/TestQuaternionD.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,22 @@ public void testMultiplyQuaternion() {
6060

6161
@Test
6262
public void testDivideByScalar() {
63-
QuaternionD q = new QuaternionD(1.2, 1.4, -2.1, 3.0);
64-
assertEqualApprox(q.dividedBy(1.2), 1.0, 1.16666666667, -1.75, 2.5);
63+
QuaternionD q = new QuaternionD(1.5, 2.0, -2.5, 3.0);
64+
assertEqualApprox(q.dividedBy(2.0), 0.75, 1.0, -1.25, 1.5);
6565
}
6666

6767
@Test
6868
public void testDivideQuaternion() {
69-
QuaternionD q = new QuaternionD(1.2, 1.4, -2.1, 3.0);
70-
QuaternionD p = new QuaternionD(0.3, -1.5, 1.1, 0.0);
71-
assertEqualApprox(q.divide(p), -1.1408450704225355, 1.5549295774647889, 0.7183098591549296, 0.7070422535211269);
69+
QuaternionD q = new QuaternionD(1.0, 1.0, 1.0, 1.0);
70+
QuaternionD p = new QuaternionD(1.0, 0.0, 1.0, 0.0);
71+
assertEqualApprox(q.divide(p), 1.0, 1.0, 0.0, 0.0);
7272
}
7373

7474
@Test
7575
public void testDivideFourValues() {
76-
QuaternionD q = new QuaternionD(1.2, 1.4, -2.1, 3.0);
77-
QuaternionD p = q.divide(0.3, -1.5, 1.1, 0.0);
78-
assertEqualApprox(p, -1.1408450704225355, 1.5549295774647889, 0.7183098591549296, 0.7070422535211269);
76+
QuaternionD q = new QuaternionD(1.0, 1.0, 1.0, 1.0);
77+
QuaternionD p = q.divide(1.0, 0.0, 1.0, 0.0);
78+
assertEqualApprox(p, 1.0, 1.0, 0.0, 0.0);
7979
}
8080

8181
@Test
@@ -85,9 +85,9 @@ public void testEqualsFourValues() {
8585
}
8686

8787
private static void assertEqualApprox(QuaternionD a, double w, double x, double y, double z) {
88-
Assert.assertTrue(Math.abs(a.w() - w) < 1e-9);
89-
Assert.assertTrue(Math.abs(a.x() - x) < 1e-9);
90-
Assert.assertTrue(Math.abs(a.y() - y) < 1e-9);
91-
Assert.assertTrue(Math.abs(a.z() - z) < 1e-9);
88+
Assert.assertTrue(Math.abs(a.w() - w) < 1e-6);
89+
Assert.assertTrue(Math.abs(a.x() - x) < 1e-6);
90+
Assert.assertTrue(Math.abs(a.y() - y) < 1e-6);
91+
Assert.assertTrue(Math.abs(a.z() - z) < 1e-6);
9292
}
9393
}

src/test/java/io/github/hexagonnico/vecmatlib/quaternion/TestQuaternionF.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,22 @@ public void testMultiplyQuaternion() {
6060

6161
@Test
6262
public void testDivideByScalar() {
63-
QuaternionF q = new QuaternionF(1.2f, 1.4f, -2.1f, 3.0f);
64-
assertEqualApprox(q.dividedBy(1.2f), 1.0f, 1.16666666667f, -1.75f, 2.5f);
63+
QuaternionF q = new QuaternionF(1.5f, 2.0f, -2.5f, 3.0f);
64+
assertEqualApprox(q.dividedBy(2.0f), 0.75f, 1.0f, -1.25f, 1.5f);
6565
}
6666

6767
@Test
6868
public void testDivideQuaternion() {
69-
QuaternionF q = new QuaternionF(1.2f, 1.4f, -2.1f, 3.0f);
70-
QuaternionF p = new QuaternionF(0.3f, -1.5f, 1.1f, 0.0f);
71-
assertEqualApprox(q.divide(p), -1.1408450704225355f, 1.5549295774647889f, 0.7183098591549296f, 0.7070422535211269f);
69+
QuaternionF q = new QuaternionF(1.0f, 1.0f, 1.0f, 1.0f);
70+
QuaternionF p = new QuaternionF(1.0f, 0.0f, 1.0f, 0.0f);
71+
assertEqualApprox(q.divide(p), 1.0f, 1.0f, 0.0f, 0.0f);
7272
}
7373

7474
@Test
7575
public void testDivideFourValues() {
76-
QuaternionF q = new QuaternionF(1.2f, 1.4f, -2.1f, 3.0f);
77-
QuaternionF p = q.divide(0.3f, -1.5f, 1.1f, 0.0f);
78-
assertEqualApprox(p, -1.1408450704225355f, 1.5549295774647889f, 0.7183098591549296f, 0.7070422535211269f);
76+
QuaternionF q = new QuaternionF(1.0f, 1.0f, 1.0f, 1.0f);
77+
QuaternionF p = q.divide(1.0f, 0.0f, 1.0f, 0.0f);
78+
assertEqualApprox(p, 1.0f, 1.0f, 0.0f, 0.0f);
7979
}
8080

8181
@Test

0 commit comments

Comments
 (0)