Shade3D 公式

24 掃引(3/3)[ Shade Labo ]


#1

**2016 / 04 / 18**    [ script 24 - 2 ], [ script 24 - 3 ] 修正   重点コーナーをサポート

2016 / 05 / 17    [ script 24 - 2 ], [ script 24 - 3 ] 修正   quaternion に係わる修正

2016 / 05 / 18    [ script 24 - 2 ], [ script 24 - 3 ] 修正   corner 判定に係わる修正

2016 / 06 / 14    [ script 24 - 2 ], [ script 24 - 3 ] 追加   v2_prev に係わる記述を追加

                         修正内容は 29 掃引の断面配置への追加事項 参照


関連記事:

  • 22 掃引 ( 1/3 )
  • 23 掃引 ( 2/3 )

24 - 1  コーナーでの断面配置


**23 掃引( 2/3 )** で、掃引体の交差方向線形状の handle 長さの求め方まで述べました。

最後に、コーナー部分での断面配置に方法について述べます。


Shade では 三次元的に屈曲するコーナーでの断面配置 が不思議な振る舞いをします。

[ script 24 - 1 ] はその検証用 script で、その結果を下図に示します。



**1)二次元コーナー + 二次元曲がり**     問題なし

          [ fig 24 - 1 ]



**2)二次元コーナー + 三次元曲がり**      終端部の断面が **中心線接線ベクトルの方向を向かない**

          [ fig 24 - 2 ]



**3)三次元コーナー + 三次元曲がり**     コーナー部分の **断面が傾く**

          [ fig 2 - 3 ]



このような仕様がどのような意図によるものかは不明ですが、これを再現しようとするとコードが余計に複雑化してしまいます。

シミュレーション script の作成に当たっては、これらの再現は行わず、下図のような 好ましいと考えられる配置 を採用します。



**2)二次元コーナー + 三次元曲がり**

          [ fig 24 - 4 ]



**3)三次元コーナー + 三次元曲がり**

          [ fig 24 - 5 ]


[ script 24 - 1 ]
scene = xshade.scene()
scene.exit_modify_mode()

scene.create_part()
ob1 = scene.active_shape()
scene.begin_creating()
scene.begin_line('中心線', False)
scene.append_point([-10000, 0, 0], None, [-10000, 4000, 0], None, None)
scene.append_point([0, 10000, 0], [-5000, 7500, 0], [5000, 7500, 0], None, None)
scene.append_point([10000, 0, 0], [10000, 4000, 0], None, None, None)
scene.end_line()
scene.end_creating()
scene.memory()

scene.begin_creating()
scene.create_line(None, [[-12000, 0, 1000], [-8000, 0, 1000],[ -8000, 0, -1000], [-12000, 0, -1000]], True)
scene.end_creating()
scene.sweep()
ob = scene.active_shape().dad
ob.disclosed = False
ob1.activate()


scene.create_part()
ob2 = scene.active_shape()
scene.begin_creating()
scene.begin_line('中心線', False)
scene.append_point([-10000, 0, 0], None, [-10000, 4000, 0], None, None)
scene.append_point([0, 10000, 0], [-5000, 7500, 0], [5000, 7500, 0], None, None)
scene.append_point([10000, 0, -10000], [10000, 4000, -10000], None, None, None)
scene.end_line()
scene.end_creating()
scene.memory()

scene.begin_creating()
scene.create_line(None, [[-12000, 0, 1000], [-8000, 0, 1000],[ -8000, 0, -1000], [-12000, 0, -1000]], True)
scene.end_creating()
scene.sweep()
ob = scene.active_shape().dad
ob.disclosed = False
ob2.activate()


scene.create_part()
ob3 = scene.active_shape()
scene.begin_creating()
scene.begin_line('中心線', False)
scene.append_point([-10000, 0, 0], None, [-10000, 4000, 0], None, None)
scene.append_point([0, 10000, 0], [-5000, 7500, 0], [5000, 7500, -5000], None, None)
scene.append_point([10000, 0, -10000], [10000, 4000, -10000], None, None, None)
scene.end_line()
scene.end_creating()
scene.memory()

scene.begin_creating()
scene.create_line(None, [[-12000, 0, 1000], [-8000, 0, 1000],[ -8000, 0, -1000], [-12000, 0, -1000]], True)
scene.end_creating()
scene.sweep()
ob = scene.active_shape().dad
ob.disclosed = False


scene.active_shapes = [ob1, ob2, ob3]

#2

24 - 2  コーナー判定


掃引中心線の anchor point **P** がコーナーになっているか否かの判定は次によります。
          [ fig 24 - 6 ]

#3

24 - 3  コーナーでの断面配置


**2016 / 04 / 1**8    [ script 24 - 2 ] 修正   重点コーナーをサポート
コーナーでの断面配置は次のようになります。

鋭角コーナーでは掃引断面の scale 値が非常に大きくなるので、一定の鋭角以上では scale 値を 1 に制限しています。


          [ fig 24 - 7 ]


          [ fig 24 - 8 ]
[ script 24 - 2 ] は コーナーでの配置処理の組み込みによって書き直した script の部分を抜粋したものです。
[ script 24 - 2 ]

2016 / 04 / 18 修正       重点コーナーをサポート

2016 / 05 / 17 修正       quaternion に係わる修正

2016 / 05 / 18 修正       corner 判定に係わる修正

2016 / 06 / 14 追加       v2_prev に係わる記述を追加

	#  掃引断面を配置
	p1 = bZ[0][0]
	Mt1 = matrix().translate(-p1[0], -p1[1], -p1[2])
	Q1 = quaternion()
	v2_prev = None
	n = nop - 1
		
	for i in range(n) :
		p2 = bZ[i][3]
		v2 = labo.bezier_line_tangent(bZ[i], 1)
		Mt2 = matrix().translate(p2[0], p2[1], p2[2])
		Q2 = sweep_section_quaternion(bZ[i])
		Q2 = Q2*Q1
		Mr = Q2.matrix()
		
		if i == n - 1 and not closed :
			M = mx2*Mt1*Mr*Mt2*mx1
		
		else :
			v3 = labo.bezier_line_tangent(bZ[i + 1], 0)
			if v3.abs2() == 0 :						#  重点
				M = mx2*Mt1*Mr*Mt2*mx1
				if v2_prev == None :				#  最初の重点
					v2_prev = v2
				
			elif v2.abs2() == 0 :					#  最後の重点
				if v2_prev == None :				#   始点が重点から始まっている		#  16 / 06 / 14 追加
					M = mx2*Mt1*Mr*Mt2*mx1											#  16 / 06 / 14 追加
				else :																#  16 / 06 / 14 追加
					Qc = quaternion().slerp(v2_prev, v3, 1, False)	#  16 / 05 / 17 修正
					if Qc != None :									#  16 / 05 / 17 修正
						Q2 = Qc*Q2									#  16 / 05 / 17 修正
						Mc = Qc.matrix()							#  16 / 05 / 17 修正
						M = mx2*Mt1*Mr*Mc*Mt2*mx1					#  16 / 05 / 17 修正
					else :											#  16 / 05 / 17 修正
						M = mx2*Mt1*Mr*Mt2*mx1						#  16 / 05 / 17 修正
					v2_prev = None

			else :									#  16 / 05 / 18 修正	
				if abs(v2.dot(v3)) >= 0.99999994 :	#  掃引中心線の point が corner point  ではない		#  16 / 05 / 18 修正
					M = mx2*Mt1*Mr*Mt2*mx1
				
				else :								#  掃引中心線の point が corner point 
					w = v2 + v3
					w.norm()
					Q3 = quaternion().slerp(v2, w)
					Mq = Q3.matrix()
					
					if v2.dot(v3) <= -0.98 :		#  鋭角 corner
						M = mx2*Mt1*Mr*Mq*Mt2*mx1	
					else :							#  鋭角コーナー以外
						v = v2 - v3
						cosT = w.dot(v2)
						Q4 = quaternion().slerp(v, vec3(0, 1, 0))
						Mqs1 = Q4.matrix()
						Q5 = Q4.inverse()
						Mqs2 = Q5.matrix()
						Ms = matrix().scale(1, 1/cosT, 1)
						
						M = mx2*Mt1*Mr*Mq*Mqs1*Ms*Mqs2*Mt2*mx1
				
					Q6 = quaternion().slerp(v2, v3)
					Q2 = Q6*Q2						#   次の point での掃引断面配置に必要

	
		ob2.copy_object(M)
		ob = ob2.bro
		ob.place_brother(i)
	
		Q1 = Q2

#4

24 - 4  Script


以上より、掃引シミュレーション script の完成品を[ script 24 - 3 ] に記します。
[ script 24 - 3 ]

2016 / 04 / 18 修正       重点コーナーをサポート

2016 / 05 / 17 修正       quaternion に係わる修正 ( 修正内容は [ script 24 - 2 ] 参照 )

2016 / 05 / 18 修正       corner 判定に係わる修正 ( 修正内容は [ script 24 - 2 ] 参照 )

2016 / 06 / 14 追加       v2_prev に係わる記述を追加 ( 修正内容は 29 掃引の断面配置への追加事項 参照 )


import labo
from matrix import *
from quaternion import *

		

#  sweep_section_quaternion(bz as vec3 matrix ,atrix) as quaternion
#
#	bezier data bz から掃引時の断面形状の姿勢を与える quaternion を返す

def sweep_section_quaternion(bz) :
	from math import acos
	
	v1 = labo.bezier_line_tangent(bz, 0)		#  outhandle 側接線ベクトル
	v2 = labo.bezier_line_tangent(bz, 1)		#  inhandle 側接線ベクトル
	vn = bz[3] - bz[0]							#   始端と終端を結ぶ vector
	u1 = vn*v1
	u2 = vn*v2
	vn.norm()
	u1.norm()
	u2.norm()
	cosA = u1.dot(u2)							#  4つの bezier control point の平坦性を表す角度の cos値						
	cosB = v1.dot(v2)							#  両端の接線ベクトルの交差角の cos値
	
	#  flat : 4 つの bezier control point の平坦性 flag
	if (u1.abs2() == 0) or (u2.abs2() == 0)  :
		flat = True
	elif abs(cosA) >= 0.98  :
		flat = True
	else :
		flat = False

	# pi_rotaion :  両端の接線ベクトルの回転角が180度に近い
	if (v1.abs2() == 0) and (v2.abs2() == 0) :	#  handle の出ていない重点						
		pi_rotation = False
	elif cosB >= -0.9 :
		pi_rotation = False
	else :
		pi_rotation = True
		

	if flat :									#  4 つの bezier control point  が平坦
		if not pi_rotation :					#  両端の接線ベクトルの回転角が180度に近くはない
			if (v1.abs2() == 0) and (v2.abs2() == 0) :	#  handle の出ていない重点
				return quaternion()
			else :
				if v1.dot(v2) >= 0.99999994 :
					return quaternion()
				else :
					return quaternion().slerp(v1, v2)
		
		else :								#  両端の接線ベクトルの回転角が180度に近い
			if vn.abs2() == 0 :				#  重点
				return quaternion()
					
			elif abs(vn.dot(v1)) >= 0.99999994 or abs(vn.dot(v2)) >= 0.99999994 :		#  handle が point 間に平行
				if v1.dot(v2) > -0.99999994 :
					return quaternion().slerp(v1, v2)
				else :
					return quaternion()		#  この特殊ケースのみ Shade の断面配置を再現できない
					
			else :
				v = v2 - v1 
				u = u1 + u2
				axis = u*v
				if axis.abs2() <1e-24 :
					axis = v*vn
                
				phi = acos(u1.dot(-u2))/2
				if (u1*u2).dot(vn) < 0 :
					phi = -phi
				Q = quaternion().rotation(v, phi)			
				axis = Q*axis
				axis.norm()					#  掃引断面の quaternion 回転の回転軸	
				
				w1 = axis*v1
				w2 = axis*v2

				return quaternion().slerp(w1, w2)
			
	
	else :									#  4 つの bezier control point  が平坦でない
		t1 = 0
		v1 = labo.bezier_line_tangent(bz, t1)
		Q = quaternion()
		for i in range(16) :				#  bezier parameter を16等分して細かく回転を16回繰り返す
			t2 = float(i + 1)/16
			v2 = labo.bezier_line_tangent(bz, t2)
			Q = quaternion().slerp(v1, v2)*Q
			t1 = t2
			v1 = v2
	
		return Q			
	

#  get_phi(psi as float) as float
#
#	psi から線形補間で補正角 phi を返す

def get_phi(psi) :
	from math import pi

	x = [None for i in range(14)]
	y = [None for i in range(14)]
	x[0] = 0.
	y[0] = 0.
	x[1] = pi*15./180
	y[1] = 0.0182718
	x[2] = pi*30./180
	y[2] = 0.0387312
	x[3] = pi*60./180
	y[3] = 0.0741119
	x[4] = pi*90./180
	y[4] = 0.1021102
	x[5] = pi*120./180
	y[5] = 0.1167868
	x[6] = pi*126./180
	y[6] = 0.1172989
	x[7] = pi*130./180
	y[7] = 0.1170475
	x[8] = 150*pi/180
	y[8] = 0.1064218
	x[9] = 165*pi/180
	y[9] = 0.0842835
	x[10] = 170*pi/180
	y[10] = 0.067609
	x[11] = 175*pi/180
	y[11] = 0.051760
	x[12] = 177.5*pi/180
	y[12] = 0.031373
	x[13] = pi
	y[13] = 0.
	
	for i in range(12, -1, -1) :
		if x[i] <= psi :
			return y[i] + (y[i + 1] - y[i])*(psi - x[i])/(x[i + 1] - x[i])
			
			
				
#  sweep_plane_normal(p0 as vec3, p1 as vec3, p2 as vec3) as vec3
#
#	掃引中心線の bezier data bz (p0, p1, p2) から、掃引体交差方向の handle 長さを求めるための面法線を返す
#	基準となる anchor point に handle  が出ていることが前提
#
#		p0, p1, p2 :		outhandle 側を求める場合は		bz[0], bz[1], bz[2]
#							inhandle 側を求める場合は		bz[3], bz[2], bz[1]

def sweep_plane_normal(p0, p1, p2) :
	from math import pi, acos
	
	v1 = p1 - p0					#  v1.abs() != 0 が前提
	v2 = p2 - p1
	v1.norm()
	v2.norm()
	
	if v2.abs2() == 0 :
		return v1
	else :
		a = v1.dot(v2)
		if abs(a) >= 0.99999994 :
			return v1
			
		elif a > 0 :
			vn = v1 + v2
			axis = v1*v2
			theta = acos(-v1.dot(v2))
			psi = 2*theta - pi
			phi = get_phi(psi)			#  psi から線形補間で補正角 phi を返す
			Q = quaternion().rotation(axis, phi)
			vp = Q*vn
			
		else :
			axis = v1*v2
			Q = quaternion().rotation(axis, pi/4)
			vp = Q*v1
			
		vp.norm()	
		return vp
	
	
	
#  sweep_handle(w as vec3, u as vec3,  Q as vec3, r as float, p as vec3, vp as vec3) as vec3
#
#	handle 座標を返す

def sweep_handle(w, u, Q, r, p, vp) :
	#     L :  点 w を通り、u 方向に進む直線
	#	PL0 : 点 Q を通り面法線 u なる面
	#	PL1 : 点 p を通り面法線 vp なる面
	#	精度確保のため r = 1 とはせず、r = bounding box size / 16 としてある

	a = u.dot(w - Q)			#  点 w と面 PL0 との符号付き距離		
	d1 = vp.dot(w - p)			#  点 w と 面 PL1 との符号付き距離
	d2 = vp.dot(w + r*u - p)	#  点 w + r*u と 面 PL1 との符号付き距離
	d = r*d1/(d1 - d2)			#  点 w と、直線 L と 面 PL1 との交点までの 符号付き距離
	d = d + a					#  handle の符号つき長さ
	if d < 0 :					#  食い込みが発生する
		d = -d/12
	return w + d*u
		


#  sweep(ob1 as shape, ob2 as shape) as shape
#
#	掃引体を Shade 上に作成し、その shape を返す
#		ob1 :	掃引中心線形状
#		ob2 :	掃引断面形状	

def sweep(ob1, ob2) :
	if (ob1 == None) or (ob2 == None) :
		return
	if (not isinstance(ob1, xshade.line)) or (not isinstance(ob2, xshade.line)) :
		return

	#  掃引中心線データ取得 nop, closed, bZ
	ob1.activate()
	nop = ob1.number_of_control_points		#  ポイント数
	if nop < 2 :
		return
	closed = ob1.closed						#  開閉情報
	bZ = labo.get_bezier(xshade, -1)		#  全区間の bezier data [matrix, matrix, … ]
	
	
	# mx1, mx2, mx22
	mx1 = matrix(ob2.world_to_local_matrix)
	mx2 = matrix(ob2.local_to_world_matrix)
	if not closed :
		mx22 = matrix(ob1.local_to_world_matrix)
		
		
	#  掃引中心線 の handle 情報取得
	has_in_handle = []			#  掃引中心線の inhandle の有無のリスト
	has_out_handle = []			#  掃引中心線の outhandle の有無のリスト
	linked = []					#  掃引中心線の hanndle link リスト
	for i in range(nop) :
		has_in_handle.append(ob1.control_point(i).has_in_handle)
		has_out_handle.append(ob1.control_point(i).has_out_handle)
		linked.append(ob1.control_point(i).linked)

	
	#  ob0 : 掃引体を格納する自由曲面
	ob2.activate()
	scene.create_surface_part(None)
	ob0 = scene.active_shape()				#   掃引体を格納する自由曲面
	ob0.surface_closed = closed
	ob2.place_child(1)

	
	#  掃引断面を配置
	p1 = bZ[0][0]
	Mt1 = matrix().translate(-p1[0], -p1[1], -p1[2])
	Q1 = quaternion()
	v2_prev = None
	n = nop - 1
		
	for i in range(n) :
		p2 = bZ[i][3]
		v2 = labo.bezier_line_tangent(bZ[i], 1)
		Mt2 = matrix().translate(p2[0], p2[1], p2[2])
		Q2 = sweep_section_quaternion(bZ[i])
		Q2 = Q2*Q1
		Mr = Q2.matrix()
		
		if i == n - 1 and not closed :
			M = mx2*Mt1*Mr*Mt2*mx1
		
		else :
			v3 = labo.bezier_line_tangent(bZ[i + 1], 0)
			if v3.abs2() == 0 :						#  重点
				M = mx2*Mt1*Mr*Mt2*mx1
				if v2_prev == None :				#  最初の重点
					v2_prev = v2
				
			elif v2.abs2() == 0 :					#  最後の重点
				if v2_prev == None :				#  始点が重点から始まっている		#  16 / 06 / 14 追加
					M = mx2*Mt1*Mr*Mt2*mx1											#  16 / 06 / 14 追加
				else :																#  16 / 06 / 14 追加
					Qc = quaternion().slerp(v2_prev, v3, 1, False)	#  16 / 05 / 17 修正
					if Qc != None :									#  16 / 05 / 17 修正
						Q2 = Qc*Q2									#  16 / 05 / 17 修正
						Mc = Qc.matrix()							#  16 / 05 / 17 修正
						M = mx2*Mt1*Mr*Mc*Mt2*mx1					#  16 / 05 / 17 修正
					else :											#  16 / 05 / 17 修正
						M = mx2*Mt1*Mr*Mt2*mx1						#  16 / 05 / 17 修正
					v2_prev = None

			else :									#  16 / 05 / 18 修正	
				if abs(v2.dot(v3)) >= 0.99999994 :	#  掃引中心線の point が corner point  ではない		#  16 / 05 / 18 修正
					M = mx2*Mt1*Mr*Mt2*mx1
				
				else :								#  掃引中心線の point が corner point 
					w = v2 + v3
					w.norm()
					Q3 = quaternion().slerp(v2, w)
					Mq = Q3.matrix()
					
					if v2.dot(v3) <= -0.98 :		#  鋭角 corner
						M = mx2*Mt1*Mr*Mq*Mt2*mx1	
					else :							#  鋭角コーナー以外
						v = v2 - v3
						cosT = w.dot(v2)
						Q4 = quaternion().slerp(v, vec3(0, 1, 0))
						Mqs1 = Q4.matrix()
						Q5 = Q4.inverse()
						Mqs2 = Q5.matrix()
						Ms = matrix().scale(1, 1/cosT, 1)
						
						M = mx2*Mt1*Mr*Mq*Mqs1*Ms*Mqs2*Mt2*mx1
				
					Q6 = quaternion().slerp(v2, v3)
					Q2 = Q6*Q2						#   次の point での掃引断面配置に必要

	
		ob2.copy_object(M)
		ob = ob2.bro
		ob.place_brother(i)
	
		Q1 = Q2

	
	#  掃引体の交差方向 handle をセット
	bb = labo.vec3_bounding_box_size(bZ)
	r = bb[3]/16												#  sweep_handle( ) の引数に使用
	
	n = ob2.number_of_control_points
	ob0.switch()
	ob = ob0.son
	for i in range(n) :
		ob = ob.bro												#  掃引体内部の交差方向線形状
		
		for j in range(nop) :
			w = vec3(ob.control_point(j).position)*mx2			#  交差方向線形状の point 座標
			
			if not has_in_handle[j] :
				inH = w											#  inhandle 座標
			else :
				if (j == 0) and not closed :
					p = vec3(ob1.control_point(0).in_handle)*mx22
					inH = w + p - bZ[0][0]						#  inhandle 座標
				else :
					if j == 0 :
						k = nop - 1
					else :
						k = j - 1
					vp = sweep_plane_normal(bZ[k][3], bZ[k][2], bZ[k][1])
					u = bZ[k][2] - bZ[k][3] 
					u.norm()
					p = bZ[k][2]
					Q = bZ[k][3]
					inH = sweep_handle(w, u, Q, r, p, vp)		#  inhandle 座標
					
	
			if not has_out_handle[j] :
				outH = w										#  outhandle 座標
			else :
				if (j == nop - 1) and not closed :
					p = vec3(ob1.control_point(nop - 1).out_handle)*mx22
					outH = w + p - bZ[nop - 2][3]				#  outhandle 座標
				else :
					vp = sweep_plane_normal(bZ[j][0], bZ[j][1], bZ[j][2])
					u = bZ[j][1] - bZ[j][0] 
					u.norm()
					p = bZ[j][1]
					Q = bZ[j][0]
					outH = sweep_handle(w, u, Q, r, p, vp)		#  outhandle 座標
						

			ob.control_point(j).in_handle = inH*mx1
			ob.control_point(j).out_handle = outH*mx1					
			ob.control_point(j).linked = linked[j]
	
		
	ob0.switch()
	ob0.activate()
	ob0.disclosed = False
	
	return ob0
	

	
	
#  Shade  上で 掃引中心線 , 掃引断面 の順で選択して実行

scene = xshade.scene()

scene.exit_modify_mode()
[ob1, ob2] = scene.active_shapes

#  Shade original sweep
ob1.activate()
scene.memory()
ob2.activate()
ob2.copy_object(None)
ob2.bro.activate()
scene.sweep()
ob3 = scene.active_shape().dad
ob3.activate()
ob3.name = 'Shade sweep'
ob3.disclosed = False


#  simulated sweep
ob2.copy_object(None)
ob4 = ob2.bro
ob5 = sweep(ob1, ob4)
ob5.name = 'simulated'

scene.active_shapes = [ob3, ob5]

#5

24 - 5  コーナーにおける留意点


Shade 掃引によるコーナー部分での断面配置に関して留意点を記します。
**< 形状 >**
比較的掃引断面が大きい場合、コーナー部の掃引形状には太さの不均一性が顕著になります。

[ script 24 - 4 ] はそのサンプルです。

custom 掃引 で、形状の改善を提案します。

          [ fig 24 - 9 ]


[ script 24 - 4 ]
scene = xshade.scene()
scene.exit_modify_mode()


scene.begin_creating()
scene.begin_line('中心線', False)
scene.append_point([-8000, 0, 0], None, [-8000, 5000, 0], None, None)
scene.append_point([0, 15000, 0], [-5000, 12000, 0], [5000, 12000, 0], None, None)
scene.append_point([8000, 0, 0], [8000, 5000, 0], None, None, None)
scene.end_line()
scene.end_creating()
scene.memory()

scene.begin_creating()
scene.create_line(None, [[-13000, 0, 0], [-8000, 0, 0], [-3000, 0, 0]], False)
scene.end_creating()
scene.sweep()
ob1 = scene.active_shape().dad
ob1.disclosed = False
ob1.activate()

scene.begin_creating()
scene.create_line(None, [[-15000, 0, 0], [-8000, 0, 0], [-1000, 0, 0]], False)
scene.end_creating()
scene.sweep()
ob2 = scene.active_shape().dad
ob2.disclosed = False


scene.active_shapes = [ob1, ob2]


**< scale 制限 >**
[ fig 24 - 8 ] で示したように、極端な鋭角コーナーでは掃引断面の scale 値が大きくなるので、limitter を設けていますが、一定の鋭角以上では scale 値を 1 にする という仕様なので、handle の向きの変化に対して大きな掃引形状のギャップが生じてしまいます。

また、かなりの鋭角まで limitter が働かないので、下図のような曲線コーナーで掃引断面が大きい場合、かなり歪な掃引形状を与えてしまいます。


[ script 24 - 5 ] はそのサンプルです。

custom 掃引 で、limitter の定義を変更し、limitter 値をユーザー指定できるような提案をします。


          [ fig 24 - 10 ]


[ script 24 - 5 ]
scene = xshade.scene()
scene.exit_modify_mode()


scene.begin_creating()
scene.begin_line('中心線', False)
scene.append_point([-10000, 0, 0], None, [-5000, 0, 0], None, None)
scene.append_point([0, 5000, 0], [-1500, 2500, 0], [1500, 2500, 0], None, None)
scene.append_point([10000, 0, 0], [5000, 0, 0], None, None, None)
scene.end_line()
scene.end_creating()
scene.memory()

scene.begin_creating()
scene.create_line(None, [[-10000, 2500, 0], [-10000, 0, 0], [-10000, -2500, 0]], False)
scene.end_creating()
scene.sweep()
ob1 = scene.active_shape().dad
ob1.disclosed = False
ob1.activate()


scene.begin_creating()
scene.begin_line('中心線', False)
scene.append_point([-10000, 0, 0], None, [-5000, 0, 0], None, None)
scene.append_point([0, 5000, 0], [-500, 2500, 0], [500, 2500, 0], None, None)
scene.append_point([10000, 0, 0], [5000, 0, 0], None, None, None)
scene.end_line()
scene.end_creating()
scene.memory()

scene.begin_creating()
scene.create_line(None, [[-10000, 2500, 0], [-10000, 0, 0], [-10000, -2500, 0]], False)
scene.end_creating()
scene.sweep()
ob2 = scene.active_shape().dad
ob2.disclosed = False
ob2.activate()


scene.begin_creating()
scene.begin_line('中心線', False)
scene.append_point([-10000, 0, 0], None, [-5000, 0, 0], None, None)
scene.append_point([0, 5000, 0], [-200, 2500, 0], [200, 2500, 0], None, None)
scene.append_point([10000, 0, 0], [5000, 0, 0], None, None, None)
scene.end_line()
scene.end_creating()
scene.memory()

scene.begin_creating()
scene.create_line(None, [[-10000, 2500, 0], [-10000, 0, 0], [-10000, -2500, 0]], False)
scene.end_creating()
scene.sweep()
ob3 = scene.active_shape().dad
ob3.disclosed = False


scene.active_shapes = [ob1, ob2, ob3]