Hi,
Quaternions is a great way to rotate points around a vector and avoid gimble lock.
It can be hard to understand, but is actually quite simple…
With help from youtube and other places I have made a simple RotationQuaternion class.
In this snippet it rotates a point 36 times around a vector and make a circle…
Enjoy!
Installation:
open ruby console → use this snippet pasted into the Ruby Console +enter to actually open the folder itself:" UI.openURL(“file:///#{Sketchup.find_support_file(‘Plugins’)}”)
Copy the files from zip into folder. It was made in SU2016.
Restart SU and press the new button:
rs_quaternionSU.zip (9.1 KB)
rs_quaternionSU.zip (9.1 KB)
if the installaton dont work the code is here: (2 files)
rs_quaternion_main.rb:
module RS_2019; end
module RS_2019::RS_Quaternion
load 'rs_quaternionSU\rs_quaternion_class.rb'
SKETCHUP_CONSOLE.show
SKETCHUP_CONSOLE.clear
puts "hello quaternion"
@model = Sketchup.active_model
@ents = @model.active_entities
@defs = @model.definitions
rot_a_deg = 0
rot_axis = {"x" => rand(20), "y" => rand(20), "z" => rand(20)}
point1 = [20,0,0]
q = RsRotationQuaternion.new(rot_a_deg, rot_axis)
@model.start_operation "rotate_point_and_draw"
while rot_a_deg <= 360
rotated_point = q.point_to_rotate(point1)
@ents.add_cpoint rotated_point
rot_a_deg = rot_a_deg + 10
q.set(rot_a_deg,rot_axis)
end
@ents.add_line [0,0,0], [rot_axis['x'], rot_axis['y'], rot_axis['z']]
@model.commit_operation
end #module RS_2019::RS_Quaternion
next file: rs_quaternion_class.rb:
module RS_2019; end
module RS_2019::RS_Quaternion
class RsRotationQuaternion
def initialize(rot_a_deg, rot_axis)
a = rot_a_deg * Math::PI / 180
v_norm = Math.sqrt(rot_axis['x']**2 + rot_axis['y']**2 + rot_axis['z']**2)
vx = rot_axis['x'] / v_norm # x rotation axis/unit vector
vy = rot_axis['y'] / v_norm # y rotation axis/unit vector
vz = rot_axis['z'] / v_norm # z rotation axis/unit vector
# create rotation quaternion
qa = Math.cos(a/2) # angle to rotate
qb = vx * Math.sin(a/2)
qc = vy * Math.sin(a/2)
qd = vz * Math.sin(a/2)
q_check = qa**2 + qb**2 + qc**2 + qd**2 # Result always 1
# puts "q_check : #{q_check}" # Delete
@q = {"a" => qa, "b" => qb, "c" => qc, "d" => qd}
# create conjugate rotation quaternion (inverse)
qa_inv = qa
qb_inv = -qb
qc_inv = -qc
qd_inv = -qd
q_inv_check = qa_inv**2 + qb_inv**2 + qc_inv**2 + qd_inv**2 # Result always 1
# puts "q_inv_check : #{q_inv_check}" # Delete
@q_inv = {"a" => qa_inv, "b" => qb_inv, "c" => qc_inv, "d" => qd_inv}
end # initialize
def set(rot_a_deg, rot_axis)
initialize(rot_a_deg, rot_axis)
end
def point_to_rotate(point)
p = {"a" => 0, "b" => point[0], "c" => point[1], "d" => point[2]}
# p' = q*p*q_inv, where p' is the rotated point, q the rotation quaternion, p the point to rotate and q_inv the conjugate rotation quaternion
# To calculate a rotation of a point with a quaternion is a 2 step operation
# First step is to calculate p_half_rot = q * p.
# Second step is to calculate p' = p_half_rot * q_inv.
# Step 1:
p_half_rot = multiply_quaternions(@q,p)
# Step 2:
p_rot = multiply_quaternions(p_half_rot, @q_inv)
return [p_rot['b'], p_rot['c'], p_rot['d']]
end # rotationQuaternion
def multiply_quaternions(q1, q2)
q1r = q1['a']
q1i = q1['b']
q1j = q1['c']
q1k = q1['d']
q2r = q2['a']
q2i = q2['b']
q2j = q2['c']
q2k = q2['d']
# https://math.stackexchange.com/questions/296349/quaternions-why-does-ijk-1-and-ij-k-and-ji-k
# https://www.youtube.com/watch?v=LjSD103Rw4E
# https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
# r => real number
# x-axis: i^2 = -1, ij = k, ji = -k
# y-axis: j^2 = -1, jk = i, kj = -i
# z-axis: k^2 = -1, ki = j, ik = -j
#| q1r | q1i | q1j | q1k ||
#=====================================================================
#| (q1r*q2r) (r) | (q1i*q2r) (i) | (q1j*q2r) (j) | (q1k*q2r) (k) || q2r
#---------------------------------------------------------------------
#| (q1r*q2i) (i) |-(q1i*q2i) (-r) |-(q1j*q2i) (-k) | (q1k*q2i) (j) || q2i
#---------------------------------------------------------------------
#| (q1r*q2j) (j) | (q1i*q2j) (k) |-(q1j*q2j) (-r) |-(q1k*q2j) (-i) || q2j
#---------------------------------------------------------------------
#| (q1r*q2k) (k) |-(q1i*q2k) (-j) | (q1j*q2k) (i) |-(q1k*q2k) (-r)|| q2k
#---------------------------------------------------------------------
q12r = (q1r*q2r)-(q1i*q2i)-(q1j*q2j)-(q1k*q2k)
q12i = (q1i*q2r)+(q1r*q2i)-(q1k*q2j)+(q1j*q2k)
q12j = (q1j*q2r)+(q1k*q2i)+(q1r*q2j)-(q1i*q2k)
q12k = (q1k*q2r)-(q1j*q2i)+(q1i*q2j)+(q1r*q2k)
return {"a" => q12r, "b" => q12i, "c" => q12j, "d" => q12k}
end # multiply_quaternions(...)
end # class RsRotationQuaternion
end #module RS_2019::RS_Quaternion