Shade3D 公式

21 Quaternion [ Shade Labo ]


#1

21 - 1  汎用性のある Quaternion 関数


オブジェクトを任意の姿勢で任意の位置に配置するに必要な情報は
  • 移動ベクトル
  • 方向を規定する2つのベクトル

という3つの情報が必要です。




一般的なのは matrix 変換による制御ですが、ここでは quaternion を利用した汎用性のある script 関数を紹介します。

quaternion 回転 2回分の回転を一度で処理します。




[ script 21 - 1 ] は labo に登録されている関数 attitude_control_quaternion( ) で、Shade 上で選択したobject を次のルールで配置する quaternion を返します。


def attitude_control_quaternion(u1 as vec3, v1 as vec3, u2 as vec3, v2 as vec3, fit as integer = 0) as matrix

  • 一組のベクトル u1, v1 が、もう一組のベクトル u2, v2 に一致するように回転する quaternion を返す

  • u1, v1 は単位ベクトルでなくてもよく、直交条件も必要ないが、平行であってはならない

  • u2, v2 も単位ベクトルでなくてもよく、直交条件も必要ないが、平行であってはならない

  • u1, v1 のなす角と u2, v2 のなす角が異なる場合、
         fit == 0 ならば、u1 と u2 を一致させる
         fit == 1 ならば、u1,v1 の中間線と u2, v2 の中間線を一致させる


[ script 21 - 1 ]

     注意:2016 / 03 / 16 に改訂された quaternion module を使用しています

#  attitude_control_quaternion(u1 as vec3, v1 as vec3, u2 as vec3, v2 as vec3, fit as int = 0) as quaternion
#		
#	一組のベクトルu1, v1 を、もう一組のベクトルu2, v2 に一致させるような quaternion を返す
#	u1, v1 は単位ベクトルでなくてよいが、平行ではないこと
#	u2, v2 も単位ベクトルでなくてよいが、平行ではないこと
#	u1, v1 のなす角と u2, v2 のなす角が異なる場合、
#		fit = 0 ならば、u1 と u2 を一致させる
#		fit = 1 ならば、 u1,v1 の中間線と u2, v2 の中間線 を一致させる
	
def attitude_control_quaternion(u1, v1, u2, v2, fit = 0) :	

	#  w1, w2, ww1, ww2
	w1 = u1*v1
	w2 = u2*v2
	
	if w1.abs2() == 0 or w2.abs2() == 0 :
		return None						#  u1, v1 あるいは u2, v2 が、平行 or 0 vector 

	if fit == 0 :
		ww1 = vec3(u1)
		ww2 = vec3(u2)
	else :
		uu1 = vec3(u1)
		vv1 = vec3(v1)
		uu2 = vec3(u2)
		vv2 = vec3(v2)
		uu1.norm()
		vv1.norm()
		uu2.norm()
		vv2.norm()
		ww1 = uu1 + vv1
		ww2 = uu2 + vv2

	Q1 = rotational_quaternion(w1, w2, u1)				#  回転
	ww1 = Q1*ww1
	Q2 = rotational_quaternion(ww1, ww2, w2)			#  回転

	return Q2*Q1
	

		
#  rotational_quaternion(v1 as vec3, v2 as vec3, altV as vec3) as quaternion
#
# 	原点回りに v1 が v2 に重なるような回転 quaternion を返す
#	180度回転になる場合は、altV の回りに回転する
#	v1, v2 は単位ベクトルでなくてよいが、0 vector ではないこと

def rotational_quaternion(v1, v2, altV) :
	Q = quaternion().slerp(v1, v2, 1, False)	#  v1, v2 が180度平行の場合に erroe message を出力しない
	if Q != None :
		return Q
	else :										#  v1, v2 が180度平行
		from math import pi
		return quaternion().rotation(altV, pi)

#2

21 - 2  xshade.get_zmat( ) と attitude_control_quaternion( ) の違い


**xshade.get_zmat( )** と **attitude_control_quaternion( )** は、返り値が matrix か quaternion かの違いだけではありません。
**xshade.get_zmat( )**

vector u と v = vec3(0, 0, 1) の間の球面補間を与える matrix を返す、quaternion 回転一回分の matrix に相当

以下で得られる matrix M1 と M2 は同じです。

u = vec3(x, y, z)
M1 = xshade.get_zmat(u) 

v = vec3(0, 0, 1)
Q = quaternion().slerp(u, v)
M2 = Q.matrix()

     [ fig 21 - 1 ]


**attitude_control_quaternion( )**

2つの vector u, v 間の球面補間を与える quaternion に加えて、vector v 回りの回転制御も行います。

     [ fig 21 - 2 ]


#3

21 - 3  使用例 - ロクロ回転( その1 )


[ script 21 - 2 ] はサンプルポリゴンメッシュを出力するもので、球状のポリゴンメッシュにランダムに回転と移動を与えます。

[ script 21 - 3 ] は Shade 上で選択したポリゴンメッシュの選択面を基準として次のようなロクロ回転を与えます。( local 座標系もサポート )

  • 選択面の面法線が +Z 方向を向く
  • 選択面が view の中央に位置する
  • 選択面の水平方向は保たれ、垂直方向は +Y 方向になる
  • ただし、面法線が Y 方向を向いている場合には X 方向が保たれる

     

     [ fig 21 - 3 ]


[ script 21 - 2 ]     サンプルポリゴンメッシュを出力

from matrix import *
from math import pi
import random	


	
##  ポリゴンメッシュの半径  ######
size = 10000
########################

scene = xshade.scene()

scene.begin_creating()
scene.create_sphere(None, [0, 0, 0], size)
scene.end_creating()
scene.convert_to_polygon_mesh(8, 5)
ob = scene.active_shape()

mx1 = matrix(ob.world_to_local_matrix)
mx2 = matrix(ob.local_to_world_matrix)

ob_local = scene.local
if ob_local == None :				#  global 座標系
	M = mx1
else :								#  local 座標系
	M_local = matrix(ob_local.transformation_matrix) 
	M_local_2 = M_local.inverse()
	M = M_local*mx1

ob.transform(M)


#  ob をランダムに 移動, 回転
M1 = matrix().translate(size*random.random(), size*random.random(), size*random.random())
M2 = matrix().rotate_x(pi*random.random())
M3 = matrix().rotate_y(pi*random.random())
M4 = matrix().rotate_z(pi*random.random())	
M = mx2*M1*M2*M3*M4*mx1
ob.transform(M)

[ script 21 - 3 ]     Shade 上で ポリゴンメッシュの面を一つ選択して実行
import labo
from matrix import *
		
			

scene = xshade.scene()

#  f
ob = scene.active_shape()
fL = ob.active_face_indices
f = fL[0]							#  基準面


#  p : 基準面の頂点座標
fL = ob.face(f).vertex_indices
p = []
for idx in fL :
	p.append(ob.vertex(idx).position)
p = vec3list(p)						#  基準面の頂点座標

mx2 = matrix(ob.local_to_world_matrix)
ob_local = scene.local
if ob_local == None :				#  global 座標系
	mx2.transform(p)
else	:							#  local 座標系
	M_local = matrix(ob_local.transformation_matrix) 
	M_local_2 = M_local.inverse()
	M2 = mx2*M_local_2
	M2.transform(p)


#  Q, c
u1 = (p[2] - p[1])*(p[0] - p[1])	#  面法線
u1.norm()							#  5行下の平行判定 is_parallel( ) のため ( ベクトル長さが極端に異なると 0 vector と見なされ非平行と判定される )
v1 = vec3(0, 1, 0)					#  垂直方向
u2 = vec3(0, 0, 1)					#  面法線 u1 を一致させたい方向 ( 正面図手前 )
v2 = vec3(0, 1, 0)					#  v1 を向けたい方向

if labo.is_parallel(u1, v1) :		#  平行判定を | u1*v1 | == 0 で行わないこと
	v1 = vec3(1, 0, 0)
	v2 = vec3(1, 0, 0)

Q = labo.attitude_control_quaternion(u1, v1, u2, v2, 0)		#  第5引数 fit = 0
c = labo.vec3_mean(p)				#  面 f の中心座標 
	 

#  ロクロ 回転
scene.scroll(c)						#  scroll は world 座標 で指定
scene.lathe_rotation = list(Q.conj())



####  以下は マーカー出力  #####

#  M1 matrix
mx1 = matrix(ob.world_to_local_matrix)
if ob_local == None :				#  global 座標系
	M1 = mx1
else	:							#  local 座標系
	M_local = matrix(ob_local.transformation_matrix) 
	M1 = M_local*mx1


#  面法線 - 垂直線 マーカー出力
scene.create_part('marker')
ob2 = scene.active_shape()

bb = labo.vec3_bounding_box_size(p)
size = bb[3]/2
u1.norm()
v1.norm()
u1 = u1*size
v1 = v1*size
L = [c + u1, c, c + v1]
M1.transform(L)				

scene.begin_creating()
scene.create_line("面法線 - 垂直線", L, True)
scene.end_creating()

                             
#  基準面マーカー出力
M1.transform(p)			
scene.begin_creating()
scene.create_line("face " + str(f), p, True)
scene.end_creating()


ob2.activate()

#4

21 - 4  使用例 - ロクロ回転( その2 )


[ script 21 - 4 ] は Shade 上で選択したポリゴンメッシュの指定する面を基準として次のようなロクロ回転を与えます。( local 座標系もサポート )
  • 指定面の面法線が +Z 方向を向く
  • 指定面の稜線の一つが X 方向を向く

     

     [ fig 21 - 4 ]


[ script 21 - 4 ]     Shade 上で ポリゴンメッシュの面を一つ選択して実行

import labo
from matrix import *
		
			

scene = xshade.scene()


#  f matrix
ob = scene.active_shape()
fL = ob.active_face_indices
f = fL[0]							#  基準面


#  p : 基準面の頂点座標
pL = ob.face(f).vertex_indices
p = []
for idx in pL :
	p.append(ob.vertex(idx).position)
p = vec3list(p)						#  基準面の頂点座標
					
mx2 = matrix(ob.local_to_world_matrix)
ob_local = scene.local
if ob_local == None :				#  global 座標系
	mx2.transform(p)
else	:							#  local 座標系
	M_local = matrix(ob_local.transformation_matrix) 
	M_local_2 = M_local.inverse()
	M2 = mx2*M_local_2
	M2.transform(p)

#  Q, c			u1, v1 は平行でないので、平行判定は不要
u1 = (p[2] - p[1])*(p[0] - p[1])	#  面法線
v1 = p[2] - p[1]					#  v2 に一致させたい方向
u2 = vec3(0, 0, 1)					#  面法線 u1 を一致させたい方向 ( 正面図手前 )
v2 = vec3(1, 0, 0)					#  v1 を向けたい方向

Q = labo.attitude_control_quaternion(u1, v1, u2, v2, 0)		#  第5引数 fit = 0
c = labo.vec3_mean(p)				#  面 f の中心座標 
	 

#  ロクロ 回転
scene.scroll(c)						#  scroll は world 座標 で指定
scene.lathe_rotation = list(Q.conj())
ob.face(f).active = True	



####  以下は マーカー出力  #####

#  M1 matrix
mx1 = matrix(ob.world_to_local_matrix)
if ob_local == None :				#  global 座標系
	M1 = mx1
else	:							#  local 座標系
	M_local = matrix(ob_local.transformation_matrix) 
	M1 = M_local*mx1
	
	
#  面法線 - 垂直線 マーカー出力
scene.create_part('marker')
ob2 = scene.active_shape()

bb = labo.vec3_bounding_box_size(p)
size = bb[3]/2
u1.norm()
v1.norm()
u1 = u1*size
v1 = v1*size
L = [c + u1, c, c + v1]
M1.transform(L)				

scene.begin_creating()
scene.create_line("面法線 - 垂直線", L, True)
scene.end_creating()

                             
#  基準面マーカー出力
M1.transform(p)			
scene.begin_creating()
scene.create_line("face " + str(f), p, True)
scene.end_creating()


ob2.activate()

#5

21 - 5  使用例 - ロクロ回転( その3 )


[ script 21 - 5 ] は Shade 上で選択した線形状の始端での接線ベクトルを基準として次のようなロクロ回転を与えます。( local 座標系もサポート )

基本的には [ script 21 - 3 ] の線形状版ですが、掃引断面の作成を容易にすることを目的としており、始端ポイントの位置にカーソル位置をセットします。

  • 接線ベクトルが -Z 方向を向く
  • 始端ポイントが view の中央に位置する
  • 始端での水平方向は保たれ、垂直方向は +Y 方向になる
  • ただし、接線ベクトルが Y 方向を向いている場合には X 方向が保たれる
  • 始端ポイントにカーソル位置がセットされる

     

 [ fig 21 - 5 ]


[ script 21 - 5 ]     Shade 上で線形所を選択して実行

import labo
from matrix import *		
			

scene = xshade.scene()

#  bz
ob = scene.active_shape()
bz = labo.get_bezier(xshade, 0)		#  始端の bezier

ob_local = scene.local
if ob_local != None :				#  local 座標系
	M_local = matrix(ob_local.transformation_matrix) 
	M_local_2 = M_local.inverse()
	M_local_2.transform(bz)

	
#  vt : 始端での接線ベクトル
vt = labo.bezier_line_tangent(bz, 0)


#  Q, c
u1 = -vt							#  始端での接線ベクトルの逆向き
v1 = vec3(0, 1, 0)					#  垂直方向
u2 = vec3(0, 0, 1)					#  面法線 u1 を一致させたい方向 ( 正面図手前 )
v2 = vec3(0, 1, 0)					#  v1 を向けたい方向

if labo.is_parallel(u1, v1) :		#  平行判定を | u1*v1 | == 0 で行わないこと
	v1 = vec3(1, 0, 0)
	v2 = vec3(1, 0, 0)

Q = labo.attitude_control_quaternion(u1, v1, u2, v2, 0)		#  第5引数 fit = 0
c = bz[0]							#   始端座標 
	 

#  ロクロ 回転
scene.scroll(c)						#  scroll は world 座標 で指定
scene.lathe_rotation = list(Q.conj())

scene.cursor_position = c

#6

21 - 6  使用例 - local 座標系


[ script 21 - 6 ] は Shade 上で選択した線形状の始端での接線ベクトルを基準として次のような local 座標系を与えます。( global 座標系に対してのみ操作可 )

[ script 21 - 5 ] の local 座標系版です。

  • 接線ベクトルが -Z 方向を向く
  • 始端ポイントが原点に位置する
  • 始端での水平方向は保たれ、垂直方向は +Y 方向になる
  • ただし、接線ベクトルが Y 方向を向いている場合には X 方向が保たれる
  • 始端ポイント = 原点 にカーソル位置がセットされる

     

     [ fig 21 - 6 ]


[ script 21 - 6 ]     Shade 上で線形所を選択して実行

import labo
from matrix import *		
			

scene = xshade.scene()

#  bz
ob = scene.active_shape()
bz = labo.get_bezier(xshade, 0)		#  始端の bezier

ob_local = scene.local
if ob_local != None :				#  local 座標系
	raise ValueError('local  座標系では使用できません')

	
#  vt : 始端での接線ベクトル
vt = labo.bezier_line_tangent(bz, 0)


#  Q, c
u1 = -vt							#  始端での接線ベクトルの逆向き
v1 = vec3(0, 1, 0)					#  垂直方向
u2 = vec3(0, 0, 1)					#  面法線 u1 を一致させたい方向 ( 正面図手前 )
v2 = vec3(0, 1, 0)					#  v1 を向けたい方向

if labo.is_parallel(u1, v1) :		#  平行判定を | u1*v1 | == 0 で行わないこと
	v1 = vec3(1, 0, 0)
	v2 = vec3(1, 0, 0)

Q = labo.attitude_control_quaternion(u1, v1, u2, v2, 0)		#  第5引数 fit = 0
	 

#  local 座標 セット
mx1 = matrix(ob.world_to_local_matrix)
c = bz[0]*mx1						#   始端座標 
Mt = matrix().translate(c[0], c[1], c[2])
Q = Q.inverse()
Mq = Q.matrix()
M = Mq*Mt

scene.create_part('temp')
ob2 = scene.active_shape()
ob2.transform(M)
scene.use_local()
scene.scroll([0, 0, 0])				#  scroll は world 座標 で指定

scene.lathe_rotation = [1, 0, 0, 0]
scene.cursor_position = [0, 0, 0]

ob.activate()

#7

21 - 7  使用例 - ポリゴン面に張りつけ


[ script 21 - 2 ] ではベースとなるポリゴンメッシュの四角面の上に、小さな四角錐状のポリゴンメッシュを張りつけます。

四角錐状のポリゴンメッシュはランダムに移動, 回転された状態で置かれます。

       [ fig 21 - 7 ]


関数 attitude_control_matrix(u1, v1, u2, v2, fit) に与えられる各引数を次のように定義します。

          [ fig 21 - 8 ]


script を実行すると、次のように配置されます。

          [ fig 21 - 9 ]


[ script 21 - 7 ]

import labo
from matrix import *
from quaternion import *
from math import pi
import random


	
def create_base_object() :
	scene.begin_creating()
	scene.create_sphere(None, [0, 0, 0], 10000)
	scene.end_creating()
	scene.convert_to_polygon_mesh(8, 5)
	
	return scene.active_shape()
	
	

#  create_object(size as float) as shape, vec3 list
#
#	配置 object を Shade 上に作成	し、その shape と頂点座標リストを返す

def create_object(size) :	
	pL = []
	pL.append([0, size, 0])
	pL.append([0, 0, size/2])
	pL.append([size, 0, 0])
	pL.append([0, 0, -size/2])
	pL.append([-size/2, 0, 0])
	
	pL = vec3list(pL)
	mx = matrix(scene.active_shape().world_to_local_matrix)
	mx.transform(pL)
	
	scene.begin_creating()
	scene.begin_polygon_mesh(None)
	for p in pL :
		scene.append_polygon_mesh_vertex(p)
	for i in range(1, 5) :
		scene.append_polygon_mesh_edge(0, i)
	for i in range(1, 4) :
		scene.append_polygon_mesh_edge(i, i + 1)
	scene.append_polygon_mesh_edge(4, 1)
	scene.end_polygon_mesh()
	scene.end_creating()

	ob = scene.active_shape()
	for i in range(5) :
		ob.face(i).flip()
	
	#  ob をランダムに 移動, 回転
	mx1 = matrix(ob.world_to_local_matrix)
	mx2 = matrix(ob.local_to_world_matrix)

	M1 = matrix().translate(size*2*random.random(), size*2*random.random(), size*2*random.random())
	M2 = matrix().rotate_x(pi*random.random())
	M3 = matrix().rotate_y(pi*random.random())
	M4 = matrix().rotate_z(pi*random.random())	
	M = mx2*M1*M2*M3*M4*mx1
	ob.transform(M)                           	

	#  ob の頂点座標取得
	p = []
	for i in range(5) :
		p.append(ob.vertex(i).position)
	p = vec3list(p)
	mx2.transform(p)
	
	return ob, p
	
	
	

scene = xshade.scene()

#  ob1 : 		ベースポリゴンメッシュ
ob1 = create_base_object()

#  ob2 :		配置 object
ob2, p = create_object(3000)				#  p : ob2 の頂点座標リスト

#  c1, u1, v1
c1 = (p[2] + p[4])/2
u1 = p[2] - p[3]
v1 = p[2] - p[1]

#  ob2 の copy object を ob1 上に配置	
scene.create_part()
ob3 = scene.active_shape()
ob3.disclosed = False

mx1 = matrix(ob2.world_to_local_matrix)
mx2 = matrix(ob2.local_to_world_matrix)

nof = ob1.number_of_faces
for f in range(nof) :
	if ob1.face(f).number_of_vertices == 4 :
		fL = ob1.face(f).vertex_indices
		q = []
		for idx in fL :
			q.append(ob1.vertex(idx).position)
		q = vec3list(q)
		mx2.transform(q)
		
		#  c2, u2, v2
		c2 = labo.vec3_mean(q)
		u2 = q[3] - q[1]
		v2 = q[0] - q[2]
		
		Q = labo.attitude_control_quaternion(u1, v1, u2, v2, 1)
		M = Q.matrix()
		M1 = matrix().translate(-c1[0], -c1[1], -c1[2])
		M2 = matrix().translate(c2[0], c2[1], c2[2])
		M = mx2*M1* M*M2*mx1
		ob2.copy_object(M)
		ob4 = ob2.bro
		ob4.place_child(1)
	
scene.active_shapes = [ob2, ob3]

#8

21 - 8  使用例 - 自由曲面に張りつけ


関連記事:          ( 2016 / 03 / 17 追記 )
  • 08 Bezier Line 等分割

[ script 21 - 8 ] では Shade 上で選択した自由曲面の parameter s で指定される U方向線形状に沿って、小さな四角錐状のポリゴンメッシュを張りつけます。

四角錐状のポリゴンメッシュはランダムに移動, 回転された状態で置かれます。

          [ fig 21 - 10 ]


関数 attitude_control_matrix(u1, v1, u2, v2, fit) に与えられる各引数を次のように定義します。

          [ fig 21 - 11 ]


script 実行例

          [ fig 21 - 12 ]


[ script 21 - 8 ]     Shade 上で自由曲面を選択して実行

import labo
from matrix import *
from quaternion import *
from math import pi
import random


#  配置先、配置数  #####################################################
s = 0.5		#  選択自由曲面の parameter s の U line 上にオブジェクトを配置
num = 12	#  配置数
#####################################################################
	
	

#	control point list bz で与えられる bezier 曲線の
#	パラメータ t における微分ベクトルの絶対値を返す
#
def bezier_abs_derivative(bz, t) :
	v = 3*t**2*(-bz[0] + 3*bz[1] - 3*bz[2] + bz[3]) + 6*t*(bz[0] - 2*bz[1] + bz[2]) + 3*(-bz[0] + bz[1])
	return v.abs()
	
	#	matrix  演算で記述すれば
	#	td = labo.bezier_t_derivative(t)
	#	m = labo.bezier_m()
	#	v = td*m*matrix(True, bz)
	#	return v.abs()
	
	

	
		
#  get_n_divide_parameter_list(bZs as vec3 list, n as int)  as list	
#
#	bZs で与えられる bezier line の全長を n 等分する点の parameter list を返す

def get_n_divide_parameter_list(bZs, n) :	
	# total, brL, sL
	total = 0	#  Bezier line 全長
	bcL = []		#  各ブロック毎の線長
	sL = [0]		#  全長を 1 に規格化した時の各ブロックまでの累積長さ
	
	for bz in bZs :
		r = labo.bezier_simpson(bezier_abs_derivative, bz, 0, 1)	#  parameter 区間 0~1 で線長を求める
		total += r
		bcL.append(r)
		sL.append(total)
	m = len(sL)
	for i in range(m) :
		sL[i] /=total
			
	#  dL : n 分割点の距離パラメーリスト
	#  dL[i] = 2.36 ならば、bLs[2] の 距離 0.36 (各ブロック毎の規格化距離) の点が分割点
	dL = []
	k = 1
	for i in range(1, n) :
		a = i/float(n)
		while a > sL[k] :
			k += 1
		else :
			dL.append((k - 1) + (a - sL[k - 1])/(sL[k] - sL[k - 1]))	
			
			
	#  Newton 法により、距離パラメータリスト dL から bezier parameter リスト tL を求める	
	allow = 0.000001		#  Newton 法での収束判定しきい値	
	maxN = 50				#  収束計算打ち切り回数
	tL = []					#  bezier parameter リスト
	
	for d in dL :
		k = int(d)
		t = d - k
		bz = bZs[k]
		target = t*bcL[k]	#  bcL : 各ブロック毎の線長
	
		p = t				#  反復計算のための初期値
		calculated = target	#  反復計算のための初期値
		d = 1				#  反復計算のための初期値

		for i in range(maxN) :
			p = p + (target - calculated)/d
			calculated = labo.bezier_simpson(bezier_abs_derivative, bz, 0., p)		#  parameter 区間 0~p で線長を求める														
			d = bezier_abs_derivative(bz, p)										#  parameter p における微分ベクトルの絶対値
			if abs(target - calculated)/target < allow :
				break							

		tL.append(k + p)
		
	return tL
	
	

#  create_object(size as float) as shape, vec3 list
#
#	配置 object を Shade 上に作成	し、その shape と頂点座標リストを返す

def create_object(size) :	
	pL = []
	pL.append([0, size, 0])
	pL.append([0, 0, size/2])
	pL.append([size, 0, 0])
	pL.append([0, 0, -size/2])
	pL.append([-size/2, 0, 0])
	
	pL = vec3list(pL)
	mx = matrix(scene.active_shape().world_to_local_matrix)
	mx.transform(pL)
	
	scene.begin_creating()
	scene.begin_polygon_mesh(None)
	for p in pL :
		scene.append_polygon_mesh_vertex(p)
	for i in range(1, 5) :
		scene.append_polygon_mesh_edge(0, i)
	for i in range(1, 4) :
		scene.append_polygon_mesh_edge(i, i + 1)
	scene.append_polygon_mesh_edge(4, 1)
	scene.end_polygon_mesh()
	scene.end_creating()

	ob = scene.active_shape()
	for i in range(5) :
		ob.face(i).flip()
	
	#  ob をランダムに 移動, 回転
	mx1 = matrix(ob.world_to_local_matrix)
	mx2 = matrix(ob.local_to_world_matrix)

	M1 = matrix().translate(size*2*random.random(), size*2*random.random(), size*2*random.random())
	M2 = matrix().rotate_x(pi*random.random())
	M3 = matrix().rotate_y(pi*random.random())
	M4 = matrix().rotate_z(pi*random.random())	
	M = mx2*M1*M2*M3*M4*mx1
	ob.transform(M)                           	

	#  ob の頂点座標取得
	p = []
	for i in range(5) :
		p.append(ob.vertex(i).position)
	p = vec3list(p)
	mx2.transform(p)
	
	return ob, p
	
	
	

scene = xshade.scene()

#  ob1 : 		選択自由曲面
ob1 = scene.active_shape()

nop = ob1.total_number_of_control_points
nv = ob1.number_of_sons
nu = nop/nv
closed_u = ob1.closed
if not closed_u :
	nu += -1		#  U 方向ブロック数
	nd = num - 1	#  分割数
else :
	nd = num
m = int(s)			#  V ブロック No.
	
#  bZs :		ob1 の parameter s における U line の bezier control points list を格納した list
bPs = []
bZs = []
for i in range(nu) :
	bP = labo.get_bezier_patch(xshade, m, i)
	bPs.append(bP)
	ss = s - int(s)
	bZs.append(labo.bezier_surface_u_line(bP, ss))

#  tL :		bZs を nd 分割する点の parameter list
tL = get_n_divide_parameter_list(bZs, nd)	
tL.insert(0, 0)
if not closed_u :
	tL.append(nu)
	
#  ob2 :		配置 object
bb = ob1.bounding_box_size
size = (bb[0] + bb[1] + bb[2])/(num*2)		#  size : 配置オブジェクトの size
ob2, p = create_object(size)				#  p : ob2 の頂点座標リスト
	
#  c1, u1, v1
c1 = (p[1] + p[3])/2
u1 = p[2] - p[4]
v1 = p[3] - p[1]

#  ob2 の copy object を ob1 上に配置	
scene.create_part('s = ' + str(s))
ob3 = scene.active_shape()
ob3.disclosed = False

mx1 = matrix(ob2.world_to_local_matrix)
mx2 = matrix(ob2.local_to_world_matrix)

ss = s - int(s)
for i in range(num) :
	t = tL.pop()
	k = int(t)
	if k == len(bPs) :
		k += -1
		t = k + 1
	c2 = labo.bezier_surface_position(bPs[k], ss, t - k)
	u2 = labo.bezier_surface_u_tangent_2(bPs[k], ss, t - k)
	v2 = labo.bezier_surface_v_tangent_2(bPs[k], ss, t - k)
	Q = labo.attitude_control_quaternion(u1, v1, u2, v2)
	M = Q.matrix()
	M1 = matrix().translate(-c1[0], -c1[1], -c1[2])
	M2 = matrix().translate(c2[0], c2[1], c2[2])
	M = mx2*M1* M*M2*mx1
	ob2.copy_object(M)
	ob4 = ob2.bro
	ob4.place_child(1)
	
scene.active_shapes = [ob2, ob3]