lib/nmatrix/homogeneous.rb in nmatrix-0.1.0.rc5 vs lib/nmatrix/homogeneous.rb in nmatrix-0.1.0
- old
+ new
@@ -138,6 +138,104 @@
end
n[0..2,3] = xyz
n
end
end
+
+ #
+ # call-seq:
+ # quaternion -> NMatrix
+ #
+ # Find the quaternion for a 3D rotation matrix.
+ #
+ # Code borrowed from: http://courses.cms.caltech.edu/cs171/quatut.pdf
+ #
+ # * *Returns* :
+ # - A length-4 NMatrix representing the corresponding quaternion.
+ #
+ # Examples:
+ #
+ # n.quaternion # => [1, 0, 0, 0]
+ #
+ def quaternion
+ raise(ShapeError, "Expected square matrix") if self.shape[0] != self.shape[1]
+ raise(ShapeError, "Expected 3x3 rotation (or 4x4 homogeneous) matrix") if self.shape[0] > 4 || self.shape[0] < 3
+
+ q = NMatrix.new([4], dtype: self.dtype == :float32 ? :float32: :float64)
+ rotation_trace = self[0,0] + self[1,1] + self[2,2]
+ if rotation_trace >= 0
+ self_w = self.shape[0] == 4 ? self[3,3] : 1.0
+ root_of_homogeneous_trace = Math.sqrt(rotation_trace + self_w)
+ q[0] = root_of_homogeneous_trace * 0.5
+ s = 0.5 / root_of_homogeneous_trace
+ q[1] = (self[2,1] - self[1,2]) * s
+ q[2] = (self[0,2] - self[2,0]) * s
+ q[3] = (self[1,0] - self[0,1]) * s
+ else
+ h = 0
+ h = 1 if self[1,1] > self[0,0]
+ h = 2 if self[2,2] > self[h,h]
+
+ case_macro = Proc.new do |i,j,k,ii,jj,kk|
+ qq = NMatrix.new([4], dtype: :float64)
+ self_w = self.shape[0] == 4 ? self[3,3] : 1.0
+ s = Math.sqrt( (self[ii,ii] - (self[jj,jj] + self[kk,kk])) + self_w)
+ qq[i] = s*0.5
+ s = 0.5 / s
+ qq[j] = (self[ii,jj] + self[jj,ii]) * s
+ qq[k] = (self[kk,ii] + self[ii,kk]) * s
+ qq[0] = (self[kk,jj] - self[jj,kk]) * s
+ qq
+ end
+
+ case h
+ when 0
+ q = case_macro.call(1,2,3, 0,1,2)
+ when 1
+ q = case_macro.call(2,3,1, 1,2,0)
+ when 2
+ q = case_macro.call(3,1,2, 2,0,1)
+ end
+
+ self_w = self.shape[0] == 4 ? self[3,3] : 1.0
+ if self_w != 1
+ s = 1.0 / Math.sqrt(self_w)
+ q[0] *= s
+ q[1] *= s
+ q[2] *= s
+ q[3] *= s
+ end
+ end
+
+ q
+ end
+
+ #
+ # call-seq:
+ # angle_vector -> [angle, about_vector]
+ #
+ # Find the angle vector for a quaternion. Assumes the quaternion has unit length.
+ #
+ # Source: http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/
+ #
+ # * *Returns* :
+ # - An angle (in radians) describing the rotation about the +about_vector+.
+ # - A length-3 NMatrix representing the corresponding quaternion.
+ #
+ # Examples:
+ #
+ # q.angle_vector # => [1, 0, 0, 0]
+ #
+ def angle_vector
+ raise(ShapeError, "Expected length-4 vector or matrix (quaternion)") if self.shape[0] != 4
+ raise("Expected unit quaternion") if self[0] > 1
+
+ xyz = NMatrix.new([3], dtype: self.dtype)
+
+ angle = 2 * Math.acos(self[0])
+ s = Math.sqrt(1.0 - self[0]*self[0])
+
+ xyz[0..2] = self[1..3]
+ xyz /= s if s >= 0.001 # avoid divide by zero
+ return [angle, xyz]
+ end
end
\ No newline at end of file