Shade3D 公式

18 Custom Smooth ( 1 / 3 ) [ Shade Labo ]


#1

18 - 1  Shade Smooth の特徴


関連記事:
  • 17 Smooth

17 Smooth で述べた smooth の処理方法を規定する二つの条件については、ほぼ異論の無いところでしょう。
[ 条件 17 - 1 ]
  • P0, P1, P2 が一直線上に並んでいる場合、p1 の inhandle 長さは | P0 - P1 | / 3, outhandle 長さは | P2 - P1 | / 3

  • 4点の正方形を形作る閉じた線形状に smooth をかけた場合、得られる曲線の中点が、4点の外接円上に存在する


Shade smooth の特徴は handle の方向にあり、基本的に **丸くする** という傾向があります。

          [ fig 18 - 1 ]


この特徴は handle の方向 Vh の決定方法にあります。

          [ fig 17 - 1 ]

          [ 式 17 - 1 ]


#2

18 - 2  Custom Smooth


関連記事:
  • 17 Smooth

ここで、handle の方向を表す **Vh** の方向のカスタマイズを考えてみます。

     

     [ 式 18 - 1 ]




Vh を係数 c( 0 <= c <= 1 )を用いて次のように定義すれば、original , custom - 1, custom - 2 の全てをカバーできます。

          [ 式 18 - 2 ]




一方、handle 長さについては、ハンドル長さの係数 h は 0 < h < 1 ですので、長さを調整するべき乗の係数 d を与え、次のように定義します。

          [ 式 18 - 3 ]




[ 式 17 - 1 ] で与えられる開端部の mirror handle も 丸くする という傾向に合致するものですが、開端部を直線的な形状にするオプションを追加します。

このオプションでは開端部の handle を次のように定めます。

          [ 式 18 - 4 ]


#3

18 - 3  Script


[ script 18 - 1 ] では、以上の custom 機能を組み込んだ関数 **custom_smooth( )** を定義し、Shade 上で選択された線形状に対し custom smooth 処理を施します。
[ script 18 - 1 ]

     2016/02/13 smooth_handle() 修正   3重点をフォロー

from vec3 import *
from matrix import *


#  custom smooth 係数 c, d, s   ########
#
#	c : handle 方向に係わる係数	0 <= c <= 1
#	d : handle 長さに係わる係数	d != 0
#	s : 開端部の形状を指定			s = True : 曲線的	s = False : 直線的
#	c = 0, d = 1, s = False で Shade の original smooth と一致

c = 0
d = 1
s = False
################################



# custom_smooth_handle(p0 as vec3, p1 as vec3, p2 as vec3, c as float, d as float) as vec3, vec3
#	3 点 p0, p1, p2 の座標から 中央のポイントの inhandle, outhandle 座標を返す 
#	0 <= c <= 1, d != 0 で、c = 0, d = 1 で Shade の original smooth と一致

def custom_smooth_handle(p0, p1, p2, c, d) :
	v0 = p0 - p1
	v2 = p2 - p1
	
	r0 = v0.norm()
	r2 = v2.norm()
	if (r0 == 0) and (r2 == 0) :			#  2015/02/13 追加
		return vec3(p1), vec3(p1)			#  handle なし
	elif min(r0, r2)/max(r0, r2) <=1e-4 :	#  隣接する2点のいずれかが同一座標とみなし、handle を設けない
		return vec3(p1), vec3(p1)			#  handle なし

	#  vH : handle の方向
	vH = ((1 - c)*r0 + c*r2)*v2 - (c*r0 + (1 - c)*r2)*v0	
	vH.norm()
	
	#  h : handle 長さの係数
	v = v0*v2
	a = v.abs()
	h = 1./3 + a*(4./3*(1 - 2**0.5/2) - 1./3)
	h = h**(1./d)
	
	inH = p1 - (r0*h*vH)
	outH = p1 + r2*h*vH
	
	return inH, outH
	
	
	

#  terminal_handle(p0 as vec3, p1 as vec3, h as vec3, s as bool = False) as vec3, vec3
#	開端部の handle を返す
#	s = True ならば、直線状の handle

def terminal_handle(p0, p1, h, s) :
	if not s :					#  曲線状
		c = (p0 + p1)/2
		v = p1 - p0
		v.norm()
		d = (h - c).dot(v)
		h2 =  h - 2*d*v
		h1 = 2*p0 - h2
		
	else :						#  直線状
		v = h - p0
		v.norm()
		u = h - p1
		d = u.abs()
		h2 =  p0 + d/2*v
		h1 = p0 - d/2*v
	
	return h1, h2
	
	
	

	
#  custom_smooth(xshade as xshade, c as float, d as float, s as bool = False)
#	Shade 上で選択されている線形状 / 自由曲面に custum smooth をかける
#	0 <= c <= 1
#	d != 0
#	s = True ならば、開端部を直線状にする
#	c = 0, d = 1, s = False で Shade の original smooth と一致する

def custom_smooth(c, d, s, obj = None) :
	if obj == None :
		ob = scene.active_shape()
	else :
		ob = obj
		
	if not isinstance(ob, xshade.line) :			#   線形状以外なら、
		if isinstance(ob, xshade.part) :
			if ob.part_type == 1 :					#  surfaace part なら、
				if ob.has_son :
					obj = ob.son
					while obj.has_bro :
						obj = obj.bro
						custom_smooth(c, d, s, obj)
					return
				else :
					return
			else :
				return
		else :
			return
		
	nop = ob.number_of_control_points				#  point 数
	closed = ob.closed								#  線形状の開閉
	if nop == 1 :									#  point 数が1個のみなら、何もしない
		return
		
	is_modify_mode = scene.is_modify_mode			#  modify mode

	if not is_modify_mode :
		target = [i for i in range(nop)]			#  smooth 処理対象ポイントリスト
		n = nop										#  対象ポイント数
	else :
		n = ob.number_of_active_control_points		#  対象ポイント数
		if n == 0 :									#  modify mode で選択ポイント数が 0 ならば、何もしない
			return
			
		target = ob.active_vertex_indices
		target = list(target)						#  tuple から list に変換
		
	last_p = (not closed) and (target[n - 1] == nop - 1)
	if last_p :										#  開いた線形状で終端が対象なら、target list から終端を削除
		target.pop()
		n -= 1
	
	if n == 0 :
		first_p = False
	else :
		first_p = (not closed) and (target[0] == 0)
		if first_p :								#  開いた線形状で始端が対象なら、target list から始端を削除
			target.remove(0)
	
	mx1 = matrix(ob.local_to_world_matrix)
	mx2 = matrix(ob.world_to_local_matrix)
				
				
	for k in target :
		if closed and k == 0 :
			p0 = vec3(ob.control_point(nop - 1).position)
		else :
			p0 = vec3(ob.control_point(k - 1).position)
			
		p1 = vec3(ob.control_point(k).position)
		
		if closed and k == nop - 1 :
			p2 =vec3(ob.control_point(0).position)
		else :
			p2 =vec3(ob.control_point(k + 1).position)	
				
		mx1.transform([p0, p1, p2])			
		inH, outH = custom_smooth_handle(p0, p1, p2, c, d)
		mx2.transform([inH, outH])
		ob.control_point(k).in_handle = inH
		ob.control_point(k).out_handle = outH
		ob.control_point(k).linked = True

	if first_p :									#  開いた線形状で始端が対象なら
		p0 = vec3(ob.control_point(0).position)
		p1 = vec3(ob.control_point(1).position)
		
		if not closed and nop == 2 :
			mx1.transform([p0, p1])
			inH = 4./3*p0 - 1./3*p1
			outH = 2./3*p0 + 1./3*p1
		else :
			h = vec3(ob.control_point(1).in_handle)
			mx1.transform([p0, p1, h])	
			inH, outH = terminal_handle(p0, p1, h, s)
			
		mx2.transform([inH, outH])
		ob.control_point(0).in_handle = inH
		ob.control_point(0).out_handle = outH
		ob.control_point(0).linked = True
		
	if last_p :										#  開いた線形状で終端が対象なら
		p0 = vec3(ob.control_point(nop - 1).position)
		p1 = vec3(ob.control_point(nop - 2).position)
		
		if not closed and nop == 2 :
			mx1.transform([p0, p1])
			inH = 2./3*p0 + 1./3*p1
			outH = 4./3*p0 - 1./3*p1
		else :
			h = vec3(ob.control_point(nop - 2).out_handle)
			mx1.transform([p0, p1, h])
			outH , inH = terminal_handle(p0, p1, h, s)
		mx2.transform([inH, outH])
		ob.control_point(nop - 1).in_handle = inH
		ob.control_point(nop - 1).out_handle = outH
		ob.control_point(nop - 1).linked = True
		
		
		
		
scene = xshade.scene()

custom_smooth(c, d, s)
ob = scene.active_shape()
ob.name = 'c = ' + str(c) + ',   d = ' +str(d) + ',   s = ' + str(s)


**適用例**

     

     [ fig 18 - 2 ]

     

     [ fig 18 - 3 ]

     

     [ fig 18 - 4 ]

          [ fig 18 - 5 ]

          [ fig 18 - 6 ]