パッチのメモ


v2.patch

diff -urN rt_v1/cg.py rt_v2/cg.py
--- rt_v1/cg.py	2018-03-16 21:06:29.000000000 +0900
+++ rt_v2/cg.py	2018-03-17 19:20:42.000000000 +0900
@@ -13,6 +13,7 @@
 import img
 import way
 import wf
+import rt
 
 if __name__ == "__main__":
 
@@ -24,6 +25,7 @@
 		'l2g': [ ax.zoom_all(50) ],
 	},{
 		'kind': 'ball',
+		'def_col': [128,0,0],
 		'l2g': [ ax.zoom_all(20) ],
 	} ]
 
球体のデフォルト色を[b,g,r]=[128,0,0]に
四角の方は指定してないので[b,g,r]=[128,128,128]になる

@@ -53,7 +55,10 @@
 
 		eye2g = ax.new_line( eye_f( cnt.cur_sec() ) )
 
-		wf.draw(data, eye2g, wh2eye, sc_sz, video)
+		f = rt.draw
+		if 'wf' in sys.argv:
+			f = wf.draw
+		f(data, eye2g, wh2eye, sc_sz, video)
 
 		video.add_img()
 		fn = ( name if n == 1 else name+'0000{}'.format(i)[-5:] ) + '.jpg'

 コマンド引数に wf が指定されてると従来通りワイヤーフレーム


v3.patch

diff -urN rt_v2/cg.py rt_v3/cg.py
--- rt_v2/cg.py	2018-03-17 19:20:42.000000000 +0900
+++ rt_v3/cg.py	2018-03-18 10:31:07.000000000 +0900
@@ -29,6 +29,10 @@
 		'l2g': [ ax.zoom_all(20) ],
 	} ]
 
+	lights = [
+		{ 'v': [0,0,-1], 'e': 1.0 },
+	]
+

 真上から真下の方向の強さ1.0の照明を1つ追加


 	(w, h) = ( ut.arg_i('w', 640) , ut.arg_i('h', 480) )
 	div = ut.arg_i('div', 1)
 	(w, h) = map( lambda v: v / div, (w, h) )
@@ -58,7 +62,7 @@
 		f = rt.draw
 		if 'wf' in sys.argv:
 			f = wf.draw
-		f(data, eye2g, wh2eye, sc_sz, video)
+		f(data, lights, eye2g, wh2eye, sc_sz, video)
 
wf.draw(), rt.draw()の2つ目の引数にlightsを渡すよう変更


 		video.add_img()
 		fn = ( name if n == 1 else name+'0000{}'.format(i)[-5:] ) + '.jpg'
diff -urN rt_v2/rt.py rt_v3/rt.py
--- rt_v2/rt.py	2018-03-17 19:19:33.000000000 +0900
+++ rt_v3/rt.py	2018-03-18 10:57:44.000000000 +0900
@@ -10,15 +10,44 @@
 	def_col = d.get( 'def_col', v.all(128) )
 	return def_col
 
-def get_col(data, l_g):
+def diffusivity(lights, crsp, nv, eyev_nega):
+	ang_nv_eye = v.dot_product( nv, eyev_nega )
+
+	def f(ltd):
+		ltv = [0,0,1]
+		if 'v' in ltd:
+			ltv = v.nega( ltd.get('v') )
+		elif 'p' in ltd:
+			ltv = v.sub_unit( ltd.get('p'), crsp )
+		ang_nv_lt = v.dot_product( nv, ltv )
+		if ang_nv_eye * ang_nv_lt < 0:
+			return 0
+		return max( ang_nv_lt, 0 ) * ltd.get('e', 1.0)
+
+	return sum( map( f, lights ) )
+
+def get_col(data, lights, l_g):

get_col()の2つ目の引数にlightsを渡すよう変更
拡散光の強さを計算するdiffusivity()を追加


 	lst = map( lambda d: ( d, cross.cross_g(l_g, d) ), data )
 	lst = filter( lambda (d, crs): 't' in crs, lst )
 	if not lst:
 		return []
 	(d, crs) = min( lst, key=lambda (d, crs): crs.get('t') )
-	return map_col(d, crs)
+	col = map_col(d, crs)
+
+	rt = d.get('rt', {})
+	base = rt.get('base', 0.3)

baseは周囲光あるいは自発光の強さとしてベタ(一定)


+
+	crsp = crs.get('p')

物体表面の交点


+	nv = v.unit( crs.get('nv') )

物体表面の法線


+	eyev = v.unit(l_g.v)

視線


+	eyev_nega = v.nega(eyev)

物体表面から視点への向き


+
+	diff = diffusivity(lights, crsp, nv, eyev_nega) * rt.get('diff', 0.9)

拡散光の強さを算出


+	col = v.op1( '*', col, base + diff )

周囲光と拡散光の強さで色を算出


+
+	return map( lambda v: min( int(v), 255 ), col )
 
色の値の飽和を考慮して色を返す


-def draw(data, eye2g, wh2eye, sc_sz, video):
+def draw(data, lights, eye2g, wh2eye, sc_sz, video):

draw()の2つ目の引数にlightsを渡すよう変更


 	(w, h, d_) = sc_sz
 	wh2g = lstx.opt( wh2eye + [ eye2g ] )
 	cnt = ut.new_cnt_wh(w, h, 'wh')
@@ -26,7 +55,7 @@
 		(ix, iy) = cnt.cur()
 		p = lstx.tr( wh2g, 'l2g', [ix,iy,-d_] )
 		l_g = line.new_p2(eye2g.p, p)
-		col = get_col(data, l_g)
+		col = get_col(data, lights, l_g)

get_col()の2つ目の引数にlightsを渡すよう変更


 		if col:
 			video.set(ix, iy, col)
 # EOF
diff -urN rt_v2/wf.py rt_v3/wf.py
--- rt_v2/wf.py	2018-03-16 20:50:41.000000000 +0900
+++ rt_v3/wf.py	2018-03-18 10:29:07.000000000 +0900
@@ -70,7 +70,7 @@
 		(pa, pb) = r
 	return [pa, pb]
 
-def draw(data, eye2g, wh2eye, sc_sz, video):
+def draw(data, lights, eye2g, wh2eye, sc_sz, video):

draw()の2つ目の引数にlightsを渡すよう変更


 	col = v.all(255) # white
 	for d in data:
 		ps = []


v4.patch

diff -urN rt_v3/cg.py rt_v4/cg.py
--- rt_v3/cg.py	2018-03-18 10:31:07.000000000 +0900
+++ rt_v4/cg.py	2018-03-18 17:56:56.000000000 +0900
@@ -20,11 +20,15 @@
 	copen = 'copen-090419.jpg'
 	beat = 'beat.jpg'
 
+	rtd = { 'reflect': 0.3 }
+
 	data = [ {
 		'kind': 'square',
+		'rtd': rtd,
 		'l2g': [ ax.zoom_all(50) ],
 	},{
 		'kind': 'ball',
+		'rtd': rtd,
 		'def_col': [128,0,0],
 		'l2g': [ ax.zoom_all(20) ],
 	} ]

レイトレーシング用のデータとして反射率を追加


diff -urN rt_v3/rt.py rt_v4/rt.py
--- rt_v3/rt.py	2018-03-18 10:57:44.000000000 +0900
+++ rt_v4/rt.py	2018-03-18 18:31:18.000000000 +0900
@@ -10,41 +10,66 @@
 	def_col = d.get( 'def_col', v.all(128) )
 	return def_col
 
-def diffusivity(lights, crsp, nv, eyev_nega):
-	ang_nv_eye = v.dot_product( nv, eyev_nega )
-
-	def f(ltd):
-		ltv = [0,0,1]
-		if 'v' in ltd:
-			ltv = v.nega( ltd.get('v') )
-		elif 'p' in ltd:
-			ltv = v.sub_unit( ltd.get('p'), crsp )
-		ang_nv_lt = v.dot_product( nv, ltv )
-		if ang_nv_eye * ang_nv_lt < 0:
-			return 0
-		return max( ang_nv_lt, 0 ) * ltd.get('e', 1.0)
-
-	return sum( map( f, lights ) )

diffusivity()はget_col()の内部関数に移動


+def get_col(data, lights, l_g, nest_rate=1.0, ref_len=None):
+	if nest_rate < 0.01:
+		return []
-def get_col(data, lights, l_g):
 
再帰呼び出しで初回呼び出しへの影響が1%を切ると終了


 	lst = map( lambda d: ( d, cross.cross_g(l_g, d) ), data )
 	lst = filter( lambda (d, crs): 't' in crs, lst )
 	if not lst:
 		return []
+
+	if ref_len:
+		lst = filter( lambda (d, crs): crs.get('t') > ref_len * 0.000001, lst )
+		if not lst:
+			return []
+

物体表面を新たな視点としてトレースする時、
起点の交点そのものは次の交点の候補から外す処理


 	(d, crs) = min( lst, key=lambda (d, crs): crs.get('t') )
 	col = map_col(d, crs)
 
-	rt = d.get('rt', {})
-	base = rt.get('base', 0.3)
+	rtd = d.get('rtd', {})
+	base = rtd.get('base', 0.3)

レイトレーシング用のデータの名前をrtからrtdに修正

 
 	crsp = crs.get('p')
 	nv = v.unit( crs.get('nv') )
 	eyev = v.unit(l_g.v)
 	eyev_nega = v.nega(eyev)
+	ang_nv_eye = v.dot_product( nv, eyev_nega )
+
+	def diffusivity():
+		diff = rtd.get('diff', 0.9)	
+		if not diff:
+			return 0
 
-	diff = diffusivity(lights, crsp, nv, eyev_nega) * rt.get('diff', 0.9)
+		def f(ltd):
+			ltv = [0,0,1]
+			if 'v' in ltd:
+				ltv = v.nega( ltd.get('v') )
+			elif 'p' in ltd:
+				ltv = v.sub_unit( ltd.get('p'), crsp )
+			ang_nv_lt = v.dot_product( nv, ltv )
+			if ang_nv_eye * ang_nv_lt < 0:
+				return 0
+			return max( ang_nv_lt, 0 ) * ltd.get('e', 1.0)
+
+		return sum( map( f, lights ) ) * diff
+
+	diff = diffusivity()
 	col = v.op1( '*', col, base + diff )
 
diffusivity()をget_col()の内部関数に変更


+	def reflect_col():
+		reflect = rtd.get('reflect', 0)
+		if not reflect:
+			return []
+		ref_v = v.op2( '+', v.op1( '*', nv, 2 * ang_nv_eye ), eyev )
+		ref_l = line.new( crsp, ref_v )
+		col = get_col( data, lights, ref_l, nest_rate * reflect, crs.get('t') )
+		return v.op1( '*', col, reflect )
+
+	col_ = reflect_col()
+	if col_:
+		col = v.op2( '+', col, col_ )
+
 	return map( lambda v: min( int(v), 255 ), col )
 
 def draw(data, lights, eye2g, wh2eye, sc_sz, video):

反射光の処理を追加


v5.patch

diff -urN rt_v4/cg.py rt_v5/cg.py
--- rt_v4/cg.py	2018-03-18 17:56:56.000000000 +0900
+++ rt_v5/cg.py	2018-03-19 15:36:06.000000000 +0900
@@ -20,7 +20,7 @@
 	copen = 'copen-090419.jpg'
 	beat = 'beat.jpg'
 
-	rtd = { 'reflect': 0.3 }
+	rtd = { 'reflect': 0.3, 'reflact': 0.3, 'density': 1.1 }
 
 	data = [ {
 		'kind': 'square',

レイトレーシング用のデータとして屈折光の割合と
屈折率を求めるための物体の密度を追加


diff -urN rt_v4/mt.py rt_v5/mt.py
--- rt_v4/mt.py	2018-03-16 20:38:43.000000000 +0900
+++ rt_v5/mt.py	2018-03-19 14:49:47.000000000 +0900
@@ -34,6 +34,8 @@
 ###
 
 def quadratic_formula(a, b, c):
+	if a == 0:
+		return [ -c / b ] if b != 0 else []
 	d = b * b - 4 * a * c
 	if d < 0:
 		return []

二次方程式の解でaが0さらにbが0の場合を考慮するように修正


diff -urN rt_v4/rt.py rt_v5/rt.py
--- rt_v4/rt.py	2018-03-18 18:31:18.000000000 +0900
+++ rt_v5/rt.py	2018-03-19 15:39:17.000000000 +0900
@@ -1,5 +1,6 @@
 #!/usr/bin/env python
 
+import math
 import ut
 import v
 import line
@@ -20,7 +21,8 @@
 		return []
 
 	if ref_len:
-		lst = filter( lambda (d, crs): crs.get('t') > ref_len * 0.000001, lst )
+		ref_rate = 0.000001
+		lst = filter( lambda (d, crs): crs.get('t') > ref_len * ref_rate, lst )
 		if not lst:
 			return []
 
係数を変更しやすいようにちょっとだけ変更


@@ -31,10 +33,12 @@
 	base = rtd.get('base', 0.3)
 
 	crsp = crs.get('p')
+	crst = crs.get('t')
 	nv = v.unit( crs.get('nv') )
 	eyev = v.unit(l_g.v)
 	eyev_nega = v.nega(eyev)
-	ang_nv_eye = v.dot_product( nv, eyev_nega )
+	ang_nv_eyev = v.dot_product( nv, eyev )
+	ang_nv_eyev_nega = -ang_nv_eyev
 
 	def diffusivity():
 		diff = rtd.get('diff', 0.9)	

変数名の統一感のため少々修正


@@ -48,7 +52,7 @@
 			elif 'p' in ltd:
 				ltv = v.sub_unit( ltd.get('p'), crsp )
 			ang_nv_lt = v.dot_product( nv, ltv )
-			if ang_nv_eye * ang_nv_lt < 0:
+			if ang_nv_eyev_nega * ang_nv_lt < 0:
 				return 0
 			return max( ang_nv_lt, 0 ) * ltd.get('e', 1.0)
 
変数名の統一感のため少々修正


@@ -61,15 +65,43 @@
 		reflect = rtd.get('reflect', 0)
 		if not reflect:
 			return []
-		ref_v = v.op2( '+', v.op1( '*', nv, 2 * ang_nv_eye ), eyev )
+		ref_v = v.op2( '+', v.op1( '*', nv, 2 * ang_nv_eyev_nega ), eyev )
 		ref_l = line.new( crsp, ref_v )
-		col = get_col( data, lights, ref_l, nest_rate * reflect, crs.get('t') )
+		col = get_col( data, lights, ref_l, nest_rate * reflect, crst )
 		return v.op1( '*', col, reflect )
 
変数名の統一感のため少々修正


 	col_ = reflect_col()
 	if col_:
 		col = v.op2( '+', col, col_ )
 
+	def reflact_v():
+		density = rtd.get('density', 1)
+		if density == 1 or abs(ang_nv_eyev) == 1 or ang_nv_eyev == 0:
+			return eyev
+		cos_v = v.op1( '*', nv, ang_nv_eyev )
+		sin_v = v.sub( eyev, cos_v )
+		if ang_nv_eyev < 0:
+			density = 1.0 / density
+		r_sin_v = v.op1( '*', sin_v, density )
+		sin_len2 = v.len2( r_sin_v )
+		sin_len2 = min( sin_len2, 1 )
+		cos_len = math.sqrt( 1 - sin_len2 )
+		r_cos_v = v.op1( '*', cos_v, cos_len / v.len1( cos_v ) )
+		return v.unit( v.op2( '+', r_sin_v, r_cos_v ) )

屈折光の方向ベクトルを返す


+
+	def reflact_col():
+		reflact = rtd.get('reflact', 0)
+		if not reflact:
+			return []
+		ref_v = reflact_v()
+		ref_l = line.new( crsp, ref_v )
+		col = get_col( data, lights, ref_l, nest_rate * reflact, crst )
+		return v.op1( '*', col, reflact )
+
+	col_ = reflact_col()
+	if col_:
+		col = v.op2( '+', col, col_ )
+

屈折光の処理を追加



 	return map( lambda v: min( int(v), 255 ), col )
 
 def draw(data, lights, eye2g, wh2eye, sc_sz, video):


v6.patch

diff -urN rt_v5/cg.py rt_v6/cg.py
--- rt_v5/cg.py	2018-03-19 15:36:06.000000000 +0900
+++ rt_v6/cg.py	2018-03-20 15:39:53.000000000 +0900
@@ -15,24 +15,67 @@
 import wf
 import rt
 
+def if_not_set_opt(d, k, v):
+	ut.if_not_set(d, k, v)
+	ut.dic_set( d, k, lstx.opt( d.get(k) ) )
+
 m_setup(), d_setup() で使うユーティリティ関数
dict d に key k がなければ value v を設定して、
その value を lstx の変換のリストとして扱い、最適化する


+def m_setup(d, m):
+	# 'fn' in d
+
+	if_not_set_opt( m, 'wh2xyz', [ ax.fn_map( m.get('fn') ) ] )
+
+	if 'xyz2t' not in m:
+		lst = []
+		if 'r' in m:
+			lst += [ cylx.new( m.get('r') ) ]
+		if 'dz' in m:
+			lst += [ fcx.new( m.get('dz') ) ]
+		if_not_set_opt( m, 'xyz2t', lst )
+
+	if_not_set_opt( m, 't2m', [] )
+	if_not_set_opt( m, 't2g', m.get('t2m') + d.get('m2g') )
+	if_not_set_opt( m, 'xyz2g', m.get('xyz2t') + m.get('t2g') )
+	if_not_set_opt( m, 'wh2g', m.get('wh2xyz') + m.get('xyz2g') )

画像マッピングデータの初期設定
なるべく最初から分かってる分は計算しておく

+
+def d_setup(d):
+	# return lst [d]
+
+	if_not_set_opt( d, 'l2m', [] )
+	if_not_set_opt( d, 'm2g', [] )
+	if_not_set_opt( d, 'l2g', d.get('l2m') + d.get('m2g') )
+
+	for m in d.get( 'maps', [] ):
+		m_setup(d, m)
+
+	return [d]
+
データの初期設定
なるべく最初から分かってる分は計算しておく


 if __name__ == "__main__":
 
 	copen = 'copen-090419.jpg'
 	beat = 'beat.jpg'
 
-	rtd = { 'reflect': 0.3, 'reflact': 0.3, 'density': 1.1 }
+	#rtd = { 'reflect': 0.3, 'reflact': 0.3, 'density': 1.1 }
+	rtd = {}

まずはレイトレーシング用の設定はデフォルトで試す


+
+	maps = [ { 'fn': copen, 't2m': [ ax.zoom_all(2) ] } ]
 
画像マッピングの設定
wの長さを2にする拡大率でマッピング空間に投影


 	data = [ {
 		'kind': 'square',
 		'rtd': rtd,
-		'l2g': [ ax.zoom_all(50) ],
+		'l2m': [],
+		'maps': maps,
+		'm2g': [ ax.zoom_all(50) ],

2x2サイズの四角のままマッピング空間に配置して、
そこから50倍に拡大してグローバル座標系に配置


 	},{
 		'kind': 'ball',
 		'rtd': rtd,
 		'def_col': [128,0,0],
-		'l2g': [ ax.zoom_all(20) ],
+		'l2m': [ ax.zoom_all(20.0/50) ],
+		'maps': maps,
+		'm2g': [ ax.zoom_all(50) ],
 	} ]
 
半径1の球を20/50倍にしてからマッピング空間に配置して、
そこから50倍に拡大してグローバル座標系に配置
これで従来と同じ半径20の球がグローバル座標系に配置される


+	data = sum( map( d_setup, data ), [] )
+

dataのリスト要素が増える場合に備えてる
将来例えば「立方体」はここで4つの「四角」に展開する


 	lights = [
 		{ 'v': [0,0,-1], 'e': 1.0 },
 	]
diff -urN rt_v5/rt.py rt_v6/rt.py
--- rt_v5/rt.py	2018-03-19 15:39:17.000000000 +0900
+++ rt_v6/rt.py	2018-03-20 15:33:43.000000000 +0900
@@ -6,10 +6,30 @@
 import line
 import lstx
 import cross
+import img
 
-def map_col(d, crs):
+def map_col(d, crs, rev):

引数にrev追加
物体表面の法線に対して裏側からの視線になってるとrev=True


 	def_col = d.get( 'def_col', v.all(128) )
-	return def_col
+
+	if 'maps' not in d:
+		return def_col
+
+	def f(m):
+		xyz2g = m.get('xyz2g')
+		nv_line = lstx.tr( xyz2g, 'g2l', crs.get('l') )
+		img_nv = v.z1
+		if v.dot_product( nv_line.v, img_nv ) < 0:
+			return []
+		fn = m.get( 'fn_r' if rev else 'fn', '' )
+		if not fn:
+			return []
+		(x, y, _) = lstx.tr( m.get('wh2xyz'), 'g2l', nv_line.p )
+		return img.imgs.col( fn, int(x), int(y), [] )
+
+	cols = map( f, d.get('maps') )
+	cols = filter( lambda col: col != [], cols )
+	col = v.lst_add(cols) if cols else def_col
+	return map( lambda v: min(v, 255), col )
 
デフォルト色から画像ファイルのピクセルの色へ


 def get_col(data, lights, l_g, nest_rate=1.0, ref_len=None):
 	if nest_rate < 0.01:
@@ -27,7 +47,6 @@
 			return []
 
 	(d, crs) = min( lst, key=lambda (d, crs): crs.get('t') )
-	col = map_col(d, crs)
 
 	rtd = d.get('rtd', {})
 	base = rtd.get('base', 0.3)
@@ -39,6 +58,9 @@
 	eyev_nega = v.nega(eyev)
 	ang_nv_eyev = v.dot_product( nv, eyev )
 	ang_nv_eyev_nega = -ang_nv_eyev
+	rev = ang_nv_eyev_nega < 0
+
+	col = map_col( d, crs, rev )
 
 	def diffusivity():
 		diff = rtd.get('diff', 0.9)	

revを算出してから呼び出すようにmap_col()の呼び出し位置を移動


v7.patch

diff -urN rt_v6/ax.py rt_v7/ax.py
--- rt_v6/ax.py	2018-03-16 20:38:43.000000000 +0900
+++ rt_v7/ax.py	2018-03-21 21:15:26.000000000 +0900
@@ -70,7 +70,7 @@
 wh_map = lambda w, h: slide( [-w*0.5,-h*0.5,0] ).compo( zoom_all(1.0/w) ).compo( rot_x(180) )
 
 def fn_map(fn):
-	(w, h) = img.imgs.wh(fn)
+	(w, h) = img.wh(fn)
 	return wh_map(w, h)
 
fnが動画の場合も対応したimg.wh(fn)へ変更


 new_line = lambda l: new( l.p, vecs.by_fix_ref('y', l.v, 'z', v.z1) )
diff -urN rt_v6/cg.py rt_v7/cg.py
--- rt_v6/cg.py	2018-03-20 15:39:53.000000000 +0900
+++ rt_v7/cg.py	2018-03-21 22:28:22.000000000 +0900
@@ -54,10 +54,12 @@
 	copen = 'copen-090419.jpg'
 	beat = 'beat.jpg'
 
-	#rtd = { 'reflect': 0.3, 'reflact': 0.3, 'density': 1.1 }
-	rtd = {}
+	rtd = { 'base': 1.0, 'diff': 0 }
 
周囲光(自発光)のみにして、拡散光もなしのベタ塗り設定に


-	maps = [ { 'fn': copen, 't2m': [ ax.zoom_all(2) ] } ]
+	maps = [
+		{ 'fn': 'g43.gif', 'fn_r': 'g43.gif', 't2m': [ ax.zoom_all(8) ] },
+		{ 'fn': 'g43.gif', 't2m': [ ax.zoom_all(8), ax.zoom_z(-1) ] },
+	]
 
平面の表裏とも同じ動画を投影
球は北半球の内側にも投影されるが、視点が外からなら無意味
2行目は南半球用にz方向を-1倍した座標系にして投影空間に配置


 	data = [ {
 		'kind': 'square',
diff -urN rt_v6/img.py rt_v7/img.py
--- rt_v6/img.py	2018-03-16 20:38:43.000000000 +0900
+++ rt_v7/img.py	2018-03-22 11:28:39.000000000 +0900
@@ -5,8 +5,8 @@
 import cv2
 import ut
 
-wh = lambda img: ( lambda (h, w, d): (w, h) )( img.shape )
-resize = lambda img, w, h: img if wh(img) == (w, h) else cv2.resize( img, (w, h) )
+img_wh = lambda img: ( lambda (h, w, d): (w, h) )( img.shape )
+resize = lambda img, w, h: img if img_wh(img) == (w, h) else cv2.resize( img, (w, h) )
 
旧img.wh()はimg.img_wh()に変更旧img.wh()はimg.img_wh()に変更動画を読み込んで画像の配列にして情報を添えて返す


+
+def new_videos():
+	d = {}
+	e = ut.Empty()
+
+	def get(fn):
+		if fn not in d:
+			d[fn] = read_video(fn)
+		return d.get(fn)
+	e.get = get
+
+	e.w = lambda fn: get(fn).get('w')
+	e.h = lambda fn: get(fn).get('h')
+	e.wh = lambda fn: ( e.w(fn), e.h(fn) )
+	e.n = lambda fn: get(fn).get('n')
+	e.fps = lambda fn: get(fn).get('fps')
+	e.imgs = lambda fn: get(fn).get('imgs')
+
+	img_sec = lambda fn, sec: e.imgs(fn)[ int( e.fps(fn) * sec ) % e.n(fn) ]
+	e.in_img = lambda fn, x, y: ( lambda (w, h): 0 <= x and x < w and 0 <= y and y < h )( e.wh(fn) )
+	e.col = lambda fn, sec, x, y, def_col=[128,128,128]: img_sec(fn, sec)[y, x] if e.in_img(fn, x, y) else def_col
+
+	return e

読み出し用として複数の動画ファイルパスと動画データを
キャッシュして管理する
(new_imgs()の動画版)


+
+videos = new_videos()

複数の動画ファイルパスと動画データをキャッシュして管理する
インスタンスを生成
(new_imgs()の動画版)


+
+def fn_typ(fn):
+	# 'v': video, 'i': image
+	vs = [ 'mp4', 'avi', 'mov', 'gif' ]
+	vs = sum( map( lambda s: [ s, s.upper() ], vs ) , [] )
+	for s in vs:
+		if fn.endswith('.' + s):
+			return 'v'
+	return 'i'

fnが動画か画像かを拡張子だけでゆるーく判定


+
+wh = lambda fn: ( videos if fn_typ(fn) == 'v' else imgs ).wh(fn)

fnが動画と画像に対応した(w, h)を返す関数


+
+def col(fn, sec, x, y, def_col=[128,128,128]):
+	if fn_typ(fn) == 'v':
+		return videos.col(fn, sec, x, y, def_col)
+	return imgs.col(fn, x, y, def_col)
+

fnが動画と画像に対応した色を返す関数
画像の場合はsecの指定は無効


 def new_wimg(w, h, def_col=[0,0,0]):
 	e = ut.Empty()
 	e.img = np.empty( (h, w, 3), dtype=np.uint8 )
@@ -80,19 +141,27 @@
 	fourcc = f(*codec)
 	(vw, vh) = ( int(w*zm), int(h*zm) )
 	writer = cv2.VideoWriter( fn, fourcc, fps, (vw, vh) )
-	e.add_img = lambda : writer.write( resize( e.img, vw, vh ) )
+
+	def add_img():
+		writer.write( resize( e.img, vw, vh ) )
+		e.i += 1
+	e.add_img = add_img
+
 	e.show_zm = lambda title='', sec=-1: e.show(title, sec, zm)
 	e.fin = lambda : writer.release()
+	e.i = 0
+	e.cur_sec = lambda : float(e.i) / fps
 	return e
 
動画生成時に追加したフレーム数をe.iとして追加
現在のフレームの時刻を返すe.cur_sec()を追加


 def imgs_to_video(img_name, video_name, fps, zm):
 	cmd = 'ls {}[0-9]*.jpg'.format(img_name)
 	lst = ut.cmd_exec(cmd).strip().split('\n')
 
-	imgs = new_imgs()
 	(w, h) = imgs.wh(lst[0])
 	codec = 'H264'
-	fn = video_name + '.mp4'
+	fn = video_name
+	if not fn.endswith('.mp4'):
+		fn += '.mp4'
 	video = new_video(codec, fn, fps, w, h, zm)
 
 	for fn in lst:
@@ -100,16 +169,46 @@
 		video.add_img()
 	video.fin()
 
+def video_to_imgs(img_name, video_name):
+	fn = video_name
+	if fn_typ(fn) != 'v':
+		fn += '.mp4'
+	n = videos.n(fn)
+	imgs = videos.imgs(fn)
+	ut.mkdir_if_not(img_name)
+	for i in range(n):
+		fn = ( img_name+'0000{}'.format(i)[-5:] ) + '.jpg'
+		cv2.imwrite(fn, imgs[i] )

動画ファイルから複数の連番の画像ファイルを生成


+
+def video_play(fn):
+	if fn_typ(fn) != 'v':
+		fn += '.mp4'
+	fps = videos.fps(fn)
+	st_sec = ut.now_sec()
+	wait_sec = lambda i: float(i) / fps - ( ut.now_sec() - st_sec )
+	for (i, img) in enumerate( videos.imgs(fn) ):
+		cv2.imshow('', img)
+		sec = max( wait_sec(i), 0 )
+		msec = max( 1, int(sec * 1000) )
+		cv2.waitKey(msec)

動画ファイルのお試し再生


+
 if __name__ == "__main__":
 	if len(sys.argv[1:]) < 2:
-		print 'Usage: {} img_name video_name fps= zm= codec='.format(sys.argv[0])
+		print 'Usage: {} img_name video_name fps= zm= cmd='.format(sys.argv[0])
 		print '  fps is int value (default 30)'
 		print '  zm is float value (default 1.0)'
+		print '  cmd is v2i, i2v or play (default i2v)'
 		sys.exit(0)
 
 	img_name = sys.argv[1]
 	video_name = sys.argv[2]
-	fps = ut.arg_i('fps', 30)
-	zm = ut.arg_f('zm', 1.0)
-	imgs_to_video(img_name, video_name, fps, zm)
+	cmd = ut.arg_s('cmd', 'i2v')
+	if cmd == 'i2v':
+		fps = ut.arg_i('fps', 30)
+		zm = ut.arg_f('zm', 1.0)
+		imgs_to_video(img_name, video_name, fps, zm)
+	elif cmd == 'v2i':
+		video_to_imgs(img_name, video_name)
+	elif cmd == 'play':
+		video_play(video_name)
 # EOF
diff -urN rt_v6/rt.py rt_v7/rt.py
--- rt_v6/rt.py	2018-03-20 15:33:43.000000000 +0900
+++ rt_v7/rt.py	2018-03-21 21:20:02.000000000 +0900
@@ -8,7 +8,7 @@
 import cross
 import img
 
-def map_col(d, crs, rev):
+def map_col(d, crs, rev, video):

引数に動画生成情報のvideoを追加


 	def_col = d.get( 'def_col', v.all(128) )
 
 	if 'maps' not in d:
@@ -24,14 +24,15 @@
 		if not fn:
 			return []
 		(x, y, _) = lstx.tr( m.get('wh2xyz'), 'g2l', nv_line.p )
-		return img.imgs.col( fn, int(x), int(y), [] )
+		sec = video.cur_sec()
+		return img.col( fn, sec, int(x), int(y), [] )

fnが動画の場合も対応するように、
動画生成情報のvideoから現在時刻を求め、
その時刻の貼り付ける方の動画(あるいは画像)の色を返す

 
 	cols = map( f, d.get('maps') )
 	cols = filter( lambda col: col != [], cols )
 	col = v.lst_add(cols) if cols else def_col
 	return map( lambda v: min(v, 255), col )
 
-def get_col(data, lights, l_g, nest_rate=1.0, ref_len=None):
+def get_col(data, lights, l_g, video, nest_rate=1.0, ref_len=None):

引数に動画生成情報のvideoを追加


 	if nest_rate < 0.01:
 		return []
 
@@ -60,7 +61,7 @@
 	ang_nv_eyev_nega = -ang_nv_eyev
 	rev = ang_nv_eyev_nega < 0
 
-	col = map_col( d, crs, rev )
+	col = map_col( d, crs, rev, video )
 
引数に動画生成情報のvideoを追加


 	def diffusivity():
 		diff = rtd.get('diff', 0.9)	
@@ -89,7 +90,7 @@
 			return []
 		ref_v = v.op2( '+', v.op1( '*', nv, 2 * ang_nv_eyev_nega ), eyev )
 		ref_l = line.new( crsp, ref_v )
-		col = get_col( data, lights, ref_l, nest_rate * reflect, crst )
+		col = get_col( data, lights, ref_l, video, nest_rate * reflect, crst )

引数に動画生成情報のvideoを追加


 		return v.op1( '*', col, reflect )
 
 	col_ = reflect_col()
@@ -117,7 +118,7 @@
 			return []
 		ref_v = reflact_v()
 		ref_l = line.new( crsp, ref_v )
-		col = get_col( data, lights, ref_l, nest_rate * reflact, crst )
+		col = get_col( data, lights, ref_l, video, nest_rate * reflact, crst )

引数に動画生成情報のvideoを追加


 		return v.op1( '*', col, reflact )
 
 	col_ = reflact_col()
@@ -134,7 +135,7 @@
 		(ix, iy) = cnt.cur()
 		p = lstx.tr( wh2g, 'l2g', [ix,iy,-d_] )
 		l_g = line.new_p2(eye2g.p, p)
-		col = get_col(data, lights, l_g)
+		col = get_col(data, lights, l_g, video)

引数に動画生成情報のvideoを追加


 		if col:
 			video.set(ix, iy, col)
 # EOF


v8.patch

diff -urN rt_v7/cg.py rt_v8/cg.py
--- rt_v7/cg.py	2018-03-21 22:28:22.000000000 +0900
+++ rt_v8/cg.py	2018-03-22 13:58:02.000000000 +0900
@@ -57,8 +57,7 @@
 	rtd = { 'base': 1.0, 'diff': 0 }
 
 	maps = [
-		{ 'fn': 'g43.gif', 'fn_r': 'g43.gif', 't2m': [ ax.zoom_all(8) ] },
-		{ 'fn': 'g43.gif', 't2m': [ ax.zoom_all(8), ax.zoom_z(-1) ] },
+		{ 'fn': 'IMG_3999_3.mov', 'fn_r': 'IMG_3999_4.mov', 't2m': [ ax.zoom_all(3) ] },

動画を変更して、拡大もし過ぎない程度に


 	]
 
 	data = [ {
@@ -69,10 +68,9 @@
 		'm2g': [ ax.zoom_all(50) ],
 	},{
 		'kind': 'ball',
-		'rtd': rtd,
+		'rtd': { 'base':0.1, 'diff': 0, 'reflact': 0.5, 'density': 2 },
 		'def_col': [128,0,0],
 		'l2m': [ ax.zoom_all(20.0/50) ],
-		'maps': maps,
 		'm2g': [ ax.zoom_all(50) ],
 	} ]
 
球体の方は貼り付けなしで、青く薄く自発光、拡散光なし、反射光なし
屈折だけは密度2でがっちりと


v9.patch

diff -urN rt_v8/cg.py rt_v9/cg.py
--- rt_v8/cg.py	2018-03-22 13:58:02.000000000 +0900
+++ rt_v9/cg.py	2018-03-23 11:46:23.000000000 +0900
@@ -54,21 +54,19 @@
 	copen = 'copen-090419.jpg'
 	beat = 'beat.jpg'
 
-	rtd = { 'base': 1.0, 'diff': 0 }
-
 	maps = [
 		{ 'fn': 'IMG_3999_3.mov', 'fn_r': 'IMG_3999_4.mov', 't2m': [ ax.zoom_all(3) ] },
 	]
 
 	data = [ {
 		'kind': 'square',
-		'rtd': rtd,
+		'rtd': { 'base': 0.1, 'diff': 1.0, 'reflect': 0.9 },
 		'l2m': [],
 		'maps': maps,
 		'm2g': [ ax.zoom_all(50) ],
 	},{
 		'kind': 'ball',
-		'rtd': { 'base':0.1, 'diff': 0, 'reflact': 0.5, 'density': 2 },
+		'rtd': { 'base':0.1, 'diff': 0, 'reflect': 0.5, 'reflact': 0.5, 'density': 2 },
 		'def_col': [128,0,0],
 		'l2m': [ ax.zoom_all(20.0/50) ],
 		'm2g': [ ax.zoom_all(50) ],

反射を有効にして、屈折とのコラボな設定に


@@ -77,7 +75,8 @@
 	data = sum( map( d_setup, data ), [] )
 
 	lights = [
-		{ 'v': [0,0,-1], 'e': 1.0 },
+		{ 'p': v.op1( '*', [1,-1,-1], 30 ), 'e': 2.5 },
+		{ 'p': v.op1( '*', [-1,1,1], 30 ), 'e': 2.5 },
 	]
 
 	(w, h) = ( ut.arg_i('w', 640) , ut.arg_i('h', 480) )

真上からの平行の光源をやめて、
至近距離に強めの点光源を2つ置いて斜めから照らしてみる


diff -urN rt_v8/rt.py rt_v9/rt.py
--- rt_v8/rt.py	2018-03-21 21:20:02.000000000 +0900
+++ rt_v9/rt.py	2018-03-23 11:23:36.000000000 +0900
@@ -69,15 +69,14 @@
 			return 0
 
 		def f(ltd):
-			ltv = [0,0,1]
-			if 'v' in ltd:
-				ltv = v.nega( ltd.get('v') )
-			elif 'p' in ltd:
-				ltv = v.sub_unit( ltd.get('p'), crsp )
+			ltv = ltd.get( 'v', [0,0,-1] )
+			if 'p' in ltd:
+				ltv = v.sub_unit( crsp, ltd.get('p') )
 			ang_nv_lt = v.dot_product( nv, ltv )
-			if ang_nv_eyev_nega * ang_nv_lt < 0:
+			ang_nv_lt_nega = -ang_nv_lt
+			if ang_nv_eyev_nega * ang_nv_lt_nega < 0:
 				return 0

ネーミング変更してルール統一
ltvは光源からの向き
光源への向きはltv_negaにする


-			return max( ang_nv_lt, 0 ) * ltd.get('e', 1.0)
+			return abs( ang_nv_lt ) * ltd.get('e', 1.0)
 
バグ修正
裏面に光が当たってるのを裏面から(当たって側を)見てるときに、
拡散光が0になってました


 		return sum( map( f, lights ) ) * diff
 
@@ -88,6 +87,8 @@
 		reflect = rtd.get('reflect', 0)
 		if not reflect:
 			return []
+		if ang_nv_eyev_nega < 0 and not rtd.get('reflect_backside', False):
+			return []

裏面は基本反射しない
明示的にTrue設定があるときだけ反射させる


 		ref_v = v.op2( '+', v.op1( '*', nv, 2 * ang_nv_eyev_nega ), eyev )
 		ref_l = line.new( crsp, ref_v )
 		col = get_col( data, lights, ref_l, video, nest_rate * reflect, crst )
diff -urN rt_v8/ut.py rt_v9/ut.py
--- rt_v8/ut.py	2018-03-16 20:38:43.000000000 +0900
+++ rt_v9/ut.py	2018-03-23 11:00:56.000000000 +0900
@@ -4,6 +4,7 @@
 import os
 import time
 import subprocess
+from decimal import *
 
 arg_s = lambda k, def_val='': next( ( s[ len(k)+1: ] for s in sys.argv if s.startswith(k+'=') ), def_val )
 arg_v = lambda fs, k, def_val: ( lambda s: fs(s) if s else def_val )( arg_s(k) )
@@ -54,6 +55,7 @@
 	si = int(sec)
 	m = si / 60
 	s = si % 60 + int( (sec - si) * 100 ) * 0.01
+	s = Decimal(s).quantize( Decimal('1.00') )

表示形式のちょっとした修正


 	h = m / 60
 	m %= 60
 	lst = [ (s, 's') ]


v10.patch

diff -urN rt_v9/cg.py rt_v10/cg.py
--- rt_v9/cg.py	2018-03-23 11:46:23.000000000 +0900
+++ rt_v10/cg.py	2018-03-24 12:41:14.000000000 +0900
@@ -40,6 +40,21 @@
 def d_setup(d):
 	# return lst [d]
 
+	kind = d.get('kind')
+
+	if kind == 'cube':
+		def cube_f( (l, deg) ):
+			d_ = d.copy()
+			d_['kind'] = 'square'
+			d_['l2m'] = [ ax.slide_z(1), ax.rot(l, deg) ] + d_.get( 'l2m', [] )
+			return d_setup( d_ )
+		rots = [
+			(line.x1, 0), (line.x1, 90), (line.x1, 180), (line.x1, -90),
+			(line.y1, 90), (line.y1, -90)
+		]
+		return sum( map( cube_f, rots ), [] )
+	###
+
 	if_not_set_opt( d, 'l2m', [] )
 	if_not_set_opt( d, 'm2g', [] )
 	if_not_set_opt( d, 'l2g', d.get('l2m') + d.get('m2g') )

立方体の設定は、6つの平面(四角)に展開


@@ -65,7 +80,7 @@
 		'maps': maps,
 		'm2g': [ ax.zoom_all(50) ],
 	},{
-		'kind': 'ball',
+		'kind': 'cube',
 		'rtd': { 'base':0.1, 'diff': 0, 'reflect': 0.5, 'reflact': 0.5, 'density': 2 },
 		'def_col': [128,0,0],
 		'l2m': [ ax.zoom_all(20.0/50) ],

球体を立方体に入れ替え


diff -urN rt_v9/wf.py rt_v10/wf.py
--- rt_v9/wf.py	2018-03-18 10:29:07.000000000 +0900
+++ rt_v10/wf.py	2018-03-24 12:35:50.000000000 +0900
@@ -6,8 +6,8 @@
 import lstx
 
 def wf_ps_idxs(d):
-	ps_to_idxs = lambda ps: map( lambda i: ( i, (i+1)%n ), range( len(ps) ) )
-	rv_ps = lambda ps: ( ps, ps_to_idxs(ps) )
+	len_to_idxs = lambda n, add=0: map( lambda i: ( i, (i+1)%n ), range(add, add+n) )
+	rv_ps = lambda ps: ( ps, len_to_idxs( len(ps) ) )
 
 	kind = d.get('kind')
 
インデックスによる線分のデータを生成するユーティリティ関数を
オフセットをつけれるように改良


@@ -34,17 +34,29 @@
 		(ps, idxs) = c3[1]
 		n = len(ps)
 		ps = map( lambda p: ax.rot_x(90).tr('l2g', p), ps )
-		idxs = map( lambda idx: map( lambda id: id + n, idx ), idxs )
+		idxs = len_to_idxs(n, n)
 		c3[1] = (ps, idxs)
 
 		(ps, idxs) = c3[2]
-		n *= 2
 		ps = map( lambda p: ax.rot_y(90).tr('l2g', p), ps )
-		idxs = map( lambda idx: map( lambda id: id + n, idx ), idxs )
+		idxs = len_to_idxs(n, 2*n)
 		c3[2] = (ps, idxs)
 
 		return map( lambda xs_lst: sum( xs_lst, [] ), zip(*c3) )
 
+	z_pn = lambda ps: map( lambda (x,y,z): [x,y,1], ps ) + map( lambda (x,y,z): [x,y,-1], ps )
+
+	def mk_prism(ps):
+		n = len(ps)
+		ps = z_pn(ps)
+		idxs = len_to_idxs(n) + len_to_idxs(n, n) + map( lambda i: (i,i+n), range(n) )
+		return (ps, idxs)
+
+	if kind == 'cube':
+		d = { 'kind': 'square' }
+		(ps, _) = wf_ps_idxs(d)
+		return mk_prism(ps)
+

立方体のワイヤーフレームのデータ
(点のリストと、そのインデックスによる線分のデータ)を返す処理を追加


 	return ([],[])
 
 def nv_clip(nv, pa, pb):


v12.patch

diff -urN rt_v11/cg.py rt_v12/cg.py
--- rt_v11/cg.py	2018-03-25 11:58:52.000000000 +0900
+++ rt_v12/cg.py	2018-03-28 22:09:24.000000000 +0900
@@ -192,7 +192,7 @@
 		def poly_n_pyramid_side_f(i):
 			d_ = d.copy()
 			d_['kind'] = 'triangle'
-			d_['l2m'] = [ ax.zoom([b,h2,1]), ax.rot_x(deg2), ax.slide_y(-h), ax.rot_z(deg*i)  ] + d_.get( 'l2m', [] )
+			d_['l2m'] = [ ax.zoom([b,h2,1]), ax.rot_x(deg2), ax.slide_y(-h), ax.rot_z(deg*i) ] + d_.get( 'l2m', [] )
 			return d_setup( d_ )
 
 		return sum( map( poly_n_pyramid_side_f, range(n) ), [] )
diff -urN rt_v11/dat.py rt_v12/dat.py
--- rt_v11/dat.py	2018-03-27 09:44:30.000000000 +0900
+++ rt_v12/dat.py	2018-03-28 22:31:01.000000000 +0900
@@ -1,5 +1,6 @@
 #!/usr/bin/env python
 
+import ut
 import v
 import line
 import ax
@@ -8,15 +9,72 @@
 import lstx
 
 copen = 'copen-090419.jpg'
-beat = 'beat.jpg'
+
+maps_copen = [ { 'fn': copen, 't2m': [ ax.zoom_all(2) ] } ]
+
+copen = [ {
+	'kind': 'square',
+	'l2m': [],
+	'maps': maps_copen,
+	'm2g': [ ax.zoom_all(50) ],
+},{
+	'kind': 'ball',
+	'def_col': [128,0,0],
+	'l2m': [ ax.zoom_all(20.0/50) ],
+	'maps': maps_copen,
+	'm2g': [ ax.zoom_all(50) ],
+} ]
 
 maps = [
 	{ 'fn': 'IMG_3999_3.mov', 'fn_r': 'IMG_3999_4.mov', 't2m': [ ax.zoom_all(3) ] },
 ]
 
+ball = [ {
+	'kind': 'square',
+	'rtd': { 'base': 0.1, 'diff': 1.0, 'reflect': 0.9 },
+	'l2m': [],
+	'maps': maps,
+	'm2g': [ ax.zoom_all(50) ],
+},{
+	'kind': 'ball',
+	'rtd': { 'base':0.1, 'diff': 0, 'reflect': 0.5, 'reflact': 0.5, 'density': 2 },
+	'def_col': [128,0,0],
+	'l2m': [ ax.zoom_all(20.0/50) ],
+	'm2g': [ ax.zoom_all(50) ],
+} ]
+
+cube = [ {
+	'kind': 'square',
+	'rtd': { 'base': 0.1, 'diff': 1.0, 'reflect': 0.9 },
+	'l2m': [],
+	'maps': maps,
+	'm2g': [ ax.zoom_all(50) ],
+},{
+	'kind': 'cube',
+	'rtd': { 'base':0.1, 'diff': 0, 'reflect': 0.5, 'reflact': 0.5, 'density': 2 },
+	'def_col': [128,0,0],
+	'l2m': [ ax.zoom_all(20.0/50) ],
+	'm2g': [ ax.zoom_all(50) ],
+} ]
+
+poly_n_pyramid = [ {
+	'kind': 'square',
+	'rtd': { 'base': 0.1, 'diff': 1.0, 'reflect': 0.9 },
+	'l2m': [],
+	'maps': maps,
+	'm2g': [ ax.zoom_all(50) ],
+},{
+	'kind': 'poly_n_pyramid',
+	'n': 5,
+	'rtd': { 'base':0.1, 'diff': 0, 'reflect': 0.5, 'reflact': 0.5, 'density': 2 },
+	'def_col': [128,0,0],
+	'l2m': [ ax.zoom_z(2), ax.slide_z(-1), ax.zoom_all(20.0/50) ],
+	'm2g': [ ax.zoom_all(50) ],
+} ]
+
 rtd = { 'base':0.1, 'diff': 0, 'reflect': 0.5, 'reflact': 0.5, 'density': 2 }
 
-data = [ {
+objs = [ {
 	'kind': 'square',
 	'rtd': { 'base': 0.1, 'diff': 1.0, 'reflect': 0.9 },
 	'l2m': [],
@@ -92,6 +150,16 @@
 	'l2m': [ ax.zoom_z(2), ax.slide_z(-1), ax.zoom_all(5), ax.slide([30,30,0]) ],
 } ]
 
+data_dic = {
+	'copen': copen,
+	'ball': ball,
+	'cube': cube,
+	'poly_n_pyramid': poly_n_pyramid,
+	'objs': objs,
+}
+
+data = data_dic.get( ut.arg_s('data_name'), [] )
+
 lights = [
 	{ 'p': v.op1( '*', [1,-1,-1], 30 ), 'e': 2.5 },
 	{ 'p': v.op1( '*', [-1,1,1], 30 ), 'e': 2.5 },

コマンド引数で data_name=objs などと指定するようにしてみました


diff -urN rt_v11/img.py rt_v12/img.py
--- rt_v11/img.py	2018-03-22 11:28:39.000000000 +0900
+++ rt_v12/img.py	2018-03-28 15:03:44.000000000 +0900
@@ -28,11 +28,21 @@
 
 def read_video(fn):
 	vc = cv2.VideoCapture(fn)
-	ks = [
-		cv2.CAP_PROP_FRAME_WIDTH,
-		cv2.CAP_PROP_FRAME_HEIGHT,
-		cv2.CAP_PROP_FPS,
-	]
+
+	ks = [ 3, 4, 5 ]
+	if hasattr(cv2, 'CAP_PROP_FRAME_WIDTH'):
+		ks = [
+			cv2.CAP_PROP_FRAME_WIDTH,
+			cv2.CAP_PROP_FRAME_HEIGHT,
+			cv2.CAP_PROP_FPS,
+		]
+	elif hasattr(cv2.cv, 'CV_CAP_PROP_FRAME_WIDTH'):
+		ks = [
+			cv2.cv.CV_CAP_PROP_FRAME_WIDTH,
+			cv2.cv.CV_CAP_PROP_FRAME_HEIGHT,
+			cv2.cv.CV_CAP_PROP_FPS,
+		]
+
 	(w, h, fps) = map( vc.get, ks )
 	(w, h) = map( int, (w, h) )
 
OpenCVの古いバージョンでもある程度は動作するようにと


diff -urN rt_v11/vecs.py rt_v12/vecs.py
--- rt_v11/vecs.py	2018-03-16 20:38:43.000000000 +0900
+++ rt_v12/vecs.py	2018-03-28 22:25:34.000000000 +0900
@@ -12,10 +12,15 @@
 	e.typ = 'vecs'
 	e.v3 = v3
 	l2g = lambda v_: v.lst_add( map( lambda (a, e): v.op1('*', a, e), zip(v3, v_) ) )
-	g2l = lambda v_: map( lambda a: v.dot_product_len(v_, a), v3 )
+	g2l = lambda v: e.rev().tr('l2g', v)
 	e.tr = lambda d, v: { 'l2g': l2g, 'g2l': g2l }.get(d, l2g)(v)
 
-	e.rev = lambda : new( mt.v3_rev(v3) )
+	e.rev_vs = None
+	def rev():
+		if not e.rev_vs:
+			e.rev_vs = new( mt.v3_rev(v3) )
+		return e.rev_vs
+	e.rev = rev
 
 	e.zoom = lambda zm3: new( map( lambda (v_, zm): v.op1('*', v_, zm), zip(v3, zm3) ) )
 	e.zoom_all = lambda zm: e.zoom( [zm,zm,zm] )

ここがバグの原因でした。
処理時間を気にしつつ、素直に逆行列を使うようにして対処してみました。


v15.patch

diff -urN rt_v14/ax.py rt_v15/ax.py
--- rt_v14/ax.py	2018-03-30 16:27:00.000000000 +0900
+++ rt_v15/ax.py	2018-04-03 10:03:55.000000000 +0900
@@ -37,7 +37,7 @@
 		# o.p + o.vs x (i.p + i.vs x p)
 		# = (o.p + o.vs x i.p) + o.vs x i.vs x p
 		d = 'l2g'
-		vs_ = vecs.new( map( lambda v: ax_outside.tr(d, v, typ='v'), vs.v3 ) )
+		vs_ = vecs.new( ut.map_lst( lambda v: ax_outside.tr(d, v, typ='v'), vs.v3 ) )
 		p_ = ax_outside.tr(d, p)
 		return new(p_, vs_)
 	e.compo = compo

mapがlistを返さなくなったので自前のlistを返すmapに置き換え


diff -urN rt_v14/cg.py rt_v15/cg.py
--- rt_v14/cg.py	2018-03-30 16:33:57.000000000 +0900
+++ rt_v15/cg.py	2018-04-03 09:18:48.000000000 +0900
@@ -59,7 +59,7 @@
 		return sum( map( poly_n_f, range(n) ), [] )
 
 	if kind == 'cube':
-		def cube_f( (l, deg) ):
+		def cube_f(l, deg):
 			d_ = d.copy()
 			d_['kind'] = 'square'
 			d_['l2m'] = [ ax.slide_z(1), ax.rot(l, deg) ] + d_.get( 'l2m', [] )

関数の引数のアンパックができなくなったので対策


@@ -68,7 +68,7 @@
 			(line.x1, 0), (line.x1, 90), (line.x1, 180), (line.x1, -90),
 			(line.y1, 90), (line.y1, -90)
 		]
-		return sum( map( cube_f, rots ), [] )
+		return sum( ut.map_up( fube_f, rots ), [] )
 
 	if kind == 'tri_prism_side':
 		d0 = d.copy()

関数の引数のアンパックができなくなったので対策
自前のアンパック仕様のmapに置き換え


@@ -249,7 +249,7 @@
 
 	(w, h) = ( ut.arg_i('w', 640) , ut.arg_i('h', 480) )
 	div = ut.arg_i('div', 1)
-	(w, h) = map( lambda v: v / div, (w, h) )
+	(w, h) = map( lambda v: v // div, (w, h) )
 	zm = div
 
 	w_deg = ut.arg_f('w_deg', 30.0)

整数の切り捨て割り算の箇所は '//' に


diff -urN rt_v14/cross.py rt_v15/cross.py
--- rt_v14/cross.py	2018-04-01 20:44:56.000000000 +0900
+++ rt_v15/cross.py	2018-04-03 09:41:23.000000000 +0900
@@ -94,6 +94,7 @@
 
 def ball_nv(crs):
 	t = crs.get('t')
+	l = crs.get('l')
 	crs['p'] = p = l.on_line_p(t)
 	crs['nv'] = v.unit(p)
 
バグってました


@@ -129,8 +130,8 @@
 	ts = sorted( filter( lambda v: v > 0, ts ) )
 	if not ts:
 		return False
-	tpzs = map( lambda t: ( lambda (x,y,z): (t, [x,y,z], z) )( l.on_line_p(t) ), ts )
-	tpzs = filter( lambda (t,p,z): abs(z) < 1, tpzs )
+	tpzs = map( lambda t: ( lambda x,y,z: (t, [x,y,z], z) )( *l.on_line_p(t) ), ts )
+	tpzs = ut.filter_up_lst( lambda t,p,z: abs(z) < 1, tpsz )
 	if not tpzs:
 		return False
 	(t, p, _) = tpzs[0]

関数の引数のアンパックができなくなったので対策
filterがlistを返さなくなったので自前のlistを返すfilterに置き換え
さらに自前のアンパック仕様のfilterに置き換え


@@ -187,8 +188,8 @@
 	if not ts:
 		return False
 
-	tpzs = map( lambda t: ( lambda (x,y,z): (t, [x,y,z], z) )( l.on_line_p(t) ), ts )
-	tpzs = filter( lambda (t,p,z): 0<z; and z<1, tpzs )
+	tpzs = map( lambda t: ( lambda x,y,z: (t, [x,y,z], z) )( *l.on_line_p(t) ), ts )
+	tpzs = filter_up_lst( lambda t,p,z: 0<z; and z<1 , tpzs )
 	if not tpzs:
 		return False
 	(t, p, _) = tpzs[0]

関数の引数のアンパックができなくなったので対策
filterがlistを返さなくなったので自前のlistを返すfilterに置き換え
さらに自前のアンパック仕様のfilterに置き換え


diff -urN rt_v14/fcx.py rt_v15/fcx.py
--- rt_v14/fcx.py	2018-03-30 14:51:18.000000000 +0900
+++ rt_v15/fcx.py	2018-04-02 17:22:06.000000000 +0900
@@ -35,5 +35,5 @@
 
 	rev = lambda : new(e.r, not rev)
 
-        return e
+	return e
 # EOF

インデントミスしてました (Python 3 だと厳しくなった?)


diff -urN rt_v14/img.py rt_v15/img.py
--- rt_v14/img.py	2018-03-28 15:03:44.000000000 +0900
+++ rt_v15/img.py	2018-04-02 18:30:53.000000000 +0900
@@ -5,7 +5,7 @@
 import cv2
 import ut
 
-img_wh = lambda img: ( lambda (h, w, d): (w, h) )( img.shape )
+img_wh = lambda img: ( lambda h, w, d: (w, h) )( *img.shape )
 resize = lambda img, w, h: img if img_wh(img) == (w, h) else cv2.resize( img, (w, h) )
 
 def new_imgs():

関数の引数のアンパックができなくなったので対策


@@ -19,7 +19,7 @@
 	e.get = get
 
 	e.wh = lambda fn: img_wh( get(fn) )
-	e.in_img = lambda fn, x, y: ( lambda (w, h): 0 <= x and x < w and 0 <= y and y < h )( e.wh(fn) )
+	e.in_img = lambda fn, x, y: ( lambda w, h: 0 <= x and x < w and 0 <= y and y < h )( *e.wh(fn) )
 	e.col = lambda fn, x, y, def_col=[128,128,128]: get(fn)[y, x] if e.in_img(fn, x, y) else def_col
 
 	return e

関数の引数のアンパックができなくなったので対策


@@ -74,7 +74,7 @@
 	e.imgs = lambda fn: get(fn).get('imgs')
 
 	img_sec = lambda fn, sec: e.imgs(fn)[ int( e.fps(fn) * sec ) % e.n(fn) ]
-	e.in_img = lambda fn, x, y: ( lambda (w, h): 0 <= x and x < w and 0 <= y and y < h )( e.wh(fn) )
+	e.in_img = lambda fn, x, y: ( lambda w, h: 0 <= x and x < w and 0 <= y and y < h )( *e.wh(fn) )
 	e.col = lambda fn, sec, x, y, def_col=[128,128,128]: img_sec(fn, sec)[y, x] if e.in_img(fn, x, y) else def_col
 
 	return e

関数の引数のアンパックができなくなったので対策


@@ -204,10 +204,10 @@
 
 if __name__ == "__main__":
 	if len(sys.argv[1:]) < 2:
-		print 'Usage: {} img_name video_name fps=<fps> zm=<zm> cmd=<cmd>'.format(sys.argv[0])
-		print '  fps is int value (default 30)'
-		print '  zm is float value (default 1.0)'
-		print '  cmd is v2i, i2v or play (default i2v)'
+		print( 'Usage: {} img_name video_name fps=<fps> zm=<zm> cmd=<cmd>'.format(sys.argv[0]) )
+		print( '  fps is int value (default 30)' )
+		print( '  zm is float value (default 1.0)' )
+		print( '  cmd is v2i, i2v or play (default i2v)' )
 		sys.exit(0)
 
 	img_name = sys.argv[1]

printは関数になったので


diff -urN rt_v14/lstx.py rt_v15/lstx.py
--- rt_v14/lstx.py	2018-03-31 17:25:01.000000000 +0900
+++ rt_v15/lstx.py	2018-04-02 18:13:19.000000000 +0900
@@ -1,5 +1,6 @@
 #!/usr/bin/env python
 
+from functools import reduce
 import ut
 import v
 import line

reduceはimportが必要になったので


diff -urN rt_v14/rt.py rt_v15/rt.py
--- rt_v14/rt.py	2018-04-01 20:40:13.000000000 +0900
+++ rt_v15/rt.py	2018-04-03 09:46:11.000000000 +0900
@@ -32,22 +32,22 @@
 		return img.col( fn, sec, int(x), int(y), [] )
 
 	cols = map( f, d.get('maps') )
-	cols = filter( lambda col: col != [], cols )
+	cols = ut.filter_lst( lambda col: col != [], cols )
 	col = np.sum(cols, axis=0) if cols else def_col
-	return map( lambda v: min(v, 255), col )
+	return ut.map_lst( lambda v: min(v, 255), col )
 
map, filterがlistを返さなくなったので自前のlistを返すものに置き換え


 def get_col(data, lights, l_g, video, nest_rate=1.0, ref_len=None):
 	if nest_rate < 0.01:
 		return []
 
 	lst = map( lambda d: { 'l_g': l_g, 'd': d }, data )
-	lst = filter( lambda crs: cross.cross(crs), lst )
+	lst = ut.filter_lst( lambda crs: cross.cross(crs), lst )
 	if not lst:
 		return []
 
 	if ref_len:
 		ref_rate = 0.000001
-		lst = filter( lambda crs: crs.get('t') > ref_len * ref_rate, lst )
+		lst = ut.filter_lst( lambda crs: crs.get('t') > ref_len * ref_rate, lst )
 		if not lst:
 			return []
 
filterがlistを返さなくなったので自前のlistを返すfilterに置き換え


@@ -147,7 +147,7 @@
 		p = lstx.tr( wh2g, 'l2g', mt.arr( [ix,iy,-d_] ) )
 		l_g = line.new_p2(eye2g.p, p)
 		col = get_col(data, lights, l_g, video)
-		col = map( lambda v: min( int(v), 255 ), col )
+		col = ut.map_lst( lambda v: min( int(v), 255 ), col )
 		if col != []:
 			video.set(ix, iy, col)
 # EOF

mapがlistを返さなくなったので自前のlistを返すmapに置き換え


diff -urN rt_v14/ut.py rt_v15/ut.py
--- rt_v14/ut.py	2018-03-31 12:12:15.000000000 +0900
+++ rt_v15/ut.py	2018-04-03 09:55:40.000000000 +0900
@@ -6,6 +6,14 @@
 import subprocess
 from decimal import *
 
+map_lst = lambda f, lst: list( map( f, lst ) )
+map_up = lambda f, lst: map( lambda a: f(*a), lst )
+map_up_lst = lambda f, lst: list( map_up( f, lst ) )
+
+filter_lst = lambda f, lst: list( filter( f, lst ) )
+filter_up = lambda f, lst: filter( lambda a: f(*a), lst )
+filter_up_lst = lambda f, lst: list( filter_up(f, lst) )
+
 arg_s = lambda k, def_val='': next( ( s[ len(k)+1: ] for s in sys.argv if s.startswith(k+'=') ), def_val )
 arg_v = lambda fs, k, def_val: ( lambda s: fs(s) if s else def_val )( arg_s(k) )
 arg_i = lambda k, def_val: arg_v( int, k, def_val )

自前のmap, filter追加
リストを返すものと、引数のアンパック仕様


@@ -28,7 +36,7 @@
 def dic_to_cls(d, c=None):
 	if not c:
 		c = Empty()
-	map( lambda (k, v): setattr(c, k, v), d.items() )
+	map_lst( lambda kv: setattr(c, *kv), d.items() )
 	return c
 
 extends = lambda pcls, c=None: dic_to_cls( cls_to_dic(pcls), c )

mapがlistを返さなくなったので自前のlistを返すmapに置き換え


@@ -53,17 +61,17 @@
 
 def str_hms(sec):
 	si = int(sec)
-	m = si / 60
+	m = si // 60
 	s = si % 60 + int( (sec - si) * 100 ) * 0.01
 	s = Decimal(s).quantize( Decimal('1.00') )
-	h = m / 60
+	h = m // 60
 	m %= 60
 	lst = [ (s, 's') ]

整数の切り捨て割り算の箇所は '//' に


 	if m:
 		lst.insert( 0, (m, 'm') )
 	if h:
 		lst.insert( 0, (h, 'h') )
-	return ' '.join( map( lambda (v, s): str(v)+s, lst ) )
+	return ' '.join( map_up( lambda v, s: str(v)+s, lst ) )
 
関数の引数のアンパックができなくなったので対策
自前のアンパック仕様のmapに置き換え


 ###
 
@@ -117,14 +125,15 @@
 		rest = e.total_sec - ( now_sec() - e.st_sec )
 		return 'rest {} : {}'.format( str_hms(rest), str_dt( e.st_sec + e.total_sec ) )
 
-	stat_str = lambda : '{}{}/{}({}%) : {}'.format( id_str, e.i, n, 1000*e.i/n*0.1, fin_str() )
+	pcent = lambda : Decimal(100*e.i/n).quantize( Decimal('1.0') )
+	stat_str = lambda : '{}{}/{}({}%) : {}'.format( id_str, e.i, n, pcent(), fin_str() )
 
Decimalを使うように統一


 	def show_stat(now=None):
 		if not now:
 			now = now_sec()
 		if e.show_sec == None or e.i >= n or now - e.show_sec >= e.show_interval:
 			e.show_sec = now
-			print stat_str()
+			print( stat_str() )
 
 	return e
 
printは関数になったので


@@ -154,7 +163,7 @@
 	n = w * h
 	cnt = new_cnt(n, id_str)
 	e = Empty(cnt)
-	e.cur = lambda : ( lambda i: ( i%w, i/w ) )( cnt.cur() )
+	e.cur = lambda : ( lambda i: ( i%w, i//w ) )( cnt.cur() )
 	return e
 
 ###

整数の切り捨て割り算の箇所は '//' に


diff -urN rt_v14/vecs.py rt_v15/vecs.py
--- rt_v14/vecs.py	2018-03-31 17:54:33.000000000 +0900
+++ rt_v15/vecs.py	2018-04-02 17:18:41.000000000 +0900
@@ -48,7 +48,7 @@
 	v3[fix_i] = fix
 
 	if all( fix == ref ):
-		ref = ( lambda (x, y, z): (-y, x, z) )( ref ) if all( ref != v.z1 ) else v.x1
+		ref = ( lambda x, y, z: (-y, x, z) )( *ref ) if all( ref != v.z1 ) else v.x1
 
 	(a, b) = (fix, ref) if (fix_i + 1) % 3 == ref_i else (ref, fix)
 	v3[i] = c = v.cross_product_unit(a, b)

関数の引数のアンパックができなくなったので対策


diff -urN rt_v14/way.py rt_v15/way.py
--- rt_v14/way.py	2018-03-30 15:56:13.000000000 +0900
+++ rt_v15/way.py	2018-04-03 09:35:07.000000000 +0900
@@ -55,8 +55,8 @@
 	return mt.linear_conv( t, 0, ts[idx], vs[idx], vs[(idx+1)%n] )
 
 def liss(ps, pe, t3):
-	lsts = map( lambda (s, e, t): [ (s, t*0.5), (e, t*0.5) ], zip(ps, pe, t3) )
-	return lambda t, itp3=True: mt.arr( map( lambda lst: get(lst, t, itp3), lsts ) )
+	lsts = ut.map_up( lambda s, e, t: [ (s, t*0.5), (e, t*0.5) ], zip(ps, pe, t3) )
+	return lambda t, itp3=True: mt.arr( ut.map_lst( lambda lst: get(lst, t, itp3), lsts ) )
 	
関数の引数のアンパックができなくなったので対策
自前のアンパック仕様のmapに置き換え
mapがlistを返さなくなったので自前のlistを返すmapに置き換え


 def liss_eye():
 # sys.argv
@@ -87,18 +87,18 @@
 	return lambda t, itp3=True: line.new_p2( f_p(t+init_sec, itp3), f_t(t+init_sec, itp3) )
 
 def test(lst, itp3=False, end_t=4, stp_t=0.25):
-	print lst, 'itp3=', itp3
+	print( lst, 'itp3=', itp3 )
 	t = 0
 	while t <= end_t:
 		v = get(lst, t, itp3)
-		print (t, v)
+		print( (t, v) )
 		t += stp_t
 	s = 0
 	for (v, t) in lst:
 		if t != None:
 			v_ = get(lst, s, itp3)
 			if v_ != v:
-				print 'NG t={} v={} (!={})'.format(t, v_, v)
+				print( 'NG t={} v={} (!={})'.format(t, v_, v) )
 				sys.exit(-1)
 			s += t
 
printは関数になったので


@@ -118,5 +118,5 @@
 	test( [(3,2),(1,None)] )
 	test( [(3,2),(1,None)], True )
 
-	print 'OK'
+	print( 'OK' )
 # EOF

printは関数になったので


diff -urN rt_v14/wf.py rt_v15/wf.py
--- rt_v14/wf.py	2018-03-30 16:29:31.000000000 +0900
+++ rt_v15/wf.py	2018-04-03 09:38:36.000000000 +0900
@@ -42,7 +42,9 @@
 		t = map( ball_f, range(3) )
 		return map( lambda lst: sum( lst, [] ), zip(*t) )
 
-	z_pn = lambda ps: map( lambda (x,y,z): [x,y,1], ps ) + map( lambda (x,y,z): [x,y,-1], ps )
+	tmp_p = ut.map_up_lst( lambda x,y,z: [x,y,1], ps )
+	tmp_n = ut.map_up_lst( lambda x,y,z: [x,y,-1], ps )
+	z_pn = tmp_p + tmp_n
 
 	def mk_prism(ps):
 		n = len(ps)

関数の引数のアンパックができなくなったので対策
自前のアンパック仕様のmapに置き換え
mapがlistを返さなくなったので自前のlistを返すmapに置き換え


@@ -112,7 +114,7 @@
 		ps = map( lambda p: lstx.tr( d.get('l2g'), 'l2g', p ), ps )
 		ps = map( lambda p: eye2g.tr('g2l', p), ps )
 
-		p2s = map( lambda (i, j): eye_clip( eye2g, wh2eye, sc_sz, ps[i], ps[j] ), idxs )
+		p2s = ut.map_up( lambda i, j: eye_clip( eye2g, wh2eye, sc_sz, ps[i], ps[j] ), idxs )
 		p2s = filter( lambda p2: p2, p2s )
 		p2s = map( lambda p2: map( lambda p: lstx.tr( wh2eye, 'g2l', p), p2 ), p2s )
 		for (pa, pb) in p2s:

関数の引数のアンパックができなくなったので対策
自前のアンパック仕様のmapに置き換え


v16.patch

diff -urN rt_v15/cg.py rt_v16/cg.py
--- rt_v15/cg.py	2018-04-03 09:18:48.000000000 +0900
+++ rt_v16/cg.py	2018-04-04 17:42:23.000000000 +0900
@@ -68,7 +68,7 @@
 			(line.x1, 0), (line.x1, 90), (line.x1, 180), (line.x1, -90),
 			(line.y1, 90), (line.y1, -90)
 		]
-		return sum( ut.map_up( fube_f, rots ), [] )
+		return sum( ut.map_up( cube_f, rots ), [] )
 
 	if kind == 'tri_prism_side':
 		d0 = d.copy()

どこかで混入したバグ残ってました


diff -urN rt_v15/cross.py rt_v16/cross.py
--- rt_v15/cross.py	2018-04-03 09:41:23.000000000 +0900
+++ rt_v16/cross.py	2018-04-04 18:29:49.000000000 +0900
@@ -2,6 +2,7 @@
 
 import math
 import numpy as np
+import ut
 import mt
 import v
 import line

import抜けてました


@@ -131,7 +132,7 @@
 	if not ts:
 		return False
 	tpzs = map( lambda t: ( lambda x,y,z: (t, [x,y,z], z) )( *l.on_line_p(t) ), ts )
-	tpzs = ut.filter_up_lst( lambda t,p,z: abs(z) < 1, tpsz )
+	tpzs = ut.filter_up_lst( lambda t,p,z: abs(z) < 1, tpzs )
 	if not tpzs:
 		return False
 	(t, p, _) = tpzs[0]

typoしてました


@@ -189,7 +190,7 @@
 		return False
 
 	tpzs = map( lambda t: ( lambda x,y,z: (t, [x,y,z], z) )( *l.on_line_p(t) ), ts )
-	tpzs = filter_up_lst( lambda t,p,z: 0ぬけてました


diff -urN rt_v15/line.py rt_v16/line.py
--- rt_v15/line.py	2018-04-01 11:47:50.000000000 +0900
+++ rt_v16/line.py	2018-04-04 09:59:20.000000000 +0900
@@ -1,5 +1,6 @@
 #!/usr/bin/env python
 
+import numpy as np
 import ut
 import mt
 import v

np.dot()を使うように変更したときに、importがぬけてました


diff -urN rt_v15/way.py rt_v16/way.py
--- rt_v15/way.py	2018-04-03 09:35:07.000000000 +0900
+++ rt_v16/way.py	2018-04-04 17:07:41.000000000 +0900
@@ -55,7 +55,7 @@
 	return mt.linear_conv( t, 0, ts[idx], vs[idx], vs[(idx+1)%n] )
 
 def liss(ps, pe, t3):
-	lsts = ut.map_up( lambda s, e, t: [ (s, t*0.5), (e, t*0.5) ], zip(ps, pe, t3) )
+	lsts = ut.map_up_lst( lambda s, e, t: [ (s, t*0.5), (e, t*0.5) ], zip(ps, pe, t3) )
 	return lambda t, itp3=True: mt.arr( ut.map_lst( lambda lst: get(lst, t, itp3), lsts ) )
 	
 def liss_eye():

listを返す版にしないとダメでした


@@ -70,8 +70,8 @@
 	def p_r_t(key):
 		lst = [ [0,0,0],0,1 ]
 		lst_ = eval( '[' + ut.arg_s(key) + ']' )
-		(n, n_) = map( len, (lst, lst_) )
-		return map( lambda i: lst_[i] if i < n_ else lst[i], range(n) )
+		(n, n_) = ut.map_lst( len, (lst, lst_) )
+		return ut.map_lst( lambda i: lst_[i] if i < n_ else lst[i], range(n) )
 	
 	def liss_get(key):
 		(p, r, t) = p_r_t(key)

listを返す版にしないとダメでした


diff -urN rt_v15/wf.py rt_v16/wf.py
--- rt_v15/wf.py	2018-04-03 09:38:36.000000000 +0900
+++ rt_v16/wf.py	2018-04-04 18:24:52.000000000 +0900
@@ -1,5 +1,6 @@
 #!/usr/bin/env python
 
+import ut
 import mt
 import v
 import line

importぬけてました


@@ -7,23 +8,23 @@
 import lstx
 
 def wf_ps_idxs(d):
-	len_to_idxs = lambda n, add=0: map( lambda i: ( add+i, add+(i+1)%n ), range(n) )
+	len_to_idxs = lambda n, add=0: ut.map_lst( lambda i: ( add+i, add+(i+1)%n ), range(n) )

listを返す版にしないとダメでした


 	rv_ps = lambda ps: ( ps, len_to_idxs( len(ps) ) )
 
 	kind = d.get('kind')
 
 	if kind == 'square':
 		n = 4
-		ps = map( lambda i: ax.rot_z(i*90).tr('l2g', mt.arr( [1,1,0] ) ), range(n) )
+		ps = ut.map_lst( lambda i: ax.rot_z(i*90).tr('l2g', mt.arr( [1,1,0] ) ), range(n) )

listを返す版にしないとダメでした


 		return rv_ps(ps)
 	if kind == 'triangle':
 		n = 3
-		ps = mat.arr( [ [0,1,0],[-1,0,0],[1,0,0] ] )
+		ps = mt.arr( [ [0,1,0],[-1,0,0],[1,0,0] ] )
 		return rv_ps(ps)

typoのバグがありました


 	if kind == 'poly_n':
 		n = d.get('n', 3)
 		deg = 360.0 / n
-		ps = map( lambda i: ax.rot_z( (i+0.5)*deg ).tr('l2g', mt.arr( [0,-1,0] ) ), range(n) )
+		ps = ut.map_lst( lambda i: ax.rot_z( (i+0.5)*deg ).tr('l2g', mt.arr( [0,-1,0] ) ), range(n) )
 		return rv_ps(ps)
 	if kind == 'circle':
 		d = { 'kind': 'poly_n', 'n': 16 }

listを返す版にしないとダメでした


@@ -35,21 +36,21 @@
 		axs = [ [], [ax.rot_x(90)], [ax.rot_y(90)] ]
 
 		def ball_f(i):
-			ps_ = map( lambda p: lstx.tr( axs[i], 'l2g', p ), ps )
+			ps_ = ut.map_lst( lambda p: lstx.tr( axs[i], 'l2g', p ), ps )
 			idxs = len_to_idxs(n, i*n)
 			return (ps_, idxs)
 
-		t = map( ball_f, range(3) )
-		return map( lambda lst: sum( lst, [] ), zip(*t) )
+		t = ut.map_lst( ball_f, range(3) )
+		return ut.map_lst( lambda lst: sum( lst, [] ), zip(*t) )
 
listを返す版にしないとダメでした


-	tmp_p = ut.map_up_lst( lambda x,y,z: [x,y,1], ps )
-	tmp_n = ut.map_up_lst( lambda x,y,z: [x,y,-1], ps )
-	z_pn = tmp_p + tmp_n
+	tmp_p = lambda x,y,z: [x,y,1]
+	tmp_n = lambda x,y,z: [x,y,-1]
+	z_pn = lambda ps: ut.map_up_lst( tmp_p, ps ) + ut.map_up_lst( tmp_n, ps )
 
うーむ。勘違いしてました


 	def mk_prism(ps):
 		n = len(ps)
 		ps = z_pn(ps)
-		idxs = len_to_idxs(n) + len_to_idxs(n, n) + map( lambda i: (i,i+n), range(n) )
+		idxs = len_to_idxs(n) + len_to_idxs(n, n) + ut.map_lst( lambda i: (i,i+n), range(n) )
 		return (ps, idxs)
 
 	if kind == 'cube':

listを返す版にしないとダメでした


@@ -63,7 +64,7 @@
 
 	def mk_pyramid(ps):
 		n = len(ps)
-		idxs = len_to_idxs(n) + map( lambda i: (i,n), range(n) )
+		idxs = len_to_idxs(n) + ut.map_lst( lambda i: (i,n), range(n) )
 		return ( ps + [ mt.arr( [0,0,1] ) ], idxs )
 
 	if kind =='poly_n_pyramid':

listを返す版にしないとダメでした


@@ -83,7 +84,7 @@
 
 def nv_clip(nv, pa, pb):
 	nv_line = line.new( [0,0,0], nv )
-	(in_a, in_b) = map( lambda p: nv_line.is_plane_side(p), [pa, pb] )
+	(in_a, in_b) = ut.map_lst( lambda p: nv_line.is_plane_side(p), [pa, pb] )
 	if in_a and in_b:
 		return [pa, pb]
 	if not in_a and not in_b:

listを返す版にしないとダメでした


@@ -95,7 +96,7 @@
 	(w, h, d) = sc_sz
 	n = 4
 	p4 = mt.arr( [ [0,0,-d], [w,0,-d], [w,h,-d], [0,h,-d] ] )
-	p4 = map( lambda p: lstx.tr(wh2eye, 'l2g', p), p4 )
+	p4 = ut.map_lst( lambda p: lstx.tr(wh2eye, 'l2g', p), p4 )
 	for i in range(n):
 		nv = v.p3_to_nv( mt.arr( [0,0,0] ), p4[i], p4[(i+1)%n] )
 		r = nv_clip(nv, pa, pb)

listを返す版にしないとダメでした


@@ -112,11 +113,11 @@
 		(ps, idxs) = wf_ps_idxs(d)
 
 		ps = map( lambda p: lstx.tr( d.get('l2g'), 'l2g', p ), ps )
-		ps = map( lambda p: eye2g.tr('g2l', p), ps )
+		ps = ut.map_lst( lambda p: eye2g.tr('g2l', p), ps )
 
 		p2s = ut.map_up( lambda i, j: eye_clip( eye2g, wh2eye, sc_sz, ps[i], ps[j] ), idxs )
 		p2s = filter( lambda p2: p2, p2s )
-		p2s = map( lambda p2: map( lambda p: lstx.tr( wh2eye, 'g2l', p), p2 ), p2s )
+		p2s = map( lambda p2: ut.map_lst( lambda p: lstx.tr( wh2eye, 'g2l', p), p2 ), p2s )
 		for (pa, pb) in p2s:
 			(ax, ay, _) = pa
 			(bx, by, _) = pb

listを返す版にしないとダメでした


v23.patch

diff -urN rt_v22/dat.py rt_v23/dat.py
--- rt_v22/dat.py	2018-04-15 18:19:02.000000000 +0900
+++ rt_v23/dat.py	2018-04-16 21:00:00.000000000 +0900
@@ -9,9 +9,7 @@
 import fcx
 import lstx
 
-copen = 'copen-090419.jpg'
-
-maps_copen = [ { 'fn': copen, 't2m': [ ax.zoom_all(2) ] } ]
+maps_copen = [ { 'fn': 'copen-090419.jpg', 't2m': [ ax.zoom_all(2) ] } ]
 
 copen = [ {
 	'kind': 'square',

名前copenがうっかり重複してたので修正


@@ -26,6 +24,22 @@
 	'm2g': [ ax.zoom_all(50) ],
 } ]
 
+maps_copen_rep = [ { 'fn': 'copen-090419.jpg', 't2m': [ ax.zoom_all(0.5) ], 'rep': (True,True) } ]
+
+rtd = { 'base':0.1, 'diff': 0, 'reflect': 0.5, 'reflact': 0.5, 'density': 2 }
+
+copen_rep = [ {
+	'kind': 'square',
+	'l2m': [],
+	'maps': maps_copen_rep,
+	'm2g': [ ax.zoom_all(50), ax.slide_z(-20) ],
+},{
+	'kind': 'ball',
+	'rtd': rtd,
+	'def_col': [128,0,0],
+	'm2g': [ ax.zoom_all(20), ax.slide_z(20) ],
+} ]
+
 maps = [
 	{ 'fn': 'IMG_3999_3.mov', 'fn_r': 'IMG_3999_4.mov', 't2m': [ ax.zoom_all(3) ] },
 ]

画像の繰り返し展開を試すデータを追加


@@ -73,8 +87,6 @@
 	'm2g': [ ax.zoom_all(50) ],
 } ]
 
-rtd = { 'base':0.1, 'diff': 0, 'reflect': 0.5, 'reflact': 0.5, 'density': 2 }
-
 objs = [ {
 	'kind': 'square',
 	'rtd': { 'base': 0.1, 'diff': 1.0, 'reflect': 0.9 },

rtd定義行は前方に移動


@@ -176,16 +188,26 @@
 	'm2g': [ ax.zoom_all(7), ax.slide_x(-10) ]
 } ]
 
-data_dic = {
-	'copen': copen,
-	'ball': ball,
-	'cube': cube,
-	'poly_n_pyramid': poly_n_pyramid,
-	'objs': objs,
-	'ball_world': ball_world,
-}
+ball_world2 = [ {
+	'kind': 'ball',
+	'rtd': { 'diff': 0.3 },
+	'maps': [ { 'fn': 'IMG_3999_3.mov', 'fn_r': 'IMG_3999_4.mov', 'r': 0.3, 'rep': (True,True) } ],
+	'm2g': [ ax.zoom_all(r), ax.slide_z(-r/2) ],
+},{
+	'kind': 'circle',
+	'rtd': { 'diff': 0.1, 'reflect': 0.3 },
+	'm2g': [ ax.zoom_all(r), ax.slide_z(-r/2) ],
+},{
+	'kind': 'ball',
+	'rtd': rtd,
+	'm2g': [ ax.zoom_all(10), ax.slide_x(10) ]
+},{
+	'kind': 'cube',
+	'rtd': rtd,
+	'm2g': [ ax.zoom_all(7), ax.slide_x(-10) ]
+} ]
 
ball_worldのデータをコピーしてちょっと変更したものを追加
外側の球には動画を円柱マッピングで投影しつつ、動画は繰り返し展開指定で
円自体に動画を投影するのはやめて、反射で写すだけに


-data = data_dic.get( ut.arg_s('data_name'), [] )
+data = eval( ut.arg_s('data_name', 'copen') )
 
データが増えるたびにdata_dicへの追加がナンセンスなのでevalするように変更


 lights = [
 	{ 'p': mt.arr( [1,-1,-1] ) * 30, 'e': 2.5 },
diff -urN rt_v22/img.py rt_v23/img.py
--- rt_v22/img.py	2018-04-06 15:32:01.000000000 +0900
+++ rt_v23/img.py	2018-04-16 21:00:00.000000000 +0900
@@ -8,6 +8,14 @@
 img_wh = lambda img: ( lambda h, w, d: (w, h) )( *img.shape )
 resize = lambda img, w, h: img if img_wh(img) == (w, h) else cv2.resize( img, (w, h) )
 
+def rep_xy(x, y, w, h, rep):
+	(rx, ry) = rep;
+	if rx and not (0 <= x and x < w):
+		x = x % w if x > 0 else w - (-x % w)
+	if ry and not (0 <= y and y < h):
+		y = y % h if y > 0 else h - (-y % h)
+	return (x, y)
+
 def new_imgs():
 	d = {}
 	e = ut.Empty()

画像や動画の繰り返し展開用
repは(bool,bool)のタプルでX,Yの繰り返し展開を指定


@@ -20,7 +28,17 @@
 
 	e.wh = lambda fn: img_wh( get(fn) )
 	e.in_img = lambda fn, x, y: ( lambda w, h: 0 <= x and x < w and 0 <= y and y < h )( *e.wh(fn) )
-	e.col = lambda fn, x, y, def_col=[128,128,128]: get(fn)[y, x] if e.in_img(fn, x, y) else def_col
+
+	def col(fn, x, y, def_col=[128,128,128], rep=None):
+		if e.in_img(fn, x, y):
+			return get(fn)[y, x] 
+		if rep:
+			(w, h) = e.wh(fn)
+			(x, y) = rep_xy(x, y, w, h, rep)
+			if e.in_img(fn, x, y):
+				return get(fn)[y, x] 
+		return def_col
+	e.col = col
 
 	return e
 
画像の繰り返し展開処理を追加


@@ -75,7 +93,17 @@
 
 	img_sec = lambda fn, sec: e.imgs(fn)[ int( e.fps(fn) * sec ) % e.n(fn) ]
 	e.in_img = lambda fn, x, y: ( lambda w, h: 0 <= x and x < w and 0 <= y and y < h )( *e.wh(fn) )
-	e.col = lambda fn, sec, x, y, def_col=[128,128,128]: img_sec(fn, sec)[y, x] if e.in_img(fn, x, y) else def_col
+
+	def col(fn, sec, x, y, def_col=[128,128,128], rep=None):
+		if e.in_img(fn, x, y):
+			return img_sec(fn, sec)[y, x]
+		if rep:
+			(w, h) = e.wh(fn)
+			(x, y) = rep_xy(x, y, w, h, rep)
+			if e.in_img(fn, x, y):
+				return img_sec(fn, sec)[y, x]
+		return def_col
+	e.col = col
 
 	return e
 
動画の繰り返し展開処理を追加


@@ -92,10 +120,10 @@
 
 wh = lambda fn: ( videos if fn_typ(fn) == 'v' else imgs ).wh(fn)
 
-def col(fn, sec, x, y, def_col=[128,128,128]):
+def col(fn, sec, x, y, def_col=[128,128,128], rep=None):
 	if fn_typ(fn) == 'v':
-		return videos.col(fn, sec, x, y, def_col)
-	return imgs.col(fn, x, y, def_col)
+		return videos.col(fn, sec, x, y, def_col, rep)
+	return imgs.col(fn, x, y, def_col, rep)
 
 def new_wimg(w, h, def_col=[0,0,0]):
 	e = ut.Empty()

画像、動画、共通関数の繰り返し展開対応


diff -urN rt_v22/Makefile rt_v23/Makefile
--- rt_v22/Makefile	2018-04-13 22:02:41.000000000 +0900
+++ rt_v23/Makefile	2018-04-16 21:00:00.000000000 +0900
@@ -5,7 +5,7 @@
 all: sock
 
 sock: $(OBJS)
-	gcc -o $@ $(OBJS)
+	gcc -o $@ $(OBJS) -lm
 clean:
 	rm -f *.o *~
 # EOF

ubuntu14な環境でmakeしてみるとlibmの指定がなく怒られたので追加 

diff -urN rt_v22/rt.py rt_v23/rt.py
--- rt_v22/rt.py	2018-04-15 18:02:30.000000000 +0900
+++ rt_v23/rt.py	2018-04-16 21:00:00.000000000 +0900
@@ -32,7 +32,7 @@
 		#(x, y, _) = lstx.tr( m.get('wh2xyz'), 'g2l', nv_line.p )
 		(x, y, _) = lstx.tr_p( m.get('wh2xyz'), 'g2l', nv_line.p )
 		sec = video.cur_sec() + ut.arg_f('init_sec', 0.0)
-		return img.col( fn, sec, int(x), int(y), [] )
+		return img.col( fn, sec, int(x), int(y), [], m.get('rep') )
 
 	cols = map( f, d.get('maps') )
 	cols = ut.filter_lst( lambda col: col != [], cols )

マッピングデータの繰り返し展開指定対応


工事中