Shade3D 公式

09 Bezier line 接線ベクトル [ Shade Labo ]


#1

関連記事
  • 06 Bezier line 基礎
  • 07 Bezier line 長さ

## 09 - 1 ハンドルが出ているポイントの接線ベクトル
[ 06 - 2 接線ベクトル ] で触れたように bezier line の4つの control point 座標 [ **p0**, **p1**, **p2**, **p3** ] と bezier parameter ( 0 ~1 ) から parameter t での bezier line 上の 接線ベクトル **Vt**( t ) ( 単位ベクトル )は、その点での座標値を paremeter t で微分することで求まります。

          [ 式 06 - 02 ]

          [ 式 09 - 01 ]


ここで t = 0 とすると

          [ 式 09 - 02 ]


となり、ポイント p0 では、outhandle の方向が接線の方向になるという、よく知られた結果通りになります。

          [ fig 09 - 01 ]


#2

09 - 2 ハンドルが出ていないポイントの接線ベクトル


2016 / 01 / 23     **記述追加**

ここでハンドルの出ていないポイントでの接線ベクトルを考えます。

outhandle が出ていない場合、 p0 = p1 なので、接線ベクトルは 0 ベクトルになってしまいます。

           [ 式 09 - 03 ]


これは 接線の方向が求められない のであって、接線が定義できない ことではありません。

何故求められないのかについては 10 曲線の接線ベクトル で解説します。


このケースでは次のように考えます。

[ 式 09 - 01 ] を次のように書き換えます。

           [ 式 09 - 04 ]


p1 = p0 とすれば

           [ 式 09 - 05 ]


ここで t を限りなく 0 に近づけると、t^2 は t に比べて急速に 0 に近づくので無視することができ、

           [ 式 09 - 06 ]


つまり、outhandle の長さが 0 の場合には P2 - P0 で P0 における接線の方向が求められます。

           [ fig 09 - 02 ]


さらに、P2 が P0 に一致する場合には直線になり、p3 - p0で p0 における接線の方向が求まります。( 2016 / 01 / 23 追加 )


なお、bezier line の長さを測る [ script 07 - 03 ] でも同様なことが’生じますが、長さを測る場合には端点で接線ベクトルが 0 ベクトルになっても問題はなく、そのままで正しい計算が実行されます。


#3

09 - 3 bezier line 接線ベクトルを求める関数


2016/01/23     **[ script 09 - 01 ] にコード追加**

以上を踏まえると、bezier line の接線を求める関数は次のようになり、labo module に登録してあります。
[ script 09 - 01 ]
#  bezier line bz の parameter t における接線ベクトルを返す ( 単位ベクトル )
#
#  bezier_line_tangent(bz as vec3 list, t as float) as vec3
#
#		bz : bezier control point 座標を格納した vec3 list
#		bz = [ position, out_handle, in_handle, position ]

def bezier_line_tangent(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]
	v.norm()
	if v.abs2() < 0.5 :
		if t < 0.5 :				#  outhandle が出ていなくて t = 0
			v = bz[2] - bz[0]
		else :						#  inhandle が出ていなくて t = 1
			v = bz[3] - bz[1]
		v.norm()

		if v.abs2() < 0.5 :			#  2016 / 01 / 23 追加
			v = bz[3] - bz[0]
			v.norm()
		
	return v


最後に Shade 上で選択した線形状の接線を表示する script を書いてみます。

ここでは一つの bezier 区間に対して t = 0, 0.25, 0.5, 0.75 の位置での接線マーカーを線形状として表します。

マーカー長さは、元の線形状の bounding box size の1/16 とします。


[ script 09 - 02 ]     Shade 上で線形状を選択して実行

import labo

scene = xshade.scene()

#  線形状全データ取得			bZs = [bz, bz, bz, …]   bz = [vec3, vec3, vec3, vec3] transposed
bZs = labo.get_bezier(xshade, -1)

ob = scene.active_shape()
num = ob.total_number_of_control_points		#  point 数
closed = ob.closed							#  開閉情報
size = labo.vec3_bounding_box_size(bZs)		#  bZs の bounding box siz
r = size[3]/16								#  接線マーカーのサイズ				

if not closed :
	k = num - 1
else :
	k = num
	
L = [0, 0.25, 0.5, 0.75]

scene.begin_creating()
scene.begin_part()
for i in range(k) :
	if (i == k - 1) and not closed :
		L.append(1)
	for t in L :
		p1 = labo.bezier_line_position(bZs[i], t)	#  bezier 曲線上の座標
		v = labo.bezier_line_tangent(bZs[i], t)		#  bezier  曲線の接線ベクトル
		p2 = p1 + r*v
		labo.make_simple_line(xshade, [p1, p2], False, 't = ' + str(i + t))
scene.end_part()
scene.end_creating()