glmath 0.0.1.dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/glmath.rb +21 -0
- data/lib/glmath/euler_angle.rb +60 -0
- data/lib/glmath/line.rb +19 -0
- data/lib/glmath/matrix.rb +413 -0
- data/lib/glmath/matrix2.rb +81 -0
- data/lib/glmath/matrix3.rb +108 -0
- data/lib/glmath/matrix4.rb +145 -0
- data/lib/glmath/matrix_stack.rb +91 -0
- data/lib/glmath/quaternion.rb +212 -0
- data/lib/glmath/rect.rb +135 -0
- data/lib/glmath/scalar.rb +33 -0
- data/lib/glmath/vector.rb +155 -0
- data/lib/glmath/vector2.rb +45 -0
- data/lib/glmath/vector3.rb +66 -0
- data/lib/glmath/vector4.rb +54 -0
- data/lib/version.rb +4 -0
- metadata +90 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
module GLMath
|
2
|
+
class Matrix2
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def dimension
|
6
|
+
2
|
7
|
+
end
|
8
|
+
|
9
|
+
#conter-clockwise rotation
|
10
|
+
def rotation(theta, homogenous = false)
|
11
|
+
s, c = Math.sin(theta), Math.cos(theta)
|
12
|
+
m = new(c, -s, s, c)
|
13
|
+
homogenous ? m.expand : m
|
14
|
+
end
|
15
|
+
|
16
|
+
def scale(x, y, homogenous = false)
|
17
|
+
m = diagonal(x, y)
|
18
|
+
homogenous ? m.expand : m
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
include Matrix
|
23
|
+
|
24
|
+
def *(v)
|
25
|
+
case v
|
26
|
+
when Vector2
|
27
|
+
v.class.new(@m[0] * v.x + @m[1] * v.y, @m[2] * v.x + @m[3] * v.y)
|
28
|
+
when Matrix2
|
29
|
+
m = v.instance_variable_get(:@m)
|
30
|
+
self.class.new(@m[0]*m[0] + @m[1]*m[2], @m[0]*m[1] + @m[1]*m[3],
|
31
|
+
@m[2]*m[0] + @m[3]*m[2], @m[2]*m[1] + @m[3]*m[3])
|
32
|
+
when Numeric
|
33
|
+
super
|
34
|
+
else
|
35
|
+
raise ArgumentError
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def adjugate!
|
40
|
+
@m = [ @m[3], -@m[1],
|
41
|
+
-@m[2], @m[0]]
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def determinant
|
46
|
+
@m[0] * @m[3] - @m[1] * @m[2]
|
47
|
+
end
|
48
|
+
|
49
|
+
def expand
|
50
|
+
Matrix3.new(@m[0], @m[1], 0.0,
|
51
|
+
@m[2], @m[3], 0.0,
|
52
|
+
0.0, 0.0, 1.0)
|
53
|
+
end
|
54
|
+
|
55
|
+
def inverse
|
56
|
+
d = determinant
|
57
|
+
raise ArgumentError, 'Determinant is zero' if d.zero?
|
58
|
+
adjugate * (1.0 / d)
|
59
|
+
end
|
60
|
+
|
61
|
+
def lup
|
62
|
+
raise ArgumentError, "Determinant is zero" if singular?
|
63
|
+
|
64
|
+
if @m[0].abs > @m[1].abs
|
65
|
+
a, b, c, d = 0, 1, 2, 3
|
66
|
+
p = self.class.new(1.0, 0.0, 0.0, 1.0)
|
67
|
+
else
|
68
|
+
a, b, c, d = 2, 3, 0, 1
|
69
|
+
p = self.class.new(0.0, 1.0, 1.0, 0.0)
|
70
|
+
end
|
71
|
+
|
72
|
+
l = self.class.new(1.0, 0.0, @m[c] / @m[a], 1.0)
|
73
|
+
u = self.class.new(@m[a], @m[b], 0.0, @m[d] - @m[b]*@m[c] / @m[a])
|
74
|
+
[l, u, p]
|
75
|
+
end
|
76
|
+
|
77
|
+
alias_method :det, :determinant
|
78
|
+
alias_method :lup_decomposition, :lup
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module GLMath
|
2
|
+
class Matrix3
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def dimension
|
6
|
+
3
|
7
|
+
end
|
8
|
+
|
9
|
+
def rotation(angle, axis, homogenous = false)
|
10
|
+
n = axis.normalize
|
11
|
+
ct = Math.cos(angle)
|
12
|
+
st = Math.sin(angle)
|
13
|
+
ct1 = 1 - ct
|
14
|
+
xy = n.x * n.y * ct1
|
15
|
+
xz = n.x * n.z * ct1
|
16
|
+
yz = n.y * n.z * ct1
|
17
|
+
|
18
|
+
m = new(n.x*n.x*ct1 + ct, xy + n.z * st, xz - n.y * st,
|
19
|
+
xy - n.z * st, n.y*n.y*ct1 + ct, yz + n.x * st,
|
20
|
+
xz + n.y * st, yz - n.x * st, n.z*n.z*ct1 + ct)
|
21
|
+
homogenous ? m.expand : m
|
22
|
+
end
|
23
|
+
|
24
|
+
def scale(x, y, z, homogenous = false)
|
25
|
+
m = diagonal(x, y, z)
|
26
|
+
homogenous ? m.expand : m
|
27
|
+
end
|
28
|
+
|
29
|
+
def translation(x, y)
|
30
|
+
Matrix3.new(1.0, 0.0, 0.0,
|
31
|
+
0.0, 1.0, 0.0,
|
32
|
+
x, y, 1.0)
|
33
|
+
end
|
34
|
+
|
35
|
+
def from_euler_angle(x:, y:, z:)
|
36
|
+
# TODO
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
include Matrix
|
41
|
+
|
42
|
+
def *(v)
|
43
|
+
case v
|
44
|
+
when Vector3
|
45
|
+
v.class.new(@m[0] * v.x + @m[1] * v.y + @m[2] * v.z,
|
46
|
+
@m[3] * v.x + @m[4] * v.y + @m[5] * v.z,
|
47
|
+
@m[6] * v.x + @m[7] * v.y + @m[8] * v.z)
|
48
|
+
when Matrix3
|
49
|
+
m = v.instance_variable_get(:@m)
|
50
|
+
self.class.new(
|
51
|
+
@m[0]*m[0]+@m[1]*m[3]+@m[2]*m[6], @m[0]*m[1]+@m[1]*m[4]+@m[2]*m[7], @m[0]*m[2] + @m[1]*m[5] + @m[2]*m[8],
|
52
|
+
@m[3]*m[0]+@m[4]*m[3]+@m[5]*m[6], @m[3]*m[1]+@m[4]*m[4]+@m[5]*m[7], @m[3]*m[2] + @m[4]*m[5] + @m[5]*m[8],
|
53
|
+
@m[6]*m[0]+@m[7]*m[3]+@m[8]*m[6], @m[6]*m[1]+@m[7]*m[4]+@m[8]*m[7], @m[6]*m[2] + @m[7]*m[5] + @m[8]*m[8])
|
54
|
+
else
|
55
|
+
super
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def adjugate!
|
60
|
+
@m =[@m[4] * @m[8] - @m[5] * @m[7],
|
61
|
+
@m[5] * @m[6] - @m[3] * @m[8],
|
62
|
+
@m[3] * @m[7] - @m[4] * @m[6],
|
63
|
+
@m[2] * @m[7] - @m[1] * @m[8],
|
64
|
+
@m[0] * @m[8] - @m[2] * @m[6],
|
65
|
+
@m[1] * @m[6] - @m[0] * @m[7],
|
66
|
+
@m[1] * @m[5] - @m[2] * @m[4],
|
67
|
+
@m[2] * @m[3] - @m[0] * @m[5],
|
68
|
+
@m[0] * @m[4] - @m[1] * @m[3]]
|
69
|
+
self
|
70
|
+
end
|
71
|
+
|
72
|
+
def determinant
|
73
|
+
@m[0] * ( @m[4] * @m[8] - @m[5] * @m[7] ) +
|
74
|
+
@m[1] * ( @m[5] * @m[6] - @m[3] * @m[8] ) +
|
75
|
+
@m[2] * ( @m[3] * @m[7] - @m[4] * @m[6] )
|
76
|
+
end
|
77
|
+
|
78
|
+
def expand
|
79
|
+
Matrix4.new(@m[0], @m[1], @m[2], 0.0,
|
80
|
+
@m[3], @m[4], @m[5], 0.0,
|
81
|
+
@m[6], @m[7], @m[8], 0.0,
|
82
|
+
0.0, 0.0, 0.0, 1.0)
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_axis_angle
|
86
|
+
# TODO
|
87
|
+
end
|
88
|
+
|
89
|
+
def orthonormalize
|
90
|
+
# TODO
|
91
|
+
end
|
92
|
+
|
93
|
+
def eigen_decomposition()
|
94
|
+
# TODO
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_euler_angle
|
98
|
+
# TODO
|
99
|
+
end
|
100
|
+
|
101
|
+
def slerp
|
102
|
+
# TODO
|
103
|
+
end
|
104
|
+
|
105
|
+
alias_method :det, :determinant
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module GLMath
|
2
|
+
# Left Handed
|
3
|
+
class Matrix4
|
4
|
+
class << self
|
5
|
+
def dimension
|
6
|
+
4
|
7
|
+
end
|
8
|
+
|
9
|
+
def look_at(eye, center, up)
|
10
|
+
f = (center - eye).normalize!
|
11
|
+
s = f.cross(up).normalize!
|
12
|
+
u = s.cross(f).normalize!
|
13
|
+
new(s.x, u.x, -f.x, 0.0,
|
14
|
+
s.y, u.y, -f.y, 0.0,
|
15
|
+
s.z, u.z, -f.z, 0.0,
|
16
|
+
-eye.x, -eye.y, -eye.z, 1.0)
|
17
|
+
end
|
18
|
+
|
19
|
+
def ortho(left, right, bottom, top, near, far)
|
20
|
+
rl = right.to_f - left.to_f
|
21
|
+
tb = top.to_f - bottom.to_f
|
22
|
+
fn = far.to_f - near.to_f
|
23
|
+
|
24
|
+
new(2.0/rl, 0.0, 0.0, 0.0,
|
25
|
+
0.0, 2.0/tb, 0.0, 0.0,
|
26
|
+
0.0, 0.0, -2.0/fn, 0.0,
|
27
|
+
-(right + left)/rl, -(top + bottom)/tb, -(far + near)/fn, 1.0)
|
28
|
+
end
|
29
|
+
|
30
|
+
def perspective(fovy, aspect, near, far)
|
31
|
+
zoom = 1.0 / Math.tan(fovy * 0.5)
|
32
|
+
fn = 1.0 / (far - near)
|
33
|
+
new(zoom/aspect, 0.0, 0.0, 0.0,
|
34
|
+
0.0, zoom, 0.0, 0.0,
|
35
|
+
0.0, 0.0, (far + near)*fn, 1.0,
|
36
|
+
0.0, 0.0, -2.0*far*near*fn, 0.0)
|
37
|
+
end
|
38
|
+
|
39
|
+
def rotation(angle, axis)
|
40
|
+
Matrix3.rotation(angle, axis, true)
|
41
|
+
end
|
42
|
+
|
43
|
+
def scale(x, y, z, w = 1.0)
|
44
|
+
diagonal(x, y, z, w)
|
45
|
+
end
|
46
|
+
|
47
|
+
def translation(x, y, z)
|
48
|
+
Matrix4.new(1.0, 0.0, 0.0, 0.0,
|
49
|
+
0.0, 1.0, 0.0, 0.0,
|
50
|
+
0.0, 0.0, 1.0, 0.0,
|
51
|
+
x, y, z, 1.0)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
include Matrix
|
56
|
+
|
57
|
+
def *(v)
|
58
|
+
case v
|
59
|
+
when Vector4
|
60
|
+
v.class.new(
|
61
|
+
@m[ 0] * v.x + @m[ 1] * v.y + @m[ 2] * v.z + @m[ 3] * v.w,
|
62
|
+
@m[ 4] * v.x + @m[ 5] * v.y + @m[ 6] * v.z + @m[ 7] * v.w,
|
63
|
+
@m[ 8] * v.x + @m[ 9] * v.y + @m[10] * v.z + @m[11] * v.w,
|
64
|
+
@m[12] * v.x + @m[13] * v.y + @m[14] * v.z + @m[15] * v.w)
|
65
|
+
when Matrix4
|
66
|
+
m = v.instance_variable_get(:@m)
|
67
|
+
self.class.new(
|
68
|
+
@m[ 0] * m[0] + @m[ 1] * m[4] + @m[ 2] * m[ 8] + @m[ 3] * m[12],
|
69
|
+
@m[ 0] * m[1] + @m[ 1] * m[5] + @m[ 2] * m[ 9] + @m[ 3] * m[13],
|
70
|
+
@m[ 0] * m[2] + @m[ 1] * m[6] + @m[ 2] * m[10] + @m[ 3] * m[14],
|
71
|
+
@m[ 0] * m[3] + @m[ 1] * m[7] + @m[ 2] * m[11] + @m[ 3] * m[15],
|
72
|
+
@m[ 4] * m[0] + @m[ 5] * m[4] + @m[ 6] * m[ 8] + @m[ 7] * m[12],
|
73
|
+
@m[ 4] * m[1] + @m[ 5] * m[5] + @m[ 6] * m[ 9] + @m[ 7] * m[13],
|
74
|
+
@m[ 4] * m[2] + @m[ 5] * m[6] + @m[ 6] * m[10] + @m[ 7] * m[14],
|
75
|
+
@m[ 4] * m[3] + @m[ 5] * m[7] + @m[ 6] * m[11] + @m[ 7] * m[15],
|
76
|
+
@m[ 8] * m[0] + @m[ 9] * m[4] + @m[10] * m[ 8] + @m[11] * m[12],
|
77
|
+
@m[ 8] * m[1] + @m[ 9] * m[5] + @m[10] * m[ 9] + @m[11] * m[13],
|
78
|
+
@m[ 8] * m[2] + @m[ 9] * m[6] + @m[10] * m[10] + @m[11] * m[14],
|
79
|
+
@m[ 8] * m[3] + @m[ 9] * m[7] + @m[10] * m[11] + @m[11] * m[15],
|
80
|
+
@m[12] * m[0] + @m[13] * m[4] + @m[14] * m[ 8] + @m[15] * m[12],
|
81
|
+
@m[12] * m[1] + @m[13] * m[5] + @m[14] * m[ 9] + @m[15] * m[13],
|
82
|
+
@m[12] * m[2] + @m[13] * m[6] + @m[14] * m[10] + @m[15] * m[14],
|
83
|
+
@m[12] * m[3] + @m[13] * m[7] + @m[14] * m[11] + @m[15] * m[15])
|
84
|
+
else
|
85
|
+
super
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def adjoint!
|
90
|
+
a = @m[4] * @m[9] - @m[5] * @m[8]
|
91
|
+
b = @m[4] * @m[10] - @m[6] * @m[8]
|
92
|
+
c = @m[4] * @m[11] - @m[7] * @m[8]
|
93
|
+
d = @m[4] * @m[13] - @m[5] * @m[12]
|
94
|
+
f = @m[4] * @m[14] - @m[6] * @m[12]
|
95
|
+
g = @m[4] * @m[15] - @m[7] * @m[12]
|
96
|
+
h = @m[5] * @m[10] - @m[6] * @m[9]
|
97
|
+
i = @m[5] * @m[11] - @m[7] * @m[9]
|
98
|
+
j = @m[5] * @m[14] - @m[6] * @m[13]
|
99
|
+
k = @m[5] * @m[15] - @m[7] * @m[13]
|
100
|
+
l = @m[6] * @m[11] - @m[7] * @m[10]
|
101
|
+
m = @m[6] * @m[15] - @m[7] * @m[14]
|
102
|
+
n = @m[8] * @m[13] - @m[9] * @m[12]
|
103
|
+
o = @m[8] * @m[14] - @m[10] * @m[12]
|
104
|
+
p = @m[8] * @m[15] - @m[11] * @m[12]
|
105
|
+
q = @m[9] * @m[14] - @m[10] * @m[13]
|
106
|
+
r = @m[9] * @m[15] - @m[11] * @m[13]
|
107
|
+
s = @m[10] * @m[15] - @m[11] * @m[14]
|
108
|
+
|
109
|
+
@m = [+ @m[5] * s - @m[6] * r + @m[7] * q,
|
110
|
+
- @m[4] * s + @m[6] * p - @m[7] * o,
|
111
|
+
+ @m[4] * r - @m[5] * p + @m[7] * n,
|
112
|
+
- @m[4] * q + @m[5] * o - @m[6] * n,
|
113
|
+
- @m[1] * s + @m[2] * r - @m[3] * q,
|
114
|
+
+ @m[0] * s - @m[2] * p + @m[3] * o,
|
115
|
+
- @m[0] * r + @m[1] * p - @m[3] * n,
|
116
|
+
+ @m[0] * q - @m[1] * o + @m[2] * n,
|
117
|
+
+ @m[1] * m - @m[2] * k + @m[3] * j,
|
118
|
+
- @m[0] * m + @m[2] * g - @m[3] * f,
|
119
|
+
+ @m[0] * k - @m[1] * g + @m[3] * d,
|
120
|
+
- @m[0] * j + @m[1] * f - @m[2] * d,
|
121
|
+
- @m[1] * l + @m[2] * i - @m[3] * h,
|
122
|
+
+ @m[0] * l - @m[2] * c + @m[3] * b,
|
123
|
+
- @m[0] * i + @m[1] * c - @m[3] * a,
|
124
|
+
+ @m[0] * h - @m[1] * b + @m[2] * a]
|
125
|
+
self
|
126
|
+
end
|
127
|
+
|
128
|
+
def determinant
|
129
|
+
a = @m[10] * @m[15] - @m[11] * @m[14]
|
130
|
+
b = @m[11] * @m[13] - @m[9] * @m[15]
|
131
|
+
c = @m[9] * @m[14] - @m[10] * @m[13]
|
132
|
+
d = @m[8] * @m[15] - @m[11] * @m[12]
|
133
|
+
e = @m[10] * @m[12] - @m[8] * @m[14]
|
134
|
+
f = @m[8] * @m[13] - @m[9] * @m[12]
|
135
|
+
|
136
|
+
@m[0] * ( @m[5] * a + @m[6] * b + @m[7] * c) +
|
137
|
+
@m[1] * (-@m[4] * a + @m[6] * d + @m[7] * e) +
|
138
|
+
@m[2] * (-@m[4] * b - @m[5] * d + @m[7] * f) +
|
139
|
+
@m[3] * (-@m[4] * c - @m[5] * e - @m[6] * f)
|
140
|
+
end
|
141
|
+
|
142
|
+
alias_method :det, :determinant
|
143
|
+
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module GLMath
|
2
|
+
class MatrixStack
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@stack = [Matrix4.I]
|
6
|
+
end
|
7
|
+
|
8
|
+
%w'* - +'.each do |m|
|
9
|
+
define_method(m, ->(v) do
|
10
|
+
@stack[-1] = @stack.last.send(m, check(v))
|
11
|
+
self
|
12
|
+
end)
|
13
|
+
end
|
14
|
+
|
15
|
+
def <<(m)
|
16
|
+
@stack << check(m)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def current
|
21
|
+
@stack.last.dup
|
22
|
+
end
|
23
|
+
|
24
|
+
def dup
|
25
|
+
MatrixStack.new.tap { |ms| ms.instance_variable_set(:@stack, @stack.map(&:dup)) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def push(m = current)
|
29
|
+
self << m
|
30
|
+
return self unless block_given?
|
31
|
+
yield self
|
32
|
+
pop
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def pop(n = 1)
|
37
|
+
@stack.pop(n)
|
38
|
+
end
|
39
|
+
|
40
|
+
def load(*args)
|
41
|
+
@stack[-1] = case
|
42
|
+
when args.length == 16 then Matrix4[*args]
|
43
|
+
when args.length == 9 then Matrix3[*args].expand
|
44
|
+
when args.length == 1 then check(args[0])
|
45
|
+
else raise ArgumentError
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_identity
|
50
|
+
load(Matrix4.I)
|
51
|
+
end
|
52
|
+
|
53
|
+
def look_at(eye, center, up)
|
54
|
+
self * Matrix4.look_at(eye, center, up)
|
55
|
+
end
|
56
|
+
|
57
|
+
def ortho(left, right, bottom, top, near, far)
|
58
|
+
self * Matrix4.ortho(left, right, bottom, top, near, far)
|
59
|
+
end
|
60
|
+
|
61
|
+
def perspective(fovy, aspect, near, far)
|
62
|
+
self * Matrix4.perspective(fovy, aspect, near, far)
|
63
|
+
end
|
64
|
+
|
65
|
+
def rotate(arg, axis = nil)
|
66
|
+
self * case axis
|
67
|
+
when Vector3, Vector4 then Matrix4.rotation(arg, axis)
|
68
|
+
when Quaternion, EulerAngle then arg.to_matrix
|
69
|
+
else raise ArgumentError
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def scale(x, y, z, w = 1.0)
|
74
|
+
self * Matrix4.scale(x, y, z, w)
|
75
|
+
end
|
76
|
+
|
77
|
+
#def shear()
|
78
|
+
# #TODO
|
79
|
+
#end
|
80
|
+
|
81
|
+
def translate(x, y, z)
|
82
|
+
self * Matrix4.translation(x, y, z)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
def check(m)
|
87
|
+
raise ArgumentError unless m.is_a?(Matrix4)
|
88
|
+
m
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
module GLMath
|
2
|
+
class Quaternion
|
3
|
+
class << self
|
4
|
+
def [](*args)
|
5
|
+
new(*args)
|
6
|
+
end
|
7
|
+
|
8
|
+
def from_angle_axis(angle, axis)
|
9
|
+
angle *= 0.5
|
10
|
+
axis = axis.normalize
|
11
|
+
sa = Math.sin(angle)
|
12
|
+
new(Math.cos(angle), sa * axis.x, sa * axis.y, sa * axis.z)
|
13
|
+
end
|
14
|
+
|
15
|
+
def identity
|
16
|
+
new(1.0, 0.0, 0.0, 0.0)
|
17
|
+
end
|
18
|
+
|
19
|
+
def slerp(q0, q1, t)
|
20
|
+
(q1 * q0.inverse).power(t) * q0
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :I, :identity
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(w, x, y, z)
|
27
|
+
@q = w, x, y, z
|
28
|
+
raise ArgumentError, 'arguments must be Numeric' unless @q.all? { |e| e.is_a?(Numeric) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def +(v)
|
32
|
+
case v
|
33
|
+
when Numeric
|
34
|
+
self.class.new(v + w, x, y, z)
|
35
|
+
when Quaternion
|
36
|
+
self.class.new(q + v.w, x + v.x, y + v.y, z + v.z)
|
37
|
+
else
|
38
|
+
v + self
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def -(q)
|
43
|
+
self + (-q)
|
44
|
+
end
|
45
|
+
|
46
|
+
def *(v)
|
47
|
+
case v
|
48
|
+
when Numeric
|
49
|
+
self.class.new(q * v, x * v, y * v, z * v)
|
50
|
+
when Quaternion
|
51
|
+
cross_product(v)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def /(v)
|
56
|
+
case v
|
57
|
+
when Numeric
|
58
|
+
self.class.new(*@q.map { |i| i/v})
|
59
|
+
when Quaternion
|
60
|
+
self * v.inverse
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def -@
|
65
|
+
self.class.new(*@q.map(&:-@))
|
66
|
+
end
|
67
|
+
|
68
|
+
def ==(other)
|
69
|
+
self.class === other && @q == other.instance_variable_get(:@q)
|
70
|
+
end
|
71
|
+
|
72
|
+
%w'w x y z'.each_with_index do |m, i|
|
73
|
+
define_method m, ->() { @q[i] }
|
74
|
+
define_method "#{m}=", ->(v) { @q[i] = v }
|
75
|
+
end
|
76
|
+
|
77
|
+
def coerce(v)
|
78
|
+
case v
|
79
|
+
when Numeric then return Scalar.new(v), self
|
80
|
+
else raise TypeError, "#{self.class} can't be coerced into #{v.class}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def conjugate
|
85
|
+
self.class.new(w, -x, -y, -z)
|
86
|
+
end
|
87
|
+
|
88
|
+
def conjugate!
|
89
|
+
@q = w, -x, -y, -z
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
#Warning: standard notation.
|
94
|
+
def cross_product(q)
|
95
|
+
self.class.new(
|
96
|
+
w * q.w - x * q.x - y * q.y - z * q.z,
|
97
|
+
w * q.x + x * q.w + y * q.z - z * q.y,
|
98
|
+
w * q.y - x * q.z + y * q.w + z * q.x,
|
99
|
+
w * q.z + x * q.y - y * q.x + z * q.w)
|
100
|
+
end
|
101
|
+
|
102
|
+
def difference(q)
|
103
|
+
q * inverse
|
104
|
+
end
|
105
|
+
|
106
|
+
def dot_product(q)
|
107
|
+
w * q.w + x * q.x + y * q.y + z * q.z
|
108
|
+
end
|
109
|
+
|
110
|
+
def dup
|
111
|
+
self.class.new(*@q)
|
112
|
+
end
|
113
|
+
|
114
|
+
def exp
|
115
|
+
a = Math.sqrt(x*x + y*y + z*z)
|
116
|
+
ew = Math.exp(w)
|
117
|
+
s = ew * Math.sin(a) / a
|
118
|
+
self.class.new(ew * Math.cos(a), x * s, y * s, z * s)
|
119
|
+
end
|
120
|
+
|
121
|
+
def imaginary
|
122
|
+
[x, y, z]
|
123
|
+
end
|
124
|
+
|
125
|
+
def inspect
|
126
|
+
"Quaternion[#{w}, #{x}, #{y}, #{z}]"
|
127
|
+
end
|
128
|
+
|
129
|
+
def inverse
|
130
|
+
conjugate / norm
|
131
|
+
end
|
132
|
+
|
133
|
+
def log
|
134
|
+
nv = x*x + y*y + z*z
|
135
|
+
nq = Math.sqrt(w*w + nv)
|
136
|
+
s = Math.acos(w/nq) / Math.sqrt(nv)
|
137
|
+
self.class.new(Math.log(nq), x * s, y * s, z * s)
|
138
|
+
end
|
139
|
+
|
140
|
+
def norm
|
141
|
+
Math.sqrt(squared_norm)
|
142
|
+
end
|
143
|
+
|
144
|
+
def normalize
|
145
|
+
self / norm
|
146
|
+
end
|
147
|
+
|
148
|
+
def normalize!
|
149
|
+
i = 1.0 / norm
|
150
|
+
@q.map! { |e| e * i }
|
151
|
+
self
|
152
|
+
end
|
153
|
+
|
154
|
+
def power(p)
|
155
|
+
(log*p).exp
|
156
|
+
end
|
157
|
+
|
158
|
+
def real
|
159
|
+
w
|
160
|
+
end
|
161
|
+
|
162
|
+
def rotate(v3)
|
163
|
+
Vector3[*(self * Quaternion[0.0, *v3] * inverse).to_a[1..-1]]
|
164
|
+
end
|
165
|
+
|
166
|
+
def squared_norm
|
167
|
+
w * w + x * x + y * y + z * z
|
168
|
+
end
|
169
|
+
|
170
|
+
def to_a
|
171
|
+
@q.dup
|
172
|
+
end
|
173
|
+
|
174
|
+
def to_s(notation = nil)
|
175
|
+
case notation
|
176
|
+
when :scalar_vector
|
177
|
+
"(#{w}, [#{x}, #{y}, #{z}])"
|
178
|
+
when :vertical
|
179
|
+
"w=#{w}\nx=#{x}\ny=#{y}\nz=#{z}"
|
180
|
+
when :angle_axis
|
181
|
+
else
|
182
|
+
"Quaternion[w=#{w}, x=#{x}, y=#{y}, z=#{z}]"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def to_matrix
|
187
|
+
xx = 2 * x * x
|
188
|
+
yy = 2 * y * y
|
189
|
+
zz = 2 * z * z
|
190
|
+
xy = 2 * x * y
|
191
|
+
wz = 2 * w * z
|
192
|
+
xz = 2 * x * z
|
193
|
+
wy = 2 * w * y
|
194
|
+
yz = 2 * y * z
|
195
|
+
wx = 2 * w * x
|
196
|
+
|
197
|
+
Matrix4[1 - yy - zz, xy - wz, xz + wy, 0,
|
198
|
+
xy + wz, 1 - xx - zz, yz - wx, 0,
|
199
|
+
xz - wy, yz + wx, 1 - xx - yy, 0,
|
200
|
+
0, 0, 0, 1]
|
201
|
+
end
|
202
|
+
|
203
|
+
alias_method :conj, :conjugate
|
204
|
+
alias_method :conj!, :conjugate
|
205
|
+
alias_method :cross, :cross_product
|
206
|
+
alias_method :dot, :dot_product
|
207
|
+
alias_method :im, :imaginary
|
208
|
+
alias_method :imag, :imaginary
|
209
|
+
alias_method :inv, :inverse
|
210
|
+
alias_method :re, :real
|
211
|
+
end
|
212
|
+
end
|