| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- /*
- * Copyright (c) 2012-2026 Daniele Bartolini et al.
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
- namespace Crown
- {
- [Compact]
- public struct Quaternion
- {
- public double x;
- public double y;
- public double z;
- public double w;
- public Quaternion(double x, double y, double z, double w)
- {
- this.x = x;
- this.y = y;
- this.z = z;
- this.w = w;
- }
- public Quaternion.from_array(Gee.ArrayList<Value?> arr)
- {
- this.x = (double)arr[0];
- this.y = (double)arr[1];
- this.z = (double)arr[2];
- this.w = (double)arr[3];
- }
- public Quaternion.from_axis_angle(Vector3 axis, float angle)
- {
- double ha = angle * 0.5;
- double sa = Math.sin(ha);
- double ca = Math.cos(ha);
- this.x = axis.x * sa;
- this.y = axis.y * sa;
- this.z = axis.z * sa;
- this.w = ca;
- }
- public Quaternion.from_euler(double rx, double ry, double rz)
- {
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/
- double c1 = Math.cos(ry*0.5);
- double s1 = Math.sin(ry*0.5);
- double c2 = Math.cos(rz*0.5);
- double s2 = Math.sin(rz*0.5);
- double c3 = Math.cos(rx*0.5);
- double s3 = Math.sin(rx*0.5);
- double c1c2 = c1*c2;
- double s1s2 = s1*s2;
- double nw = c1c2*c3 - s1s2*s3;
- double nx = c1c2*s3 + s1s2*c3;
- double ny = s1*c2*c3 + c1*s2*s3;
- double nz = c1*s2*c3 - s1*c2*s3;
- this.x = nx;
- this.y = ny;
- this.z = nz;
- this.w = nw;
- }
- public Quaternion.from_matrix(Matrix4x4 m)
- {
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
- double tr = m.x.x + m.y.y + m.z.z;
- if (tr > 0.0) {
- double sq = Math.sqrt(1.0 + tr) * 0.5;
- double inv = 0.25 / sq;
- this.w = sq;
- this.x = (m.y.z - m.z.y) * inv;
- this.y = (m.z.x - m.x.z) * inv;
- this.z = (m.x.y - m.y.x) * inv;
- } else if ((m.x.x > m.y.y) && (m.x.x > m.z.z)) {
- double sq = Math.sqrt(1.0 + m.x.x - m.y.y - m.z.z) * 0.5;
- double inv = 0.25 / sq;
- this.x = sq;
- this.w = (m.y.z - m.z.y) * inv;
- this.y = (m.x.y + m.y.x) * inv;
- this.z = (m.z.x + m.x.z) * inv;
- } else if (m.y.y > m.z.z) {
- double sq = Math.sqrt(1.0 + m.y.y - m.x.x - m.z.z) * 0.5;
- double inv = 0.25 / sq;
- this.y = sq;
- this.w = (m.z.x - m.x.z) * inv;
- this.x = (m.x.y + m.y.x) * inv;
- this.z = (m.y.z + m.z.y) * inv;
- } else {
- double sq = Math.sqrt(1.0 + m.z.z - m.x.x - m.y.y) * 0.5;
- double inv = 0.25 / sq;
- this.z = sq;
- this.w = (m.x.y - m.y.x) * inv;
- this.x = (m.z.x + m.x.z) * inv;
- this.y = (m.y.z + m.z.y) * inv;
- }
- }
- public Quaternion.look(Vector3 dir, Vector3 up)
- {
- Vector3 xaxis = dir.cross(up);
- Vector3 zaxis = xaxis.cross(dir);
- Matrix4x4 m = {};
- m.x.x = xaxis.x;
- m.x.y = xaxis.y;
- m.x.z = xaxis.z;
- m.x.w = 0.0;
- m.y.x = dir.x;
- m.y.y = dir.y;
- m.y.z = dir.z;
- m.y.w = 0.0;
- m.z.x = zaxis.x;
- m.z.y = zaxis.y;
- m.z.z = zaxis.z;
- m.z.w = 0.0;
- m.t.x = 0.0;
- m.t.y = 0.0;
- m.t.z = 0.0;
- m.t.w = 1.0;
- Quaternion q = Quaternion.from_matrix(m);
- q.normalize();
- this.x = q.x;
- this.y = q.y;
- this.z = q.z;
- this.w = q.w;
- }
- /// Returns the dot product between quaternions @a a and @a b.
- public double dot(Quaternion b)
- {
- return this.w * b.w + this.x * b.x + this.y * b.y + this.z * b.z;
- }
- /// Returns the length of @a q.
- public double length()
- {
- return Math.sqrt(dot(this));
- }
- public void normalize()
- {
- double len = length();
- double inv_len = 1.0f / len;
- this.x *= inv_len;
- this.y *= inv_len;
- this.z *= inv_len;
- this.w *= inv_len;
- }
- public Gee.ArrayList<Value?> to_array()
- {
- Gee.ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
- arr.add(this.x);
- arr.add(this.y);
- arr.add(this.z);
- arr.add(this.w);
- return arr;
- }
- public Vector3 to_euler()
- {
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
- double test = x*y + z*w;
- if (test > 0.499) { // singularity at north pole
- double rx = 0.0;
- double ry = 2.0 * Math.atan2(x, w);
- double rz = Math.PI*0.5;
- return Vector3(rx, ry, rz);
- }
- if (test < -0.499) { // singularity at south pole
- double rx = +0.0;
- double ry = -2.0 * Math.atan2(x, w);
- double rz = -Math.PI*0.5;
- return Vector3(rx, ry, rz);
- }
- double xx = x*x;
- double yy = y*y;
- double zz = z*z;
- double rrx = Math.atan2(2.0*x*w - 2.0*y*z, 1.0 - 2.0*xx - 2.0*zz);
- double rry = Math.atan2(2.0*y*w - 2.0*x*z, 1.0 - 2.0*yy - 2.0*zz);
- double rrz = Math.asin(2.0*test);
- return Vector3(rrx, rry, rrz);
- }
- public string to_string()
- {
- return "%f, %f, %f, %f".printf(x, y, z, w);
- }
- public static bool equal_func(Quaternion? a, Quaternion? b)
- {
- return a.x == b.x
- && a.y == b.y
- && a.z == b.z
- && a.w == b.w
- ;
- }
- }
- public const Quaternion QUATERNION_IDENTITY = { 0.0, 0.0, 0.0, 1.0 };
- } /* namespace Crown */
|