Shade3D 公式

36 Cap( 3/3 ) [ Shade Labo ]


#1

関連記事:
  • 34 Cap( 1/3 )
  • 35 Cap( 2/3 )

## 36 - 1  膨らみ
Cap 形状にさらなるバリエーションを持たせるために、**膨らみ** に係わるオプションを追加します。

これは Cap の根元に scaling した端面線形状を挿入追加するもので、高さ は挿入位置、接続 は挿入断面と端面との間の Cap 根元の接続部分の形状を指定します。


          [ fig 36 - 1 ]

          [ fig 36 - 2 ]


#2

36 - 2  接続部分の形状


接続部の形状は **接続** で指定し、次の選択肢があります。
  • スムーズ
  • 円弧
  • 角度指定

     

     [ fig 36 - 3 ]




**スムーズ** 指定の場合の handle 長さの求め方は次の通り

     

     [ fig 36 - 4 ]




**円弧** 指定の場合の handle 長さの求め方は次の通り

     

     [ fig 36 - 5 ]




**角度指定** の場合の handle 長さの求め方は次の通り

     

     

     [ fig 36 - 6 ]


#3

36 - 3  挿入断面を平坦化しない


default では挿入断面は中心線ベクトルの方向に平坦化されて追加されますが、この平坦化を off にするオプションです。

          [ fig 36 - 7 ]


#4

36 - 4  使用例


     

     [ fig 36 - 8 ]


#5

36 - 4  Script


[ script 36 - 1 ]

Tubu 状の自由曲面を選択して実行します。( 複数選択可 )

U 方向が開いて Tube 形状になっていなくても機能します。


import labo
from vec3 import *
from matrix import *
from quaternion import *


#  open_dialog() as list
#
#	 入力 dialog を開く

def open_dialog() :
	#  dialog uuid は Online GUID Generator により生成		https://www.guidgenerator.com
	dialog = xshade.create_dialog_with_uuid('73800238-db37-4961-b66f-c1fb12221705')

	dialog.begin_box(False)
	
	dialog.begin_group('対象')			#  target
	dialog.begin_box(False)
	idx_1a = dialog.append_bool('始端')		#  start
	idx_1b = dialog.append_bool('終端')		#  end
	idx_1c = dialog.append_bool('original を残す')		#  make copy shape
	dialog.end_box()
	dialog.end_group()

	dialog.begin_group('先端の接平面')							#   tangential plane
	idx_1d = dialog.append_selection('/端面に平行/中心軸に垂直')	# orthogonal to axis / parallel to terminal plane
	dialog.end_group()
	
	dialog.end_box()


	dialog.begin_group('高さ')			#  height
	dialog.begin_box(False)

	
	idx_2a = dialog.append_float('丸み', '')	#  round
	idx_2b = dialog.append_float('尖り', '')	#  sharp
	dialog.set_value(idx_2a , 100)

	unit_id = scene.unit
	if unit_id == 0 :
		unit = 'mm'
	elif unit_id == 1 :
		unit = 'cm'
	elif unit_id == 2 :
		unit = 'm'
	elif unit_id == 3 :
		unit = 'km'
	elif unit_id == 4 :
		unit = 'inch'
	elif unit_id == 5 :
		unit = 'foot'
	elif unit_id == 6 :
		unit = 'yard'
	elif unit_id == 7 :
		unit = 'mile'
	idx_2c = dialog.append_selection('単位/平均半径比 ( % )/冠球高さ比 ( % )/' + unit)		#  unit / spherical ratio / current unit
	dialog.end_box()

	idx_2d = dialog.append_bool('ねじれに尖り高さを考慮')			#  twist including sharp height
	dialog.end_group()


	dialog.begin_group('丸み')											#   round
	dialog.begin_box(False)
	idx_3a = dialog.append_float('軸')										#  axis
	dialog.set_value(idx_3a , 0.5)
	idx_3b = dialog.append_float('半径', '       ( 0 〜 1  標準値 = 0.5 )')	#  radius
	dialog.set_value(idx_3b , 0.5)
	dialog.end_box()
	dialog.end_group()
	

	dialog.begin_group('膨らみ')									#   blow
	dialog.begin_box(False)
	idx_4a = dialog.append_bool('適用')								#  blow flag
	idx_4b = dialog.append_float('Scale', '')						#  blow scale
	dialog.set_value(idx_4b , 1)
	idx_4c = dialog.append_float('高さ', '( 丸み, 尖りと同じ単位 )')	#   blow height
	dialog.set_value(idx_4c , 0)
	dialog.end_box()
	
	dialog.begin_box(False)
	idx_4d = dialog.append_selection('      接続/スムーズ/円弧/角度指定')
	dialog.set_value(idx_4d , 0)
	idx_4e = dialog.append_float('     角度', '  ( -1 〜 1 )')
	dialog.set_value(idx_4e , 0)
	dialog.end_box()
	
	dialog.begin_box(False)
	idx_4f = dialog.append_bool('挿入断面を平坦化しない')				#  none_flattening
	dialog.end_box()
	dialog.end_group()
	




	if not dialog.ask('Cap'):
		return None
		
	else :
		target_s =  dialog.get_value(idx_1a)		#	始端に作成
		target_e =  dialog.get_value(idx_1b)		#	終端に作成
		if not target_s and not target_e :
			print ''
			print 'cap 作成場所を指定して下さい'
			print ''
			return None
		
		copy_shape = dialog.get_value(idx_1c)		#	original を残す指定 flag
		tangential_p =  dialog.get_value(idx_1d)	#	接平面の選択 flag
			
		round_h =  dialog.get_value(idx_2a)			#	丸み高さ
		sharp_h =  dialog.get_value(idx_2b)			#	尖り高さ
		unit =  dialog.get_value(idx_2c)			#	高さ指定の単位
		option=  dialog.get_value(idx_2d)			#	ねじれに尖り高さを考慮する flag
		
		round_a =  dialog.get_value(idx_3a)			#	軸方向丸み
		round_r =  dialog.get_value(idx_3b)			#	半径方向丸み
		round_a = min(1, max(0, round_a))
		round_r = min(1, max(0, round_r))
		
		blow = dialog.get_value(idx_4a)				#	膨らみ flag
		blow_s = dialog.get_value(idx_4b)			#	膨らみ scale
		blow_s = max(0, blow_s)
		blow_h = dialog.get_value(idx_4c)			#	膨らみ高さ
		if not blow :
			blow_h = 0
		transition = dialog.get_value(idx_4d)		# 	cap 接続形態
		tilt = dialog.get_value(idx_4e)				#	接続角度指標
		tilt = min(1, max(-1, tilt))
		if (blow_s == 1) or (transition !=2) :
			tilt = 0
		flattening = not dialog.get_value(idx_4f)	#	挿入断面平坦化 flag

		
		return [target_s, target_e, copy_shape, tangential_p, round_h, sharp_h, unit, option, round_a, round_r, blow, blow_s, blow_h, transition, tilt, flattening]





#  sphere_height(bZs as matrix list, c as vec3, vc as vec3) as float
#
#	冠球高さを返す
#
#	bZs :	交差線形状の bezier data
#	c :		端面中心座標
#	vc :	中心線ベクトル

def sphere_height(bZs, c, vc) :

	h = 0
	for bz in bZs :
		p = bz[0]
		vr = p - c
		if vr.abs2() != 0 :
			q = c + vc*(vc.dot(vr))
			u = p - q
			r = u.abs()
			vt = - labo.bezier_line_tangent(bz, 0)
			if vt.abs2() == 0 :
				cosT = 1
				sinT = 0
			else :
				cosT = vc.dot(vt)	
				sinT = (max(0, 1 - cosT**2))**0.5
			
			if vt.dot(u) > 0 :
				sinT *= -1
			
			h += r*(1 - sinT)/cosT
				
	h /= len(bZs)
			
	return h		
		
		
	
		
#  oval_arch(p0 as vec3, p1 as vec3, p2 as vec3, out_coeff as float, in_coeff as float) as vec3, vec3
#
#	3点の座標 p0, p1, p2 から 楕円弧を表す bezier 曲線の handle 座標を返す
#	out_coeff, in_coeff :		outhandle, inhandle 長さの係数

def oval_arch(p0, p1, p2, out_coeff, in_coeff) :
	v1 = p1 - p0
	v2 = p1 - p2
	L1 = v1.norm()
	L2 = v2.norm()
	
	if L1 == 0 or L2 == 0 :		#  p0, p1 あるいは p1, p2 が同一座標
		return p0, p2
	else :
		switch = (L2 > L1)
		if switch :				#  L2 > L1 ならば、v1, v2 と L1, L2 を入れ替える
			v1, v2 = v2, v1
			L1, L2 = L2, L1

	t = L2/L1
	cosT = v1.dot(v2)
	bb = (1 - t*cosT)**2
	if bb == 0 :				#  p0, p2 が同一点
		return p0, p2
	else :		
		B = 1 + (1 - t**2*cosT**2)/bb
		
	ratio = (4./3)*(1/(1 + B**0.5))
	h1 = ratio*L1				#  楕円弧のhandle 長さ
	h2 = ratio*L2				#  楕円弧のhandle 長さ
	
	if switch :					#  互いに入れ替えた v1, v2 と L1, L2 を元に戻す
		v1, v2 = v2, v1
		L1, L2 = L2, L1
		h1, h2 = h2, h1			#  h1, h2 を入れ替える
	
	
	#  out_coeff / in_coeff による h1, h2 の調整
	if out_coeff < 0.5 :
		h1 *= 2*out_coeff
	elif out_coeff > 0.5 :
		h1 += (L1 - h1)*2*(out_coeff - 0.5)
	
	if in_coeff < 0.5 :
		h2 *= 2*in_coeff
	elif in_coeff > 0.5 :
		h2 += (L2 - h2)*2*(in_coeff - 0.5)
	
	outH = p0 + h1*v1			#  handle 座標
	inH = p2 + h2*v2			#  handle 座標
	
	
	return outH, inH
	
	
	
	
#  oval_arch_2(p1 as vec3, v1 as vec, p2 as vec3, v2 as vec3, out_coeff as float, in_coeff as float) as vec3, vec3
#
#	2直線 ( 点 p1/p2 を通り v1/v2 に進む直線 ) から 一般化された楕円弧を表す bezier 曲線の handle 座標を返す
#	v1, v2 は単位ベクトル
#	out_coeff, in_coeff :		outhandle, inhandle 長さの係数

def oval_arch_2(p1, v1, p2, v2, out_coeff, in_coeff) :
	#  L1, L2 :	2直線の最接近点までの p1, p2 からの符合つき距離
	s = v1.dot(v2)	
	u = p2 - p1
	s1 = v1.dot(u)
	s2 = -v2.dot(u)
	
	ss = 1 - s**2
	if ss < 1e-8 :					#  2 直線が平行
		return None, None
	else :
		L1 = (s2*s + s1)/ss
		L2 = (s1*s + s2)/ss
		if (L1 < 0) or (L2 < 0) :	#  handle の向きが逆転する
			return None, None

	switch = (L2 > L1)
	if switch :						#  v1, v2 と L1, L2 を入れ替える
		v1, v2 = v2, v1
		L1, L2 = L2, L1

	t = L2/L1
	cosT = v1.dot(v2)	
	
	B = 1 + (1 - t**2*cosT**2)/(1 - t*cosT)**2	#  分母 != 0  既に 2 直線の平行判定が行われている
		
	ratio = (4./3)*(1/(1 + B**0.5))
	h1 = ratio*L1			#  楕円弧のhandle 長さ
	h2 = ratio*L2			#  楕円弧のhandle 長さ
	
	if switch :				#  互いに入れ替えた v1, v2 と L1, L2 を元に戻す
		v1, v2 = v2, v1
		L1, L2 = L2, L1
		h1, h2 = h2, h1		#  h1, h2 を入れ替える
	
	
	#  out_coeff / in_coeff による h1, h2 の調整
	if out_coeff < 0.5 :
		h1 *= 2*out_coeff
	elif out_coeff > 0.5 :
		h1 += (L1 - h1)*2*(out_coeff - 0.5)
	
	if in_coeff < 0.5 :
		h2 *= 2*in_coeff
	elif in_coeff > 0.5 :
		h2 += (L2 - h2)*2*(in_coeff - 0.5)
	
	outH = p1 + h1*v1		#  handle 座標
	inH = p2 + h2*v2		#  handle 座標
	
	
	return outH, inH
	

	
		

#   get_basic_data(ob as xshade.shape, closed as boole, tangential_p as int, unit as int, round_h as float, sharp_h as float, blow_h as float, flattening as bool) as data list
#
#	基本諸量を返す

def get_basic_data(ob, closed, tangential_p, unit, round_h, sharp_h, blow_h, flattening) :

	#  引数
	#	ob :			U, V を逆転した tube 状自由曲面
	#  	closed :		端面の開閉 flag
	#	tangential_p :	接平面の選択 flag		( 入力 dialog 指定 )
	#	unit :			高さ指定の単位		( 入力 dialog 指定 )
	#	round_h :		丸み高さ 			( 入力 dialog 指定 )
	#	sharp_h :		尖り高さ 			( 入力 dialog 指定 )
	#	blow_h :		膨らみ高さ			( 入力 dialog 指定 )
	#	flattening :	挿入断面平坦化 flag	( 入力 dialog 指定 )

	#  返り値
	#  	pL :			端面ポイント座標 list
	#  	vL :			交差線接線ベクトル list
	#  	vc :			中心線 ベクトル
	#  	c :				端面中心座標
	#	vf :			端面面法線
	#  	vp :			先端接平面の面法線
	#  	r :				端面平均半径
	#  	bb :			端面 boundig box size / 16
	#  	hr, hs :		丸み高さ, 尖り高さ
	#  	hb :			膨らみ高さ
	#	is_flat :		端面平坦性 flag
	
	
	vc = vec3(0, 0, 0)				#  中心線 ベクトル
	pL = []							#  端面ポイント座標 list
	vL = []							#  交差線接線ベクトル list
	bZs = []						#  交差線形状の bezier data list
									#	冠球高さ比指定による高さを求める際に使用
	
	obj = ob.son
	while obj.has_bro :
		obj = obj.bro
		obj.activate()
		bz = labo.get_bezier(xshade, 0)
		bZs.append(bz)
		vt = - labo.bezier_line_tangent(bz, 0)	#  符合を逆向きに
		vc  = vc + vt	
		pL.append(bz[0])
		vL.append(vt)
		
		
	#  vc :	中心線 ベクトル
	if vc.norm() < 1e-5 :			#  この条件に引っかかるのは以下のケース
		ob.switch()					#	端面が重線
		return None					#  	交差線の接線ベクトルがはぼ平面上に存在
		
		
	#  c :	端面中心座標
	c = labo.vec3_mean(pL)	 
	rr = 0
	for p in pL :
		v = p - c
		rr += v.abs()
	if rr == 0 :
		ob.switch()
		return None					#  端面が一点収束で cap を設けられない
		
	
	#  vf :	端面面法線
	if closed :
		pL.append(pL[0])
		
	n = len(pL) - 1
	vf = vec3(0, 0, 0)
	v2 = pL[0] - c
	for i in range(n) :
		v1 = v2
		v2 = pL[i + 1] - c
		v = v1*v2
		if v.abs2() > v1.abs2()*v2.abs2()*1e-8 :		#  面積の二乗で判定
			vf = vf + v
		
	if vf.norm() == 0 :	
		vf = vc
	elif vc.dot(vf) < 0 :
		vf = -vf

	if closed :
		pL.pop()
		
		
	#  is_flat :	端面平坦性 flag
	if  not flattening :				#   挿入断面平坦化指定なし
		is_flat = True
	else :								#   挿入断面平坦化指定
		is_flat = True
		for p in pL :
			v = p - c
			v.norm()
			if abs(vf.dot(v)) > 0.0001 :
				is_flat = False
				break
	
		
	#  vp :	先端接平面の面法線
	if tangential_p == 1 :			#  中心軸に平行	
		vp = vc
	else :							#  端面に平行
		vp = vf
			
			
	#  r :	端面平均半径
	r = 0
	for p in pL :
		d = vp.dot(p - c)
		pp = p - d*vp				#  pp : 	c を通り 面法線 vp なる面に、p を投影した座標
		v = (pp - c)
		r += v.abs()
	r /= len(pL)
	

	#  bb :		端面 boundig box size / 16
	box = labo.vec3_bounding_box_size(pL)
	bb = box[3]/16 
	if bb == 0 :						#  端面開口部が閉じている
		return None
		
	
	#  r check
	if r/bb < 1e-4 :					#  端面開口部が閉じているとみなす
		return None
		
	
	#  hr, hs, hb :  丸み高さ, 尖り高さ, 膨らみ高さ
	if unit == 2 :						#  current unit 指定
		utnu = scene.user_to_native_unit
		hr, hs, hb = utnu*round_h, utnu*sharp_h, utnu*blow_h
		
	elif unit == 0 :					#  平均半径比指定
		hr, hs, hb = r*round_h/100, r*sharp_h/100, r*blow_h/100
		
	else :								#  冠球高さ比指定
		sh = sphere_height(bZs, c, vc)
		hr, hs, hb = sh*round_h/100, sh*sharp_h/100, sh*blow_h/100	

 	
	
	return [pL, vL, vc, c, vf, vp, r, bb, hr, hs, hb, is_flat]
	


	
#  insert_section(ob as xshade.shape, c as vec3, vc as vec3, vp as vec3, vf as vec3, hr as float, hs as float, blow as bool, blow_s as float, hb as float, flattening as bool, tangential_p as int, pL as vec3 list, mx1 as matrix, mx2 as matrix) as vec3, vec3 list
#
#	断面形状を追加挿入する ( 交差 handle はそのまま )
#	膨らみ指定の場合は、挿入断面の中心座標とポイント座標 list を返す
#	膨らみ指定がない場合は None, None を返す
#
#	ob :			tube 状自由曲面
#	c :				端面中心座標
#	vc :			中心線ベクトル
#  	vp :			先端接平面の面法線
#	vf :			端面面法線
#	hr, hs :		丸み高さ, 尖り高さ
#	blow :			膨らみ flag
#	blow_s :		膨らみ scale
#	hb :			膨らみ高さ
#	flattening :	挿入断面平坦化 flag
#	tangential_p :	接平面の選択 flag
#	pL :			端面ポイント座標 list
#	mx1 :			world to local matrix
#	mx2 :			local to world matrix

def insert_section(ob, c, vc, vp, vf, hr, hs, blow, blow_s, hb, flattening, tangential_p, pL, mx1, mx2) :
	from math import acos
	
	#  断面追加
	ob.switch()
	
	#  cap 頂点断面追加
	obj = ob.son.bro							#  端面 line
	obj.activate()
	obj.copy_object(None)						#  端面 line を copy
	obj.activate()
	vb = vc*(hr + hs + hb)						#  移動量ベクトル
	scene.degenerate()							#  端面 line を一点収束 = cap 頂点
	Mt = matrix().translate(vb[0], vb[1], vb[2])
	M = mx2*Mt*mx1
	obj.move_object(M)
	
	
	#  膨らみ指定による断面追加
	if not blow :
		c2 = None
		pL2 = None
		
	else :
		ob1 = scene.active_shape().bro			#  端面 line
		ob1.copy_object(None)					#  端面 line を copy
		vb = vc*hb								#  移動量ベクトル
	
		if not flattening :						#  挿入断面を平坦化しない
			Mt1 = matrix().translate(-c[0], -c[1], -c[2])
			Ms = matrix().scale(blow_s, blow_s, blow_s)
			Mt2 = matrix().translate(c[0] + vb[0], c[1] + vb[1], c[2] + vb[2])
			M = Mt1*Ms*Mt2
			
		else :									#  挿入断面を平坦化
			Mt1 = matrix().translate(-c[0], -c[1], -c[2])
			
			if tangential_p == 1 :				#  cap 先端説平面の面法線 = 中心線ベクトル	( vp = vc )
				Q1 = quaternion().slerp(vc, vec3(0, 1, 0))
			else :								#  cap 先端説平面の面法線 = 端面面法線	( vp = vf )
				Q1 = labo.attitude_control_quaternion(vc, vf, vec3(0, 1, 0), vec3(1, 0, 0), 0)
			Q2 = Q1.inverse()
			Mq1 = Q1.matrix()
			Mq2 = Q2.matrix()
			Ms  = matrix().scale(blow_s, 0, blow_s)
			Mt2 = matrix().translate(c[0] + vb[0], c[1] + vb[1], c[2] + vb[2])
			
			if tangential_p == 1 :				#  cap 先端説平面の面法線 = 中心線ベクトル	( vp = vc )
				Mx = matrix(4)					#  4 x 4 の単位 matrix
			else :								#  cap 先端説平面の面法線 = 端面面法線	( vp = vf )
				cosT = vc.dot(vf)
				Ms2 = matrix().scale(1/cosT, 1, 1)
				Mr = matrix().rotate_z(-acos(cosT))
				Mx = Ms2*Mr

			M = Mt1*Mq1*Ms*Mx*Mq2*Mt2

		ob1.move_object(mx2*M*mx1)		

		
		vb = vc*hb								#  移動量ベクトル
		c2 = c + vb								#  挿入断面中心座標
		
		if not flattening :						#  挿入断面を平坦化しない
			pL2 = [p + vb for p in pL]			#  挿入断面ポイント座標 list
			n = len(pL2)
			for i in range(n) :				
				pL2[i] = pL2[i] + (blow_s - 1)*(pL2[i] - c2)
			
		else :									#  挿入断面を平坦化
			pL2 = [vec3(p) for p in pL]			#  挿入断面ポイント座標 list
			M.transform(pL2)    
	
	ob.switch()
	
	return c2, pL2
	
	
	
	
#  set_smooth_handle(ob as xshade.shape, vc as vec3, hb as float, vL, pL as vec3 list, pL2 as vec3 list, flattening as bool, mx1 as matrix)
#
#	 ob の交差方向 handle をセット ( スムーズ接続 )
#
#	ob :			tube 状自由曲面 ( switch されている )
#  	vc :			中心線 ベクトル
#	hb :			膨らみ高さ
#	vL :			交差線接線ベクトル list
#  	pL :			端面ポイント座標 list
#	pL2 : 			膨らみ適用による挿入断面のポイント座標 list
#	flattening :	挿入断面平坦化 flag
#	mx1 :			world to local matrix
	
def set_smooth_handle(ob, vc, hb, vL, pL, pL2, flattening, mx1) :
	obj = ob.son
	k = -1
	while obj.has_bro :
		obj = obj.bro
		k += 1
		
		#  p0, p2, vt
		p0 = pL2[k]										#  ポイント座標 ( world 座標 )
		p2 = pL[k]										#  ポイント座標 ( world 座標 )
		vt = vL[k]										#  交差線接線ベクトル
		
		#  h :  handle 長さ
		if not flattening :								#  挿入断面平坦化なし
			h = hb/2
		else :											#  挿入断面平坦化指定
			v = p0 - p2
			h = vc.dot(v)/2						
		
		#  handle セット
		inH = (p2 + vt*h)*mx1							#  lateral inhandle 座標  ( local 座標 )
		obj.control_point(2).in_handle = inH			#  lateral inhandle 座標をセット
		obj.control_point(2).linked = True				#  lateral linked をセット

		outH = (p0 - vt*h)*mx1							#  lateral outhandle 座標  ( local 座標 )
		obj.control_point(1).out_handle = outH			#  lateral outhandle 座標をセット
		
		
		
	
#  set_arch_handle(ob as xshade.shape, vc as vec3, hb as float, vL as vec3 list, pL as vec3 list, pL2 as vec3 list, mx1 as matrix)
#
#	 ob の交差方向 handle をセット ( 円弧接続 )
#
#	ob :		tube 状自由曲面 ( switch されている )
#	vc :		中心線ベクトル
#	hb :		膨らみ高さ
#	vL :		交差線接線ベクトル list
#  	pL :		端面ポイント座標 list
#	pL2 : 		膨らみ適用による挿入断面のポイント座標 list
#	mx1 :		world to local matrix

def set_arch_handle(ob, vc, hb, vL, pL, pL2, mx1) :
	obj = ob.son
	k = -1
	while obj.has_bro :
		obj = obj.bro
		k += 1
		
		#  p0, p2, vt
		p0 = pL2[k]										#  ポイント座標 ( world 座標 )
		p2 = pL[k]										#  ポイント座標 ( world 座標 )
		vt = vL[k]										#  交差線接線ベクトル
		
		#  inH, outH :	inhandle / outhandle 座標
		v = p0 - p2
		r = v.norm()/2
		cosT = v.dot(vt)
		
		if abs(cosT) >= 1e-4 :
			h = r/cosT
			if hb >= 0 : 
				if vc.dot(v) >= -1e-4 :
					p1 = p0 - h*vt
				else :
					p1 = p2 - h*vt    	
			else :
				if vc.dot(v) >= -1e-4 :
					p1 = p2 + h*vt
				else :
					p1 = p0 + h*vt
					
			outH, inH = oval_arch(p0, p1, p2, 0.5, 0.5)	#  out /in handle 座標 ( world 座標 )
			
		else :
			if hb >= 0 :									#  hb の符合で判定
				outH = p0 - vt*4./3*r
				inH = p2 - vt*4./3*r
			else :
				outH = p0 + vt*4./3*r
				inH = p2 + vt*4./3*r	
		
		#  handle セット
		obj.control_point(2).in_handle = inH*mx1
		obj.control_point(1).out_handle = outH*mx1
			
		#  link セット
		v1 = vt
		v2 = inH - p2
		v1.norm()
		v2.norm()
		v= v1*v2
		obj.control_point(2).linked = (v.abs2() < 1e-8)
			
			
			
				
#  set_transition_handle(ob as xshade.shape, vc as vec3, hb as float, tilt as float, vf as vec3, vL, pL as vec3 list, pL2 as vec3 list, flattening as bool, mx1 as matrix)
#
#	 ob の交差方向 handle をセット ( 角度指定による楕円弧接続 )
#
#	ob :			tube 状自由曲面 ( switch されている )
#	vc :			中心線ベクトル
#	hb :			膨らみ高さ
#	tilt :			傾き
#	vf :			端面面法線
#	vL :			交差線接線ベクトル list
#  	pL :			端面ポイント座標 list
#	pL2 : 			膨らみ適用による挿入断面のポイント座標 list
#	flattening :	挿入断面平坦化 flag
#	mx1 :			world to local matrix


def set_transition_handle(ob, vc, hb, tilt, vf, vL, pL, pL2, flattening, mx1) :
	obj = ob.son
	k = -1
	while obj.has_bro :
		obj = obj.bro
		k += 1
		
		#  p0, p2, vt
		p0 = pL2[k]										#  ポイント座標 ( world 座標 )
		p2 = pL[k]										#  ポイント座標 ( world 座標 )
		vt = vL[k]										#  交差線接線ベクトル
		
		if tilt == 0 :
			obj.control_point(2).in_handle = obj.control_point(2).position
			obj.control_point(1).out_handle = obj.control_point(1).position
			
		else :
			#  u
			v = p0 - p2
			if flattening :								#  挿入断面平坦化指定
				u = vt*vc.dot(v)/vc.dot(vt)
			else :										#  挿入断面平坦化なし
				u = vt*vf.dot(v)/vf.dot(vt)

			#  p1
			if tilt > 0 :
				p1 = p0 - u*tilt
			else :
				p1 = p2 + u
				p1 = p0*(1 + tilt) - p1*tilt
				
			#  handle セット
			outH, inH = oval_arch(p0, p1, p2, 0.5, 0.5)	#  out /in handle 座標 ( world 座標 )
			obj.control_point(2).in_handle = inH*mx1
			obj.control_point(1).out_handle = outH*mx1
			
			#  link  セット
			v1 = vL[k]
			v2 = inH - p2
			v1.norm()
			v2.norm()
			v= v1*v2
			obj.control_point(2).linked = (v.abs2() < 1e-8)
			

	
	
#  make_cap(ob as shape, data as list) as shape
#
#	ob : 対象となる自由曲面

def make_cap(ob, data) :
	from math import pi, asin
	
	#  入力データ
	#	tangential_p :	接平面の選択 flag
	#	round_h :		丸み高さ
	#	sharp_h :		尖り高さ
	#	unit :			高さ指定の単位
	#	option :		ねじれに尖り高さを考慮する flag
	#	round_a :		軸方向丸み
	#	round_r :		半径方向丸み
	#	blow :			膨らみ flag
	#	blow_s :		膨らみ scale
	#	blow_h :		膨らみ高さ
	#  	transition :	cap 接続形態
	#	tilt :			接続角度指標
	#	flattening :	挿入断面平坦化 flag
	
	tangential_p, round_h, sharp_h, unit, option, round_a, round_r, blow, blow_s, blow_h, transition, tilt, flattening = data
	

	
	#  基本諸量を求める
	#  	pL :			端面ポイント座標 list
	#  	vL :			交差線接線ベクトル list
	#  	vc :			中心線 ベクトル
	#  	c :				端面中心座標
	#	vf :			端面面法線
	#  	vp :			先端接平面の面法線
	#  	r :				端面平均半径
	#  	bb :			端面 boundig box size / 16
	#  	hr, hs :		丸み高さ, 尖り高さ
	#  	hb :			膨らみ高さ
	#	is_flat :		端面平坦性 flag
	
	closed = ob.closed				#  端面の開閉 flag
	ob.switch()
	obj = ob.son.bro
	mx1 = matrix(obj.world_to_local_matrix)
	mx2 = matrix(obj.local_to_world_matrix)
	
	data = get_basic_data(ob, closed, tangential_p, unit, round_h, sharp_h, blow_h, flattening)
	if data == None :
		return None
	else :
		[pL, vL, vc, c, vf, vp, r, bb, hr, hs, hb, is_flat] = data

		#  flatteening 平坦化指定 の再定義 ( 無駄な計算を省くため )
		if flattening :
			if vp.dot(vf) >= 0.9999 :				#  先端接平面の面法線 と 端面面法線が同一 
				flattening = not is_flat	
		else :
			flattening = not is_flat
					

			
	#  断面追加 ( cap 頂点と、 膨らみ適用による断面挿入	handle はそのまま )
	#	c2 :	膨らみ適用による挿入断面の中心座標
	#	pL2 : 	膨らみ適用による挿入断面のポイント座標 list
	#	膨らみ適用がない場合は c2, pL2 = None	
	c2, pL2 = insert_section(ob, c, vc, vp, vf, hr, hs, blow, blow_s, hb, flattening, tangential_p, pL, mx1, mx2)

	
	
	#  膨らみ適用時の cap 接合部 handle 調整
	if blow :
		if transition == 0 :						#  smooth 接合
			set_smooth_handle(ob, vc, hb, vL, pL, pL2, flattening, mx1)
		elif transition == 1 :						#  円弧接続
			set_arch_handle(ob, vc, hb, vL, pL, pL2, mx1)
		else :										#  角度指定
			set_transition_handle(ob, vc, hb, tilt, vf, vL, pL, pL2, flattening, mx1)
	


	
	#  変数
	#	q :			p1 を求める際に使用される接平面の基準点
	#				option = False ならば、q =  c + 丸み高さ
	#				option = True ならば、 q = c + 丸み高さ + 尖り高さ
	#  	p0 :		cap 先端座標
	#  	p1 :		先端接平面と交差線接線ベクトルとの交点
	#  	p2 :		端面ポイント座標
	#	u1 :		cap 先端から伸びる outhandle の方向
	#	u2 :		端面ポイントから伸びる inhendle の方向
	#	outH :		cap 先端から伸びる outhandle 座標
	#	inH :		端面ポイントから伸びる inhendle 座標
	
	
	#  膨らみ適用の場合、c, pL の書き換え	
	if blow :
		c = c2
		pL = pL2
		
		
	#  q :	p1 を求める際に使用される接平面の基準点
	if not option :
		q = c + hr*vc				#  丸み高さのみによる cap 先端座標
	else :							#  ねじれに尖り高さを考慮する場合
		q = c + (hr + hs)*vc		#  丸み高さ + 尖り高さ による cap 先端座標
		

	#  cap handle 調整	途中の計算は world 座標
	obj = ob.son
	pL.reverse()
	vL.reverse()
	
	while obj.has_bro :
		result = False
		obj = obj.bro
		p2 = pL.pop()				#  端面ポイント座標
		vt = vL.pop()				#  交差線接線ベクトル ( 逆向き )
		b = option					#  ねじれに尖り高さを考慮する option
		qq = q						#  p1 を求める際に使用される接平面の基準点
									#  ねじれに尖り高さを考慮する場合、拡張楕円弧が求められなかったなら
									#  qq は 書き換えられて while loop の先頭に戻り、再計算される
									#  ただし、通常の形状で拡張楕円弧が求められないことは、ほとんどありえない
		
		while not result :
			#  p1		先端接平面と交差線接線ベクトルとの交点
			d1 = vp.dot(qq - p2)			#  点 p2 と 先端接平面 との符号付き距離
			d2 = vp.dot(qq - p2 - bb*vt)	#  点 p2 + bb*vt と 先端接平面 との符号付き距離
			d = bb*d1/(d1 - d2)				#  点 p2 と、交差線接線ベクトル と 先端接平面 との交点までの 符号付き距離
			p1 = p2 + d*vt					#  先端接平面と交差線接線ベクトルとの交点			

			#  outH, inH :	handle 座標		
			if not b :						#  ねじれに尖り高さを考慮しない場合
				p0 = qq + hs*vc			#  cap 先端座標 ( 尖り高さを含む )	
				outH, inH = oval_arch(p0, p1, p2, round_r, round_a)			#  cap handle 座標
				result = True				#  exit while roop
			
			else :							#  ねじれに尖り高さを考慮する場合
				p0 = qq						#  cap 先端座標 ( qq には尖り高さが含まれている )
				u1 = p1 - hs*vc - p0
				u1.norm()					#  cap 先端から伸びる outhandle の方向
				u2 = p1 - p2
				u2.norm()					#  端面ポイントから伸びる inhendle の方向	
				outH, inH = oval_arch_2(p0, u1, p2, u2, round_r, round_a)	#  cap handle 座標
				
				result = (outH != None)
				if not result :				#  拡張楕円弧が求められなかったなら、ねじれに尖り高さを考慮する option を off にして、やり直し
					qq = c + hr*vc			#  丸み高さのみによる cap 先端座標
					b = False				#  ねじれに尖り高さを考慮する option を off に切り替え
		
		
		 #  cap handle セット
		obj.control_point(0).out_handle = outH*mx1
		obj.control_point(1).in_handle = inH*mx1
		linked = (not blow) or (transition != 2) or (tilt > 0)
		obj.control_point(1).linked = linked
		
		
		
	ob.switch()			
	return ob


	
	
	
scene = xshade.scene()

data = open_dialog()			#  入力 dialog を開く
if data != None :
	target_s = data[0]
	target_e = data[1]
	copy_shape = data[2]
	del data[2]
	del data[1]
	del data[0]

	scene.exit_modify_mode()
	obL = scene.active_shapes
	objL = []
	for ob in obL :
		if isinstance(ob, xshade.part) and ob.part_type == 1 :
			if not ob.surface_closed and ob.number_of_sons >= 2 :
				obj = ob.son.bro
				if obj.number_of_control_points >= 2 :
					if not copy_shape :
						ob0 = ob
					else :						#  original を残す が指定
						ob.copy_object(None)
						ob0 = ob.bro
						
					obj = None
					if target_s :
						ob1 = make_cap(ob0, data)
						if ob1 != None :
							obj = ob1
					if target_e :
						ob0.switch()
						ob0.reverse()
						ob0.switch()
						ob2 = make_cap(ob0, data)
						if ob2 != None :
							obj = ob2
						ob0.switch()
						ob0.reverse()
						ob0.switch()
						
					if obj !=None :				#  始端 / 終端で cap が追加された
						objL.append(obj)
					else :						#  cap は作られなかった
						if copy_shape :
							ob0.remove()
			
	if len(objL) > 0 :
		scene.active_shapes = objL