class Mittsu::Quaternion

Constants

EPS

Attributes

w[R]
x[R]
y[R]
z[R]

Public Class Methods

new(x = 0.0, y = 0.0, z = 0.0, w = 1.0) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 7
def initialize(x = 0.0, y = 0.0, z = 0.0, w = 1.0)
  @x, @y, @z, @w = x, y, z, w
  @on_change_callback = nil
end
slerp(qa, qb, qm, t) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 303
def self.slerp(qa, qb, qm, t)
  qm.copy(qa).slerp(qb, t)
end

Public Instance Methods

==(quaternion) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 268
def ==(quaternion)
  (quaternion.x == @x) && (quaternion.y == @y) && (quaternion.z == @z) && (quaternion.w == @w)
end
clone() click to toggle source
# File lib/mittsu/math/quaternion.rb, line 299
def clone
  Mittsu::Quaternion.new(@x, @y, @z, @w)
end
conjugate() click to toggle source
# File lib/mittsu/math/quaternion.rb, line 173
def conjugate
  @x *= -1.0
  @y *= -1.0
  @z *= -1.0
  self.on_change_callback
  self
end
copy(quaternion) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 41
def copy(quaternion)
  @x = quaternion.x
  @y = quaternion.y
  @z = quaternion.z
  @w = quaternion.w
  self.on_change_callback
  self
end
dot(v) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 181
def dot(v)
  @x * v._x + @y * v._y + @z * v._z + @w * v._w
end
from_array(array, offset = 0) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 272
def from_array(array, offset = 0)
  @x = array[offset]
  @y = array[offset + 1]
  @z = array[offset + 2]
  @w = array[offset + 3]
  self.on_change_callback
  self
end
inverse() click to toggle source
# File lib/mittsu/math/quaternion.rb, line 168
def inverse
  self.conjugate.normalize
  self
end
length() click to toggle source
# File lib/mittsu/math/quaternion.rb, line 189
def length
  ::Math.sqrt(@x * @x + @y * @y + @z * @z + @w * @w)
end
length_sq() click to toggle source
# File lib/mittsu/math/quaternion.rb, line 185
def length_sq
  @x * @x + @y * @y + @z * @z + @w * @w
end
multiply(q) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 211
def multiply(q)
  self.multiply_quaternions(self, q)
end
multiply_quaternions(a, b) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 215
def multiply_quaternions(a, b)
  # from http:#www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
  qax = a.x; qay = a.y; qaz = a.z; qaw = a.w
  qbx = b.x; qby = b.y; qbz = b.z; qbw = b.w
  @x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby
  @y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz
  @z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx
  @w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz
  self.on_change_callback
  self
end
normalize() click to toggle source
# File lib/mittsu/math/quaternion.rb, line 193
def normalize
  l = self.length
  if l == 0.0
    @x = 0.0
    @y = 0.0
    @z = 0.0
    @w = 1.0
  else
    l = 1.0 / l
    @x = @x * l
    @y = @y * l
    @z = @z * l
    @w = @w * l
  end
  self.on_change_callback
  self
end
on_change(&callback) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 289
def on_change(&callback)
  @on_change_callback = callback
  self
end
on_change_callback() click to toggle source
# File lib/mittsu/math/quaternion.rb, line 294
def on_change_callback
  return unless @on_change_callback
  @on_change_callback.call
end
set(x, y, z, w) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 12
def set(x, y, z, w)
  @x = x.to_f
  @y = y.to_f
  @z = z.to_f
  @w = w.to_f
  self.on_change_callback
  self
end
set_from_axis_angle(axis, angle) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 95
def set_from_axis_angle(axis, angle)
  # http:#www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
  # assumes axis is normalized
  half_angle = angle / 2.0
  s = ::Math.sin(half_angle)
  @x = axis.x * s
  @y = axis.y * s
  @z = axis.z * s
  @w = ::Math.cos(half_angle)
  self.on_change_callback
  self
end
set_from_euler(euler, update = true) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 50
def set_from_euler(euler, update = true)
  # http:#www.mathworks.com/matlabcentral/fileexchange/
  #   20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
  #  content/SpinCalc.m
  c1 = ::Math.cos(euler.x / 2.0)
  c2 = ::Math.cos(euler.y / 2.0)
  c3 = ::Math.cos(euler.z / 2.0)
  s1 = ::Math.sin(euler.x / 2.0)
  s2 = ::Math.sin(euler.y / 2.0)
  s3 = ::Math.sin(euler.z / 2.0)
  if euler.order == 'XYZ'
    @x = s1 * c2 * c3 + c1 * s2 * s3
    @y = c1 * s2 * c3 - s1 * c2 * s3
    @z = c1 * c2 * s3 + s1 * s2 * c3
    @w = c1 * c2 * c3 - s1 * s2 * s3
  elsif euler.order == 'YXZ'
    @x = s1 * c2 * c3 + c1 * s2 * s3
    @y = c1 * s2 * c3 - s1 * c2 * s3
    @z = c1 * c2 * s3 - s1 * s2 * c3
    @w = c1 * c2 * c3 + s1 * s2 * s3
  elsif euler.order == 'ZXY'
    @x = s1 * c2 * c3 - c1 * s2 * s3
    @y = c1 * s2 * c3 + s1 * c2 * s3
    @z = c1 * c2 * s3 + s1 * s2 * c3
    @w = c1 * c2 * c3 - s1 * s2 * s3
  elsif euler.order == 'ZYX'
    @x = s1 * c2 * c3 - c1 * s2 * s3
    @y = c1 * s2 * c3 + s1 * c2 * s3
    @z = c1 * c2 * s3 - s1 * s2 * c3
    @w = c1 * c2 * c3 + s1 * s2 * s3
  elsif euler.order == 'YZX'
    @x = s1 * c2 * c3 + c1 * s2 * s3
    @y = c1 * s2 * c3 + s1 * c2 * s3
    @z = c1 * c2 * s3 - s1 * s2 * c3
    @w = c1 * c2 * c3 - s1 * s2 * s3
  elsif euler.order == 'XZY'
    @x = s1 * c2 * c3 - c1 * s2 * s3
    @y = c1 * s2 * c3 - s1 * c2 * s3
    @z = c1 * c2 * s3 + s1 * s2 * c3
    @w = c1 * c2 * c3 + s1 * s2 * s3
  end
  self.on_change_callback if update
  self
end
set_from_rotation_matrix(m) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 108
def set_from_rotation_matrix(m)
  # http:#www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
  # assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
  te = m.elements
  m11 = te[0]; m12 = te[4]; m13 = te[8]
  m21 = te[1]; m22 = te[5]; m23 = te[9]
  m31 = te[2]; m32 = te[6]; m33 = te[10]
  trace = m11 + m22 + m33
  if trace > 0
    s = 0.5 / ::Math.sqrt(trace + 1.0)
    @w = 0.25 / s
    @x = (m32 - m23) * s
    @y = (m13 - m31) * s
    @z = (m21 - m12) * s
  elsif m11 > m22 && m11 > m33
    s = 2.0 * ::Math.sqrt(1.0 + m11 - m22 - m33)
    @w = (m32 - m23) / s
    @x = 0.25 * s
    @y = (m12 + m21) / s
    @z = (m13 + m31) / s
  elsif m22 > m33
    s = 2.0 * ::Math.sqrt(1.0 + m22 - m11 - m33)
    @w = (m13 - m31) / s
    @x = (m12 + m21) / s
    @y = 0.25 * s
    @z = (m23 + m32) / s
  else
    s = 2.0 * ::Math.sqrt(1.0 + m33 - m11 - m22)
    @w = (m21 - m12) / s
    @x = (m13 + m31) / s
    @y = (m23 + m32) / s
    @z = 0.25 * s
  end
  self.on_change_callback
  self
end
set_from_unit_vectors(v_from, v_to) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 145
def set_from_unit_vectors(v_from, v_to)
  # http:#lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
  # assumes direction vectors v_from and v_to are normalized
  v1 = Mittsu::Vector3.new
  r = v_from.dot(v_to) + 1.0
  if r < EPS
    r = 0.0
    if v_from.x.abs > v_from.z.abs
      v1.set(-v_from.y, v_from.x, 0.0)
    else
      v1.set(0.0, -v_from.z, v_from.y)
    end
  else
    v1.cross_vectors(v_from, v_to)
  end
  @x = v1.x
  @y = v1.y
  @z = v1.z
  @w = r
  self.normalize
  self
end
slerp(qb, t) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 227
def slerp(qb, t)
  return self if t.zero?
  return self.copy(qb) if t == 1.0
  _x, _y, _z, _w = @x, @y, @z, @w
  # http:#www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
  cos_half_theta = _w * qb.w + _x * qb.x + _y * qb.y + _z * qb.z
  if cos_half_theta < 0.0
    @w = -qb.w
    @x = -qb.x
    @y = -qb.y
    @z = -qb.z
    cos_half_theta = - cos_half_theta
  else
    self.copy(qb)
  end
  if cos_half_theta >= 1.0
    @w = _w
    @x = _x
    @y = _y
    @z = _z
    return self
  end
  half_theta = ::Math.acos(cos_half_theta)
  sin_half_theta = ::Math.sqrt(1.0 - cos_half_theta * cos_half_theta)
  if sin_half_theta.abs < 0.001
    @w = 0.5 * (_w + @w)
    @x = 0.5 * (_x + @x)
    @y = 0.5 * (_y + @y)
    @z = 0.5 * (_z + @z)
    return self
  end
  ratio_a = ::Math.sin((1.0. - t) * half_theta) / sin_half_theta,
  ratio_b = ::Math.sin(t * half_theta) / sin_half_theta
  @w = (_w * ratio_a + @w * ratio_b)
  @x = (_x * ratio_a + @x * ratio_b)
  @y = (_y * ratio_a + @y * ratio_b)
  @z = (_z * ratio_a + @z * ratio_b)
  self.on_change_callback
  self
end
to_array(array = [], offset = 0) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 281
def to_array(array = [], offset = 0)
  array[offset] = @x
  array[offset + 1] = @y
  array[offset + 2] = @z
  array[offset + 3] = @w
  array
end
w=(w) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 36
def w=(w)
  @w = w.to_f
  self.on_change_callback
end
x=(x) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 21
def x=(x)
  @x = x.to_f
  self.on_change_callback
end
y=(y) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 26
def y=(y)
  @y = y.to_f
  self.on_change_callback
end
z=(z) click to toggle source
# File lib/mittsu/math/quaternion.rb, line 31
def z=(z)
  @z = z.to_f
  self.on_change_callback
end