Shade3D 公式

32 太さの変わる掃引(3/3 ) [ Shade Labo ]


#1

関連記事:
  • 31 太さの変わる掃引( 1/3 )
  • 32 太さの変わる掃引( 2/3 )

## 33 - 1  Sample
下図は [ script 33 - 1 ] で作成されるサンプルに対して 太さの変わる掃引 c_sweep_x( ) を実行したものです。

          [ fig 33 - 1 ]


[ script 33 - 1 ]
import labo
from vec3 import *


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

scale = 1000


#  中心線
scene.create_disk('中心線', [0, 0, 0], 10*scale, 1)
scene.convert_to_line_object()
ob1 = scene.active_shape()

	
#  断面
scene.create_disk('断面', [0, 0, 10*scale], 5*scale, 0)
scene.convert_to_line_object()
ob2 = scene.active_shape()
ob2.activate()
scene.unsmooth()


#  graph frame
p0 = vec3(5, 20, 0)*scale
p1 = vec3(5, 15, 0)*scale
p2 = vec3(0, 15, 0)*scale
p3 = vec3(0, 20, 0)*scale

scene.begin_creating()
scene.create_line('graph frame',[p0, p1, p2, p3], True)
scene.end_creating()
ob3 = scene.active_shape()


#  graph data
p1 = vec3(0, 16, 0)*scale
outh1 = vec3(1.5, 16, 0)*scale

inh2 = vec3(2, 25, 0)*scale
p2 = vec3(2.5, 25, 0)*scale
outh2 = vec3(3, 25, 0)*scale

inh3 = vec3(3.5, 16, 0)*scale
p3 = vec3(5, 16, 0)*scale

scene.begin_creating()
scene.begin_line('graph data', False)
scene.append_point(p1, None, outh1, None, None)
scene.append_point(p2, inh2, outh2, None, None)
scene.append_point(p3, inh3, None, None, None)
scene.end_line()
scene.end_creating()
ob4 = scene.active_shape()


scene.active_shapes = [ob1, ob2, ob3, ob4]


下図は [ script 33 - 2 ] で作成されるサンプルに対して 太さの変わる水平掃引 h_sweep_x( ) を実行したものです。

          [ fig 33 - 2 ]


[ script 33 - 2 ]
mport labo
from vec3 import *


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

scale = 1000


#  中心線
scene.create_disk('中心線', [0, 0, 0], 10*scale, 1)
scene.convert_to_line_object()
ob1 = scene.active_shape()

	
#  断面
scene.create_disk('断面', [0, 0, 10*scale], 5*scale, 0)
scene.convert_to_line_object()
ob2 = scene.active_shape()
ob2.activate()
scene.unsmooth()

scene.active_shapes = [ob1, ob2]
scene.move_object([0, 0, 0], None, [45, 0, 0], None)
ob2.activate()


#  graph frame
p0 = vec3(5, 20, 0)*scale
p1 = vec3(5, 15, 0)*scale
p2 = vec3(0, 15, 0)*scale
p3 = vec3(0, 20, 0)*scale

scene.begin_creating()
scene.create_line('graph frame',[p0, p1, p2, p3], True)
scene.end_creating()
ob3 = scene.active_shape()


#  graph data
p1 = vec3(0, 16, 0)*scale
outh1 = vec3(1.5, 16, 0)*scale

inh2 = vec3(2, 25, 0)*scale
p2 = vec3(2.5, 25, 0)*scale
outh2 = vec3(3, 25, 0)*scale

inh3 = vec3(3.5, 16, 0)*scale
p3 = vec3(5, 16, 0)*scale

scene.begin_creating()
scene.begin_line('graph data', False)
scene.append_point(p1, None, outh1, None, None)
scene.append_point(p2, inh2, outh2, None, None)
scene.append_point(p3, inh3, None, None, None)
scene.end_line()
scene.end_creating()
ob4 = scene.active_shape()


scene.active_shapes = [ob1, ob2, ob3, ob4]

#2

33 - 2  c_sweep_x( )


太さの変わる掃引 c_sweep_x( ) を [ script 33 - 3 ] に記します。

[ script 33 - 3 ]


c_sweep( ) からの変更や追加部分には行末に ############ を付しています

4つの線形状を選択して実行します。

選択順は次のうちのいずれか

  • 掃引中心線, 掃引断面, graph data, graph frame
  • 掃引中心線, 掃引断面, graph frame, graph data

import labo
from matrix import *
from quaternion import *



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

def sweep_section_quaternion(bz) :
	v1 = labo.bezier_line_tangent(bz, 0)			#  outhandle 側接線ベクトル
	v2 = labo.bezier_line_tangent(bz, 1)			#  inhandle 側接線ベクトル

	if (v1.abs2() == 0) or (v2.abs2() == 0) :		#   重点
		return quaternion()
		
	elif abs(v1.dot(v2)) >= 0.99999994 :			#  区間 bz の outhandle と inhandle が平行
		return quaternion()	
		
	else :
		t1 = 0
		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
			
		
			
				
#  sweep_plane_normal(p0 as vec3, p1 as vec3, p2 as vec3, p3 as vec3) as vec3
#
#	掃引中心線の bezier data bz (p0, p1, p2) から、掃引体交差方向の handle 長さを求めるための面法線を返す
#	基準となる anchor point に handle  が出ていることが前提
#
#		p0, p1, p2, p3 :	outhandle 側を求める場合は	bz[0], bz[1], bz[2], bz[3]
#							inhandle 側を求める場合は		bz[3], bz[2], bz[1], bz[0]

def sweep_plane_normal(p0, p1, p2, p3) :
	from math import pi, acos, sin, atan
	
	v1 = p1 - p0					#  v1.abs() != 0 が前提
	v2 = p2 - p1
	v1.norm()
	v2.norm()
	
	if v2.abs2() == 0 :
		v3 = p3 - p1
		v3.norm()
		if v3.abs2() == 0 :
			return v1
		else :
			vn = v1 + v3
			vn.norm()
			vp = v1 + vn
			vp.norm()
			return vp
		
	else :
		a = v1.dot(v2)
		if abs(a) >= 0.99999994 :
			return v1
		
		elif a >= 0 :
			vn = v1 + v2
			vn.norm()
			
			sinA = a
			cosA = (1 - sinA**2)**0.5
			beta = (pi - acos(a))/2
			r = (1 - sinA)/3
			
			axis = v1*v2
			axis.norm()
			v = v1 - v2
			v.norm()
			u = v/sin(beta) + v1*r/cosA
			u.norm()
			phi = acos(u.dot(v))

		else :
			axis = v1*v2
			phi = atan(4./3) - pi/4
			Q = quaternion().rotation(axis, pi/4)
			vn = Q*v1

		v3 = p3 - p2
		v3.norm()
		if v3.abs2() == 0 :
			return vn
		else :
			d = v1.dot(v2)
			w = -(v1-2*d*v2)
			w.norm()
			e = v3.dot(w)
			Q = quaternion().rotation(axis, e*phi)
			vp = Q*vn
			vp.norm()
			return vp

	
	

#####  sweep_handle( ) から変更  #############################################	
	
#  sweep_handle_x(w as vec3, vr as vec3, r as float, p as vec3, vp as vec3) as vec3
#
#	handle 座標を返す

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

	d1 = vp.dot(w - p)			#  点 w と 面 PL1 との符号付き距離
	d2 = vp.dot(w + r*vr - p)	#  点 w + r*vr と 面 PL1 との符号付き距離
	d = r*d1/(d1 - d2)			#  点 w を通り vr 方向に進む直線と、面 PL1 との交点までの 符号付き距離
		
	if d >= 0 :
		if d1 > 0 :				#  点 w が基準面 PL1 の外側にある
			d /= 12	
	else :
		d /= -12			
		
	return w + d*vr
		
########################################################################

			

	
#  bezier_inflection_point( bz as vec3 list) as float list
#
#		bz : bezier control point 座標を格納した vec3 list
#		bezier line bz の屈曲点位置の bezier parameter を返す。
#		屈曲点がない場合は、空 list が返される

def bezier_inflection_point(bz) :
	w1 = (bz[1] - bz[0])*(bz[2] - bz[3])
	w2 = (bz[2] - bz[1])*(bz[3] - bz[0])
	w3 = (bz[2] - bz[0])*(bz[1] - bz[3])
	r1 = w1.norm()
	r2 = w2.norm()
	r3 = w3.norm()
	L = [[r1, w1], [r2, w2], [r3, w3]]
	L.sort()
	vn = L[2][1]
	if vn.norm() == 0 :				#  bz は直線であり、変曲点は存在しない
		return []

	#  bz2 : 原点を通り面法線 vn なる面に bz を投影	
	bz2 = []
	for i in range(4) :
		d = vn.dot(bz[i])
		bz2.append(bz[i] - d*vn)
	
	#  Q : bz2 の階差表現
	bz2 = matrix(False, bz2)
	Md = labo.bezier_d()
	Q = bz2*Md
	
	#  こちらの方法でも可
#	bz2 = matrix(True, bz2)
#	Md = labo.bezier_d()
#	Md.transpose()
#	Q = Md*bz2			

	A = Q[2]*Q[3]
	B = Q[1]*Q[3]
	C = Q[1]*Q[2]
	
	bb = labo.vec3_bounding_box_size(Q)
	bb.pop()
	bb2 = sorted(bb)
	for i in range(3) :
		if bb2[0] == bb[i] :
			a = A[i]
			b = B[i]
			c = C[i]
			t = []
			if a != 0 :
				d = b**2 - 4*a*c
				if d > 0 :
					tt = (-b - d**0.5)/(2*a)
					if tt > 0 and tt < 1 :
						t.append(tt)
					tt = (-b + d**0.5)/(2*a)
					if tt >= 0 and tt <= 1 :
						t.append(tt)
				elif d == 0 :
					tt = -b/(2*a)
					if tt > 0 and tt < 1 :
						t.append(tt)
			elif b != 0 :
				tt = -c/b
				if tt > 0 and tt < 1 :
					t.append(tt)
			
			if len(t) == 1 :
				v1 = labo.bezier_line_tangent(bz, 0)
				v2 = labo.bezier_line_tangent(bz, 1)
				v0 = labo.bezier_line_tangent(bz, t[0])
				if v0.dot(v1) >= 0.9999 or  v0.dot(v2) >= 0.9999 :	#  屈曲点での接線ベクトルが、始端 / 終端での接線ベクトルと
					t.pop()											#  ほとんど同じであれば、その屈曲点では分割しない
					
			elif len(t) == 2 :
				t.sort()	
				v1 = labo.bezier_line_tangent(bz, 0)
				v2 = labo.bezier_line_tangent(bz, 1)
				v0 = labo.bezier_line_tangent(bz, t[1])
				if v0.dot(v2) >= 0.9999 :							#  屈曲点での接線ベクトルが、終端での接線ベクトルと
					del t[1]										#  ほとんど同じであれば、その屈曲点では分割しない
				v0 = labo.bezier_line_tangent(bz, t[0])
				if v0.dot(v1) >= 0.9999 :							#  屈曲点での接線ベクトルが、始端での接線ベクトルと
					del t[0]										#  ほとんど同じであれば、その屈曲点では分割しない
			return t
				
	

#  bezier_divide_by_total_curvature( bz as vec3 list, threshold as float) as float list
#
#		bz : 		bezier control point 座標を格納した vec3 list
#		threshold :	分割しきい値
#
#		bezier line bz を分割する bezier parameter を返す。
#		分割がない場合は、空 list が返される	

def bezier_divide_by_total_curvature(bz, threshold) :
	paramL = []								#  分割点の bezier parameter を格納する list
	
	v1 = labo.bezier_line_tangent(bz, 0)
	if v1.abs2() == 0 :
		return paramL						#  重点による長さのない区間
	
	#  bz に最適分割を行って cL[], pL[] にデータを格納する	
	pL = [0.]								#  各区間を分割する bezer parameter を格納する list
	cL = [0.]								#  各区間までの全曲率を格納する list
	v2 = labo.bezier_line_tangent(bz, 1)
	
	a = v1.dot(v2)
	if abs(a) > 0.99999994 :
		w = bz[3] - bz[0]
		if w.abs2() == 0 :
			return paramL					#  重点とみなす
		elif a < - 0.99999994 :
			w.norm()
			if abs(w.dot(v1)) > 0.99999994 :
				return paramL				#  重点と同じ扱いにする
	
	make_total_curvature_list(bz, 0., v1, 1., v2, pL, cL, 8)	#  最適分割を行って cL[], pL[] にデータを格納する 再帰関数
	theta = cL[len(cL) - 1]										#  bz の全曲率
	
	if theta > threshold :								#  bz の全曲率がしきい値を越えている
		n = int((theta - 0.00001)/threshold) + 1		#  分割数 ( 丸め誤差による分割数の不均一防止のため 0.00001 を減ずる )
		dc = theta/n				
		paramL = [0.]									#  分割点の bezier parameter を格納 (最初は 0. )
		j = 1
		for i in range(1, n) :
			c = i*dc
			while c > cL[j] :
				j += 1
			paramL.append(pL[j] - (pL[j] - pL[j - 1])*(cL[j] - c)/(cL[j ] - cL[j - 1]))	#  分割点の bezier parameter
		paramL.append(1.)														#  分割点の bezier parameter list の最後に 1 を追加
		
	return paramL
	

		
		
#  make_total_curvature_list(bz as vec3 list, t1 as float, v1 as vec3, t2 as float, v2 as vec3, pL as float list, cL as float list, n as int)
#
#		bz の parameter 区間 t1〜t2 を最適サイズに分割して、cL[], pL[] にデータを格納する再帰関数
#
#		bz : 		bezier control point 座標を格納した vec3 list
#		t1, v1 :	計算対象区間の 始端 bezier parameter と その点での接線ベクトル	
#		t2, v2 :	計算対象区間の 終端 bezier parameter と その点での接線ベクトル
#		pL :		各区間を分割する bezer parameter を格納
#		cL :		分割された各区間までの全曲率を格納
#		n :			分割数 ( bezier parameter t1〜t2 を n 分割する )

def make_total_curvature_list(bz, t1, v1, t2, v2, pL, cL, n) :
	from math import acos
	
	u = v1
	s1 = t1
	ds = (t2 - t1)/n								#  parameter  増分
	
	for i in range(1, n + 1) :						#  分割数 n で分割
		s2 = s1 + ds

		if i < n :
			v = labo.bezier_line_tangent(bz, s2)	# parameter t での接線ベクトル
		else :
			v = v2
			
		cosT = max(-1, min(1, u.dot(v)))
		
		if cosT >= 0.99966 :								#  0.99966 = cos(1.5度)
			pL.append(s2)
			cL.append(cL[len(cL) - 1] + acos(cosT))	
		else :
			theta =  acos(cosT)
			m = max(2, 1 + int(theta*180/pi/1.5))					#  分割数
			make_total_curvature_list(bz, s1, u, s2, v, pL, cL, m)	#  再分割
			
		u = v
		s1 = s2
		
		

#####  追加  ########################################################################

#  bezier_abs_derivative( bz as vec3 list, t as float ) as float
#
#	コントロールポイント座標 list bz で与えられる bezier 曲線の
#	パラメータ t における微分ベクトル の絶対値を返す

def bezier_abs_derivative(bz, t) :
	v = -3*(1 - t)**2*bz[0] + 3*(1 - t)*(1 - 3*t)*bz[1] + 3*t*(2 - 3*t)*bz[2] + 3*t**2*bz[3]			
	return v.abs()
	
	
	
		
#  make _x_list(bZs as vec3 list, closed as bool) as float list, float list
#
#	bZs の各区間の長さと、それを規格化した list を返す
#
#	bZs :	掃引中心線の bezier control point 座標を格納した vec3 list
	
def make_x_list(bZs) :
	totalL = 0
	xL = []
	Ls = []
	for bz in bZs :
		if bz[0] != bz[1] or bz[1] != bz[2] or bz[2] != bz[3] :
			r = labo.bezier_simpson(bezier_abs_derivative, bz, 0, 1)	#  parameter 区間 0〜1 で線長を求める
			totalL += r
		else :
			r = 0			#  bz[0] == bz[1] or bz[1] == bz[2] or bz[2] == bz[3] であっても、bezier_simpson( ) で bz の長さは 0 にならない
		xL.append(totalL)
		Ls.append(r)		#  bZs の各区間の長さ list
		
	n = len(xL)
	for i in range(n) :
		xL[i] /= totalL		#  bZs の各区間の長さを規格化した list
		
	return Ls, xL
		

				

#  make_scaleL_Ls(bZs as vec3 list, closed as bool, graph as simple_graph) as float list, float list
#
#	bZs の各 anchor point における グラフ指定による scale 値の list と bZs の各 bezier 区間の長さの list を返す
#	
#	bZs :		掃引中心線の bezier control point 座標を格納した vec3 list
#	closed :	掃引中心線の開閉情報
#	graph :		simple_graph

def make_scaleL_Ls(bZs, closed, graph) :		
	Ls, xL = make_x_list(bZs)			#  Ls : bZs の各区間の長さ list  		
	xL.insert(0, 0.)	
	scaleL = []
	for x in xL :						#  xL : bZs  の各区間の長さを規格化した list
		scale = graph.y(x)
		scaleL.append(scale)			#  scaleL : bZs の各 anchor point におけるグラフ指定 scale 値の list
	if closed :
		scaleL.pop()
		scaleL.append(scaleL[0])
		
	return scaleL, Ls
	
	
	
	
#  handle_length(w as vec3, u as vec3, q as vec3, r as float, p as vec3, vp as vec3, corner as bool) as float
#
#	dummy handle 長さを返す
#
#	w :	断面ポイント座標
#	u :	掃引中心線接線ベクトル
#	q :	掃引中心線 point 座標
#	r :	掃引中心線 bounding box size / 16
#	p :	掃引中心線の handle 座標
#	vp :	基準面法線
#	corner :	corner flag

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

	d1 = vp.dot(w - p)			#  点 w と 面 PL1 との符号付き距離
	d2 = vp.dot(w + r*u - p)	#  点 w + r*u と 面 PL1 との符号付き距離
	d = r*d1/(d1 - d2)			#  点 w を通り u 方向に進む直線と、面 PL1 との交点までの符号つき距離
	
	if corner :
		a = u.dot(w - q)		#  点 w と面 PL0 との符号つき距離	
		if a > 0 :
			d += a				#  d が長くなる場合のみ補正を行う
	
	return abs(d)
	
	

	
				
#  get_vh_A(L1 as float, L2 as float, r0 as float, r1 as float, r2 as float) as vec3
#
#	scaling に伴う交差方向線形状 handle の傾きを表すベクトルを返す
#
#	L1, L2 :		X 方向長さ ( scaling factor は適用済み )
#	r0, r1, r2 :	Y 方向長さ

def get_vh_A(L1, L2, r0, r1, r2) :
	#  3点のポイントの X, Y size を規格化したものに smooth をかけた smooth vector を作り、
	#  その Y size を original の proportion に戻す
	
	#  x0, x1, x2, y0, y1, y2 : 3点のポイントの X, Y size を規格化
	sx = 1/(L1 + L2)
	x0 = 0
	x1 = L1*sx
	x2 = 1
	
	r = (abs(r1 - r0) + abs(r2 - r1))
	if r == 0 :						#  r0 = r1 = r2 = 0 であり、断面ポイント座標と掃引中心線の配置ポイント座標が一致している
		return vec3(1, 0, 0)
	else :
		sy = 1/r
	y0 = 0
	y1 = (r1 - r0)*sy
	y2 = (r2 - r0)*sy
	
	#  v : 規格化された3点の中央の point に smooth をかけて得られる smooth vector
	p0 = vec3(x0, y0, 0)
	p1 = vec3(x1, y1, 0)
	p2 = vec3(x2, y2, 0)

	u0 = p0 - p1
	u2 = p2 - p1
	a0 = u0.norm()
	a2 = u2.norm()
	v = a0*u2 - a2*u0

	#  smooth vector w の Y size を original の proportion に戻す
	vh = vec3(v[0], v[1]*sx/sy, 0)
	vh.norm()
	
	return vh
	
	
	

#  get_vh_B(L1 as float, L2 as float, r0 as float, r1 as float, r2 as float) as vec3
#
#	開いた線形状の terminal 点における scaling に伴う交差方向線形状 handle の傾きを表すベクトルを返す
#
#	L1, L2 :		X 方向長さ ( scaling factor は適用済み )
#	r0, r1, r2 :	Y 方向長さ

def get_vh_B(L1, L2, r0, r1, r2) :
	tan1 = (r1 - r0)/L1
	tan2 = (r2 - r1)/L2
				
	if abs(tan2) < abs(tan1) :
		a = 2
	else :
		a = 3
		
	tan0 = tan1 - (tan2 - tan1)/a
	vh = vec3(1, tan0, 0)
	vh.norm()
	
	return vh

	
#####  追加  ここまで  ###################################################################

	
	
#  c_sweep_x(ob1 as shape, ob2 as shape, graph as simple_graph, threshold as float, limitter1 as float = 10., limitter2 as float = 3.) as shape
#
#	掃引体を Shade 上に作成し、その shape を返す
#		ob1 :		掃引中心線形状
#		ob2 :		掃引断面形状
#		graph :		simple_graph object
#		threshold:	分割しきい値
#		limitter1 :	直線コーナーの scale liitter	
#		limitter2 :	曲線コーナーの scale liitter	

def c_sweep_x(ob1, ob2, graph, threshold, limitter1 = 10., limitter2 = 3.) :
	if (ob1 == None) or (ob2 == None) :
		return
	if (not isinstance(ob1, xshade.line)) or (not isinstance(ob2, xshade.line)) :
		return
		
	#  check						
	if threshold < 0 :					
		return						
	if limitter1 < 1 or limitter2 < 1 :	
		return						
	

	#  掃引中心線データ取得 nop, closed, bZ
	ob1.activate()
	nop = ob1.number_of_control_points	#  ポイント数
	if nop < 2 :
		return
	closed = ob1.closed						#  開閉情報
	bZs = labo.get_bezier(xshade, -1)		#  全区間の bezier data [matrix, matrix, … ]
	
	
	# mx1, mx2, mx22, ps, pe
	mx1 = matrix(ob2.world_to_local_matrix)
	mx2 = matrix(ob2.local_to_world_matrix)
	if not closed :
		mx22 = matrix(ob1.local_to_world_matrix)
		ps = vec3(ob1.control_point(0).in_handle)*mx22			#  掃引体の交差方向 handle 作成時に使用
		pe = vec3(ob1.control_point(nop - 1).out_handle)*mx22	#  掃引体の交差方向 handle 作成時に使用
		
	#  r							
	bb = labo.vec3_bounding_box_size(bZs)	
	r = bb[3]/16												#  sweep_handle( ), handle_length() の引数に使用	
	
		
	#  掃引中心線 の 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)

		
	#  bZs を分割		
	if threshold > 0 :

		#  屈曲点で分割
		bZs1 = []
		insert_list = []
		k = 0
		for bz in bZs :
			k += 1
			t = bezier_inflection_point(bz)
			n = len(t)
			if n == 0 :				#  屈曲点なし
				bZs1.append(bz)
			elif n == 1 :			#  屈曲点が一カ所あり
				bZs1.append(labo.bezier_line_subdivision(bz, 0, t[0]))
				bZs1.append(labo.bezier_line_subdivision(bz, t[0], 1))
				insert_list.append(k)
				nop += 1
			else :					#  屈曲点が二カ所あり
				bZs1.append(labo.bezier_line_subdivision(bz, 0, t[0]))
				bZs1.append(labo.bezier_line_subdivision(bz, t[0], t[1]))
				bZs1.append(labo.bezier_line_subdivision(bz, t[1], 1))
				insert_list.append(k)
				insert_list.append(k)
				nop += 2

		if len(insert_list) > 0 :
			insert_list.reverse()
			for k in insert_list :
				has_in_handle.insert(k, True)
				has_out_handle.insert(k, True)
				linked.insert(k, True)

		
		#  threshold に従って分割
		bZs2 = []
		insert_list = []
		k = 0
		for bz in bZs1 :
			k += 1
			paramL = bezier_divide_by_total_curvature(bz, threshold)
			n = len(paramL)
			if n == 0 : 
				bZs2.append(bz)
			else :
				for i in range(n - 1) :
					bZs2.append(labo.bezier_line_subdivision(bz, paramL[i], paramL[i + 1]))
					insert_list.append(k)
					nop += 1
				insert_list.pop()
				nop += -1
					
		if len(insert_list) > 0 :
			insert_list.reverse()
			for k in insert_list :
				has_in_handle.insert(k, True)
				has_out_handle.insert(k, True)
				linked.insert(k, True)
				
		bZs = bZs2
																			
	
	#####  追加  ########################################################################
	#  bZs の各区間を parameter 基準で8等分して長さの概算値を求め、区間長の平均の1.5倍の長さのある区間を分割する
	Ls = []
	totalL = 0.
	kk = 0
	for bz in bZs :
		L = 0.
		p1 = bz[0]
		for i in range(1, 9) :
			t = float(i)/8
			p2 = labo.bezier_line_position(bz, t)
			v = p2 - p1
			L += v.abs()
			p1 = p2
		Ls.append(L)				#  区間長
		totalL += L
		if L == 0 :
			kk += 1
		
	ns = len(bZs)
	maxL = 1.5*totalL/(ns - kk)		#  区間長の平均値 x 1.5	( 長さ 0 の区間は平均長さの算定に加えない )
	
	bZs3 = []
	insert_list = []
	k = 0
	for i in range(ns) :
		k += 1
		kk = i + 1
		if kk > nop - 1 :
			kk = 0
			
		if (Ls[i] <= maxL) or (not has_out_handle[i] and not has_in_handle[kk]) :
			bZs3.append(bZs[i])
		else :
			nj = int(Ls[i]/maxL) + 1		#  分割数
			param1 = 0.
			for j in range(nj) :
				param2 = float(j + 1)/nj
				bZs3.append(labo.bezier_line_subdivision(bZs[i], param1, param2))
				param1 = param2
				insert_list.append(k)
				nop += 1
			insert_list.pop()
			nop += -1
			
	if len(insert_list) > 0 :
		insert_list.reverse()
		for k in insert_list :
			has_in_handle.insert(k, True)
			has_out_handle.insert(k, True)
			linked.insert(k, True)
				
	bZs = bZs3
	
	#####  追加  ここまで  ###################################################################
	
	
	#  ob0 : 掃引体を格納する自由曲面
	ob2.activate()
	scene.create_surface_part(None)
	ob0 = scene.active_shape()				#   掃引体を格納する自由曲面
	ob0.surface_closed = closed
	ob2.place_child(1)

	
	#  掃引断面を配置
	p1 = bZs[0][0]
	Mt1 = matrix().translate(-p1[0], -p1[1], -p1[2])
	Q1 = quaternion()
	n = nop - 1
	v2_prev = None
	
	#####  追加  ########################################################
	#  cornerL :	 	corner handle flag	断面交差方向 handle の corner 属性
	#  in_terminal :	in_terminal flag	断面交差方向の out 方向が重点、あるいは、開いた掃引中心線の終端
	#  out_terminal :	out_terminal flag	断面交差方向の in 方向が重点、あるいは、開いた掃引中心線の始端
	#
	#  上記の 3 flag は実際に handle が伸びているか否かは考慮せずにセットする
	#  これらの flag を参照して交差方向 handle の長さを求める際には、has_in_handle, has_out_handle で handle が伸びているか否かの screening を併用する
	
	cornerL = [False]
	if closed :
		in_terminal = [has_in_handle[0] and not has_out_handle[0]]
		out_terminal = [not has_in_handle[0] and has_out_handle[0]]
	else :
		in_terminal = [False]
		out_terminal = [True]
	#####  追加  ここまで  ################################################
		
	for i in range(n) :
		p2 = bZs[i][3]
		v2 = labo.bezier_line_tangent(bZs[i], 1)
		Mt2 = matrix().translate(p2[0], p2[1], p2[2])
		Q2 = sweep_section_quaternion(bZs[i])
		Q2 = Q2*Q1
		Mr = Q2.matrix()
		in_term = False													########### /////////////////
		out_term = False												########### /////////////////
		corner = False													########### /////////////////

		if i == n - 1 and not closed :
			M = mx2*Mt1*Mr*Mt2*mx1
			in_term = True												########### /////////////////
			
		else :
			v3 = labo.bezier_line_tangent(bZs[i + 1], 0)
			if v3.abs2() == 0 :							#  重点
				M = mx2*Mt1*Mr*Mt2*mx1
				if v2_prev == None :					#  最初の重点
					v2_prev = v2
					in_term = True										########### /////////////////
				
			elif v2.abs2() == 0 :						#  最後の重点
				out_term = True											########### /////////////////
				if v2_prev == None or v2_prev.abs2() == 0 :	#   始点が重点から始まっている		
					M = mx2*Mt1*Mr*Mt2*mx1	
				else :
					Qc = quaternion().slerp(v2_prev, v3, 1, False)
					if Qc != None :								
						Q2 = Qc*Q2								
						Mc = Qc.matrix()						
						M = mx2*Mt1*Mr*Mc*Mt2*mx1			
					else :										
						M = mx2*Mt1*Mr*Mt2*mx1				
				v2_prev = None
				
				
			else :
				if abs(v2.dot(v3)) >= 0.99999994 :		#  掃引中心線の point が corner point  ではない
					M = mx2*Mt1*Mr*Mt2*mx1

				else :									#  掃引中心線の point が corner point 
					w = v2 + v3
					w.norm() 	
					corner = True										########### /////////////////
					v1 = labo.bezier_line_tangent(bZs[i], 0)
					v4 = labo.bezier_line_tangent(bZs[i + 1], 1)

					if v1.dot(v2) + v3.dot(v4) > 1.98 :	#  直線コーナー
						limitter = limitter1
					else :								#  曲線コーナー
						limitter = limitter2
					Q3 = quaternion().slerp(v2, w)
					Mq = Q3.matrix()
					
					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, min(limitter, 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
		cornerL.append(corner)														########### /////////////////
		in_terminal.append(in_term)													########### /////////////////
		out_terminal.append(out_term)												########### /////////////////

	
	#####  追加 / 変更  ##############################################################################
	
	#  グラフ入力から指定 scale 値を読み取り、scaleL とLs を得る
	#  scaleL : scale 値の list			Ls : 掃引中心線の各 bezier 区間の長さ list
	scaleL, Ls = make_scaleL_Ls(bZs, closed, graph)
	
	
	#  rr :  掃引断面ポイントと掃引中心線始端ポイントとの距離 list	rL 作成に使用
	n = ob2.number_of_control_points
	rr = []
	c = bZs[0][0]
	for i in range(n) :
		pp = vec3(ob2.control_point(i).position)*mx2
		v = pp - c											
		rr.append(v.abs())
		
	
	#  掃引断面を格納した自由曲面の copy ob0_x を作成、scaling を施す前の座標取得に利用し、最終的には削除する
	ob0.copy_object(matrix(4))
	ob0_x = ob0.bro
	
	
	#  グラフ入力 scale 値により、掃引断面形状を scaling 
	k = -1
	ob = ob0.son
	while ob.has_bro :
		k += 1
		ob = ob.bro
		
		if closed or k != nop - 1 :
			c = bZs[k][0]
		else :
			c = bZs[nop - 2][3]
		scale = scaleL[k]

		Mt1 = matrix().translate(-c[0], -c[1], -c[2])
		Mt2 = matrix().translate(c[0], c[1], c[2])
		Ms = matrix().scale(scale, scale, scale)
		M = mx2*Mt1*Ms*Mt2*mx1
		
		ob.move_object(M)

	
	#  掃引体の交差方向 handle をセット
	ob0.switch()
	ob = ob0.son
	ob0_x.switch()
	obx = ob0_x.son
	
	for i in range(n) :
		ob = ob.bro												#  掃引体内部の交差方向線形状
		obx = obx.bro
			
		#  rL :	交差方向線形状のポイントと、それに対応する掃引中心線のポイント間距離の list	
		rL = []
		for j in range(nop) :
			rL.append(rr[i]*scaleL[j])							#  この距離は corner 配置掃引断面が受ける corner scaling を考慮しない

		
		for j in range(nop) :
			w = vec3(ob.control_point(j).position)*mx2			#  交差方向線形状の point 座標	この座標値は Shade から読み取っているので単精度しか持たない	
												
			#  k : 	j の一つ手前	
			k = j - 1	
			if k == -1 :												
				k = nop - 1
				
			# kk :	j の一つ先
			kk = j + 1	
			if kk == nop :												
				kk = 0
																												
			if has_in_handle[j] or has_out_handle[j] :
				#  s1, s2 :	曲がりによる  Ls の inhandle, outhandle 側 補正係数
				if (not closed and j == 0) or (not has_in_handle[j]) :
					s1 = 0	
				else :
					vp1 = sweep_plane_normal(bZs[k][3], bZs[k][2], bZs[k][1], bZs[k][0])	#  inhandle 側 vp
					u1 = bZs[k][2] - bZs[k][3]												#  掃引中心線の inhandle ベクトル
					uL1 = u1.norm()															#  掃引中心線の inhandle 長さ
					p1 = bZs[k][2]															#  掃引中心線の inhandle 座標
					q = bZs[k][3]															#  掃引中心線の point 座標
					hL1 = handle_length(w, u1, q, r, p1, vp1, cornerL[j])		#  交差方向線形状 の inhandle が掃引中心線の inhandle に平行な場合の inhandle 長さ	
					s1 = max(0.1, hL1/uL1)													#  曲がりによる  Ls の scaling factor
						
						
				if (not closed and j == nop - 1) or (not has_out_handle[j]) :
					s2 = 0	
				else :
					vp2 = sweep_plane_normal(bZs[j][0], bZs[j][1], bZs[j][2], bZs[j][3])	#  outhandle 側 vp
					u2 =  bZs[j][1] - bZs[j][0]												#  掃引中心線の outhandle ベクトル
					uL2 = u2.norm()															#  掃引中心線の outhandle 長さ
					p2 = bZs[j][1]															#  掃引中心線の outhandle 座標
					q = bZs[j][0]															#  掃引中心線の point 座標															
					hL2 = handle_length(w, u2, q, r, p2, vp2, cornerL[j])		#  交差方向線形状 の outhandle が掃引中心線の outhandle に平行な場合の outhandle 長さ
					s2 = max(0.1, hL2/uL2)													#  曲がりによる  Ls の scaling factor

				
				#  vh1, vh2 :	scaling による 交差方向線形状の inhandle, outhandle の傾きを表すベクトル
				if (not cornerL[j]) and (not in_terminal[j]) and (not out_terminal[j]) :	#  inhandle と outhandle が平行な smooth handle の場合
					vh1 = get_vh_A(s1*Ls[k], s2*Ls[j], rL[k], rL[j], rL[kk])
					vh2 = vh1
					
				if cornerL[j] or in_terminal[j] :			#  corner handle あるいは out 方向が重点 or 終端となっている terminal handle
					k1 = j - 1
					k2 = j - 2
					if closed :
						if k1 < 0 :
							k1 = nop - 1
							k2 = nop - 2
						if k2 < 0 :
							k2 = nop - 1
					
					if not cornerL[k1] and not out_terminal[k1] and k2 != -1 :
						vh1 = get_vh_B(-s1*Ls[k1], -s1*Ls[k2], rL[j], rL[k1], rL[k2])
					else :
						vh1 = vec3(s1*Ls[k1], rL[j] - rL[k1], 0)
						vh1.norm()
								
				if cornerL[j] or out_terminal[j] :		#  corner handle あるいは in 方向が重点 or 始端となっている terminal handle
					k1 = j + 1
					k2 = j + 2
					if closed :
						if k1 == nop :
							k1 = 0
							k2 = 1
						if k2 == nop :
							k2 = 0
							
					if not cornerL[k1] and not out_terminal[k1] and k2 != nop :
						vh2 = get_vh_B(s2*Ls[j], s2*Ls[k1], rL[j], rL[k1], rL[k2])
					else :
						vh2 = vec3(s2*Ls[j], rL[k1] - rL[j], 0)
						vh2.norm()
						
						
				#  ww :		scaling を施していない交差方向線形状の point 座標	
				ww = vec3(obx.control_point(j).position)*mx2

			
			#  inH :	inhandle 座標
			if not has_in_handle[j] :
				inH = w
			else :
				if (j == 0) and not closed :
					pass
				else :
					Qg = labo.attitude_control_quaternion(vec3(-1, 0, 0), vec3(0, 1, 0), u1, ww - q, 0)
					if Qg != None :
						vt = Qg*(-vh1)
						inH = sweep_handle_x(w, vt, r, p1, vp1)
					else :											#  ww = q
						inH = sweep_handle_x(w, u1, r, p1, vp1)

					
			#  outH :	outhandle 座標
			if not has_out_handle[j] :
				outH = w
			else :
				if (j == nop - 1) and not closed :
					pass
				else :
					Qg = labo.attitude_control_quaternion(vec3(1, 0, 0), vec3(0, 1, 0), u2, ww - q, 0)
					if Qg != None :
						vt = Qg*vh2
						outH = sweep_handle_x(w, vt, r, p2, vp2)
					else :											#  ww = q
						outH = sweep_handle_x(w, u2, r, p2, vp2)

				
			#  開いた中心線の 始端/終端 における外側に伸びる handle 座標		
			if (j == 0) and not closed and has_in_handle[j] :
				u = w - outH
				u.norm()
				v = ps - bZs[0][0]
				a = v.norm()
				inH = a/u.dot(v)*u + w
					
			if (j == nop - 1) and not closed and has_out_handle[j] :
				u = w - inH
				u.norm()
				v = pe - bZs[nop - 2][3]
				a = v.norm()
				outH = a/u.dot(v)*u + w

			
			#  handle 座標をセット
			ob.control_point(j).in_handle = inH*mx1
			ob.control_point(j).out_handle = outH*mx1					
			ob.control_point(j).linked = linked[j]
	
		
	ob0_x.remove()
	ob0.switch()
	ob0.activate()
	ob0.disclosed = False
	
	return ob0
	
	#####  追加 / 変更  ここまで  #########################################################################
	
	
#  Shade  上で 掃引中心線 , 掃引断面 の順で選択して実行
scene = xshade.scene()

#  dialog uuid は Online GUID Generator により生成		https://www.guidgenerator.com
dialog = xshade.create_dialog_with_uuid('2316bc06-5fad-4680-bdaa-9a5321be6b66')

dialog.begin_tab_group('分割')
level_ = dialog.append_selection('level/分割せず/90度/60度/45度/30度/22.5度/15度')
dialog.end_tab_group()

dialog.begin_tab_group('limitter')
limitter1_ = dialog.append_float('直線コーナー', '( defalt 10 )')
limitter2_ = dialog.append_float('曲線コーナー', '( defalt 3 )')
dialog.end_tab_group()

dialog.set_value(4, 10)
dialog.set_value(5, 3)

if dialog.ask('滑らかな掃引 X'):					#####  dialog title 変更  ##############################
	level = dialog.get_value(level_)
	from math import pi
	if level == 0 :
		threshold = 0.
	elif level == 1 :	
		threshold = pi/2	
	elif level == 2 :		
		threshold = pi/3
	elif level == 3 :	
		threshold = pi/4
	elif level == 4 :	
		threshold = pi/6
	elif level == 5 :	
		threshold = pi/8
	elif level == 6 :	
		threshold = pi/12
		
	limitter1 = dialog.get_value(limitter1_)
	limitter2 = dialog.get_value(limitter2_)

	#####  変更  ########################################################################
	scene.exit_modify_mode()
	[ob1, ob2, ob3, ob4] = scene.active_shapes
	
	graph = labo.simple_graph(xshade, ob3, ob4)
	if graph != None :
		ob2.copy_object(None)
		ob5 = ob2.bro
		ob6 = c_sweep_x(ob1, ob5, graph, threshold, limitter1, limitter2)
		if ob6 != None :
			ob6.activate()    
		else :
			scene.active_shapes = [ob1, ob2, ob3, ob4]      
	##################################################################################

#3

33 - 3  h_sweep_x( )


太さの変わる水平掃引 h_sweep_x( ) を [ script 33 - 4 ] に記します。

[ script 33 - 3 ]


h_sweep( ) からの変更や追加部分には行末に ############ を付しています

4つの線形状を選択して実行します。

選択順は次のうちのいずれか

  • 掃引中心線, 掃引断面, graph data, graph frame
  • 掃引中心線, 掃引断面, graph frame, graph data

import labo
from matrix import *
from quaternion import *



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

def h_sweep_section_quaternion(bz) :
	v1 = labo.bezier_line_tangent(bz, 0)			#  outhandle 側接線ベクトル
	v2 = labo.bezier_line_tangent(bz, 1)			#  inhandle 側接線ベクトル

	if v1.abs2() == 0 :								#   重点
		return quaternion()	
	else :	
		Q = labo.attitude_control_quaternion(v1, vec3(0, 1, 0), v2, vec3(0, 1, 0), 0)
		if Q != None :
			return Q
		else :										#  v1 or v2 が vec3(0, 1, 0) と平行
			return quaternion().slerp(v1, v2)
		
#  注記
		
			
				
#  sweep_plane_normal(p0 as vec3, p1 as vec3, p2 as vec3, p3 as vec3) as vec3
#
#	掃引中心線の bezier data bz (p0, p1, p2) から、掃引体交差方向の handle 長さを求めるための面法線を返す
#	基準となる anchor point に handle  が出ていることが前提
#
#		p0, p1, p2, p3 :	outhandle 側を求める場合は		bz[0], bz[1], bz[2], bz[3]
#							inhandle 側を求める場合は		bz[3], bz[2], bz[1], bz[0]

def sweep_plane_normal(p0, p1, p2, p3) :
	from math import pi, acos, sin, atan
	
	v1 = p1 - p0					#  v1.abs() != 0 が前提
	v2 = p2 - p1
	v1.norm()
	v2.norm()
	
	if v2.abs2() == 0 :
		v3 = p3 - p1
		v3.norm()
		if v3.abs2() == 0 :
			return v1
		else :
			vn = v1 + v3
			vn.norm()
			vp = v1 + vn
			vp.norm()
			return vp
		
	else :
		a = v1.dot(v2)
		if abs(a) >= 0.99999994 :
			return v1
		
		elif a >= 0 :
			vn = v1 + v2
			vn.norm()
			
			sinA = a
			cosA = (1 - sinA**2)**0.5
			beta = (pi - acos(a))/2
			r = (1 - sinA)/3
			
			axis = v1*v2
			axis.norm()
			v = v1 - v2
			v.norm()
			u = v/sin(beta) + v1*r/cosA
			u.norm()
			phi = acos(u.dot(v))

		else :
			axis = v1*v2
			phi = atan(4./3) - pi/4
			Q = quaternion().rotation(axis, pi/4)
			vn = Q*v1

		v3 = p3 - p2
		v3.norm()
		if v3.abs2() == 0 :
			return vn
		else :
			d = v1.dot(v2)
			w = -(v1-2*d*v2)
			w.norm()
			e = v3.dot(w)
			Q = quaternion().rotation(axis, e*phi)
			vp = Q*vn
			vp.norm()
			return vp

	

#####  h_sweep_handle( ) から変更  #############################################	
	
#  sweep_handle_x(w as vec3, vr as vec3, r as float, p as vec3, vp as vec3) as vec3
#
#	handle 座標を返す

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

	d1 = vp.dot(w - p)			#  点 w と 面 PL1 との符号付き距離
	d2 = vp.dot(w + r*vr - p)	#  点 w + r*vr と 面 PL1 との符号付き距離
	d = r*d1/(d1 - d2)			#  点 w を通り vr 方向に進む直線と、面 PL1 との交点までの 符号付き距離
		
	if d >= 0 :
		if d1 > 0 :				#  点 w が基準面 PL1 の外側にある
			d /= 12	
	else :
		d /= -12			
		
	return w + d*vr
		
########################################################################
		

	
#  bezier_inflection_point( bz as vec3 list) as float list
#
#		bz : bezier control point 座標を格納した vec3 list
#		bezier line bz の屈曲点位置の bezier parameter を返す。
#		屈曲点がない場合は、空 list が返される

def bezier_inflection_point(bz) :
	w1 = (bz[1] - bz[0])*(bz[2] - bz[3])
	w2 = (bz[2] - bz[1])*(bz[3] - bz[0])
	w3 = (bz[2] - bz[0])*(bz[1] - bz[3])
	r1 = w1.norm()
	r2 = w2.norm()
	r3 = w3.norm()
	L = [[r1, w1], [r2, w2], [r3, w3]]
	L.sort()
	vn = L[2][1]
	if vn.norm() == 0 :				#  bz は直線であり、変曲点は存在しない
		return []

	#  bz2 : 原点を通り面法線 vn なる面に bz を投影	
	bz2 = []
	for i in range(4) :
		d = vn.dot(bz[i])
		bz2.append(bz[i] - d*vn)
	
	#  Q : bz2 の階差表現
	bz2 = matrix(False, bz2)
	Md = labo.bezier_d()
	Q = bz2*Md
	
	#  こちらの方法でも可
#	bz2 = matrix(True, bz2)
#	Md = labo.bezier_d()
#	Md.transpose()
#	Q = Md*bz2			

	A = Q[2]*Q[3]
	B = Q[1]*Q[3]
	C = Q[1]*Q[2]
	
	bb = labo.vec3_bounding_box_size(Q)
	bb.pop()
	bb2 = sorted(bb)
	for i in range(3) :
		if bb2[0] == bb[i] :
			a = A[i]
			b = B[i]
			c = C[i]
			t = []
			if a != 0 :
				d = b**2 - 4*a*c
				if d > 0 :
					tt = (-b - d**0.5)/(2*a)
					if tt > 0 and tt < 1 :
						t.append(tt)
					tt = (-b + d**0.5)/(2*a)
					if tt >= 0 and tt <= 1 :
						t.append(tt)
				elif d == 0 :
					tt = -b/(2*a)
					if tt > 0 and tt < 1 :
						t.append(tt)
			elif b != 0 :
				tt = -c/b
				if tt > 0 and tt < 1 :
					t.append(tt)
			
			if len(t) == 1 :
				v1 = labo.bezier_line_tangent(bz, 0)
				v2 = labo.bezier_line_tangent(bz, 1)
				v0 = labo.bezier_line_tangent(bz, t[0])
				if v0.dot(v1) >= 0.9999 or  v0.dot(v2) >= 0.9999 :	#  屈曲点での接線ベクトルが、始端 / 終端での接線ベクトルと
					t.pop()											#  ほとんど同じであれば、その屈曲点では分割しない
					
			elif len(t) == 2 :
				t.sort()	
				v1 = labo.bezier_line_tangent(bz, 0)
				v2 = labo.bezier_line_tangent(bz, 1)
				v0 = labo.bezier_line_tangent(bz, t[1])
				if v0.dot(v2) >= 0.9999 :							#  屈曲点での接線ベクトルが、終端での接線ベクトルと
					del t[1]										#  ほとんど同じであれば、その屈曲点では分割しない
				v0 = labo.bezier_line_tangent(bz, t[0])
				if v0.dot(v1) >= 0.9999 :							#  屈曲点での接線ベクトルが、始端での接線ベクトルと
					del t[0]										#  ほとんど同じであれば、その屈曲点では分割しない
			return t
				
	

#  bezier_divide_by_total_curvature( bz as vec3 list, threshold as float) as float list
#
#		bz : 		bezier control point 座標を格納した vec3 list
#		threshold :	分割しきい値
#
#		bezier line bz を分割する bezier parameter を返す。
#		分割がない場合は、空 list が返される	

def bezier_divide_by_total_curvature(bz, threshold) :
	paramL = []								#  分割点の bezier parameter を格納する list
	
	v1 = labo.bezier_line_tangent(bz, 0)
	if v1.abs2() == 0 :
		return paramL						#  重点による長さのない区間
	
	#  bz に最適分割を行って cL[], pL[] にデータを格納する	
	pL = [0.]								#  各区間を分割する bezer parameter を格納する list
	cL = [0.]								#  各区間までの全曲率を格納する list
	v2 = labo.bezier_line_tangent(bz, 1)
	
	a = v1.dot(v2)
	if abs(a) > 0.99999994 :
		w = bz[3] - bz[0]
		if w.abs2() == 0 :
			return paramL					#  重点とみなす
		elif a < - 0.99999994 :
			w.norm()
			if abs(w.dot(v1)) > 0.99999994 :
				return paramL				#  重点と同じ扱いにする
	
	make_total_curvature_list(bz, 0., v1, 1., v2, pL, cL, 8)	#  最適分割を行って cL[], pL[] にデータを格納する 再帰関数
	theta = cL[len(cL) - 1]										#  bz の全曲率
	
	if theta > threshold :								#  bz の全曲率がしきい値を越えている
		n = int((theta - 0.00001)/threshold) + 1		#  分割数 ( 丸め誤差による分割数の不均一防止のため 0.00001 を減ずる )
		dc = theta/n				
		paramL = [0.]									#  分割点の bezier parameter を格納 (最初は 0. )
		j = 1
		for i in range(1, n) :
			c = i*dc
			while c > cL[j] :
				j += 1
			paramL.append(pL[j] - (pL[j] - pL[j - 1])*(cL[j] - c)/(cL[j ] - cL[j - 1]))	#  分割点の bezier parameter
		paramL.append(1.)															#  分割点の bezier parameter list の最後に 1 を追加
		
	return paramL
	

		
		
#  make_total_curvature_list(bz as vec3 list, t1 as float, v1 as vec3, t2 as float, v2 as vec3, pL as float list, cL as float list, n as int)
#
#		bz の parameter 区間 t1〜t2 を最適サイズに分割して、cL[], pL[] にデータを格納する再帰関数
#
#		bz : 		bezier control point 座標を格納した vec3 list
#		t1, v1 :	計算対象区間の 始端 bezier parameter と その点での接線ベクトル	
#		t2, v2 :	計算対象区間の 終端 bezier parameter と その点での接線ベクトル
#		pL :		各区間を分割する bezer parameter を格納
#		cL :		分割された各区間までの全曲率を格納
#		n :			分割数 ( bezier parameter t1〜t2 を n 分割する )

def make_total_curvature_list(bz, t1, v1, t2, v2, pL, cL, n) :
	from math import acos
	
	u = v1
	s1 = t1
	ds = (t2 - t1)/n								#  parameter  増分
	
	for i in range(1, n + 1) :						#  分割数 n で分割
		s2 = s1 + ds

		if i < n :
			v = labo.bezier_line_tangent(bz, s2)	# parameter t での接線ベクトル
		else :
			v = v2
			
		cosT = max(-1, min(1, u.dot(v)))
		
		if cosT >= 0.99966 :								#  0.99966 = cos(1.5度)
			pL.append(s2)
			cL.append(cL[len(cL) - 1] + acos(cosT))	
		else :
			theta =  acos(cosT)
			m = max(2, 1 + int(theta*180/pi/1.5))					#  分割数
			make_total_curvature_list(bz, s1, u, s2, v, pL, cL, m)	#  再分割
			
		u = v
		s1 = s2
		

		
#  get_smooth_handle(p1 as vec3, p2 as vec3, p3 as vec3) as bool
#
#	in handle, anchor point, out handle が一直線に並んでいるかを返す
#	p1 :		in handle 座標
#	p2 :		anchor point 座標
#	p3 :		out handle 座標	
	
def get_smooth_handle(p1, p2, p3) :
	v1 = p2 - p1				#  ここに入る前に v1, v2 に長さがあることは確認済み
	v2 = p3 - p2
	v1.norm()
	v2.norm()
	return v1.dot(v2) >= 0.99999994
	
	
	
#  get_poisition_list(ob as shape, nop as int, closed as bool, mx2 as matrix) as vec3 list
#
#		ob :		掃引体交差方向線形状
#		nop :		ob の ポイント数
#		closed :	ob の開閉情報
#		mx2 :		ob の xshade.scene().local_to_world_matrix
#
#		ob の 隣接する3つの anchor point 座標の lisit の list [[vec3, vec3, vec3], [vec3, vec3, vec3}, ‥]  を返す

def get_poisition_list(ob, nop, closed, mx2) :
	pL = []

	p2 = vec3(ob.control_point(1).position)*mx2
	p1 =  vec3(ob.control_point(0).position)*mx2
	if not closed :	
		p0 = p1	
	else :														
		p0 = vec3(ob.control_point(nop - 1).position)*mx2
	pL.append([p0, p1, p2])
	
	for i in range(2, nop) :
		p0 = p1
		p1 = p2
		p2 = vec3(ob.control_point(i).position)*mx2
		pL.append([p0, p1, p2])
		
	p0 = p1
	p1 = p2
	if not closed :
		p2 = p1
	else :
		p2 = pL[0][1]
	pL.append([p0, p1, p2])
	
	return pL
	
	
	
#  h_flatness_list(bZs as vec3 list, closed as bool) as bool list
#
#		bZs :		掃引中心線の control point 座標 list
#		closed :	中心線の開閉情報
#
#		掃引中心線の各 bezier line 区間の平坦性 list fL を返す
#		list の始端と終端には次なる項目を追加
#			中心線が閉じている場合、その隣接項目
#			中心線が開いている場合、False

def h_flatness_list(bZs, closed):
	flatL = []
	for bz in bZs :
		v1 = labo.bezier_line_tangent(bz, 0)			#  outhandle 側接線ベクトル
		v2 = labo.bezier_line_tangent(bz, 1)			#  inhandle 側接線ベクトル
		v = bz[3] - bz[0]
			
		if v1.abs2() == 0 :								#  重点
			flatL.append(False)
		elif (v1.dot(v2) >= 0.9999) :					#  bz が直線 ( 事前に屈曲点で分割されているので v1, v2 の平行判定のみで OK )
			flatL.append(True)
		elif v.abs2() == 0 :							#  bezier 区間の始端と終端が同一座標で、且つ handle が出ている
			flatL.append(True)
		else :
			bb = labo.vec3_bounding_box_size(bz)
			flatL.append(bb[1]/bb[3] <= 0.0001)	#  bz の bounding box が水平平坦
		
	#   関数 h_sweep_handle_x( ) に与える引数の選択において、平坦性 list fL の隣接二値を比較するため、 list の先頭と終端に値を追加
	if not closed :
		flatL.insert(0, True)
		flatL.append(True)
	else :
		flatL.insert(0, flatL[len(flatL) - 1])
		flatL.append(flatL[1])
		
	return flatL
	
	
	

#  get_vt(w as vec3 list, q as vec3, r as float) as vec3
#
#		交差線形状の handle の方向を与える vector を返す
#		w[0] :	交差線形状の当該 point の一つ手前の point 座標
#		w[1] :	交差線形状の当該 point の座標
#		w[2] :	交差線形状の当該 point の一つ先の point 座標
#		q :		中心線 point 座標
#		r :		中心線 boinding box size / 16

def get_vt(w, q, r) :
	#  w[0] = w[1] = w[2] となる三重点のケースではここに入らない
	v1 = w[0] - w[1]
	v2 = w[2] - w[1]
	v1.norm()
	v2.norm()                    
	v0 = v2 - v1
	v0.norm()
	
	v = w[1] - q
	a = v.norm()
	if a < r*1e-5 :		# w[1]  と q が一致しているとみなす
		vt = v0
	else :
		d = v.dot(v0)
		vt = v0 - d*v
		vt.norm()
		
	return vt



#####  追加  ########################################################################

#  bezier_abs_derivative( bz as vec3 list, t as float ) as float
#
#	コントロールポイント座標 list bz で与えられる bezier 曲線の
#	パラメータ t における微分ベクトル の絶対値を返す

def bezier_abs_derivative(bz, t) :
	v = -3*(1 - t)**2*bz[0] + 3*(1 - t)*(1 - 3*t)*bz[1] + 3*t*(2 - 3*t)*bz[2] + 3*t**2*bz[3]			
	return v.abs()
	
	
	
		
#  make _x_list(bZs as vec3 list, closed as bool) as float list, float list
#
#	bZs の各区間の長さと、それを規格化した list を返す
#
#	bZs :	掃引中心線の bezier control point 座標を格納した vec3 list
	
def make_x_list(bZs) :
	totalL = 0
	xL = []
	Ls = []
	for bz in bZs :
		if bz[0] != bz[1] or bz[1] != bz[2] or bz[2] != bz[3] :
			r = labo.bezier_simpson(bezier_abs_derivative, bz, 0, 1)	#  parameter 区間 0〜1 で線長を求める
			totalL += r
		else :
			r = 0				#  bz[0] == bz[1] or bz[1] == bz[2] or bz[2] == bz[3] であっても、bezier_simpson( ) で bz の長さは 0 にならない
		xL.append(totalL)
		Ls.append(r)			#  bZs の各区間の長さ list
		
	n = len(xL)
	for i in range(n) :
		xL[i] /= totalL			#  bZs の各区間の長さを規格化した list
		
	return Ls, xL
		

				

#  make_scaleL_Ls(bZs as vec3 list, closed as bool, graph as simple_graph) as float list, float list
#
#	bZs の各 anchor point における グラフ指定による scale 値の list と bZs の各 bezier 区間の長さの list を返す
#	
#	bZs :		掃引中心線の bezier control point 座標を格納した vec3 list
#	closed :	掃引中心線の開閉情報
#	graph :		simple_graph

def make_scaleL_Ls(bZs, closed, graph) :		
	Ls, xL = make_x_list(bZs)			#  Ls : bZs の各区間の長さ list  		
	xL.insert(0, 0.)	
	scaleL = []
	for x in xL :						#  xL : bZs  の各区間の長さを規格化した list
		scale = graph.y(x)
		scaleL.append(scale)			#  scaleL : bZs の各 anchor point におけるグラフ指定 scale 値の list
	if closed :
		scaleL.pop()
		scaleL.append(scaleL[0])
		
	return scaleL, Ls
	
	
	
	
#  handle_length(w as vec3, u as vec3, q as vec3, r as float, p as vec3, vp as vec3, corner as bool) as float
#
#	dummy handle 長さを返す
#
#	w :	断面ポイント座標
#	u :	掃引中心線接線ベクトル
#	q :	掃引中心線 point 座標
#	r :	掃引中心線 bounding box size / 16
#	p :	掃引中心線の handle 座標
#	vp :		基準面法線
#	corner :	corner flag

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

	d1 = vp.dot(w - p)			#  点 w と 面 PL1 との符号付き距離
	d2 = vp.dot(w + r*u - p)	#  点 w + r*u と 面 PL1 との符号付き距離
	d = r*d1/(d1 - d2)			#  点 w を通り u 方向に進む直線と、面 PL1 との交点までの符号つき距離
	
	if corner :
		a = u.dot(w - q)		#  点 w と面 PL0 との符号つき距離	
		if a > 0 :
			d += a				#  d が長くなる場合のみ補正を行う
	
	return abs(d)
	
	
	
				
#  get_vh_A(L1 as float, L2 as float, r0 as float, r1 as float, r2 as float) as vec3
#
#	scaling に伴う交差方向線形状 handle の傾きを表すベクトルを返す
#
#	L1, L2 :		X 方向長さ ( scaling factor は適用済み )
#	r0, r1, r2 :	Y 方向長さ

def get_vh_A(L1, L2, r0, r1, r2) :
	#  3点のポイントの X, Y size を規格化したものに smooth をかけた smooth vector を作り、
	#  その Y size を original の proportion に戻す
	
	#  x0, x1, x2, y0, y1, y2 : 3点のポイントの X, Y size を規格化
	sx = 1/(L1 + L2)
	x0 = 0
	x1 = L1*sx
	x2 = 1
	
	r = (abs(r1 - r0) + abs(r2 - r1))
	if r == 0 :						#  r0 = r1 = r2 = 0 であり、断面ポイント座標と掃引中心線の配置ポイント座標が一致している
		return vec3(1, 0, 0)
	else :
		sy = 1/r
	y0 = 0
	y1 = (r1 - r0)*sy
	y2 = (r2 - r0)*sy
	
	#  v : 規格化された3点の中央の point に smooth をかけて得られる smooth vector
	p0 = vec3(x0, y0, 0)
	p1 = vec3(x1, y1, 0)
	p2 = vec3(x2, y2, 0)

	u0 = p0 - p1
	u2 = p2 - p1
	a0 = u0.norm()
	a2 = u2.norm()
	v = a0*u2 - a2*u0

	#  smooth vector w の Y size を original の proportion に戻す
	vh = vec3(v[0], v[1]*sx/sy, 0)
	vh.norm()
	
	return vh
	
	
	

#  get_vh_B(L1 as float, L2 as float, r0 as float, r1 as float, r2 as float) as vec3
#
#	開いた線形状の terminal 点における scaling に伴う交差方向線形状 handle の傾きを表すベクトルを返す
#
#	L1, L2 :		X 方向長さ ( scaling factor は適用済み )
#	r0, r1, r2 :	Y 方向長さ

def get_vh_B(L1, L2, r0, r1, r2) :
	tan1 = (r1 - r0)/L1
	tan2 = (r2 - r1)/L2
				
	if abs(tan2) < abs(tan1) :
		a = 2
	else :
		a = 3
		
	tan0 = tan1 - (tan2 - tan1)/a
	vh = vec3(1, tan0, 0)
	vh.norm()
	
	return vh	
	
#####  追加  ここまで  ###################################################################




#  h_sweep_x(ob1 as shape, ob2 as shape, graph as simple_graph, threshold as float, limitter1 as float = 10., limitter2 as float = 3.) as shape
#
#	掃引体を Shade 上に作成し、その shape を返す
#		ob1 :		掃引中心線形状
#		ob2 :		掃引断面形状
#		graph :		simple_graph object
#		threshold:	分割しきい値
#		limitter1 :	直線コーナーの scale liitter	
#		limitter2 :	曲線コーナーの scale liitter	

def h_sweep_x(ob1, ob2, graph, threshold, limitter1 = 10., limitter2 = 3.) :
	if (ob1 == None) or (ob2 == None) :
		return
	if (not isinstance(ob1, xshade.line)) or (not isinstance(ob2, xshade.line)) :
		return
		
	#  check
	if threshold < 0 :	
		return
	if limitter1 < 1 or limitter2 < 1 :
		return
	

	#  掃引中心線データ取得 nop, closed, bZ
	ob1.activate()
	nop = ob1.number_of_control_points		#  ポイント数
	if nop < 2 :
		return
	closed = ob1.closed						#  開閉情報
	bZs = labo.get_bezier(xshade, -1)		#  全区間の bezier data [matrix, matrix, … ]
	
	
	# mx1, mx2, mx22, ps, pe
	mx1 = matrix(ob2.world_to_local_matrix)
	mx2 = matrix(ob2.local_to_world_matrix)
	if not closed :
		mx22 = matrix(ob1.local_to_world_matrix)
		ps = vec3(ob1.control_point(0).in_handle)*mx22			#  掃引体の交差方向 handle 作成時に使用
		pe = vec3(ob1.control_point(nop - 1).out_handle)*mx22	#  掃引体の交差方向 handle 作成時に使用
		
	#  r							
	bb = labo.vec3_bounding_box_size(bZs)	
	r = bb[3]/16												#  sweep_handle( ), get_vt( ) の引数に使用	
	
		
	#  掃引中心線 の handle 情報取得
	has_in_handle = []			#  掃引中心線の inhandle の有無のリスト
	has_out_handle = []			#  掃引中心線の outhandle の有無のリスト
	linked = []					#  掃引中心線の hanndle link リスト
	smooth_handle = []			#  中心線の smooth handle lリスト
	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)
		
		#  smooth_handle[ ]
		#		関数 h_sweep_handle_x( ) に与える引数選択のために in / out handle の直線性の識別に参照
		#		実 handle ベースで定義
		if has_in_handle[i] and has_out_handle[i] :
			if i == 0 :
				if closed :
					smooth_handle.append(get_smooth_handle(bZs[nop - 1][2], bZs[0][0], bZs[0][1]))
				else :
					smooth_handle.append(False)	
			elif i == nop - 1 :
				if closed :
					smooth_handle.append(get_smooth_handle(bZs[nop - 2][2], bZs[nop - 1][0], bZs[nop - 1][1]))
				else :
					smooth_handle.append(False)		
			else :
				smooth_handle.append(get_smooth_handle(bZs[i - 1][2], bZs[i][0], bZs[i][1]))		
		else :
			smooth_handle.append(False)


	#  bZs を分割	
	#  屈曲点で分割
	bZs1 = []
	insert_list = []
	k = 0
	for bz in bZs :
		k += 1
		t = bezier_inflection_point(bz)
		n = len(t)
		if n == 0 :				#  屈曲点なし
			bZs1.append(bz)
		elif n == 1 :			#  屈曲点が一カ所あり
			bZs1.append(labo.bezier_line_subdivision(bz, 0, t[0]))
			bZs1.append(labo.bezier_line_subdivision(bz, t[0], 1))
			insert_list.append(k)
			nop += 1
		else :					#  屈曲点が二カ所あり
			bZs1.append(labo.bezier_line_subdivision(bz, 0, t[0]))
			bZs1.append(labo.bezier_line_subdivision(bz, t[0], t[1]))
			bZs1.append(labo.bezier_line_subdivision(bz, t[1], 1))
			insert_list.append(k)
			insert_list.append(k)
			nop += 2

	if len(insert_list) > 0 :
		insert_list.reverse()
		for k in insert_list :
			has_in_handle.insert(k, True)
			has_out_handle.insert(k, True)
			linked.insert(k, True)
			smooth_handle.insert(k, True)
	
	#  threshold に従って分割	
	if threshold > 0 :		
		bZs2 = []
		insert_list = []
		k = 0
		for bz in bZs1 :
			k += 1
			t = bezier_divide_by_total_curvature(bz, threshold)
			n = len(t)
			if n == 0 : 
				bZs2.append(bz)
			else :
				for i in range(n - 1) :
					bZs2.append(labo.bezier_line_subdivision(bz, t[i], t[i + 1]))
					insert_list.append(k)
					nop += 1
				insert_list.pop()
				nop += -1
					
		if len(insert_list) > 0 :
			insert_list.reverse()
			for k in insert_list :
				has_in_handle.insert(k, True)
				has_out_handle.insert(k, True)
				linked.insert(k, True)
				smooth_handle.insert(k, True)
		bZs = bZs2
													
	
	#####  追加  ########################################################################
	#  bZs の各区間を parameter 基準で8等分して長さの概算値を求め、区間長の平均の1.5倍の長さのある区間を分割する
	Ls = []
	totalL = 0.
	kk = 0
	for bz in bZs :
		L = 0.
		p1 = bz[0]
		for i in range(1, 9) :
			t = float(i)/8
			p2 = labo.bezier_line_position(bz, t)
			v = p2 - p1
			L += v.abs()
			p1 = p2
		Ls.append(L)				#  区間長
		totalL += L
		if L == 0 :
			kk += 1
		
	ns = len(bZs)
	maxL = 1.5*totalL/(ns - kk)		#  区間長の平均値 x 1.5	( 長さ 0 の区間は平均長さの算定に加えない )
	
	
	bZs3 = []
	insert_list = []
	k = 0
	for i in range(ns) :
		k += 1
		kk = i + 1
		if kk > nop - 1 :
			kk = 0
			
		if (Ls[i] <= maxL) or (not has_out_handle[i] and not has_in_handle[kk]) :
			bZs3.append(bZs[i])
		else :
			nj = int(Ls[i]/maxL) + 1		#  分割数
			param1 = 0.
			for j in range(nj) :
				param2 = float(j + 1)/nj
				bZs3.append(labo.bezier_line_subdivision(bZs[i], param1, param2))
				param1 = param2
				insert_list.append(k)
				nop += 1
			insert_list.pop()
			nop += -1

					
	if len(insert_list) > 0 :
		insert_list.reverse()
		for k in insert_list :
			has_in_handle.insert(k, True)
			has_out_handle.insert(k, True)
			linked.insert(k, True)
			smooth_handle.insert(k, True)
				
	bZs = bZs3
	
	#####  追加  ここまで  ###################################################################										
	
	
	#  ob0 : 掃引体を格納する自由曲面
	ob2.activate()
	scene.create_surface_part(None)
	ob0 = scene.active_shape()				#   掃引体を格納する自由曲面
	ob0.surface_closed = closed
	ob2.place_child(1)

	
	#  掃引断面を配置
	p1 = bZs[0][0]
	Mt1 = matrix().translate(-p1[0], -p1[1], -p1[2])
	Q1 = quaternion()
	n = nop - 1
	v2_prev = None

	#####  追加  ########################################################
	#  cornerL :	 	corner handle flag		断面交差方向 handle の corner 属性
	#  in_terminal :	in_terminal flag		断面交差方向の out 方向が重点、あるいは、開いた掃引中心線の終端
	#  out_terminal :	out_terminal flag		断面交差方向の in 方向が重点、あるいは、開いた掃引中心線の始端
	#
	#  上記の 3 flag は実際に handle が伸びているか否かは考慮せずにセットする
	#  これらの flag を参照して交差方向 handle の長さを求める際には、has_in_handle, has_out_handle で handle が伸びているか否かの screening を併用する
	
	cornerL = [False]
	if closed :
		in_terminal = [has_in_handle[0] and not has_out_handle[0]]
		out_terminal = [not has_in_handle[0] and has_out_handle[0]]
	else :
		in_terminal = [False]
		out_terminal = [True]
	#####  追加  ここまで  ################################################						
		
	for i in range(n) :
		p2 = bZs[i][3]
		v2 = labo.bezier_line_tangent(bZs[i], 1)
		Mt2 = matrix().translate(p2[0], p2[1], p2[2])
		
		Q2 = h_sweep_section_quaternion(bZs[i])	
		Q2 = Q2*Q1
		Mr = Q2.matrix()
		in_term = False													########### /////////////////
		out_term = False												########### /////////////////
		corner = False													########### /////////////////

		if i == n - 1 and not closed :
			M = mx2*Mt1*Mr*Mt2*mx1
			in_term = True												########### /////////////////
			
		else :
			v3 = labo.bezier_line_tangent(bZs[i + 1], 0)
			if v3.abs2() == 0 :							#  重点
				M = mx2*Mt1*Mr*Mt2*mx1
				if v2_prev == None :					#  最初の重点
					v2_prev = v2
					in_term = True										########### /////////////////
				
			elif v2.abs2() == 0 :						#  最後の重点
				out_term = True											########### /////////////////
				if v2_prev == None or v2_prev.abs2() == 0 :	#   始点が重点から始まっている
					M = mx2*Mt1*Mr*Mt2*mx1
				else :
					Qc = labo.attitude_control_quaternion(v2_prev, vec3(0, 1, 0), v3, vec3(0, 1, 0), 0)
					Q2 = Qc*Q2
					Mc = Qc.matrix()
					M = mx2*Mt1*Mr*Mc*Mt2*mx1
				v2_prev = None
				
			else :
				if v2.dot(v3) >= 0.99999994 :			#  掃引中心線の point が corner point  ではない
					M = mx2*Mt1*Mr*Mt2*mx1
				
				elif v2.dot(v3) <= -0.99999994 :		#  掃引中心線の point が 180 度 corner point
					corner = True										########### /////////////////		
					M = mx2*Mt1*Mr*Mt2*mx1
					Qc = labo.attitude_control_quaternion(v2, vec3(0, 1, 0), v3, vec3(0, 1, 0), 0)	
					Q2 = Qc*Q2							#   次の point での掃引断面配置に必要
				
				else :									#  掃引中心線の point が corner point 
					w = v2 + v3
					w.norm()
					corner = True										########### /////////////////
					v1 = labo.bezier_line_tangent(bZs[i], 0)
					v4 = labo.bezier_line_tangent(bZs[i + 1], 1)

					if v1.dot(v2) + v3.dot(v4) > 1.98 :	#  直線コーナー
						limitter = limitter1
					else :								#  曲線コーナー
						limitter = limitter2

					Q3 = quaternion().slerp(v2, w)
					Mq = Q3.matrix()
					
					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, min(limitter, 1/cosT), 1)	
					M = mx2*Mt1*Mr*Mq*Mqs1*Ms*Mqs2*Mt2*mx1
					
					Q6 = labo.attitude_control_quaternion(v2, vec3(0, 1, 0), v3, vec3(0, 1, 0), 0)	
					Q2 = Q6*Q2						#   次の point での掃引断面配置に必要
					
	
		ob2.copy_object(M)
		ob = ob2.bro
		ob.place_brother(i)
	
		Q1 = Q2
		cornerL.append(corner)														########### /////////////////
		in_terminal.append(in_term)													########### /////////////////
		out_terminal.append(out_term)												########### /////////////////				


	#####  追加 / 変更  ##############################################################################
	
	#  グラフ入力から指定 scale 値を読み取り、scaleL とLs を得る
	#  scaleL : scale 値の list		Ls : 掃引中心線の各 bezier 区間の長さ list	
	scaleL, Ls = make_scaleL_Ls(bZs, closed, graph)
	
	
	#  rr :  掃引断面ポイントと掃引中心線始端ポイントとの距離 list	rL 作成に使用
	n = ob2.number_of_control_points
	rr = []
	c = bZs[0][0]
	for i in range(n) :
		pp = vec3(ob2.control_point(i).position)*mx2
		v = pp - c											
		rr.append(v.abs())
		
	
	#  掃引断面を格納した自由曲面の copy ob0_x を作成、scaling を施す前の座標取得に利用し、最終的には削除する
	ob0.copy_object(matrix(4))
	ob0_x = ob0.bro
	
	
	#  グラフ入力 scale 値により、掃引断面形状を scaling 
	k = -1
	ob = ob0.son
	while ob.has_bro :
		k += 1
		ob = ob.bro
		
		if closed or k != nop - 1 :
			c = bZs[k][0]
		else :
			c = bZs[nop - 2][3]
		scale = scaleL[k]

		Mt1 = matrix().translate(-c[0], -c[1], -c[2])
		Mt2 = matrix().translate(c[0], c[1], c[2])
		Ms = matrix().scale(scale, scale, scale)
		M = mx2*Mt1*Ms*Mt2*mx1
		
		ob.move_object(M)
			
				
	#  掃引体の交差方向 handle をセット
	ob0.switch()
	ob = ob0.son
	ob0_x.switch()
	obx = ob0_x.son

	flatL = h_flatness_list(bZs, closed)						#  中心線の各 bezier 区間の平坦性 list

	for i in range(n) :
		ob = ob.bro												#  掃引体内部の交差方向線形状
		obx = obx.bro
		pL = get_poisition_list(ob, nop, closed, mx2)			# ob の 隣接する3つの anchor point 座標の lisit
		vt = None												#  交差方向 handle の方向 ( inhandle と outhandle が平行な場合 )	
			
		#  rL :	交差方向線形状のポイントと、それに対応する掃引中心線のポイント間距離の list	
		rL = []	
		for j in range(nop) :
			rL.append(rr[i]*scaleL[j])							#  この距離は corner 配置掃引断面が受ける corner scaling を考慮しない
				
		
		for j in range(nop) :
			w = pL[j][1]										#  交差方向線形状の point 座標
			
			#  k : 	j の一つ手前	
			k = j - 1	
			if k == -1 :												
				k = nop - 1
				
			# kk :	j の一つ先
			kk = j + 1	
			if kk == nop :												
				kk = 0
			
			if has_in_handle[j] or has_out_handle[j] :
				#  s1, s2 :	曲がりによる  Ls の inhandle, outhandle 側 補正係数
				if (not closed and j == 0) or (not has_in_handle[j]) :
					s1 = 0	
				else :
					vp1 = sweep_plane_normal(bZs[k][3], bZs[k][2], bZs[k][1], bZs[k][0])	#  inhandle 側 vp
					u1 = bZs[k][2] - bZs[k][3]												#  掃引中心線の inhandle ベクトル
					uL1 = u1.norm()															#  掃引中心線の inhandle 長さ
					p1 = bZs[k][2]															#  掃引中心線の inhandle 座標
					q = bZs[k][3]															#  掃引中心線の point 座標
					if flatL[j] or flatL[j + 1] or not smooth_handle[j] :		#  掃引中心線の当該 point  前後の bezier 区間のいずれかが平坦、あるいは not smooth handle
						hL1 = handle_length(w, u1, q, r, p1, vp1, cornerL[j])
					else :
						vs1 =  -get_vt(pL[j], bZs[k][3], r)
						hL1 = handle_length(w, vs1, q, r, p1, vp1, cornerL[j])
					s1 = max(0.1, hL1/uL1)													#  曲がりによる  Ls の scaling factor

					
				if (not closed and j == nop - 1) or (not has_out_handle[j]) :
					s2 = 0	
				else :
					vp2 = sweep_plane_normal(bZs[j][0], bZs[j][1], bZs[j][2], bZs[j][3])	#  outhandle 側 vp
					u2 =  bZs[j][1] - bZs[j][0]												#  掃引中心線の outhandle ベクトル
					uL2 = u2.norm()															#  掃引中心線の outhandle 長さ
					p2 = bZs[j][1]															#  掃引中心線の outhandle 座標
					q = bZs[j][0]															#  掃引中心線の point 座標
					if flatL[j] or flatL[j + 1] or not smooth_handle[j] :		#  掃引中心線の当該 point  前後の bezier 区間のいずれかが平坦、あるいは not smooth handle
						hL2 = handle_length(w, u2, q, r, p2, vp2, cornerL[j])
					else :
						vs2 = get_vt(pL[j], bZs[j][0], r)
						hL2 = handle_length(w, vs2, q, r, p2, vp2, cornerL[j])
					s2 = max(0.1, hL2/uL2)													#  曲がりによる  Ls の scaling factor

				
				#  vh1, vh2 :	scaling による 交差方向線形状の inhandle, outhandle の傾きを表すベクトル
				if (not cornerL[j]) and (not in_terminal[j]) and (not out_terminal[j]) :	#  inhandle と outhandle が平行な smooth handle の場合
					vh1 = get_vh_A(s1*Ls[k], s2*Ls[j], rL[k], rL[j], rL[kk])
					vh2 = vh1
					
				if cornerL[j] or in_terminal[j] :			#  corner handle あるいは out 方向が重点 or 終端となっている terminal handle
					k1 = j - 1
					k2 = j - 2
					if closed :
						if k1 < 0 :
							k1 = nop - 1
							k2 = nop - 2
						if k2 < 0 :
							k2 = nop - 1
					
					if not cornerL[k1] and not out_terminal[k1] and k2 != -1 :
						vh1 = get_vh_B(-s1*Ls[k1], -s1*Ls[k2], rL[j], rL[k1], rL[k2])
					else :
						vh1 = vec3(s1*Ls[k1], rL[j] - rL[k1], 0)
						vh1.norm()
								
				if cornerL[j] or out_terminal[j] :		#  corner handle あるいは in 方向が重点 or 始端となっている terminal handle
					k1 = j + 1
					k2 = j + 2
					if closed :
						if k1 == nop :
							k1 = 0
							k2 = 1
						if k2 == nop :
							k2 = 0
							
					if not cornerL[k1] and not out_terminal[k1] and k2 != nop :
						vh2 = get_vh_B(s2*Ls[j], s2*Ls[k1], rL[j], rL[k1], rL[k2])
					else :
						vh2 = vec3(s2*Ls[j], rL[k1] - rL[j], 0)
						vh2.norm()	
						
						
				#  ww :		scaling を施していない交差方向線形状の point 座標	
				ww = vec3(obx.control_point(j).position)*mx2

			
			#  inH :	inhandle 座標
			if not has_in_handle[j] :
				inH = w
			else :
				if (j == 0) and not closed :
					pass
				else :
					Qg = labo.attitude_control_quaternion(vec3(-1, 0, 0), vec3(0, 1, 0), u1, ww - q, 0)

					if flatL[j] or flatL[j + 1] or not smooth_handle[j] :	#  掃引中心線の当該 point  前後の bezier 区間のいずれかが平坦、あるいは not smooth handle
						if Qg != None :									
							vt = Qg*(-vh1)
							inH = sweep_handle_x(w, vt, r, p1, vp1)			#  inhandle 座標
						else :												#  ww = q
							inH = sweep_handle_x(w, u1, r, p1, vp1)			#  inhandle 座標
					else :
						if Qg != None :
							Qg2 = quaternion().slerp(u1, vs1)
							vt = Qg2*Qg*(-vh1)								#  符号の付け方に注意
							vt.norm()	
							inH = sweep_handle_x(w, vt, r, p1, vp1)			#  inhandle 座標
						else :												#  ww = q
							inH = sweep_handle_x(w, u1, r, p1, vp1)			#  inhandle 座標	

					
			#  outH :	outhandle 座標
			if not has_out_handle[j] :
				outH = w
			else :
				if (j == nop - 1) and not closed :
					pass
				else :
					Qg = labo.attitude_control_quaternion(vec3(1, 0, 0), vec3(0, 1, 0), u2, ww - q, 0)

					if flatL[j] or flatL[j + 1] or not smooth_handle[j] :	#  掃引中心線の当該 point  前後の bezier 区間のいずれかが平坦、あるいは not smooth handle
						if Qg != None :
							vt = Qg*vh2
							outH = sweep_handle_x(w, vt, r, p2, vp2)		#  outhandle 座標
						else :												#  ww = q
							outH = sweep_handle_x(w, u2, r, p2, vp2)		#  outhandle 座標

					else :	
						if Qg != None : 
							Qg2 = quaternion().slerp(u2, vs2)
							vt = Qg2*Qg*vh2
							vt.norm()
							outH = sweep_handle_x(w, vt, r, p2, vp2)		#  outhandle 座標
						else :												#  ww = q
							outH = sweep_handle_x(w, u2, r, p2, vp2)		# outhandle 座標	

				
			#  開いた中心線の 始端/終端 における外側に伸びる handle 座標		
			if (j == 0) and not closed and has_in_handle[j] :
				u = w - outH
				u.norm()
				v = ps - bZs[0][0]
				a = v.norm()
				inH = a/u.dot(v)*u + w
					
			if (j == nop - 1) and not closed and has_out_handle[j] :
				u = w - inH
				u.norm()
				v = pe - bZs[nop - 2][3]
				a = v.norm()
				outH = a/u.dot(v)*u + w


			#  handle 座標をセット
			ob.control_point(j).in_handle = inH*mx1
			ob.control_point(j).out_handle = outH*mx1					
			ob.control_point(j).linked = linked[j]
	
		
	ob0_x.remove()
	ob0.switch()
	ob0.activate()
	ob0.disclosed = False
	
	return ob0
	
	#####  追加 / 変更  ここまで  #########################################################################

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

#  注記 :	掃引断面が中心線に対して直交していることが前提 ( 直交していないと、いびつな形状になる )
#			中心線に垂直に近い立ち上がり部分があると、綺麗な形状は得られにくくなる

scene = xshade.scene()

#  dialog uuid は Online GUID Generator により生成		https://www.guidgenerator.com
dialog = xshade.create_dialog_with_uuid('526575ba-18d8-4ef8-a49a-77e5cf06f050')

dialog.begin_tab_group('分割')
level_ = dialog.append_selection('level/90度/60度/45度/30度/22.5度/15度')
dialog.end_tab_group()

dialog.begin_tab_group('limitter')
limitter1_ = dialog.append_float('直線コーナー', '( defalt 10 )')
limitter2_ = dialog.append_float('曲線コーナー', '( defalt 3 )')
dialog.end_tab_group()

dialog.set_value(4, 10)
dialog.set_value(5, 3)

if dialog.ask('水平掃引 X'):					#####  dialog title 変更  ##############################
	level = dialog.get_value(level_)
	from math import pi
	if level == 0 :	
		threshold = pi/2	
	elif level == 1 :		
		threshold = pi/3
	elif level == 2 :	
		threshold = pi/4
	elif level == 3 :	
		threshold = pi/6
	elif level == 4 :	
		threshold = pi/8
	elif level == 5 :	
		threshold = pi/12
		
		
	limitter1 = dialog.get_value(limitter1_)
	limitter2 = dialog.get_value(limitter2_)

	#####  変更  ########################################################################
	scene.exit_modify_mode()
	[ob1, ob2, ob3, ob4] = scene.active_shapes
	
	graph = labo.simple_graph(xshade, ob3, ob4)
	if graph != None :
		ob2.copy_object(None)
		ob5 = ob2.bro
		ob6 = h_sweep_x(ob1, ob5, graph, threshold, limitter1, limitter2)
		if ob6 != None :
			ob6.activate()    
		else :
			scene.active_shapes = [ob1, ob2, ob3, ob4]      
	##################################################################################

#4

33 - 4  s_sweep_x( )


太さの変わる水平掃引 s_sweep_x( ) を [ script 33 - 5 ] に記します。

[ script 33 - 5 ]


s_sweep( ) からの変更や追加部分には行末に ############ を付しています

4つの線形状を選択して実行します。

選択順は次のうちのいずれか

  • 掃引中心線, 掃引断面, graph data, graph frame
  • 掃引中心線, 掃引断面, graph frame, graph data

import labo
from matrix import *
from quaternion import *

		

		
#  s_sweep_section_quaternion(bz as vec3 list, bp as vec3 matrix) as quaternion
#
#	bz, bp から掃引時の断面形状の姿勢を与える quaternion を返す
#	vn1, vn2 :	当該区間の 始端 / 終端 での面法線

def s_sweep_section_quaternion(bz, bp) :	
	u1 = labo.bezier_line_tangent(bz, 0)					#  outhandle 側接線ベクトル

	if u1.abs2() == 0 :
		return quaternion()									#  重点 ( 面積のない patch も重点と見なす )
	else :
		v1 = labo.bezier_surface_v_tangent_2(bp, 0., 0.)	#  始端での V 方向接線ベクトル
#		if (v1 == None) or (v1.abs2 == 0) :					#  V 方向につぶれた patch		( 注記-2 参照 )
#			return None										#  サポートされない自由曲面	( 注記-2 参照 )
			
	u2 = labo.bezier_line_tangent(bz, 1)					#  inhandle 側接線ベクトル
	v2 = labo.bezier_surface_v_tangent_2(bp, 0., 1.)		#   終端での V 方向接線ベクトル
#	if (v2 == None) or (v2.abs2 == 0) :						#  V 方向につぶれた patch		( 注記-2 参照 )
#		return None											#  サポートされない自由曲面	( 注記-2 参照 )
	
	vn1 = u1*v1			#  注記-1 参照
	vn2 = u2*v2			#  注記-1 参照
	return labo.attitude_control_quaternion(u1, vn1, u2, vn2, 0)	
		
#	注記-1:
#		vn1, vn2 を直接 bezier_surface_normal( ) あるいは bezier_surface_normal_2( ) で求めてはダメ
#
#	注記-2:
#		V 方向につぶれた patch を検出した場合、サポートされない自由曲面として None を返すが、
#		既に実行した関数 bezier_divide_by_total_curvature_or_torsion( ) でチェック済みであるので不要


			
				
#  sweep_plane_normal(p0 as vec3, p1 as vec3, p2 as vec3, p3 as vec3) as vec3
#
#	掃引中心線の bezier data bz (p0, p1, p2) から、掃引体交差方向の handle 長さを求めるための面法線を返す
#	基準となる anchor point に handle  が出ていることが前提
#
#		p0, p1, p2, p3 :	outhandle 側を求める場合は		bz[0], bz[1], bz[2], bz[3]
#							inhandle 側を求める場合は		bz[3], bz[2], bz[1], bz[0]

def sweep_plane_normal(p0, p1, p2, p3) :
	from math import pi, acos, sin, atan
	
	v1 = p1 - p0					#  v1.abs() != 0 が前提
	v2 = p2 - p1
	v1.norm()
	v2.norm()
	
	if v2.abs2() == 0 :
		v3 = p3 - p1
		v3.norm()
		if v3.abs2() == 0 :
			return v1
		else :
			vn = v1 + v3
			vn.norm()
			vp = v1 + vn
			vp.norm()
			return vp
		
	else :
		a = v1.dot(v2)
		if abs(a) >= 0.99999994 :
			return v1
		
		elif a >= 0 :
			vn = v1 + v2
			vn.norm()
			
			sinA = a
			cosA = (1 - sinA**2)**0.5
			beta = (pi - acos(a))/2
			r = (1 - sinA)/3
			
			axis = v1*v2
			axis.norm()
			v = v1 - v2
			v.norm()
			u = v/sin(beta) + v1*r/cosA
			u.norm()
			phi = acos(u.dot(v))

		else :
			axis = v1*v2
			phi = atan(4./3) - pi/4
			Q = quaternion().rotation(axis, pi/4)
			vn = Q*v1

		v3 = p3 - p2
		v3.norm()
		if v3.abs2() == 0 :
			return vn
		else :
			d = v1.dot(v2)
			w = -(v1-2*d*v2)
			w.norm()
			e = v3.dot(w)
			Q = quaternion().rotation(axis, e*phi)
			vp = Q*vn
			vp.norm()
			return vp

	
	
#####  h_sweep_handle( ) から変更  #############################################	

#  sweep_handle_x(w as vec3, vr as vec3, r as float, p as vec3, vp as vec3) as vec3
#
#	handle 座標を返す

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

	d1 = vp.dot(w - p)			#  点 w と 面 PL1 との符号付き距離
	d2 = vp.dot(w + r*vr - p)	#  点 w + r*vr と 面 PL1 との符号付き距離
	d = r*d1/(d1 - d2)			#  点 w を通り vr 方向に進む直線と、面 PL1 との交点までの 符号付き距離
		
	if d >= 0 :
		if d1 > 0 :				#  点 w が基準面 PL1 の外側にある
			d /= 12	
	else :
		d /= -12			
		
	return w + d*vr
		
########################################################################
	

		
#  bezier_inflection_point( bz as vec3 list) as float list
#
#		bz : bezier control point 座標を格納した vec3 list
#		bezier line bz の屈曲点位置の bezier parameter を返す。
#		屈曲点がない場合は、空 list が返される

def bezier_inflection_point(bz) :
	w1 = (bz[1] - bz[0])*(bz[2] - bz[3])
	w2 = (bz[2] - bz[1])*(bz[3] - bz[0])
	w3 = (bz[2] - bz[0])*(bz[1] - bz[3])
	r1 = w1.norm()
	r2 = w2.norm()
	r3 = w3.norm()
	L = [[r1, w1], [r2, w2], [r3, w3]]
	L.sort()
	vn = L[2][1]
	if vn.norm() == 0 :				#  bz は直線であり、変曲点は存在しない
		return []

	#  bz2 : 原点を通り面法線 vn なる面に bz を投影	
	bz2 = []
	for i in range(4) :
		d = vn.dot(bz[i])
		bz2.append(bz[i] - d*vn)
	
	#  Q : bz2 の階差表現
	bz2 = matrix(False, bz2)
	Md = labo.bezier_d()
	Q = bz2*Md
	
	#  こちらの方法でも可
#	bz2 = matrix(True, bz2)
#	Md = labo.bezier_d()
#	Md.transpose()
#	Q = Md*bz2			

	A = Q[2]*Q[3]
	B = Q[1]*Q[3]
	C = Q[1]*Q[2]
	
	bb = labo.vec3_bounding_box_size(Q)
	bb.pop()
	bb2 = sorted(bb)
	for i in range(3) :
		if bb2[0] == bb[i] :
			a = A[i]
			b = B[i]
			c = C[i]
			t = []
			if a != 0 :
				d = b**2 - 4*a*c
				if d > 0 :
					tt = (-b - d**0.5)/(2*a)
					if tt > 0 and tt < 1 :
						t.append(tt)
					tt = (-b + d**0.5)/(2*a)
					if tt >= 0 and tt <= 1 :
						t.append(tt)
				elif d == 0 :
					tt = -b/(2*a)
					if tt > 0 and tt < 1 :
						t.append(tt)
			elif b != 0 :
				tt = -c/b
				if tt > 0 and tt < 1 :
					t.append(tt)
			
			if len(t) == 1 :
				v1 = labo.bezier_line_tangent(bz, 0)
				v2 = labo.bezier_line_tangent(bz, 1)
				v0 = labo.bezier_line_tangent(bz, t[0])
				if v0.dot(v1) >= 0.9999 or  v0.dot(v2) >= 0.9999 :	#  屈曲点での接線ベクトルが、始端 / 終端での接線ベクトルと
					t.pop()											#  ほとんど同じであれば、その屈曲点では分割しない
					
			elif len(t) == 2 :
				t.sort()	
				v1 = labo.bezier_line_tangent(bz, 0)
				v2 = labo.bezier_line_tangent(bz, 1)
				v0 = labo.bezier_line_tangent(bz, t[1])
				if v0.dot(v2) >= 0.9999 :							#  屈曲点での接線ベクトルが、終端での接線ベクトルと
					del t[1]										#  ほとんど同じであれば、その屈曲点では分割しない
				v0 = labo.bezier_line_tangent(bz, t[0])
				if v0.dot(v1) >= 0.9999 :							#  屈曲点での接線ベクトルが、始端での接線ベクトルと
					del t[0]										#  ほとんど同じであれば、その屈曲点では分割しない
			return t
				
	


#  bezier_divide_by_total_curvature_or_torsion( bz as vec3 list, bp, as vec3 matrix, threshold1 as float, threshold2 as float) as float list
#
#		bz : 			bezier control point 座標を格納した vec3 list
#		bp : 			bezier patch
#		threshold1 :	分割しきい値 ( 全曲率ベース )
#		threshold2 :	分割しきい値 ( ねじれベース )
#
#		bezier line bz を分割する bezier parameter を返す。
#		当該区間の全曲率あるいは bP のねじれの変化量の内、大なる方を分割基準とする
#		分割がない場合は、空 list が返される	
#		面法線が求まらない = ねじれが求まらない 場合は、None が返される

def bezier_divide_by_total_curvature_or_torsion(bz, bp, threshold1, threshold2) :
	from math import acos, pi

	paramL = []								#  分割点の bezier parameter を格納する list
	
	u1 = labo.bezier_line_tangent(bz, 0)
	if u1.abs2() == 0 :
		return paramL						#  重点による長さのない区間
	else :
		uu1 = u1
		
	u2 = labo.bezier_line_tangent(bz, 1)
	
	a = u1.dot(u2)
	if abs(a) > 0.99999994 :
		w = bz[3] - bz[0]
		if w.abs2() == 0 :
			return paramL					#  重点とみなす
		elif a < - 0.99999994 :
			w.norm()
			if abs(w.dot(u1)) > 0.99999994 :		#  判定 - 1
				return paramL				#  重点と同じ扱いにする  
		
	v1 = labo.bezier_surface_v_tangent_2(bp, 0., 0.)
	if (v1 == None) or (v1.abs2() == 0) :	
		return None							#  patch V 方向がつぶれている
	else :
		vn1 = u1*v1
		vn1.norm()
		vvn1 = vn1	
	

	#  8 分割で全曲率 theta1 と ねじれ theta2 を求める
	theta1, theta2 = 0, 0
	for i in range(1, 9) :
		t = i/8.	
		u2 = labo.bezier_line_tangent(bz, t)
		v2 = labo.bezier_surface_v_tangent_2(bp, 0., t)	
		if (v2 == None) or (v2.abs2() == 0) :	
			return None						#  patch V 方向がつぶれている あるいは V 方向自己交差
			
		cosT1 = max(-1, min(1, u1.dot(u2)))
		theta1 += acos(cosT1)				#  全曲率
		
		vn2 = u2*v2
		vn2.norm()
		Q = quaternion().slerp(u1, u2)		#  上記の 判定 - 1 より、Q != None
		w = Q*vn1	
		
		cosT2 = max(-1, min(1, w.dot(vn2)))
		theta2 += abs(acos(cosT2))			#  ねじれ
		
		u1 = u2
		vn1 = vn2
		

	#  theta1/threshold1 と theta2/threshold2 の比較から、分割基準のベースを選択して分割
	pL = [0.]								#  各区間を分割する bezer parameter を格納する list
	cL = [0.]								#  各区間までの全曲率を格納する list
	a1 = theta1/threshold1
	a2 = theta2/threshold2

	if a1 > 0.2 or a2 > 0.2 :
		if a1 > a2 :
			make_total_curvature_list(bz, 0., uu1, 1., u2, pL, cL, 8)	#  最適分割を行って cL[], pL[] にデータを格納する 再帰関数
			theta = cL[len(cL) - 1]									#  全曲率
			if theta > threshold1 :									#  当該 bezier 区間の全曲率がしきい値を越えている
				n = int((theta - 0.00001)/threshold1) + 1			#  分割数 ( 丸め誤差による分割数の不均一防止のため 0.00001 を減ずる )
				dc = theta/n				
				paramL = [0.]										#  分割点の bezier parameter を格納 (最初は 0. )
				j = 1
				for i in range(1, n) :
					c = i*dc
					while c > cL[j] :
						j += 1
					paramL.append(pL[j] - (pL[j] - pL[j - 1])*(cL[j] - c)/(cL[j ] - cL[j - 1]))	#  分割点の bezier parameter
				paramL.append(1.)												#  分割点の bezier parameter list の最後に 1 を追加
			
			
		else :				
			make_torsion_list(bp, 0., uu1, vvn1, 1., u2, vn2, pL, cL, 8)		#  最適分割を行って cL[], pL[] にデータを格納する 再帰関数
			theta = cL[len(cL) - 1]								#  ねじれ
		
			if theta > threshold2 :								#  当該 bezier 区間のねじれ量がしきい値を越えている
				n = int((theta - 0.00001)/threshold2) + 1		#  分割数 ( 丸め誤差による分割数の不均一防止のため 0.00001 を減ずる )
			
				#   純粋にねじれ量をベースにして分割すると、曲がりに対する分割がうまくいかない 
				#  ベースとなる自由曲面との親和性も項考慮すれば、両者を折衷して、単純な parameter ベースでの分割を採用する
			
#				dc = theta/n				
#				paramL = [0.]								#  分割点の bezier parameter を格納 (最初は 0. )
#				j = 1
#				for i in range(1, n) :
#					c = i*dc
#					while c > cL[j] :
#						j += 1
#					paramL.append(pL[j] - (pL[j] - pL[j - 1])*(cL[j] - c)/(cL[j ] - cL[j - 1]))	#  分割点の bezier parameter
#				paramL.append(1.)	
			
				paramL = [float(i)/n for i in range(n + 1)]			#  分割点の bezier parameter を格納 (最初は 0. 最後は 1.)
		
	return paramL
	
	
	

#  make_total_curvature_list(bz as vec3 list, t1 as float, v1 as vec3, t2 as float, v2 as vec3, pL as float list, cL as float list, n as int)
#
#		bz の parameter 区間 t1〜t2 を最適サイズに分割して、cL[], pL[] にデータを格納する再帰関数
#
#		bz : 		bezier control point 座標を格納した vec3 list
#		t1, v1 :	計算対象区間の 始端 bezier parameter と その点での接線ベクトル	
#		t2, v2 :	計算対象区間の 終端 bezier parameter と その点での接線ベクトル
#		pL :		各区間を分割する bezer parameter を格納
#		cL :		分割された各区間までの全曲率を格納
#		n :			分割数 ( bezier parameter t1〜t2 を n 分割する )

def make_total_curvature_list(bz, t1, v1, t2, v2, pL, cL, n) :
	from math import acos
	
	u = v1
	s1 = t1
	ds = (t2 - t1)/n								#  parameter  増分
	
	for i in range(1, n + 1) :						#  分割数 n で分割
		s2 = s1 + ds

		if i < n :
			v = labo.bezier_line_tangent(bz, s2)	# parameter t での接線ベクトル
		else :
			v = v2
			
		cosT = max(-1, min(1, u.dot(v)))
		
		if cosT >= 0.99966 :						#  0.99966 = cos(1.5度)
			pL.append(s2)
			cL.append(cL[len(cL) - 1] + acos(cosT))	
		else :
			theta =  acos(cosT)
			m = max(2, 1 + int(theta*180/pi/1.5))					#  分割数
			make_total_curvature_list(bz, s1, u, s2, v, pL, cL, m)	#  再分割
			
		u = v
		s1 = s2
		

		
		
#  make_torsion_list(bp as vec3 matrix, t1 as float, uu1 as vec3, vvn1 as bec3, t2 as float, uu2 as vec3, vvn2 as vec3, pL as float list, tL as float list, n as int)
#
#		bp の 区間 t1〜t2 を最適サイズに分割して、cL[], pL[] にデータを格納する再帰関数
#
#		bp : 			bezier patch bp
#		t1, u1, vn1 :	計算対象区間の 始端 bezier parameter と その点での接線ベクトルと面法線	
#		t2, u2, vm2 :	計算対象区間の 終端 bezier parameter と その点での接線ベクトルと面法線
#		pL :			各区間を分割する bezer parameter を格納
#		tL :			分割された各区間までの全曲がり量を格納
#		n :				分割数 ( bezier parameter t1〜t2 を n 分割する )

def make_torsion_list(bp, t1, uu1, vvn1, t2, uu2, vvn2, pL, tL, n) :
	from math import acos, pi

	u1 = uu1
	vn1 = vvn1
	s1 = t1
	ds = (t2 - t1)/n										#  parameter  増分

	for i in range(1, n + 1) :								#  分割数 n で分割
		s2 = s1 + ds
		if i < n :
			u2 = labo.bezier_surface_u_tangent(bp, 0., s2)
			v2 = labo.bezier_surface_v_tangent_2(bp, 0., s2)
			vn2 = u2*v2
			vn2.norm()
												
		else :
			u2 = uu2
			vn2 = vvn2
		
		Q = quaternion().slerp(u1, u2)
		w = Q*vn1
		cosT = max(-1, min(1, w.dot(vn2)))
				
		#  捻れ量ベースの N 分割は求めないので、曲がりの時よりも最小角を大きめにとる
		if cosT >= 0.998629 :								#  0.998629 = cos(3度)	
			pL.append(s2)
			tL.append(tL[len(tL) - 1] + acos(cosT))	
		else :
			theta = acos(cosT)
			m = max(2, 1 + int(theta*180/pi/3))				#  分割数
			make_torsion_list(bp, s1, u1, vn1, s2, u2, vn2, pL, tL, m)	#  再分割
	
		u1 = u2	
		vn1 = vn2
		s1 = s2
		

	

#  get_smooth_handle(p1 as vec3, p2 as vec3, p3 as vec3) as bool
#
#	in handle, anchor point, out handle が一直線に並んでいるかを返す
#	p1 :		in handle 座標
#	p2 :		anchor point 座標
#	p3 :		out handle 座標	
	
def get_smooth_handle(p1, p2, p3) :
	v1 = p2 - p1				#  ここに入る前に v1, v2 に長さがあることは確認済み
	v2 = p3 - p2
	v1.norm()
	v2.norm()
	return v1.dot(v2) >= 0.99999994
	
	
	
	
#  get_poisition_list(ob as shape, nop as int, closed as bool, mx2 as matrix) as vec3 list
#		ob :		掃引体交差方向線形状
#		nop :		ob の ポイント数
#		closed :	ob の開閉情報
#		mx2 :		ob の xshade.scene().local_to_world_matrix
#
#		ob の 隣接する3つの anchor point 座標の lisit の list [[vec3, vec3, vec3], [vec3, vec3, vec3}, ‥]  を返す

def get_poisition_list(ob, nop, closed, mx2) :
	pL = []

	p2 = vec3(ob.control_point(1).position)*mx2
	p1 =  vec3(ob.control_point(0).position)*mx2
	if not closed :	
		p0 = p1	
	else :														
		p0 = vec3(ob.control_point(nop - 1).position)*mx2
	pL.append([p0, p1, p2])
	
	for i in range(2, nop) :
		p0 = p1
		p1 = p2
		p2 = vec3(ob.control_point(i).position)*mx2
		pL.append([p0, p1, p2])
		
	p0 = p1
	p1 = p2
	if not closed :
		p2 = p1
	else :
		p2 = pL[0][1]
	pL.append([p0, p1, p2])
	
	return pL
	
	
	
	
#  s_flatness_list(bZs as vec3 list, bPs as vec3 matrix list, closed as bool) as bool list
#		bZs :		掃引中心線の control point 座標 list
#		bPs :		掃引中心線を含む bezier patch list
#		closed :	中心線の開閉情報
#
#		bPs の各 patch 区間の 非ねじれ list を返す
#
#		list の始端と終端には次なる項目を追加
#			中心線が閉じている場合、その隣接項目
#			中心線が開いている場合、False

def s_flatness_list(bZs, bPs, closed):
	flatL = []
	n = len(bPs)
	for i in range(n) :
		bz = bZs[i]
		bp = bPs[i]
		
		u1 = labo.bezier_line_tangent(bz, 0)	#  outhandle 側接線ベクトル
		u2 = labo.bezier_line_tangent(bz, 0.5)	#  中点での接線ベクトル
		
		if u1.abs2() == 0 :						#  重点
			flatL.append(False)
		
		else :
			v1 = labo.bezier_surface_v_tangent_2(bp, 0., 0.)	#  ここの関数が呼ばれる時点で、v1, v2  が求まることは既に担保されている
			v2 = labo.bezier_surface_v_tangent_2(bp, 0., 0.5)
			vn1 = u1*v1
			vn1.norm()
			vn2 = u2*v2
			vn2.norm()
			Q = quaternion().slerp(u1, u2)
			w = Q*vn1
			flatL.append(w.dot(vn2) >= 0.99985)	#   本来なら しきい値は 0.9999 とするが、ベースとなる自由曲面の持つ理想形状との誤差を考慮し、若干緩めに設定
		
	#   関数 h_sweep_handle( ) に与える引数の選択において、平坦性 list fL の隣接二値を比較するため、 list の先頭と終端に値を追加
	if not closed :
		flatL.insert(0, True)
		flatL.append(True)
	else :
		flatL.insert(0, flatL[len(flatL) - 1])
		flatL.append(flatL[1])
		
	return flatL
	
	

#  get_vt(w as vec3 list, q as vec3, r as float) as vec3
#
#		交差線形状の handle の方向を与える vector を返す
#		w[0] :	交差線形状の当該 point の一つ手前の point 座標
#		w[1] :	交差線形状の当該 point の座標
#		w[2] :	交差線形状の当該 point の一つ先の point 座標
#		q :		中心線 point 座標
#		r :		中心線 boinding box size / 16

def get_vt(w, q, r) :
	#  w[0] = w[1] = w[2] となる三重点のケースではここに入らない
	v1 = w[0] - w[1]
	v2 = w[2] - w[1]
	v1.norm()
	v2.norm()                    
	v0 = v2 - v1
	v0.norm()
	
	v = w[1] - q
	a = v.norm()
	if a < r*1e-5 :		# w[1]  と q が一致しているとみなす
		vt = v0
	else :
		d = v.dot(v0)
		vt = v0 - d*v
		vt.norm()
		
	return vt
		
		

#####  追加  ########################################################################

#  bezier_abs_derivative( bz as vec3 list, t as float ) as float
#
#	コントロールポイント座標 list bz で与えられる bezier 曲線の
#	パラメータ t における微分ベクトル の絶対値を返す

def bezier_abs_derivative(bz, t) :
	v = -3*(1 - t)**2*bz[0] + 3*(1 - t)*(1 - 3*t)*bz[1] + 3*t*(2 - 3*t)*bz[2] + 3*t**2*bz[3]			
	return v.abs()
	
	
	
		
#  make _x_list(bZs as vec3 list, closed as bool) as float list, float list
#
#	bZs の各区間の長さと、それを規格化した list を返す
#
#	bZs :	掃引中心線の bezier control point 座標を格納した vec3 list
	
def make_x_list(bZs) :
	totalL = 0
	xL = []
	Ls = []
	for bz in bZs :
		if bz[0] != bz[1] or bz[1] != bz[2] or bz[2] != bz[3] :
			r = labo.bezier_simpson(bezier_abs_derivative, bz, 0, 1)	#  parameter 区間 0~1 で線長を求める
			totalL += r
		else :
			r = 0				#  bz[0] == bz[1] or bz[1] == bz[2] or bz[2] == bz[3] であっても、bezier_simpson( ) で bz の長さは 0 にならない
		xL.append(totalL)
		Ls.append(r)			#  bZs の各区間の長さ list
		
	n = len(xL)
	for i in range(n) :
		xL[i] /= totalL			#  bZs の各区間の長さを規格化した list
		
	return Ls, xL
		

				

#  make_scaleL_Ls(bZs as vec3 list, closed as bool, graph as simple_graph) as float list, float list
#
#	bZs の各 anchor point における グラフ指定による scale 値の list と bZs の各 bezier 区間の長さの list を返す
#	
#	bZs :	掃引中心線の bezier control point 座標を格納した vec3 list
#	closed :	掃引中心線の開閉情報
#	graph :	simple_graph

def make_scaleL_Ls(bZs, closed, graph) :		
	Ls, xL = make_x_list(bZs)			#  Ls : bZs の各区間の長さ list  		
	xL.insert(0, 0.)	
	scaleL = []
	for x in xL :						#  xL : bZs  の各区間の長さを規格化した list
		scale = graph.y(x)
		scaleL.append(scale)			#  scaleL : bZs の各 anchor point におけるグラフ指定 scale 値の list
	if closed :
		scaleL.pop()
		scaleL.append(scaleL[0])
		
	return scaleL, Ls
	
	
	
	
#  handle_length(w as vec3, u as vec3, q as vec3, r as float, p as vec3, vp as vec3, corner as bool) as float
#
#	dummy handle 長さを返す
#
#	w :	断面ポイント座標
#	u :	掃引中心線接線ベクトル
#	q :	掃引中心線 point 座標
#	r :	掃引中心線 bounding box size / 16
#	p :	掃引中心線の handle 座標
#	vp :		基準面法線
#	corner :	corner flag

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

	d1 = vp.dot(w - p)			#  点 w と 面 PL1 との符号付き距離
	d2 = vp.dot(w + r*u - p)	#  点 w + r*u と 面 PL1 との符号付き距離
	d = r*d1/(d1 - d2)			#  点 w を通り u 方向に進む直線と、面 PL1 との交点までの符号つき距離
	
	if corner :
		a = u.dot(w - q)		#  点 w と面 PL0 との符号つき距離	
		if a > 0 :
			d += a				#  d が長くなる場合のみ補正を行う
	
	return abs(d)
	
	
	
				
#  get_vh_A(L1 as float, L2 as float, r0 as float, r1 as float, r2 as float) as vec3
#
#	scaling に伴う交差方向線形状 handle の傾きを表すベクトルを返す
#
#	L1, L2 :		X 方向長さ ( scaling factor は適用済み )
#	r0, r1, r2 :	Y 方向長さ

def get_vh_A(L1, L2, r0, r1, r2) :
	#  3点のポイントの X, Y size を規格化したものに smooth をかけた smooth vector を作り、
	#  その Y size を original の proportion に戻す
	
	#  x0, x1, x2, y0, y1, y2 : 3点のポイントの X, Y size を規格化
	sx = 1/(L1 + L2)
	x0 = 0
	x1 = L1*sx
	x2 = 1
	
	r = (abs(r1 - r0) + abs(r2 - r1))
	if r == 0 :						#  r0 = r1 = r2 = 0 であり、断面ポイント座標と掃引中心線の配置ポイント座標が一致している
		return vec3(1, 0, 0)
	else :
		sy = 1/r
	y0 = 0
	y1 = (r1 - r0)*sy
	y2 = (r2 - r0)*sy
	
	#  v : 規格化された3点の中央の point に smooth をかけて得られる smooth vector
	p0 = vec3(x0, y0, 0)
	p1 = vec3(x1, y1, 0)
	p2 = vec3(x2, y2, 0)

	u0 = p0 - p1
	u2 = p2 - p1
	a0 = u0.norm()
	a2 = u2.norm()
	v = a0*u2 - a2*u0

	#  smooth vector w の Y size を original の proportion に戻す
	vh = vec3(v[0], v[1]*sx/sy, 0)
	vh.norm()
	
	return vh
	
	
	

#  get_vh_B(L1 as float, L2 as float, r0 as float, r1 as float, r2 as float) as vec3
#
#	開いた線形状の terminal 点における scaling に伴う交差方向線形状 handle の傾きを表すベクトルを返す
#
#	L1, L2 :		X 方向長さ ( scaling factor は適用済み )
#	r0, r1, r2 :	Y 方向長さ

def get_vh_B(L1, L2, r0, r1, r2) :
	tan1 = (r1 - r0)/L1
	tan2 = (r2 - r1)/L2
				
	if abs(tan2) < abs(tan1) :
		a = 2
	else :
		a = 3
		
	tan0 = tan1 - (tan2 - tan1)/a
	vh = vec3(1, tan0, 0)
	vh.norm()
	
	return vh
		
	
#####  追加  ここまで  ###################################################################


		
		
#  s_sweep_x(ob1 as shape, ob2 as shape, graph as simple_graph, threshold1 as float, threshold2 as float, limitter1 as float = 10., limitter2 as float = 3.) as shape
#
#	掃引体を Shade 上に作成し、その shape を返す
#		ob1 :		掃引中心線形状
#		ob2 :		掃引断面形状
#		graph :		simple_graph object
#		threshold1:	分割しきい値 ( 全曲率ベース )
#		threshold2:	分割しきい値 ( 面法線ベース )
#		limitter1 :	直線コーナーの scale liitter	
#		limitter2 :	曲線コーナーの scale liitter	

def s_sweep_x(ob1, ob2, graph, threshold1, threshold2, limitter1 = 10., limitter2 = 3.) :	
	if (ob1 == None) or (ob2 == None) :
		return	
		
	##  引数として渡された object の check
	if (not isinstance(ob1, xshade.line)) or (not ob1.has_dad) :
		return
	if (not isinstance(ob1.dad, xshade.part)) or (ob1.dad.part_type != 1) :	
		return
	if (ob1.dad.number_of_sons < 2) or (ob1.dad.total_number_of_control_points < 4) :
		return
	if not isinstance(ob2, xshade.line) :														
		return
	
		
	#  check
	if (threshold1 < 0) or (threshold2 < 0) :
		return
	if limitter1 < 1 or limitter2 < 1 :
		return
	

	#  掃引中心線データ取得 nop, closed, bZ
	ob1.activate()
	nop = ob1.number_of_control_points		#  ポイント数
	closed = ob1.closed						#  開閉情報
	bZs = labo.get_bezier(xshade, -1)		#  全区間の bezier data [matrix, matrix, … ]
	
	
	#  掃引中心線データ取得  bP
	if not closed :
		n = nop - 1							#  U ブロック数
	else :
		n = nop
		
	ob0 = ob1.dad
	m = ob1.ordinal - ob0.ordinal - 1		#  V ブロック No.
	lastL = False

	if (not ob0.surface_closed) and (m == ob0.number_of_sons - 1) :
		m += -1
		lastL = True
		Mrv = matrix()
		Mrv.append([0, 0, 0, 1])
		Mrv.append([0, 0, 1, 0])
		Mrv.append([0, 1, 0, 0])
		Mrv.append([1, 0, 0, 0])
		
	ob0.activate()
	bPs = []					#  ob1 の bezier patch list
	for i in range(n) :
		bp = labo.get_bezier_patch(xshade, m, i)
		if lastL :
			bp = Mrv*bp		#  中心線が交差方向が開いた自由曲面の最後のラインの場合、V 方向の向きを逆転
		bPs.append(bp)
	
	
	# mx1, mx2, mx22, ps, pe
	mx1 = matrix(ob2.world_to_local_matrix)
	mx2 = matrix(ob2.local_to_world_matrix)
	if not closed :
		mx22 = matrix(ob1.local_to_world_matrix)
		ps = vec3(ob1.control_point(0).in_handle)*mx22			#  掃引体の交差方向 handle 作成時に使用
		pe = vec3(ob1.control_point(nop - 1).out_handle)*mx22	#  掃引体の交差方向 handle 作成時に使用
		
	#  r							
	bb = labo.vec3_bounding_box_size(bZs)	
	r = bb[3]/16												#  sweep_handle( ), get_vt( ) の引数に使用	
	
		
	#  掃引中心線 の handle 情報取得
	has_in_handle = []			#  掃引中心線の inhandle の有無のリスト
	has_out_handle = []			#  掃引中心線の outhandle の有無のリスト
	linked = []					#  掃引中心線の hanndle link リスト
	smooth_handle = []			#  中心線の smooth handle lリスト
	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)
		
		#  smooth_handle[ ]
		#		関数 h_sweep_handle_x( ) に与える引数選択のために in / out handle の直線性の識別に参照
		#		実 handle ベースで定義
		if has_in_handle[i] and has_out_handle[i] :
			if i == 0 :
				if closed :
					smooth_handle.append(get_smooth_handle(bZs[nop - 1][2], bZs[0][0], bZs[0][1]))
				else :
					smooth_handle.append(False)	
			elif i == nop - 1 :
				if closed :
					smooth_handle.append(get_smooth_handle(bZs[nop - 2][2], bZs[nop - 1][0], bZs[nop - 1][1]))
				else :
					smooth_handle.append(False)		
			else :
				smooth_handle.append(get_smooth_handle(bZs[i - 1][2], bZs[i][0], bZs[i][1]))		
		else :
			smooth_handle.append(False)
			
		
	#  bZs, bPs を分割	
	#   屈曲点で分割
	bZs1 = []
	bPs1 = []							
	insert_list = []
	k = 0
	
	for bz in bZs :
		bp = bPs[k]
		k += 1
		t = bezier_inflection_point(bz)
		n = len(t)
		if n == 0 :				#  屈曲点なし
			bZs1.append(bz)
			bPs1.append(bp)
		else :
			t.insert(0, 0)
			t.append(1)
			n += 2
			for i in range(n - 1) :
				bZs1.append(labo.bezier_line_subdivision(bz, t[i], t[i + 1]))
				bPs1.append(labo.bezier_patch_subdivision(bp, 0, 1, t[i], t[i + 1]))
				insert_list.append(k)
				nop += 1
			insert_list.pop()
			nop += -1

	if len(insert_list) > 0 :
		insert_list.reverse()
		for k in insert_list :
			has_in_handle.insert(k, True)
			has_out_handle.insert(k, True)
			linked.insert(k, True)
			smooth_handle.insert(k, True)

	
	if threshold1 > 0 :	
		# threshold1 / threshold2 に従って 全曲率 あるいは 面法線基準で分割
		bZs2 = []
		bPs2 = []
		insert_list = []
		k = 0
		
		for bz in bZs1 :
			bp = bPs1[k]
			k += 1
			t = bezier_divide_by_total_curvature_or_torsion(bz, bp, threshold1, threshold2)
			if t == None :	#  面法線が求まらない場合、None が返され、途中終了する
				print '基準となる自由曲面で面法線が求まらないので、終了します。'
				return
			n = len(t)
			if n == 0 : 
				bZs2.append(bz)
				bPs2.append(bp)
			else :
				for i in range(n - 1) :
					bZs2.append(labo.bezier_line_subdivision(bz, t[i], t[i + 1]))
					bPs2.append(labo.bezier_patch_subdivision(bp, 0, 1, t[i], t[i + 1]))
					insert_list.append(k)
					nop += 1
				insert_list.pop()
				nop += -1
					
		if len(insert_list) > 0 :
			insert_list.reverse()
			for k in insert_list :
				has_in_handle.insert(k, True)
				has_out_handle.insert(k, True)
				linked.insert(k, True)
				smooth_handle.insert(k, True)
				
		bZs = bZs2
		bPs = bPs2
		
		
	#####  追加  ########################################################################
	#  bZs の各区間を parameter 基準で8等分して長さの概算値を求め、区間長の平均の1.5倍の長さのある区間を分割する
	Ls = []
	totalL = 0.
	kk = 0
	for bz in bZs :
		L = 0.
		p1 = bz[0]
		for i in range(1, 9) :
			t = float(i)/8
			p2 = labo.bezier_line_position(bz, t)
			v = p2 - p1
			L += v.abs()
			p1 = p2
		Ls.append(L)					#  区間長
		totalL += L
		if L == 0 :
			kk += 1
		
	ns = len(bZs)
	maxL = 1.5*totalL/(ns - kk)			#  区間長の平均値 x 1.5	( 長さ 0 の区間は平均長さの算定に加えない )
	
	
	bZs3 = []
	bPs3 = []
	insert_list = []
	k = 0
	for i in range(ns) :
		k += 1
		if (Ls[i] <= maxL) or (not has_out_handle[i] and not has_in_handle[kk]) :
			bZs3.append(bZs[i])
			bPs3.append(bPs[i])
		else :
			nj = int(Ls[i]/maxL) + 1	#  分割数
			param1 = 0.
			for j in range(nj) :
				param2 = float(j + 1)/nj
				bZs3.append(labo.bezier_line_subdivision(bZs[i], param1, param2))
				bPs3.append(labo.bezier_patch_subdivision(bPs[i], 0, 1, param1, param2))
				param1 = param2
				insert_list.append(k)
				nop += 1
			insert_list.pop()
			nop += -1

					
	if len(insert_list) > 0 :
		insert_list.reverse()
		for k in insert_list :
			has_in_handle.insert(k, True)
			has_out_handle.insert(k, True)
			linked.insert(k, True)
			smooth_handle.insert(k, True)
				
	bZs = bZs3
	bPs = bPs3
	
	#####  追加  ここまで  ###################################################################							
											
	
	#  ob0 : 掃引体を格納する自由曲面
	ob2.activate()
	scene.create_surface_part(None)
	ob0 = scene.active_shape()				#   掃引体を格納する自由曲面
	ob0.surface_closed = closed
	ob2.place_child(1)

	
	#  掃引断面を配置
	p1 = bZs[0][0]
	Mt1 = matrix().translate(-p1[0], -p1[1], -p1[2])
	Q1 = quaternion()
	n = nop - 1
	v2_prev = None
	vn_prev = None	
	
	#####  追加  ########################################################
	#  cornerL :	 	corner handle flag		断面交差方向 handle の corner 属性
	#  in_terminal :	in_terminal flag		断面交差方向の out 方向が重点、あるいは、開いた掃引中心線の終端
	#  out_terminal :	out_terminal flag		断面交差方向の in 方向が重点、あるいは、開いた掃引中心線の始端
	#
	#  上記の 3 flag は実際に handle が伸びているか否かは考慮せずにセットする
	#  これらの flag を参照して交差方向 handle の長さを求める際には、has_in_handle, has_out_handle で handle が伸びているか否かの screening を併用する
	
	cornerL = [False]
	if closed :
		in_terminal = [has_in_handle[0] and not has_out_handle[0]]
		out_terminal = [not has_in_handle[0] and has_out_handle[0]]
	else :
		in_terminal = [False]
		out_terminal = [True]
	#####  追加  ここまで  ################################################
	
		
	for i in range(n) :
		p2 = bZs[i][3]
		v2 = labo.bezier_line_tangent(bZs[i], 1)
		Mt2 = matrix().translate(p2[0], p2[1], p2[2])
		
		Q2 = s_sweep_section_quaternion(bZs[i], bPs[i])		
		Q2 = Q2*Q1
		Mr = Q2.matrix()
		in_term = False													########### /////////////////
		out_term = False												########### /////////////////
		corner = False													########### /////////////////
		
		if i == n - 1 and not closed :
			M = mx2*Mt1*Mr*Mt2*mx1
			in_term = True												########### /////////////////
			
		else :
			v3 = labo.bezier_line_tangent(bZs[i + 1], 0)
			if v3.abs2() == 0 :							#  重点
				M = mx2*Mt1*Mr*Mt2*mx1
				if v2_prev == None :					#  最初の重点
					v2_prev = v2
					vn_prev = labo.bezier_surface_normal_2(bPs[i], 0., 1.)
					in_term = True										########### /////////////////
				
			elif v2.abs2() == 0 :						#  最後の重点
				out_term = True										########### /////////////////
				if v2_prev == None or v2_prev.abs2() == 0 :	#  始点が重点から始まっている
					M = mx2*Mt1*Mr*Mt2*mx1	
				else :
					vn = labo.bezier_surface_normal_2(bPs[i + 1], 0., 0.)
					Qc = labo.attitude_control_quaternion(v2_prev, vn_prev, v3, vn, 0)								
					Q2 = Qc*Q2							
					Mc = Qc.matrix()						
					M = mx2*Mt1*Mr*Mc*Mt2*mx1			
					
				v2_prev = None
				vn_prev = None
				
			else :
				if v2.dot(v3) >= 0.99999994 :			#  掃引中心線の point が corner point  ではない
					M = mx2*Mt1*Mr*Mt2*mx1
				
				elif v2.dot(v3) <= -0.99999994 :		#  掃引中心線の point が 180 度 corner point
					corner = True										########### /////////////////
					M = mx2*Mt1*Mr*Mt2*mx1
					vn = labo.bezier_surface_normal_2(bPs[i + 1], 0., 0.)
					Qc = labo.attitude_control_quaternion(v2, -vn, v3, vn, 0)
					Q2 = Qc*Q2							#   次の point での掃引断面配置に必要

				else :									#  掃引中心線の point が corner point 
					w = v2 + v3
					w.norm()
					corner = True										########### /////////////////
					v1 = labo.bezier_line_tangent(bZs[i], 0)
					v4 = labo.bezier_line_tangent(bZs[i + 1], 1)

					if v1.dot(v2) + v3.dot(v4) > 1.98 :	#  直線コーナー
						limitter = limitter1
					else :								#  曲線コーナー
						limitter = limitter2
					Q3 = quaternion().slerp(v2, w)
					Mq = Q3.matrix()
					
					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, min(limitter, 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
		cornerL.append(corner)														########### /////////////////
		in_terminal.append(in_term)													########### /////////////////
		out_terminal.append(out_term)												########### /////////////////

	
	#####  追加 / 変更  ##############################################################################
	
	#  グラフ入力から指定 scale 値を読み取り、scaleL とLs を得る
	#  scaleL : scale 値の list		Ls : 掃引中心線の各 bezier 区間の長さ list
	scaleL, Ls = make_scaleL_Ls(bZs, closed, graph)
	
	
	#  rr :  掃引断面ポイントと掃引中心線始端ポイントとの距離 list	rL 作成に使用
	n = ob2.number_of_control_points
	rr = []
	c = bZs[0][0]
	for i in range(n) :
		pp = vec3(ob2.control_point(i).position)*mx2
		v = pp - c											
		rr.append(v.abs())
		
	
	#  掃引断面を格納した自由曲面の copy ob0_x を作成、scaling を施す前の座標取得に利用し、最終的には削除する
	ob0.copy_object(matrix(4))
	ob0_x = ob0.bro
	
	
	#  グラフ入力 scale 値により、掃引断面形状を scaling 
	k = -1
	ob = ob0.son
	while ob.has_bro :
		k += 1
		ob = ob.bro
		
		if closed or k != nop - 1 :
			c = bZs[k][0]
		else :
			c = bZs[nop - 2][3]
		scale = scaleL[k]

		Mt1 = matrix().translate(-c[0], -c[1], -c[2])
		Mt2 = matrix().translate(c[0], c[1], c[2])
		Ms = matrix().scale(scale, scale, scale)
		M = mx2*Mt1*Ms*Mt2*mx1
		
		ob.move_object(M)
			
				
	#  掃引体の交差方向 handle をセット
	ob0.switch()
	ob = ob0.son
	ob0_x.switch()
	obx = ob0_x.son

	flatL = s_flatness_list(bZs, bPs, closed)					#  中心線の各 bezier patch 区間の平坦性 list				##  h_sweep_x( ) と異なる点  ///////////////

	for i in range(n) :
		ob = ob.bro												#  掃引体内部の交差方向線形状
		obx = obx.bro
		pL = get_poisition_list(ob, nop, closed, mx2)			# ob の 隣接する3つの anchor point 座標の lisit
		vt = None												#  交差方向 handle の方向 ( inhandle と outhandle が平行な場合 )	
			
		#  rL :	交差方向線形状のポイントと、それに対応する掃引中心線のポイント間距離の list	
		rL = []	
		for j in range(nop) :
			rL.append(rr[i]*scaleL[j])							#  この距離は corner 配置掃引断面が受ける scaling を考慮しない
				
		
		for j in range(nop) :
			w = pL[j][1]										#  交差方向線形状の point 座標
			
			#  k : 	j の一つ手前	
			k = j - 1	
			if k == -1 :												
				k = nop - 1
				
			# kk :	j の一つ先
			kk = j + 1	
			if kk == nop :												
				kk = 0

			if has_in_handle[j] or has_out_handle[j] :
				#  s1, s2 :	曲がりによる  Ls の inhandle, outhandle 側 補正係数
				if (not closed and j == 0) or (not has_in_handle[j]) :
					s1 = 0	
				else :
					vp1 = sweep_plane_normal(bZs[k][3], bZs[k][2], bZs[k][1], bZs[k][0])	#  inhandle 側 vp
					u1 = bZs[k][2] - bZs[k][3]												#  掃引中心線の inhandle ベクトル
					uL1 = u1.norm()															#  掃引中心線の inhandle 長さ
					p1 = bZs[k][2]															#  掃引中心線の inhandle 座標
					q = bZs[k][3]															#  掃引中心線の point 座標
					if flatL[j] or flatL[j + 1] or not smooth_handle[j] :		#  掃引中心線の当該 point  前後の bezier 区間のいずれかが平坦、あるいは not smooth handle
						hL1 = handle_length(w, u1, q, r, p1, vp1, cornerL[j])
					else :
						vs1 =  -get_vt(pL[j], bZs[k][3], r)
						hL1 = handle_length(w, vs1, q, r, p1, vp1, cornerL[j])
					s1 = max(0.1, hL1/uL1)													#  曲がりによる  Ls の scaling factor


				if (not closed and j == nop - 1) or (not has_out_handle[j]) :
					s2 = 0	
				else :
					vp2 = sweep_plane_normal(bZs[j][0], bZs[j][1], bZs[j][2], bZs[j][3])	#  outhandle 側 vp
					u2 =  bZs[j][1] - bZs[j][0]												#  掃引中心線の outhandle ベクトル
					uL2 = u2.norm()															#  掃引中心線の outhandle 長さ
					p2 = bZs[j][1]															#  掃引中心線の outhandle 座標
					q = bZs[j][0]															#  掃引中心線の point 座標
					if flatL[j] or flatL[j + 1] or not smooth_handle[j] :		#  掃引中心線の当該 point  前後の bezier 区間のいずれかが平坦、あるいは not smooth handle
						hL2 = handle_length(w, u2, q, r, p2, vp2, cornerL[j])
					else :
						vs2 = get_vt(pL[j], bZs[j][0], r)
						hL2 = handle_length(w, vs2, q, r, p2, vp2, cornerL[j])
					s2 = max(0.1, hL2/uL2)													#  曲がりによる  Ls の scaling factor

				
				#  vh1, vh2 :	scaling による 交差方向線形状の inhandle, outhandle の傾きを表すベクトル
				if (not cornerL[j]) and (not in_terminal[j]) and (not out_terminal[j]) :			#  inhandle と outhandle が平行な smooth handle の場合
					vh1 = get_vh_A(s1*Ls[k], s2*Ls[j], rL[k], rL[j], rL[kk])
					vh2 = vh1
					
				if cornerL[j] or in_terminal[j] :		#  corner handle あるいは out 方向が重点 or 終端となっている terminal handle
					k1 = j - 1
					k2 = j - 2
					if closed :
						if k1 < 0 :
							k1 = nop - 1
							k2 = nop - 2
						if k2 < 0 :
							k2 = nop - 1
					
					if not cornerL[k1] and not out_terminal[k1] and k2 != -1 :
						vh1 = get_vh_B(-s1*Ls[k1], -s1*Ls[k2], rL[j], rL[k1], rL[k2])
					else :
						vh1 = vec3(s1*Ls[k1], rL[j] - rL[k1], 0)
						vh1.norm()
								
				if cornerL[j] or out_terminal[j] :		#  corner handle あるいは in 方向が重点 or 始端となっている terminal handle
					k1 = j + 1
					k2 = j + 2
					if closed :
						if k1 == nop :
							k1 = 0
							k2 = 1
						if k2 == nop :
							k2 = 0
							
					if not cornerL[k1] and not out_terminal[k1] and k2 != nop :
						vh2 = get_vh_B(s2*Ls[j], s2*Ls[k1], rL[j], rL[k1], rL[k2])
					else :
						vh2 = vec3(s2*Ls[j], rL[k1] - rL[j], 0)
						vh2.norm()	
						
						
				#  ww :		scaling を施していない交差方向線形状の point 座標	
				ww = vec3(obx.control_point(j).position)*mx2

			
			#  inH :	inhandle 座標
			if not has_in_handle[j] :
				inH = w
			else :
				if (j == 0) and not closed :
					pass
				else :
					Qg = labo.attitude_control_quaternion(vec3(-1, 0, 0), vec3(0, 1, 0), u1, ww - q, 0)

					if flatL[j] or flatL[j + 1] or not smooth_handle[j] :	#  掃引中心線の当該 point  前後の bezier 区間のいずれかが平坦、あるいは not smooth handle
						if Qg != None :
							vt = Qg*(-vh1)
							inH = sweep_handle_x(w, vt, r, p1, vp1)			#  inhandle 座標
						else :												#  ww = q
							inH = sweep_handle_x(w, u1, r, p1, vp1)			#  inhandle 座標
					else :
						if Qg != None :
							Qg2 = quaternion().slerp(u1, vs1)
							vt = Qg2*Qg*(-vh1)								#  符号の付け方に注意
							vt.norm()	
							inH = sweep_handle_x(w, vt, r, p1, vp1)			#  inhandle 座標
						else :												#  ww = q
							inH = sweep_handle_x(w, u1, r, p1, vp1)			#  inhandle 座標	

					
			#  outH :	outhandle 座標
			if not has_out_handle[j] :
				outH = w
			else :
				if (j == nop - 1) and not closed :
					pass
				else :
					Qg = labo.attitude_control_quaternion(vec3(1, 0, 0), vec3(0, 1, 0), u2, ww - q, 0)

					if flatL[j] or flatL[j + 1] or not smooth_handle[j] :	#  掃引中心線の当該 point  前後の bezier 区間のいずれかが平坦、あるいは not smooth handle
						if Qg != None :
							vt = Qg*vh2
							outH = sweep_handle_x(w, vt, r, p2, vp2)		#  outhandle 座標
						else :												#  ww = q
							outH = sweep_handle_x(w, u2, r, p2, vp2)		#  outhandle 座標

					else :		
						if Qg != None : 
							Qg2 = quaternion().slerp(u2, vs2)
							vt = Qg2*Qg*vh2
							vt.norm()
							outH = sweep_handle_x(w, vt, r, p2, vp2)		#  outhandle 座標
						else :												#  ww = q
							outH = sweep_handle_x(w, u2, r, p2, vp2)		# outhandle 座標	

				
			#  開いた中心線の 始端/終端 における外側に伸びる handle 座標		
			if (j == 0) and not closed and has_in_handle[j] :
				u = w - outH
				u.norm()
				v = ps - bZs[0][0]
				a = v.norm()
				inH = a/u.dot(v)*u + w
					
			if (j == nop - 1) and not closed and has_out_handle[j] :
				u = w - inH
				u.norm()
				v = pe - bZs[nop - 2][3]
				a = v.norm()
				outH = a/u.dot(v)*u + w


			#  handle 座標をセット
			ob.control_point(j).in_handle = inH*mx1
			ob.control_point(j).out_handle = outH*mx1					
			ob.control_point(j).linked = linked[j]
	
		
	ob0_x.remove()
	ob0.switch()
	ob0.activate()
	ob0.disclosed = False
	
	return ob0
	
	#####  追加 / 変更  ここまで  #########################################################################	

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

scene = xshade.scene()

#  dialog uuid は Online GUID Generator により生成		https://www.guidgenerator.com
dialog = xshade.create_dialog_with_uuid('91b274c6-179a-4a48-a390-cfa6f74a1701')

dialog.begin_tab_group('分割')
level_ = dialog.append_selection('level/90度/60度/45度/30度/22.5度/15度')
dialog.end_tab_group()

dialog.begin_tab_group('limitter')
limitter1_ = dialog.append_float('直線コーナー', '( defalt 10 )')
limitter2_ = dialog.append_float('曲線コーナー', '( defalt 3 )')
dialog.end_tab_group()

dialog.set_value(4, 10)
dialog.set_value(5, 3)

if dialog.ask('曲面に沿った掃引_X'):				#####  dialog title 変更  ##############################
	level = dialog.get_value(level_)
	from math import pi
	if level == 0 :	
		threshold1 = pi/2	
	elif level == 1 :		
		threshold1 = pi/3
	elif level == 2 :	
		threshold1 = pi/4
	elif level == 3 :	
		threshold1 = pi/6
	elif level == 4 :	
		threshold1 = pi/8
	elif level == 5 :	
		threshold1 = pi/12
	threshold2 = threshold1
	limitter1 = dialog.get_value(limitter1_)
	limitter2 = dialog.get_value(limitter2_)

	#####  変更  ########################################################################
	scene.exit_modify_mode()
	[ob1, ob2, ob3, ob4] = scene.active_shapes
	
	graph = labo.simple_graph(xshade, ob3, ob4)
	if graph != None :
		ob2.copy_object(None)
		ob5 = ob2.bro
		ob6 = s_sweep_x(ob1, ob5, graph, threshold1, threshold2, limitter1, limitter2)
		if ob6 != None :
			ob6.activate()    
		else :
			scene.active_shapes = [ob1, ob2, ob3, ob4]      
	##################################################################################