Class: Bullet3::Quaternion

Inherits:
Object
  • Object
show all
Defined in:
lib/bullet3/linear_math/quaternion.rb,
lib/bullet3/linear_math/quaternion.rb

Constant Summary collapse

EPSILON =
1e-6

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(x = 0.0, y = 0.0, z = 0.0, w = 1.0) ⇒ Quaternion

Returns a new instance of Quaternion.



37
38
39
40
41
42
# File 'lib/bullet3/linear_math/quaternion.rb', line 37

def initialize(x = 0.0, y = 0.0, z = 0.0, w = 1.0)
  @x = Float(x)
  @y = Float(y)
  @z = Float(z)
  @w = Float(w)
end

Instance Attribute Details

#wObject

Returns the value of attribute w.



7
8
9
# File 'lib/bullet3/linear_math/quaternion.rb', line 7

def w
  @w
end

#xObject

Returns the value of attribute x.



7
8
9
# File 'lib/bullet3/linear_math/quaternion.rb', line 7

def x
  @x
end

#yObject

Returns the value of attribute y.



7
8
9
# File 'lib/bullet3/linear_math/quaternion.rb', line 7

def y
  @y
end

#zObject

Returns the value of attribute z.



7
8
9
# File 'lib/bullet3/linear_math/quaternion.rb', line 7

def z
  @z
end

Class Method Details

.coerce(value) ⇒ Object

Raises:

  • (TypeError)


132
133
134
135
136
137
# File 'lib/bullet3/linear_math/quaternion.rb', line 132

def self.coerce(value)
  coerced = try_coerce(value)
  return coerced if coerced

  raise TypeError, "expected Bullet3::Quaternion or a 4-element Array"
end

.from_axis_angle(axis, angle) ⇒ Object



13
14
15
16
17
18
19
# File 'lib/bullet3/linear_math/quaternion.rb', line 13

def self.from_axis_angle(axis, angle)
  axis = Vector3.coerce(axis).normalized
  half_angle = Float(angle) / 2.0
  scale = Math.sin(half_angle)

  new(axis.x * scale, axis.y * scale, axis.z * scale, Math.cos(half_angle))
end

.from_euler(roll, pitch, yaw) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/bullet3/linear_math/quaternion.rb', line 21

def self.from_euler(roll, pitch, yaw)
  cr = Math.cos(Float(roll) / 2.0)
  sr = Math.sin(Float(roll) / 2.0)
  cp = Math.cos(Float(pitch) / 2.0)
  sp = Math.sin(Float(pitch) / 2.0)
  cy = Math.cos(Float(yaw) / 2.0)
  sy = Math.sin(Float(yaw) / 2.0)

  new(
    (sr * cp * cy) - (cr * sp * sy),
    (cr * sp * cy) + (sr * cp * sy),
    (cr * cp * sy) - (sr * sp * cy),
    (cr * cp * cy) + (sr * sp * sy)
  )
end

.identityObject



9
10
11
# File 'lib/bullet3/linear_math/quaternion.rb', line 9

def self.identity
  new(0.0, 0.0, 0.0, 1.0)
end

.try_coerce(value) ⇒ Object



139
140
141
142
143
144
145
# File 'lib/bullet3/linear_math/quaternion.rb', line 139

def self.try_coerce(value)
  return value if value.is_a?(self)

  return new(*value) if value.respond_to?(:to_ary) && value.to_ary.length == 4

  nil
end

Instance Method Details

#*(other) ⇒ Object



44
45
46
47
48
49
50
51
52
# File 'lib/bullet3/linear_math/quaternion.rb', line 44

def *(other)
  other = self.class.coerce(other)
  self.class.new(
    (w * other.x) + (x * other.w) + (y * other.z) - (z * other.y),
    (w * other.y) - (x * other.z) + (y * other.w) + (z * other.x),
    (w * other.z) + (x * other.y) - (y * other.x) + (z * other.w),
    (w * other.w) - (x * other.x) - (y * other.y) - (z * other.z)
  )
end

#==(other) ⇒ Object



54
55
56
57
58
59
# File 'lib/bullet3/linear_math/quaternion.rb', line 54

def ==(other)
  other = self.class.try_coerce(other)
  return false unless other

  to_a.zip(other.to_a).all? { |left, right| (left - right).abs <= EPSILON }
end

#angleObject



68
69
70
# File 'lib/bullet3/linear_math/quaternion.rb', line 68

def angle
  2.0 * Math.acos(w.clamp(-1.0, 1.0))
end

#axisObject



72
73
74
75
76
77
# File 'lib/bullet3/linear_math/quaternion.rb', line 72

def axis
  scale = Math.sqrt(1.0 - (w * w))
  return Vector3.new(1.0, 0.0, 0.0) if scale <= EPSILON

  Vector3.new(x / scale, y / scale, z / scale)
end

#inverseObject

Raises:

  • (ZeroDivisionError)


61
62
63
64
65
66
# File 'lib/bullet3/linear_math/quaternion.rb', line 61

def inverse
  magnitude = length2
  raise ZeroDivisionError, "cannot invert a zero-length quaternion" if magnitude <= EPSILON

  self.class.new(-x / magnitude, -y / magnitude, -z / magnitude, w / magnitude)
end

#normalizedObject



121
122
123
124
125
126
# File 'lib/bullet3/linear_math/quaternion.rb', line 121

def normalized
  magnitude = Math.sqrt(length2)
  return self.class.identity if magnitude <= EPSILON

  self.class.new(x / magnitude, y / magnitude, z / magnitude, w / magnitude)
end

#slerp(other, t) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/bullet3/linear_math/quaternion.rb', line 79

def slerp(other, t)
  other = self.class.coerce(other)
  t = Float(t)
  cosine = dot(other)

  if cosine.negative?
    other = self.class.new(-other.x, -other.y, -other.z, -other.w)
    cosine = -cosine
  end

  return linear_interpolate(other, t).normalized if cosine > 0.9995

  theta = Math.acos(cosine.clamp(-1.0, 1.0))
  sin_theta = Math.sin(theta)
  left = Math.sin((1.0 - t) * theta) / sin_theta
  right = Math.sin(t * theta) / sin_theta

  self.class.new(
    (x * left) + (other.x * right),
    (y * left) + (other.y * right),
    (z * left) + (other.z * right),
    (w * left) + (other.w * right)
  )
end

#to_aObject



128
129
130
# File 'lib/bullet3/linear_math/quaternion.rb', line 128

def to_a
  [x, y, z, w]
end

#to_eulerObject



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/bullet3/linear_math/quaternion.rb', line 104

def to_euler
  roll = Math.atan2(2.0 * ((w * x) + (y * z)), 1.0 - (2.0 * ((x * x) + (y * y))))
  pitch_sin = 2.0 * ((w * y) - (z * x))
  pitch = if pitch_sin.abs >= 1.0
            pitch_sin.negative? ? -Math::PI / 2.0 : Math::PI / 2.0
          else
            Math.asin(pitch_sin)
          end
  yaw = Math.atan2(2.0 * ((w * z) + (x * y)), 1.0 - (2.0 * ((y * y) + (z * z))))

  [roll, pitch, yaw]
end

#to_matrixObject



117
118
119
# File 'lib/bullet3/linear_math/quaternion.rb', line 117

def to_matrix
  Matrix3x3.from_quaternion(self)
end