Shade3D 公式

35 Cap( 2/3 ) [ Shade Labo ]


#1

35 - 1  User Interface


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

Cap を作成する script の user interface を次のように定めます。
          [ fig 35 - 1 ]

#2

35 - 2  対象 / 先端の接平面


**< 対象 >**
Cap を設ける場所を **始端** , **終端** から指定

この script では undo が効かないので、original を残す オプションを追加しています。



**< 先端の接平面 >**
接平面を **端面に平行** , **中心軸に垂直** から選択

          [ fig 35 - 2 ]


#3

35 - 3  高さ


**< 高さ >**
**丸み高さ** は折れ線のコーナー **P1** 座乗を求める際に用いられる 中心点 **C** から先端接平面までの距離

尖り高さ は 丸み高さで与えられた接平面の高さに追加される高さで、先端座標 P0 座標が決定される

          [ fig 35 - 3 ]


#4

35 - 4  高さの単位


**平均半径比** , **冠球高さ比** , **current unit** から選択

冠球高さ比 は元形状にテーパーがついている場合に意味を持ち、その意味と求め方は下図の通り

     

     [ fig 35 - 4 ]


     
          [ fig 35 - 5 ]


#5

35 - 5  ねじれに尖り高さを考慮


このオプションは、cap を被せる tube 側の交差線形状が中心線に対して **ねじれ** がある場合に、丸み高さと尖り高さの両方が指定された時に意味を持ちます。

ねじれに尖り高さを考慮する場合の楕円弧は 34 Cap ( 1/2 ) の [ script 34 - 2 ] に記した 拡張楕円弧 になります。

     

     [ fig 35 - 6 ]


          [ fig 35 - 7 ]


          [ fig 35 - 8 ]


#6

35 - 6  丸み


軸方向, 半径方向の丸みを制御し、**円弧 / 楕円弧 になるときの handle 長さを 0.5** とし、**0.5 ~ 1** で handle 長さを長く、**0.5 ~ 0** で短くします。

     

 [ fig 35 - 9 ]


#7

35 - 7  Sample


[ script 35 - 1 ] ~ [ script 35 - 7 ] に sample 形状を出力する script を記します。

[ script 35 - 1 ]      
from vec3 import *
from math import pi, sin, cos


scene = xshade.scene()
scene.exit_modify_mode()
scene.lathe_rotation = [1, 0, 0, 0]

scale = 5000
n = 6

beta = pi/n
r = scale*4/3.*(1 - cos(beta))/sin(beta)


#  中心線
scene.create_surface_part('sample - 1')
ob = scene.active_shape()
scene.begin_creating()
scene.begin_line(None, True)
for i in range(n) :
	theta = pi/2 + 2*i*pi/n
	x = scale*cos(theta)
	y = scale*sin(theta)
	p = vec3(x, y, 0)
	
	phi = -pi + 2*i*pi/n
	v = vec3(cos(phi), sin(phi), 0)
	inH = p - r*v
	outH = p + r*v
	
	scene.append_point(p, inH, outH, None, None)
scene.end_line()
scene.move_object(None, None, (-90, 0, 0), None)
scene.copy_object(None, None, None, [0, scale, 0])
scene.end_creating()
	
ob.disclosed = False
ob.activate()
ob.switch()
scene.smooth()
ob.switch()


[ script 35 - 2 ]      
from vec3 import *
from math import pi, sin, cos


scene = xshade.scene()
scene.exit_modify_mode()
scene.lathe_rotation = [1, 0, 0, 0]

scale = 5000
n = 6

beta = pi/n
r = scale*4/3.*(1 - cos(beta))/sin(beta)


#  中心線
scene.create_surface_part('sample - 2')
ob = scene.active_shape()
scene.begin_creating()
scene.begin_line(None, True)
for i in range(n) :
	theta = pi/2 + 2*i*pi/n
	x = scale*cos(theta)
	y = scale*sin(theta)
	p = vec3(x, y, 0)
	
	phi = -pi + 2*i*pi/n
	v = vec3(cos(phi), sin(phi), 0)
	inH = p - r*v
	outH = p + r*v
	
	scene.append_point(p, inH, outH, None, None)
scene.end_line()
scene.move_object(None, None, (-90, 0, 0), None)
scene.copy_object(None, [1, 1,2/3**0.5], [30, 0, 0], [0, 2*scale, 0])

scene.end_creating()
	
ob.disclosed = False
ob.activate()
ob.switch()
scene.smooth()
ob.switch()


[ script 35 - 3 ]      
from vec3 import *
from matrix import *
from math import pi, sin, cos


scene = xshade.scene()
scene.exit_modify_mode()
scene.lathe_rotation = [1, 0, 0, 0]

scale = 5000
n = 6

beta = pi/n
r = 1.5*scale*4/3.*(1 - cos(beta))/sin(beta)


#  中心線
scene.create_surface_part('sample - 3')
ob = scene.active_shape()
scene.begin_creating()
scene.begin_line(None, True)
for i in range(n) :
	theta = pi/2 + 2*i*pi/n
	x = 1.5*scale*cos(theta)
	y = 1.5*scale*sin(theta)
	p = vec3(x, y, 0)
	
	phi = -pi + 2*i*pi/n
	v = vec3(cos(phi), sin(phi), 0)
	inH = p - r*v
	outH = p + r*v
	
	scene.append_point(p, inH, outH, None, None)
scene.end_line()
scene.move_object(None, None, (-90, 0, 0), None)
scene.copy_object(None, None, None, (0, scale, 0))
scene.end_creating()


obj = scene.active_shape()
Mt1 = matrix().translate(0, -scale, 0)
Ms = matrix().scale(0.5, 1, 0.5)
Mt2 = matrix().translate(-scale/2, scale, 0)
M = Mt1*Ms*Mt2
obj.move_object(M)
	
ob.disclosed = False
ob.activate()
ob.switch()
scene.smooth()
ob.switch()


[ script 35 - 4 ]      
from vec3 import *
from matrix import *
from math import pi, sin, cos


scene = xshade.scene()
scene.exit_modify_mode()
scene.lathe_rotation = [1, 0, 0, 0]

scale = 5000
n = 6

beta = pi/n
r = scale*4/3.*(1 - cos(beta))/sin(beta)


#  中心線
scene.create_surface_part('sample - 4')
ob = scene.active_shape()
scene.begin_creating()
scene.begin_line(None, True)
for i in range(n) :
	theta = pi/2 + 2*i*pi/n
	x = scale*cos(theta)
	y = scale*sin(theta)
	p = vec3(x, y, 0)
	
	phi = -pi + 2*i*pi/n
	v = vec3(cos(phi), sin(phi), 0)
	inH = p - r*v
	outH = p + r*v
	
	scene.append_point(p, inH, outH, None, None)
scene.end_line()
scene.move_object(None, None, (-90, 0, 0), None)
scene.copy_object(None, None, None, (0, scale, 0))
scene.end_creating()


obj = scene.active_shape()
Mr = matrix().rotate_y(pi/8)
obj.move_object(Mr)
	
ob.disclosed = False
ob.activate()
ob.switch()
scene.smooth()
ob.switch()


[ script 35 - 5 ]      
from vec3 import *
from matrix import *
from math import pi, sin, cos


scene = xshade.scene()
scene.exit_modify_mode()
scene.lathe_rotation = [1, 0, 0, 0]

scale = 5000
n = 12

beta = pi/n
r = scale*4/3.*(1 - cos(beta))/sin(beta)


#  中心線
scene.create_surface_part('sample - 5')
ob = scene.active_shape()
scene.begin_creating()
scene.begin_line(None, True)
for i in range(n) :
	theta = pi/2 + 2*i*pi/n
	
	if i//2*2 == i :
		x = 1.5*scale*cos(theta)
		y = 1.5*scale*sin(theta)
	else :
		x = scale*cos(theta)
		y = scale*sin(theta)
	p = vec3(x, y, 0)
	
	phi = -pi + 2*i*pi/n
	v = vec3(cos(phi), sin(phi), 0)
	inH = p - 2*r*v
	outH = p + 2*r*v
	
	scene.append_point(p, inH, outH, None, None)
scene.end_line()
scene.move_object(None, None, (-90, 0, 0), None)
scene.copy_object(None, None, None, (0, scale, 0))
scene.end_creating()

	
ob.disclosed = False
ob.activate()
ob.switch()
scene.smooth()
ob.switch()


[ script 35 - 6 ]      
from vec3 import *
from matrix import *
from math import pi, sin, cos
import random


scene = xshade.scene()
scene.exit_modify_mode()
scene.lathe_rotation = [1, 0, 0, 0]

scale = 5000
n = 12

beta = pi/n
r = scale*4/3.*(1 - cos(beta))/sin(beta)


#  中心線
scene.create_surface_part('sample - 6')
ob = scene.active_shape()
scene.begin_creating()
scene.begin_line(None, True)
for i in range(n) :
	theta = pi/2 + 2*i*pi/n
	
	if i//2*2 == i :
		x = 1.5*scale*cos(theta)
		y = 1.5*scale*sin(theta)
	else :
		x = scale*cos(theta)
		y = scale*sin(theta)
	p = vec3(x , y, 0)
	
	phi = -pi + 2*i*pi/n
	v = vec3(cos(phi), sin(phi), 0)
	inH = p - 2*r*v
	outH = p + 2*r*v
	
	scene.append_point(p, inH, outH, None, None)
scene.end_line()
scene.move_object(None, None, (-90, 0, 0), None)
scene.copy_object(None, None, None, (0, 2*scale, 0))
scene.end_creating()


obj = scene.active_shape()
for i in range(n) :
	y = (random.random()*2 - 1)*scale
	obj.control_point(i).position = vec3(obj.control_point(i).position) + vec3(0, y, 0)
	obj.control_point(i).in_handle = vec3(obj.control_point(i).in_handle) + vec3(0, y, 0)
	obj.control_point(i).out_handle = vec3(obj.control_point(i).out_handle) + vec3(0, y, 0)
	obj.control_point(i).linked = True

	
ob.disclosed = False
ob.activate()
ob.switch()
scene.smooth()
ob.switch()


[ script 35 - 7 ]      
from vec3 import *
from matrix import *
from math import pi, sin, cos


scene = xshade.scene()
scene.exit_modify_mode()
scene.lathe_rotation = [1, 0, 0, 0]

scale = 5000
n = 12

beta = pi/n
r = scale*4/3.*(1 - cos(beta))/sin(beta)


#  中心線
scene.create_surface_part('sample - 7')
ob = scene.active_shape()
scene.begin_creating()
scene.begin_line(None, True)
for i in range(n) :
	theta = pi/2 + 2*i*pi/n
	
	if i//2*2 == i :
		x = 1.5*scale*cos(theta)
		y = 1.5*scale*sin(theta)
	else :
		x = scale*cos(theta)
		y = scale*sin(theta)
	p = vec3(x , y, 0)
	
	phi = -pi + 2*i*pi/n
	v = vec3(cos(phi), sin(phi), 0)
	inH = p - 2*r*v
	outH = p + 2*r*v
	
	scene.append_point(p, inH, outH, None, None)
scene.end_line()
scene.move_object(None, None, (-90, 0, 0), None)

obj = scene.active_shape()
Ms = matrix().scale(0.7, 1, 0.7)
Mr = matrix().rotate_y(pi/3)
Mt = matrix().translate(0, 2*scale, 0)
M = Ms*Mr*Mt
obj.copy_object(M)

Ms = matrix().scale(0.9, 1, 0.9)
Mr = matrix().rotate_y(pi/6)
Mt = matrix().translate(0, scale, 0)
M = Ms*Mr*Mt
obj.copy_object(M)

scene.end_creating()
	
ob.disclosed = False
ob.activate()
ob.switch()
scene.smooth()
ob.switch()

#8

35 - 8  Script


[ script 35 - 8 ]
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('5775fc1c-4540-483b-85ca-6621dfe4850d')

	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()
	


	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))
		
	
		return [target_s, target_e, copy_shape, tangential_p, round_h, sharp_h, unit, option, round_a, round_r]





#  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) as data list
#
#	基本諸量を返す

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

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

	#  返り値
	#  	pL :			端面ポイント座標 list
	#  	vL :			交差線接線ベクトル list
	#  	vc :			中心線 ベクトル
	#  	c :				端面中心座標
	#	vf :			端面面法線
	#  	vp :			先端接平面の面法線
	#  	r :				端面平均半径
	#  	bb :			端面 boundig box size / 16
	#  	hr, hs :		丸み高さ, 尖り高さ
	
	
	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()
	
		
	#  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 :  丸み高さ, 尖り高さ
	if unit == 2 :						#  current unit 指定
		utnu = scene.user_to_native_unit
		hr, hs = utnu*round_h, utnu*sharp_h
		
	elif unit == 0 :					#  平均半径比指定
		hr, hs = r*round_h/100, r*sharp_h/100
		
	else :								#  冠球高さ比指定
		sh = sphere_height(bZs, c, vc)
		hr, hs = sh*round_h/100, sh*sharp_h/100	

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


	
#  insert_section(ob as xshade.shape, c as vec3, vc as vec3, hr as float, hs as float, pL as vec3 list, mx1 as matrix, mx2 as matrix) as vec3, vec3 list
#
#	断面形状を追加挿入する ( 交差 handle はそのまま )
#
#	ob :		tube 状自由曲面
#	c :			端面中心座標
#	vc :		中心線ベクトル
#	hr, hs :	丸み高さ, 尖り高さ
#	pL :		端面ポイント座標 list
#	mx1 :		world to local matrix
#	mx2 :		local to world matrix

def insert_section(ob, c, vc, hr, hs, pL, mx1, mx2) :
	#  断面追加
	ob.switch()
	
	#  cap 頂点断面追加
	obj = ob.son.bro							#  端面 line
	obj.activate()
	obj.copy_object(None)						#  端面 line を copy
	obj.activate()
	vb = vc*(hr + hs)							#  移動量ベクトル
	scene.degenerate()							#  端面 line を一点収束 = cap 頂点
	Mt = matrix().translate(vb[0], vb[1], vb[2])
	M = mx2*Mt*mx1
	obj.move_object(M)
	
	ob.switch()
	

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

def make_cap(ob, data) :
	from math import pi, asin
	
	#  入力データ
	#	round_h :		丸み高さ
	#	sharp_h :		尖り高さ
	#	unit :			高さ指定の単位
	#	option :		ねじれに尖り高さを考慮する flag
	#	round_a :		軸方向丸み
	#	round_r :		半径方向丸み
	
	tangential_p, round_h, sharp_h, unit, option, round_a, round_r = data
	

	
	#  基本諸量を求める
	#  	pL :			端面ポイント座標 list
	#  	vL :			交差線接線ベクトル list
	#  	vc :			中心線 ベクトル
	#  	c :				端面中心座標
	#	vf :			端面面法線
	#  	vp :			先端接平面の面法線
	#  	r :				端面平均半径
	#  	bb :			端面 boundig box size / 16
	#  	hr, hs :		丸み高さ, 尖り高さ
	
	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)
	if data == None :
		return None
	else :
		[pL, vL, vc, c, vf, vp, r, bb, hr, hs] = data
		
		
			
	#  断面追加 ( cap 頂点と、 膨らみ適用による断面挿入	handle はそのまま )
	insert_section(ob, c, vc, hr, hs, pL, mx1, mx2)


	
	#  変数
	#	q :			p1 を求める際に使用される接平面の基準点
	#				option = False ならば、q =  c + 丸み高さ
	#				option = True ならば、 q = c + 丸み高さ + 尖り高さ
	#  	p0 :		cap 先端座標
	#  	p1 :		先端接平面と交差線接線ベクトルとの交点
	#  	p2 :		端面ポイント座標
	#	u1 :		cap 先端から伸びる outhandle の方向
	#	u2 :		端面ポイントから伸びる inhendle の方向
	#	outH :		cap 先端から伸びる outhandle 座標
	#	inH :		端面ポイントから伸びる inhendle 座標
	
		
		
	#  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
		obj.control_point(1).linked = True
		
		
		
	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