Shade3D 公式

04 quaternion module [ Shade Labo ]


#1

< 04 - 1 Shade script での quaternion の回転の向き >


Shade script の quaternion の回転の向きを調べると、通常とは逆向きで表されており、q = ( w, x, y, z ) で与えられる quaternion が、q = ( w, -x, -y, -z ) で定義されています。

Q1, Q2 の順での連続回転を一度の回転 Q3 で表す場合、quaternion 積の定義からは、

Q3 = Q2*Q1

と、逆順での積で表されますが、Shade 方式の場合、Q3 = Q1 * Q2 という順次積で計算できます。

推測に過ぎませんが、この順次積で可能ということが、 ロール・ピッチ・ヨー の X-Y-Z 型 オイラー角での制御に適しているということかもしれません。

ここではオリジナルの定義通りの回転方向を採用していますので、shade script とのデータのやりとりでは、逆回転への変更と、list 型との間での変換が必要となります。

( 下記 使用例 - 6 参照 )


#2

< 04 - 2 Reference >


Ver. 1.1( 2016 / 01 / 23 )

transform( ) で引数として渡された list / tuple の中に vec3, list, tuple 以外のものがあってもエラーにはせず、そのままパスするように変更




関連記事:

  • 01 はじめに
  • 02 vec3 module
  • 03 matrix module



module quaternion は quaternion 演算を扱う class quaternion( 小文字で始まる )を提供




注記 - 1

この中で書かれている vec3 は custum class vec3 のことであり、Shade の script reference に書かれている vec3 は list3 あるいは [ float, float, float ] と記述。




構成
quaternion は w, x, y, z の 4つの float で構成される。
ただし、w, x, y, z への直接のアクセスは不可、唯一 list への変換のみ可能
[ w, x, y, z ] = list( quaternion )




init

q = quaternion( )____________( 1., 0., 0., 0. ) の quaternion

q = quaternion( [ 1, 2, 3 ] )_____X-Y-Z 型 euler 角からの変換

q = quaternion( [ 1, 2, 3, 4 ] )___リスト / タプル と同じ内容を持つ quaternion

q = quaternion( quaternion )____quaternion の copy

q = quaternion( matrix )________Affine 変換の matrix から quaternion への変換
___________________________matrix size は 3 x 3 or 4 x 4




四則演算

q : quaternion
s : scalar ( float or int )
v : vec3

q = q + q
q = q - q
q = q / s
q = q * s
q = s * q
q = q * q _____回転の合成
v = q * v _____ vec3 を quaternion 回転


vec3 v を quaternion Q1 で回転し、さらに Q2 で回転する場合の quaternion 積は逆順となる

Q3 = Q2 * Q1
v = Q2 * Q1 * v




関数

conj( ) as quaternion
_____共役 quaternion を返す

inverse( ) as quaternion
_____逆 quaternion を返す

transform( v as vec3 )
_____vec3 を quaternion 回転 ( vec3 の要素を書き換える )

transform( L as list )
_____list 内に含まれる全ての vec3 を quaternion 回転 ( vec3 の要素を書き換える )

extract( ) as vec3, float
_____quaternion 回転に相当する回転軸ベクトル axis as vec3 と回転角 theta as float を返す

matrix( ) as matrix
_____quaternion 回転に等価な affine 変換の matrix を返す

euler( ) as list
_____quaternion 回転に等価な X-Y-Z 型 euler 角を list で返す




以下は 引数に応じた quaternion を返す関数で、自身のデータは一切使用しない
通常は次の形で用いられる

q = quaternion( ).rotation( axis, theta )
q = quaternion( ).slerp( v1, v2, t )


rotation( axis as vec3, theta as float ) as quaternion
_____回転軸 axis の回りに theta 回転する quaternion を返す

slerp( v1 as vec3, v2 as vec3, t as double = 1. ) as quaternion
_____v1 から v2 への球面線形補間を与える quaternion を返す
_____v1, v2 は一直線上にないこと
_____t : 0 ~ 1


#3

< 04 - 3 使用例 >


1 )回転 と 逆回転

from quaternion import *


def make_simple_line(pL, closed = False, name = None) :
	scene.begin_line(name, closed)
	for p in pL :
		scene.append_point(p, None, None, None, None)
	scene.end_line()

		
scene = xshade.scene()
scene.begin_creating()
scene.begin_part()

#  回転 と 逆回転

#  original 形状
L = [[500, 1000, 2000], [1000, 1500, -500], [700, 800,1200]]
ob = vec3list(L)
make_simple_line(ob, True, 'original')	

#  回転軸
axis = vec3(1000, 750, 800)
ax = [vec3(0,0,0), axis]
make_simple_line(ax, False, 'axis')	

#  回転軸周りに60度回転
Q = quaternion().rotation(axis, math.pi*60/180)
Q.transform(ob)
make_simple_line(ob, True, '回転')	

#  元の状態に逆回転
Q2 = Q.inverse()
Q2.transform(ob)
make_simple_line(ob, True, '元に逆回転')	

scene.end_part()     
scene.end_creating()

**2 )連続回転の合成**
from quaternion import *
import copy


def make_simple_line(pL, closed = False, name = None) :
	scene.begin_line(name, closed)
	for p in pL :
		scene.append_point(p, None, None, None, None)
	scene.end_line()

		
scene = xshade.scene()
scene.begin_creating()
scene.begin_part()

#  連続回転の合成

#  original 形状
L = [[500, 1000, 2000], [1000, 1500, -500], [700, 800,1200]]
ob = vec3list(L)
make_simple_line(ob, True, 'original')	


#  最初の回転
#  回転軸 - 1
axis = vec3(1000, 750, 800)
ax = [vec3(0,0,0), axis]
make_simple_line(ax, False, 'axis - 1')	

#  回転軸 -1 周りに60度回転
Q1 = quaternion().rotation(axis, math.pi*60/180)
ob2 = copy.deepcopy(ob)
Q1.transform(ob2)
make_simple_line(ob2, True, '回転 - 1')	


# 二回目の回転
#  回転軸 - 2
axis = vec3(-500, 1500, 1200)
ax = [vec3(0,0,0), axis]
make_simple_line(ax, False, 'axis - 2')	

#  回転軸 -2 周りに45度回転
Q2 = quaternion().rotation(axis, math.pi*45/180)
Q2.transform(ob2)
make_simple_line(ob2, True, '回転 - 2')	


#  2度の回転を一度の回転で再現
Q3 = Q2*Q1					#  2 度の回転の quaternion の積 ( 積の順に注意 )
axis, theta = Q3.extract()	#  Q3 から回転軸ベクトルと回転角を抽出

ax = [vec3(0,0,0), axis*1000]
make_simple_line(ax, False, 'axis - 1 + 2')	

Q3.transform(ob)
make_simple_line(ob, True, '回転 - 1 + 2')	


scene.end_part()     
scene.end_creating()

**3 )ベクトル V1 から V2 への球面線形補間**
from quaternion import *


def make_simple_line(pL, closed = False, name = None) :
	scene.begin_line(name, closed)
	for p in pL :
		scene.append_point(p, None, None, None, None)
	scene.end_line()

		
scene = xshade.scene()
scene.begin_creating()
scene.begin_part()

#  ベクトル v1 から v2 への球面線形補間
#	三角形を ベクトル v1 から v2 へ 6段階に分けて順次回転

#  ベクトル v1, v2
v1 = vec3(1, 0.75, 0.8)
v2 = vec3(-1, 1.5, 0.8)
vc1 = [vec3(0,0,0), v1*1000]
vc2 = [vec3(0,0,0), v2*1000]
make_simple_line(vc1, False, 'v1')
make_simple_line(vc2, False, 'v2')	

#  三角形
L = [[1000, 750, 800], [1000, 1500, 200], [1700, 800,400]]
ob = vec3list(L)
make_simple_line(ob, True, '0')	

# 6 ステップで回転
Q = quaternion().slerp(v1, v2, 1/6.)
for i in range(6) :
	Q.transform(ob)
	make_simple_line(ob, True, str(i + 1))	


scene.end_part()     
scene.end_creating()

**4 )等価 matrix**
from quaternion import *
import copy


def make_simple_line(pL, closed = False, name = None) :
	scene.begin_line(name, closed)
	for p in pL :
		scene.append_point(p, None, None, None, None)
	scene.end_line()

		
scene = xshade.scene()
scene.begin_creating()
scene.begin_part()

#   等価 matrix

#  original 形状
L = [[500, 1000, 2000], [1000, 1500, -500], [700, 800,1200]]
ob = vec3list(L)
make_simple_line(ob, True, 'original')	

#  回転軸
axis = vec3(1000, 750, 800)
ax = [vec3(0,0,0), axis]
make_simple_line(ax, False, 'axis')	

#  回転軸周りに60度回転
Q = quaternion().rotation(axis, math.pi*60/180)
ob2 = copy.deepcopy(ob)
Q.transform(ob2)
make_simple_line(ob2, True, 'quaternion 回転')	

#  等価 matrix
m = Q.matrix()				#  等価 matrix
m.transform(ob)
make_simple_line(ob, True, 'matrix 回転')


scene.end_part()     
scene.end_creating()

**5 )等価 quaternion**
from quaternion import *
import copy


def make_simple_line(pL, closed = False, name = None) :
	scene.begin_line(name, closed)
	for p in pL :
		scene.append_point(p, None, None, None, None)
	scene.end_line()

		
scene = xshade.scene()
scene.begin_creating()
scene.begin_part()

#   等価 quaternion

#  original 形状
L = [[500, 1000, 2000], [1000, 1500, -500], [700, 800,1200]]
ob = vec3list(L)
make_simple_line(ob, True, 'original')	

#  回転 - matrix
m1 = matrix().rotate_x(math.pi*60/180)
m2 = matrix().rotate_y(math.pi*60/180)
m3 = matrix().rotate_z(math.pi*60/180)
m = m1*m2*m3
ob2 = copy.deepcopy(ob)
m.transform(ob2)	
make_simple_line(ob2, True, 'matrix 回転')

#   回転 - 等価 quaternion
Q = quaternion(m)			#  等価 quaternion
Q.transform(ob)
make_simple_line(ob, True, 'quaternion 回転')


scene.end_part()     
scene.end_creating()

**6 )quaternion ⇔ euler 角**
from quaternion import *
from math import pi


euler = [ pi*30/180, pi*45/180, pi*60/180]			#  euler 角
Q = quaternion(euler)								#  euler 角を quaternion に変換

#  quaternion から euler 角に変換
euler_1 = Q.euler()												

#  shade script で  quaternion から euler 角に変換
Q_= Q.conj()										#  回転を逆向き ( 共役 quaternion )
euler_2 = xshade.euler_from_quaternion(list(Q_))

print euler_1
print euler_2

#  euler 角から quaternion に変換
Q1 = quaternion(euler_1)

#  shade script で euler 角から quaternion に変換
Q2_ = xshade.quaternion_from_euler(euler_2)
Q2_ = quaternion(Q2_)
Q2 = Q2_.conj()										#  回転を逆向き ( 共役 quaternion )

print Q1
print Q2

#4

< 04 - 4 Download >


Ver. 1.1( 2016 / 01 / 23 )

transform( ) で引数として渡された list / tuple の中に vec3, list, tuple 以外のものがあってもエラーにはせず、そのままパスするように変更




ここからダウンロードして下さい。

          download

インストール方法は [ 01 はじめに ] を参照下さい。


#5

04 - 5  改訂

**Ver. 1.2**  ( 2016 / 03 / 16 )

変更
     slerp( ) での平行判定を変更
     slerp( ) に引数追加 ( エラーメッセージ出力の on / off 指定 )


## Reference 変更
**slerp**(v1 as vec3, v2 as vec3, t as float = 1., show_error as bool = True) as quaternion
  • v1 から v2 への球面線形補間を与える quaternion を返す
  • v1, v2 は180度平行ではないこと
  • t : 0 〜 1
  • show_error = False ならば、v1, v2 が180度平行の場合の error message を出力しない


ダウンロードはこちらから

     download


#6

04 - 6  改訂


**Ver. 1.2.1** ( 2016 / 05 / 17 )

修正
      slerp( ) での error messae に係わる bug を修正


ダウンロードはこちらから

      download


#7

04 - 7  Download link 変更


**download link 変更** ( 2017 / 03 / 12 )
ここからダウンロードして下さい。

          download