Class: Eymiha::Quaternion

Inherits:
Object show all
Includes:
ThreeDimensions
Defined in:
lib/eymiha/math3/quaternion.rb

Overview

Quaternion instances represents Quaternions, complex numbers with one real part and three imaginary parts. While the math is a bit obscure, they can be used to manipulate 3D rotations without “gimbal lock”, the problem of coincident viewing angle alignment problems at the boundaries, for example, where the difference between the x and y coordinates of the viewing vector are zero (ie. looking straight up or straight down.)

Interestingly, Quaternions were first conceptualized by Hamilton in 1843, well before the widespread use of vector notation. While mostly put on the shelf, they still solve certain problems elegantly, and in some cases more quickly than matrix-based 3D calulations.

The quaternion’s real part is accessed through the real instance variable, the three complex parts as the quaternion’s “axis” vector, a Point3.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ThreeDimensions

#half_pi, #origin, #pi, #point3, #point3c, #point3s, #sqrt2, #sqrt3, #two_pi

Constructor Details

#initialize(axis = origin, real = 0) ⇒ Quaternion

Creates and returns a Quaternion instance. Sets the coordinates values using the set method.



30
31
32
# File 'lib/eymiha/math3/quaternion.rb', line 30

def initialize(axis=origin,real=0)
  set axis, real
end

Instance Attribute Details

#axisObject

complex axis vector part reader and writer



24
25
26
# File 'lib/eymiha/math3/quaternion.rb', line 24

def axis
  @axis
end

#realObject

real part reader and writer



26
27
28
# File 'lib/eymiha/math3/quaternion.rb', line 26

def real
  @real
end

Class Method Details

.from_axis_angle(axis, angle) ⇒ Object

Returns a new Quaternion-based encoding of a rotation.



81
82
83
84
85
# File 'lib/eymiha/math3/quaternion.rb', line 81

def Quaternion.from_axis_angle(axis,angle)
  half_angle = angle/2.0
  Quaternion.new Point3.new(axis).scale(Math.sin(half_angle)),
  Math.cos(half_angle)
end

Instance Method Details

#==(quaternion) ⇒ Object

Returns true if the parts of the instance are equal to the parts of the given quaternion.



58
59
60
# File 'lib/eymiha/math3/quaternion.rb', line 58

def ==(quaternion)
  (@axis == quaternion.axis)&&(@real == quaternion.real)
end

#absObject

Returns the absolute value of the instance.



207
208
209
# File 'lib/eymiha/math3/quaternion.rb', line 207

def abs
  Math.sqrt(norm)
end

#add(axis = origin, real = 0) ⇒ Object Also known as: +

Returns a new Quaternion instance whose parts are the original instance’s with the given amounts added:

  • axis is a Quaternion, its parts are added.

  • axis responds like a Point3, the arguments are added.

  • otherwise a TypeError is raised.



101
102
103
# File 'lib/eymiha/math3/quaternion.rb', line 101

def add(axis=origin,real=0)
  quaternion.add!(axis,real)
end

#add!(axis = origin, real = 0) ⇒ Object

Returns the modified instance with the arguments added.



108
109
110
111
112
113
114
115
116
117
# File 'lib/eymiha/math3/quaternion.rb', line 108

def add!(axis=origin,real=0)
  if axis.kind_of? Quaternion
    add!(axis.axis,axis.real)
  elsif axis.point3_like?
    set(@axis+axis,@real+real)
  else
    raise_no_conversion axis
  end
  self
end

#approximately_equals?(quaternion, epsilon = Numeric.epsilon) ⇒ Boolean Also known as: =~

Returns true if the parts of the instance are approximately equal to the parts of the given quaternion, each part less than a distance epsilon from the target.

Returns:

  • (Boolean)


65
66
67
68
# File 'lib/eymiha/math3/quaternion.rb', line 65

def approximately_equals?(quaternion,epsilon=Numeric.epsilon)
  (@axis.approximately_equals?(quaternion.axis,epsilon)&&
   @real.approximately_equals?(quaternion.real,epsilon))
end

#conjugateObject

Returns a new Quaternion instance that is the conjugate of the original instance.



192
193
194
# File 'lib/eymiha/math3/quaternion.rb', line 192

def conjugate
  quaternion.conjugate!
end

#conjugate!Object

Returns the modified instance replaced with its conjugate.



197
198
199
# File 'lib/eymiha/math3/quaternion.rb', line 197

def conjugate!
  set(@axis.mirror,@real)
end

#divide(axis = origin, real = 1) ⇒ Object

Returns a new Quaternion instance whose parts are the original instance’s divided by the given amounts:

  • axis is a Quaternion, the instance is divided by the axis’ parts.

  • axis responds like a Point3, the instance is divided by the arguments.

  • otherwise a TypeError is raised.



228
229
230
# File 'lib/eymiha/math3/quaternion.rb', line 228

def divide(axis=origin,real=1)
  quaternion.divide!(axis,origin)
end

#divide!(axis = origin, real = 1) ⇒ Object

Returns the modified instance divided by the arguments.



233
234
235
236
237
238
239
240
241
242
# File 'lib/eymiha/math3/quaternion.rb', line 233

def divide!(axis=origin,real=1)
  if axis.kind_of? Quaternion
    divide!(axis.axis,axis.real)
  elsif axis.point3_like?
    multiply!(quaternion(axis,real).inverse!)
  else
    raise_no_conversion axis
  end
  self
end

#inverseObject

Returns a new Quaternion instance that is the inverse of the original instance.



213
214
215
# File 'lib/eymiha/math3/quaternion.rb', line 213

def inverse
  quaternion.inverse!
end

#inverse!Object

Returns the modified instance replaced with its inverse.



218
219
220
221
# File 'lib/eymiha/math3/quaternion.rb', line 218

def inverse!
  conjugate!
  scale!(1.0/norm)
end

#multiply(axis = origin, real = 1) ⇒ Object Also known as: *

Returns a new Quaternion instance whose parts are the original instance’s multiplied by the given amounts:

  • axis is a Quaternion, the instance is multiplied by the axis’ parts.

  • axis responds like a Point3, the instance is multiplied by the arguments.

  • otherwise a TypeError is raised.



147
148
149
# File 'lib/eymiha/math3/quaternion.rb', line 147

def multiply(axis=origin,real=1)
  quaternion.multiply!(axis,real)
end

#multiply!(axis = origin, real = 1) ⇒ Object

Returns the modified instance multiplied by the arguments.



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/eymiha/math3/quaternion.rb', line 154

def multiply!(axis=origin,real=1)
  if axis.kind_of? Quaternion
    multiply!(axis.axis,axis.real)
  elsif axis.point3_like?
    r = (@real*real)-@axis.dot(axis)
    p3 = axis.scale r
    qp3 = @axis.scale real
    cross = @axis.cross axis
    a = p3.add!(qp3).add!(cross)
    set(a,r)
  else
    raise_no_conversion axis
  end
  self
end

#normObject

Returns the norm of the instance.



202
203
204
# File 'lib/eymiha/math3/quaternion.rb', line 202

def norm
  (@real * @real) + @axis.modulus
end

#quaternion(axis = self.axis, real = self.real) ⇒ Object

Returns a copy of a Quaternion with the given parts:

  • axis is a Quaternion, its parts are copied.

  • axis responds like a Point3, the arguments are copied.

  • otherwise a TypeError is raised.



76
77
78
# File 'lib/eymiha/math3/quaternion.rb', line 76

def quaternion(axis=self.axis,real=self.real)
  Quaternion.new(axis,real)
end

#scale(scalar = 1) ⇒ Object

Returns a new Quaternion instance whose parts are the original instance’s multiplied by the scalar:

  • scalar is a Quaternion, the instance is multiplied by the scalar’s parts.

  • axis is a Numeric, the instance’s parts are multiplied by the scalar.

  • otherwise a TypeError is raised.



175
176
177
# File 'lib/eymiha/math3/quaternion.rb', line 175

def scale(scalar=1)
  quaternion.scale!(scalar)
end

#scale!(scalar = 1) ⇒ Object

Returns the modified instance multiplied by the scalar.



180
181
182
183
184
185
186
187
188
# File 'lib/eymiha/math3/quaternion.rb', line 180

def scale!(scalar=1)
  if (scalar.kind_of? Quaternion)
    multiply!(scalar)
  elsif (scalar.kind_of? Numeric)
    set(@axis.scale(scalar),@real*scalar)
  else
    raise_no_conversion scalar, "Numeric"
  end
end

#set(axis = origin, real = 0) ⇒ Object

Sets the axis and real values of the instance. When

  • axis is a Quaternion, the arguments are interpretted as the parts of a quaternion.

  • axis reponds like a Point3, the axis and real are used to set the quaternion’s values.

  • otherwise a TypeError is raised.

The modified instance is returned.



44
45
46
47
48
49
50
51
52
53
54
# File 'lib/eymiha/math3/quaternion.rb', line 44

def set(axis=origin,real=0)
  if axis.kind_of? Quaternion
    set axis.axis, axis.real
  elsif axis.point3_like?
    (@axis ||= point3).set(axis)
    @real = real
  else
    raise_no_conversion(self.class.name,axis)
  end
  self
end

#subtract(axis = origin, real = 0) ⇒ Object Also known as: -

Returns a new Quaternion instance whose parts are the original instance’s with the given amounts subtracted:

  • axis is a Quaternion, its parts are subtracted.

  • axis responds like a Point3, the arguments are subtracted.

  • otherwise a TypeError is raised.



124
125
126
# File 'lib/eymiha/math3/quaternion.rb', line 124

def subtract(axis=origin,real=0)
  quaternion.subtract!(axis,real)
end

#subtract!(axis = origin, real = 0) ⇒ Object

Returns the modified instance with the arguments subtracted.



131
132
133
134
135
136
137
138
139
140
# File 'lib/eymiha/math3/quaternion.rb', line 131

def subtract!(axis=origin,real=0)
  if axis.kind_of? Quaternion
    subtract!(axis.axis,axis.real)
  elsif axis.point3_like?
    set(@axis-axis,@real-real)
  else
    raise_no_conversion axis
  end
  self
end

#to_axis_angleObject

Returns a new Quaternion encoded into a rotation.



88
89
90
91
92
93
94
# File 'lib/eymiha/math3/quaternion.rb', line 88

def to_axis_angle
  half_angle = Math.acos(@real)
  sin_half_angle = Math.sin(half_angle)
  axis = (sin_half_angle.abs < 0.00000001)?
  Point3.new(1,0,0) : Point3.new(@axis).scale!(1.0/sin_half_angle)
  Quaternion.new(axis,2.0*half_angle)
end

#to_sObject

Returns a string representation of the instance.



35
36
37
# File 'lib/eymiha/math3/quaternion.rb', line 35

def to_s
  "Quaternion:  axis: x #{axis.x}  y #{axis.y} z #{axis.z}   real #{real}"
end