部品のソースコードのメモ


ut.py

#!/usr/bin/env python

import sys
import os
import time
import subprocess

arg_s = lambda k, def_val='': next( ( s[ len(k)+1: ] for s in sys.argv if s.startswith(k+'=') ), def_val )

コマンド引数に foo=bar があると、arg_s('foo') で 'bar' が返る


arg_v = lambda fs, k, def_val: ( lambda s: fs(s) if s else def_val )( arg_s(k) )

文字列を引数に取る関数 fs を指定して使う
コマンド引数に foo=bar があると、arg_v(len, 'foo') で 3 が返る


arg_i = lambda k, def_val: arg_v( int, k, def_val )

コマンド引数に foo=123 があると、arg_i('foo') で 123 が返る


arg_f = lambda k, def_val: arg_v( float, k, def_val )

コマンド引数に foo=1.23 があると、arg_f('foo') で 1.23 が返る


def dic_set(d, k, v):
	d[k] = v
	return v

dic_cache = lambda d, k, f_v: d.get(k) if k in d else dic_set( d, k, f_v() )

if_not_set = lambda d, k, v: dic_cache(d, k, lambda : v)

###

def cls_to_dic(c):
	ks = filter( lambda k: not k.startswith('_'), dir(c) )
	return dict( map( lambda k: ( k, getattr(c, k) ), ks ) )

def dic_to_cls(d, c=None):
	if not c:
		c = Empty()
	map( lambda (k, v): setattr(c, k, v), d.items() )
	return c

extends = lambda pcls, c=None: dic_to_cls( cls_to_dic(pcls), c )

class Empty:
	def __init__(self, pcls=None):
		if pcls:
			dic_to_cls( cls_to_dic(pcls), self )

get_typ = lambda targ, typ='p': targ.typ if isinstance(targ, Empty) else typ

###

now_sec = time.time

現在時刻取得(1970からの秒数)


sleep = time.sleep

指定秒数待機


#str_dt = lambda sec: time.asctime( time.localtime(sec) )
def str_dt(sec):
	t = time.localtime(sec)
	lst = [ t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec ]
	return '{}/{:02}/{:02} {:02}:{:02}:{:02}'.format(*lst)

1970からの秒数を 'yyyy/mm/dd hh:mm:ss' の文字列に


def str_hms(sec):
	si = int(sec)
	m = si / 60
	s = si % 60 + int( (sec - si) * 100 ) * 0.01
	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 ) )

時刻じゃなくて時間 sec を 'xxh xxm xx.xxs' の文字列に


###

def new_cnt(n, id_str=''):
	if id_str:
		id_str += ' : '

	e = Empty()
	e.n = n
	e.i = 0
	e.st_sec = None
	e.total_sec = None
	e.show_sec = None
	e.show_interval = 1.0

	def up():
		now = now_sec()
		if e.st_sec == None:
			e.st_sec = now
		else:
			e.total_sec = now - e.st_sec
			e.i += 1
		run = e.i < e.n
		if e.total_sec != None and run:
			e.total_sec = e.total_sec * n / e.i
		show_stat()
		return run
	e.up = up

	e.cur = lambda : e.i

	def fin_str():
		if e.total_sec == None:
			return 'not start yet'
		if e.i >= n:
			return 'fin ' + str_hms(e.total_sec)
		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() )

	def show_stat():
		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()

	return e

def new_cnt_wait(id_str=''):
	sec = arg_f('sec', 1.0)
	fps = arg_f('fps', 30.0)
	n = arg_i( 'n', int( fps * sec ) )

	cnt = new_cnt(n, id_str)
	e = Empty(cnt)

	e.sec = sec
	e.fps = fps

	e.cur_sec = lambda : sec * cnt.cur() / n

	e.wait_sec = lambda : e.cur_sec() - ( now_sec() - cnt.st_sec )
	def wait():
		w = e.wait_sec()
		if w > 0:
			sleep(w)
	e.wait = wait

	return e

def new_cnt_wh(w, h, id_str=''):
	n = w * h
	cnt = new_cnt(n, id_str)
	e = Empty(cnt)
	e.cur = lambda : ( lambda i: ( i%w, i/w ) )( cnt.cur() )
	return e

###

def cmd_exec(cmd):
	try:
		return subprocess.check_output(cmd, shell=True)
	except:
		return ''

コマンド実行して結果の文字列を返す


###

exists = lambda path: os.path.exists(path)

パスの存在確認


def mkdir_if_not(file_path):
	(d, n) = os.path.split(file_path)
	if not exists(d):
		cmd_exec( "mkdir -p '{}'".format(d) )

file_path のディレクトリ部分が存在してなかったら、ディレクトリを作成する

# EOF


mt.py

#!/usr/bin/env python

import math

linear_conv = lambda s, sa, sb, da, db: da + (s - sa) * (db - da) / float(sb - sa) if sa != sb else (da + db) * 0.5

saからsbの範囲の値sをdaからdbの範囲に線形変換


deg_to_rad = lambda deg: deg * math.pi / 180

度からラジアンに変換


rad_to_deg = lambda rad: rad * 180 / math.pi

ラジアンから度に変換


def v3_det(v3):
	(x, y, z) = v3
	(xx, xy, xz) = x
	(yx, yy, yz) = y
	(zx, zy, zz) = z
	return xx*yy*zz + xy*yz*zx + xz*yx*zy - xx*yz*zy - xz*yy*zx - xy*yx*zz

def v3_rev(v3):
	d = v3_det(v3)
	if d == 0:
		return []
	d_ = 1.0 / d

	(x, y, z) = v3
	(xx, xy, xz) = x
	(yx, yy, yz) = y
	(zx, zy, zz) = z

	tx = [ yy*zz - zy*yz, zy*xz - xy*zz, xy*yz - yy*xz ]
	ty = [ zx*yz - yx*zz, xx*zz - zx*xz, yx*xz - xx*yz ]
	tz = [ yx*zy - zx*yy, zx*xy - xx*zy, xx*yy - yx*xy ]

	return map( lambda v: map( lambda e: e * d_, v ), [tx, ty, tz] )

3x3の行列の逆行列を返す

###

def quadratic_formula(a, b, c):
	d = b * b - 4 * a * c
	if d < 0:
		return []
	a2 = 2.0 * a
	if d == 0:
		return [ -b / a2 ]
	d_ = math.sqrt(d)
	return [ (-b + d_) / a2, (-b - d_) / a2 ]

2次方程式の解

###

def interp3_abcd(v0, v1, k0=None, k1=None):
	k = v1 - v0;
	k0_ = k0
	k1_ = k1
	if k0 == None:
		k0 = k
	if k1 == None:
		k1 = k
	if k0_ == None:
		k0 = 2*k - k1
	if k1_ == None:
		k1 = 2*k - k0
	d = v0
	c = k0
	b = 3*(v1 - d) - k1 - 2*c
	a = v1 - b - c - d
	return (a, b, c, d)

def interp3(v0, v1, k0, k1, t_in):

#	v = at^3 + bt^2 + ct + d
#	k = 3at^2 + 2bt + c
#	  t : 0 -> 1
#
#	v0 = d
#	k0 = c
#	v1 = a + b + c + d
#	k1 = 3a + 2b + c
#	
#	3v1 = 3a + 3b + 3c + 3d
#	k1  = 3a + 2b + c	
#
#	3v1 - k1 = b + 2c + 3d
#	b = 3v1 - k1 - 2c - 3d
#          = 3(v1 - d) - k1 - 2c
#
#	( b = 3(v1 - v0) - k1 - 2k0 )
#
#	v1 = a + b + c + d
#	a = v1 - b - c - d

	(a, b, c, d) = interp3_abcd(v0, v1, k0, k1)
	v = ((a * t_in + b) * t_in + c ) * t_in + d
	k = (3 * a * t_in + 2 * b) * t_in + c
	return (v, k)

3次式による補間
tが0の時の値v0、tが1の時の値v1、tが0の時の傾きk0、tが1の時の傾きk1
として、tがt_inの時の値vと傾きkのタプル(v, k)を返す

# EOF


v.py

#!/usr/bin/env python

import math

zero = [0,0,0]
one = [1,1,1]
x1 = [1,0,0]
y1 = [0,1,0]
z1 = [0,0,1]
x_nega = [-1,0,0]
y_nega = [0,-1,0]
z_nega = [0,0,-1]
all = lambda e: [e,e,e]

def op1(op, v, p):
	ops = {
		'+': lambda e: e + p,
		'-': lambda e: e - p,
		'*': lambda e: e * p,
		'/': lambda e: e / float(p),
	}
	f = ops.get( op, lambda e: None )
	return map(f, v)

ベクトルの各要素に同じ値の演算を施して返す
op1('+', [1,2,3], 1) は [2,3,4] を返す
op1('*', [1,2,3], 2) は [2,4,6] を返す


def op2(op, va, vb):
	ops = {
		'+': lambda (a, b): a + b,
		'-': lambda (a, b): a - b,
		'*': lambda (a, b): a * b,
		'/': lambda (a, b): a / float(b),
	}
	f = ops.get( op, lambda (a, b): None )
	return map( f, zip(va, vb) )

2つのベクトルの要素ごとに演算を施して返す
op2('+', [1,2,3], [2,3,4]) は [3,5,7] を返す
op2('*', [1,2,3], [2,3,4]) は [2,6,12] を返す


nega = lambda v: map( lambda e: -e, v )

向きを反転したベクトルを返す


lst_add = lambda vlst: map( sum, zip(*vlst) )

複数のベクトルを合算したベクトルを返す


dot_product = lambda va, vb: sum( op2('*', va, vb) )

ベクトルの内積


len2 = lambda v: dot_product(v, v)

ベクトルの長さの2乗


len1 = lambda v: math.sqrt( len2(v) )

ベクトルの長さ


unit = lambda v: ( lambda l: op1('/', v, l) if l != 0 else z1 )( len1(v) )

ベクトルの単位ベクトルを返す


sub = lambda a, b: op2('-', a, b)

ベクトルの引き算


sub_unit = lambda a, b: unit( sub(a, b) )

ベクトルの引き算結果の単位ベクトル


sub_len = lambda a, b: len1( sub(a, b) )

ベクトルの引き算結果の長さ


def dot_product_len(v_targ, v_base):
	base = dot_product(v_base, v_base)
	return float( dot_product(v_targ, v_base) ) / base if base != 0 else 0

v_targのv_base方向成分が、v_baseの長さの何倍かを求めて返す


def cross_product(va, vb):
	jks = map( lambda i: ( (i+1)%3, (i+2)%3 ), range(3) )
	return map( lambda (j, k): va[j] * vb[k] - va[k] * vb[j], jks )

ベクトルの外積


cross_product_unit = lambda va, vb: unit( cross_product(va, vb) )

ベクトルの外積を単位ベクトルにして返す


p3_to_nv = lambda o, a, b: cross_product_unit( sub(a, o), sub(b, o) )

o, a, bの3つの点を通る平面の法線ベクトル(単位ベクトル)を返す

# EOF


line.py

#!/usr/bin/env python

import ut
import v

def new(p, v_):

点pを通る方向ベクトルvの直線を返す

	e = ut.Empty()
	e.typ = 'line'
	e.p = p
	e.v = v_

	e.on_line_p = lambda t: v.op2( '+', p, v.op1('*', e.v, t) )

	直線上の点を返す
	t=0の時pを返し、t=1の時p+vを返す


	e.get_p_t = lambda : ( p, e.on_line_p(1) )

	pとp+vの2点をタプルで返す


	def tr_by_func(f):
		(p, t) = map( f, e.get_p_t() )
		return new_p2_no_unit(p, t) # no unit !!!
	e.tr_by_func = tr_by_func

	p, p+vの2点を指定の関数で変換し、変換後の2点を通る直線を返す

        ###

	#
	# (e.p, e.v) is plane
	#

	直線(点p、ベクトルv)を法線とする平面について


	e.is_plane_side = lambda p: v.dot_product( e.v, v.sub(p, e.p) ) >= 0

	点が平面の法線側の領域にあるか判定する


	def cross_plane_line(other_line):
		l = other_line
		base = v.dot_product(l.v, e.v)
		if base == 0:
			return []
		t = float( v.dot_product( v.sub(e.p, l.p), e.v ) ) / base
		return l.on_line_p(t)
	e.cross_plane_line = cross_plane_line

	平面と指定の別の直線との交点を返す

	return e


new_p2_no_unit = lambda p, t: new( p, v.sub(t, p) )

2つの点p, tを通る直線を返す(方向ベクトルは単位化しない)


new_p2 = lambda p, t: new( p, v.sub_unit(t, p) )

2つの点p, tを通る直線を返す(方向ベクトルは単位化する)


x1 = new( [0,0,0], v.x1 )

X軸


y1 = new( [0,0,0], v.y1 )

Y軸


z1 = new( [0,0,0], v.z1 )

Z軸

# EOF


vecs.py

#!/usr/bin/env python

import math
import ut
import mt
import v

def new(v3):
	# v3: [ v.x1, v.y1, v.z1 ]

3x3の行列

	e = ut.Empty()
	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 )
	e.tr = lambda d, v: { 'l2g': l2g, 'g2l': g2l }.get(d, l2g)(v)

	3x3の行列にベクトルvを掛け算した結果のベクトルを返す


	e.rev = lambda : new( mt.v3_rev(v3) )

	逆行列を生成して返す


	e.zoom = lambda zm3: new( map( lambda (v_, zm): v.op1('*', v_, zm), zip(v3, zm3) ) )

	zm3=[zmx, zmy, zmz]として
	元の行列に対してe.tr()による変換結果のベクトルが
	X成分がzmx倍、Y成分がzmy倍、Z成分がzmz倍となるような行列を返す


	e.zoom_all = lambda zm: e.zoom( [zm,zm,zm] )

	元の行列に対してe.tr()による変換結果のベクトルが
	各成分がzm倍となるような行列を返す


	e.zoom_x = lambda zm: e.zoom( [zm,1,1] )

	元の行列に対してe.tr()による変換結果のベクトルが
	X成分がzm倍となるような行列を返す


	e.zoom_y = lambda zm: e.zoom( [1,zm,1] )

	元の行列に対してe.tr()による変換結果のベクトルが
	Y成分がzm倍となるような行列を返す


	e.zoom_z = lambda zm: e.zoom( [1,1,zm] )

	元の行列に対してe.tr()による変換結果のベクトルが
	Z成分がzm倍となるような行列を返す

	return e

one = new( [v.x1, v.y1, v.z1] )

3x3の単位行列


def by_fix_ref(fix_nm, fix, ref_nm, ref):

固定のベクトルとその軸の名前 fix, fix_nm
参考にするベクトルとその軸の名前 ref, ref_nm
から互いに直交するX,Y,Z軸を3x3の行列として返す

例えば視線の方向ベクトルをY軸として、
なるべくZ軸が上方を指すように座標系を決めるときに
fix_nm='y', fix=視線方向, ref_nm='z', ref=[0,0,1]
と指定して、視線の座標系を求める


	fix_i = 'xyz'.index(fix_nm)
	ref_i = 'xyz'.index(ref_nm)

	lst = [0,1,2]
	lst.remove(fix_i)
	lst.remove(ref_i)
	i = lst[0]

	v3 = [ [],[],[] ]
	v3[fix_i] = fix

	if fix == ref:
		ref = ( lambda (x, y, z): (-y, x, z) )( ref ) if 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)

	(a, b) = (fix, c) if (fix_i + 1) % 3 == i else (c, fix)
	v3[ref_i] = v.cross_product_unit(a, b)
	
	return new(v3)

def rot_y(deg):
	rad = mt.deg_to_rad(deg)
	s = math.sin(rad)
	c = math.cos(rad)

	(x, y, z) = one.v3
	(xx, xy, xz) = x
	(yx, yy, yz) = y
	(zx, zy, zz) = z

	zz = xx = c
	xz = -s
	zx = s

	x = (xx, xy, xz)
	y = (yx, yy, yz)
	z = (zx, zy, zz)
	v3 = (x, y, z)

	return new(v3)

Y軸を軸にdeg度回転する3x3行列を返す

# EOF


ax.py

#!/usr/bin/env python

import ut
import v
import line
import vecs
import img

def new(p, vs):
	# vs: vecs

ローカル座標の原点位置をグローバル座標で表したものをp
ローカル座標のX,Y,Z軸をグローバル座標で表した3x3行列(vecsオブジェクト)をvsとして
座標系の変換としてアフィン変換もどきを生成する

	e = ut.Empty()
	e.typ = 'ax'
	e.p = p
	e.vs = vs

	l2g = lambda p: v.op2( '+', vs.tr('l2g', p), e.p )
	g2l = lambda p: vs.tr( 'g2l', v.op2('-', p, e.p) )
	e.tr_p = lambda d, p: { 'l2g': l2g, 'g2l': g2l }.get(d, l2g)(p)
	e.tr_v = lambda d, v: vs.tr(d, v)
	e.tr_line = lambda d, l: line.new( e.tr_p(d, l.p), e.tr_v(d, l.v) )

	def tr(d, targ, typ='p'):
		dic = {
			'p': e.tr_p,
			'v': e.tr_v,
			'line': e.tr_line,
		}
		f = dic.get( ut.get_typ(targ, typ), e.tr_p )
		return f(d, targ)
	e.tr = tr

	dに'l2g'を指定するとローカル座標からグローバル座標への変換
	dに'g2l'を指定するとグローバル座標からローカル座標への変換


	def compo(ax_outside):
		# 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 ) )
		p_ = ax_outside.tr(d, p)
		return new(p_, vs_)
	e.compo = compo

	他のアフィン変換もどきを合成する
	'l2g'向きで後ろに追加される


	def rev():
		# P = a.p + a.vs * p
		# p = rev(a.vs) * (P - a.p)
		# = { rev(a.vs) * (-a.p) } + rev(a.vs) * P
		vs_ = vs.rev()
		if not vs_:
			return []
		p_ = vs_.tr( 'l2g', v.nega(p) )
		return new(p_, vs_)
	e.rev = rev

	逆変換を返す

	return e

one = new( [0,0,0], vecs.one )

変換なし(ローカル座標の原点とX,Y,Z軸がグローバル座標と一致)


slide = lambda sld: new(sld, vecs.one)

平行移動


slide_x = lambda x: slide( [x,0,0] )

X方向の平行移動


slide_y = lambda y: slide( [0,y,0] )

Y方向の平行移動


slide_z = lambda z: slide( [0,0,z] )

Z方向の平行移動


zoom = lambda zm3: new( [0,0,0], vecs.one.zoom(zm3) )

拡大縮小


zoom_all = lambda zm: zoom( [zm,zm,zm] )

全方向拡大縮小


zoom_x = lambda zm: zoom( [zm,1,1] )

X方向拡大縮小


zoom_y = lambda zm: zoom( [1,zm,1] )

Y方向拡大縮小


zoom_z = lambda zm: zoom( [1,1,zm] )

Z方向拡大縮小


rot_y = lambda deg: new( [0,0,0], vecs.rot_y(deg) )

Y軸回転


rot = lambda l, deg: ( lambda ax: ax.rev().compo( rot_y(deg) ).compo(ax) )( new_line(l) )

指定の直線を軸として回転


rot_x = lambda deg: rot(line.x1, deg)

X軸回転


rot_z = lambda deg: rot(line.z1, deg)

Z軸回転


wh_map = lambda w, h: slide( [-w*0.5,-h*0.5,0] ).compo( zoom_all(1.0/w) ).compo( rot_x(180) )

w x h の矩形(左上原点)を、矩形の中心が原点で、矩形の上方向がY軸の正方向、
矩形の幅方向の0からwが、-1から1となるように拡大縮小をかけた座標系への変換


def fn_map(fn):
	(w, h) = img.imgs.wh(fn)
	return wh_map(w, h)

ファイルパスfnの画像の矩形サイズでwh_map変換を生成して返す


new_line = lambda l: new( l.p, vecs.by_fix_ref('y', l.v, 'z', v.z1) )

直線のpを原点、vをY軸として、
Z軸をなるべく上方向に取るような座標系を生成して返す

# EOF


cylx.py

#!/usr/bin/env python

import math
import ut
import v
import line

def new(r, rev=False):

半径rの円柱座標系への変換を返す
円柱座標の方がローカル座標系
rev=Trueにするとローカル座標系とグローバル座標系が逆になった変換を返す



	e = ut.Empty()
	e.typ = 'cylx'
	e.r = r

	def l2g(p):
		(x, y, z) = p
		rad = x/e.r
		r = z
		return [ r*math.cos(r), r*math.sin(r), y ]

	def g2l(p):
		(x, y, z) = p
		rad = math.atan2(y, x)
		r = v.len1( [x,y,0] )
		return [ e.r*rad, z, r ]

	dval = lambda d: d if not rev else { 'l2g': 'g2l', 'g2l': 'l2g' }.get(d, 'g2l')
	e.tr_p = lambda d, p: { 'l2g': l2g, 'g2l': g2l }.get( dval(d), l2g)(p)
	e.tr_line = lambda d, l: l.tr_by_func( lambda p: e.tr_p(d, p) )

	def tr(d, targ, typ='p'):
		dic = {
			'p': e.tr_p,
			'line': e.tr_line,
		}
		f = dic.get( ut.get_typ(targ, typ), e.tr_p )
		return f(d, targ)
	e.tr = tr

	e.rev = lambda : new(e.r, not rev)

	return e
# EOF


fcx.py

#!/usr/bin/env python

import ut
import v
import line

def new(dz):
	# dz != 0

原点を焦点とし、z=dz 平面へと投影する座標系の変換を返す
焦点座標の方がローカル座標系


	e = ut.Empty()
	e.typ = 'fcx'
	e.dz = dz

	def l2g(p):
		(x, y, z) = p
		return [ x*z/dz, y*z/dz, z ] if dz != 0 else []

	def g2l(p):
		(x, y, z) = p
		return [ x*dz/z, y*dz/z, z ] if z != 0 else []

	dval = lambda d: d if not rev else { 'l2g': 'g2l', 'g2l': 'l2g' }.get(d, 'g2l')
	e.tr_p = lambda d, p: { 'l2g': l2g, 'g2l': g2l }.get(d, l2g)(p)
	e.tr_line = lambda d, l: l.tr_by_func( lambda p: e.tr_p(d, p) )

	def tr(d, targ, typ='p'):
		dic = {
			'p': e.tr_p,
			'line': e.tr_line,
		}
		f = dic.get( ut.get_typ(targ, typ), e.tr_p )
		return f(d, targ)
	e.tr = tr

	dに'l2g'を指定するとローカル座標(焦点座標)からグローバル座標への変換
	dに'g2l'を指定するとグローバル座標からローカル座標(焦点座標)への変換


	rev = lambda : new(e.r, not rev)

	逆変換を返す


        return e
# EOF


lstx.py

#!/usr/bin/env python

import ut
import v
import line
import ax

def opt(lst):
	r = []
	is_last_ax = lambda : r and ut.get_typ(r[-1]) == 'ax'
	for x in lst:
		if ut.get_typ(x) == 'ax' and is_last_ax():
			r[-1] = r[-1].compo(x)
		else:
			r.append(x)
	return r

lstはax, cylx, fcxなどの変換のリスト
axの並びを合成してまとめて最適化する


dval = lambda d, rev: d if not rev else { 'l2g': 'g2l', 'g2l': 'l2g' }.get(d, 'g2l')

def tr_p(lst, d, p):
	if d == 'g2l':
		lst = reversed(lst)
	return reduce( lambda p, x: x.tr_p(d, p), lst, p )

def tr_line(lst, d, l):
	return l.tr_by_func( lambda p: tr_p(lst, d, p) )

def tr(lst, d, targ, typ='p'):
	dic = {
		'p': tr_p,
		'line': tr_line,
	}
	f = dic.get( ut.get_typ(targ, typ), tr_p )
	return f(lst, d, targ)

dに'l2g'を指定するとローカル座標からグローバル座標への変換
dに'g2l'を指定するとグローバル座標からローカル座標への変換

# EOF


img.py

#!/usr/bin/env python

import sys
import numpy as np
import cv2
import ut

wh = lambda img: ( lambda (h, w, d): (w, h) )( img.shape )

imgのサイズを(w, h)の並びのタプルで返す


resize = lambda img, w, h: img if wh(img) == (w, h) else cv2.resize( img, (w, h) )

imgのサイズをw, hにリサイズする


def new_imgs():

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

	d = {}
	e = ut.Empty()

	def get(fn):
		if fn not in d:
			d[fn] = cv2.imread(fn)
		return d.get(fn)
	e.get = get

	fnのファイルパスの画像データを返す


	e.wh = lambda fn: wh( get(fn) )

	fnのファイルパスの画像のサイズを(w, h)のタプルで返す


	e.in_img = lambda fn, x, y: ( lambda (w, h): 0 <= x and x < w and 0 <= y and y < h )( e.wh(fn) )

	fnのファイルパスの画像のサイズについて、x, yの位置が範囲内か判定する


	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

	fnのファイルパスの画像のx, yの位置の色を返す

	return e


imgs = new_imgs()

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


def new_wimg(w, h, def_col=[0,0,0]):

画像データ生成用のバッファ


	e = ut.Empty()
	e.img = np.empty( (h, w, 3), dtype=np.uint8 )

	def clear():
		e.img[:] = def_col
	e.clear = clear
	clear()

	e.in_img = lambda x, y: 0 <= x and x < w and 0 <= y and y < h

	def set(x, y, col):
		if e.in_img(x, y):
			e.img[y, x] = col
	e.set = set

	x, y位置に指定の色をセットする


	e.col = lambda x, y: e.img[y, x] if e.in_img(x, y) else def_col

	x, y位置の色を返す


	e.write = lambda fn: cv2.imwrite(fn, e.img)

	画像データをファイルパスfnのファイルに書き出す


	def show(title='', sec=-1, zm=1):
		img = e.img
		if zm != 1:
			img = resize( img, int(w*zm), int(h*zm) )
		cv2.imshow(title, img)
		msec = 0 if sec < 0 else max( 1, int(sec * 1000) )
		cv2.waitKey(msec)
	e.show = show

	画像データを表示する
	sec秒間表示する。secが負ならキー入力があるまで
	zmは表示するときの拡大率


	def line(sx, sy, ex, ey, col):
		r_d = lambda s, e: ( e < s, abs(e - s) )
		(rx, dx) = r_d(sx, ex)
		(ry, dy) = r_d(sy, ey)
		rxy = dx < dy
		(a, b) = (dy, dx) if rxy else (dx, dy)
		sum = j = 0;
		for i in range(a):
			sum += b;
			if sum > a:
				sum -= a
				j += 1
			(x, y) = (j, i) if rxy else (i, j)
			x = sx + (-x if rx else x)
			y = sy + (-y if ry else y)
			set(x, y, col)
	e.line = line

	指定の色の線をバッファに描画する

	return e

def new_video(codec, fn, fps, w, h, zm=1.0, def_col=[0,0,0]):

動画を作成する

	e = new_wimg(w, h, def_col)

	w, hのサイズの画像バッファを使用
	動画の1コマとして追加する際は拡大率zmをかけて追加する


	f = cv2.VideoWriter_fourcc if hasattr(cv2, 'VideoWriter_fourcc') else cv2.cv.CV_FOURCC
	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 ) )

	wimgの機能で描画された画像データを動画に追加する


	e.show_zm = lambda title='', sec=-1: e.show(title, sec, zm)

	現在の画像バッファを拡大率zmで表示する


	e.fin = lambda : writer.release()

	動画ファイルを生成する後処理

	return e

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'
	video = new_video(codec, fn, fps, w, h, zm)

	for fn in lst:
		video.img[:] = resize( imgs.get(fn), w, h )[:]
		video.add_img()
	video.fin()

if __name__ == "__main__":
	if len(sys.argv[1:]) < 2:
		print 'Usage: {} img_name video_name fps= zm= codec='.format(sys.argv[0])
		print '  fps is int value (default 30)'
		print '  zm is float value (default 1.0)'
		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)
# EOF


way.py

#!/usr/bin/env python

import sys
import ut
import mt
import v
import line

#	[ (v, t), (v, t), ... (v, t) ]
#	or
#	[ (v, t), (v, t), ... (v, None) ]
#
#	v: value
#	t: sec interval for next v

def get_idx_t(ts, t):
	s = sum(ts)
	t -= int( float(t) / s ) * s
	s = 0
	for i in range( len(ts) ):
		if s <= t and t <= s + ts[i]:
			break
		s += ts[i]
	t -= s
	return (i, t)

def get_k(lst, i):
	(v, t) = lst[i]
	if t == None:
		return None
	j = (i + 1) % len(lst)
	(v2, _) = lst[j]
	return float(v2 - v) / t

def get(lst, t, itp3=False):
	# itp3 is using mt.interp3

	(vs, ts) = zip(*lst)
	ts_ = ts if ts[-1] != None else ts[:-1]
	(idx, t) = get_idx_t(ts_, t)
	n = len(lst)
	if itp3:
		ka = get_k( lst, (idx-1+n)%n )
		kb = get_k( lst, idx )
		kc = get_k( lst, (idx+1)%n )
				
		f = lambda k, k_, ti: None if k == None or k_ == None else (k + k_) * 0.5 * ti
		ti = ts[idx]
		k0 = f(ka, kb, ti)
		k1 = f(kb, kc, ti)

		(v, k) = mt.interp3( vs[idx], vs[(idx+1)%n], k0, k1, float(t)/ti )
		return v

	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: map( lambda lst: get(lst, t, itp3), lsts )
	
def liss_eye():
# sys.argv
# 'eyep=[0,-10,0],20,40' 'eyet=[0,0,0],1,50' init_sec=5
# [x,y,z],r,t
#
# [x,y,z]: default [0,0,0]
# r: default 0
# t: default 1.0 (sec)

	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) )
	
	def liss_get(key):
		(p, r, t) = p_r_t(key)
		ps = v.op1('-', p, r)
		pe = v.op1('+', p, r)
		t3 = (t, t*4/3, t*5/3)
		return liss(ps, pe, t3)

	f_p = liss_get('eyep')
	f_t = liss_get('eyet')
	init_sec = ut.arg_f('init_sec', 0.0)
	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
	t = 0
	while t <= end_t:
		v = get(lst, t, itp3)
		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)
				sys.exit(-1)
			s += t

if __name__ == "__main__":
	test( [(0,1),(1,1)] )
	test( [(0,1),(1,1)], True )
	test( [(0,1),(1,None)] )
	test( [(0,1),(1,None)], True )

	test( [(0,1),(2,3)] )
	test( [(0,1),(2,3)], True )
	test( [(0,1),(2,None)] )
	test( [(0,1),(2,None)], True )

	test( [(3,2),(1,1)] )
	test( [(3,2),(1,1)], True )
	test( [(3,2),(1,None)] )
	test( [(3,2),(1,None)], True )

	print 'OK'
# EOF


wf.py

#!/usr/bin/env python

import v
import line
import ax
import lstx

def wf_ps_idxs(d):


物体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) )

	kind = d.get('kind')

	if kind == 'square':
		n = 4
		ps = map( lambda i: ax.rot_z(i*90).tr('l2g', [1,1,0]), range(n) )
		return rv_ps(ps)
	if kind == 'triangle':
		n = 3
		ps = [ [0,1,0],[-1,0,0],[1,0,0] ]
		return rv_ps(ps)
	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', [0,-1,0]), range(n) )
		return rv_ps(ps)
	if kind == 'circle':
		d = { 'kind': 'poly_n', 'n': 16 }
		return wf_ps_idxs(d)
	if kind == 'ball':
		d = { 'kind': 'circle' }
		c3 = map( lambda i: wf_ps_idxs(d), range(3) )

		(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 )
		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 )
		c3[2] = (ps, idxs)

		return map( lambda xs_lst: sum( xs_lst, [] ), zip(*c3) )

	return ([],[])

def nv_clip(nv, pa, pb):

点pa, pbを通る線分を原点を通り法線べクトルがnvの平面で
法線ベクトル側の領域にクリッピングして返す


	nv_line = line.new( [0,0,0], nv )
	(in_a, in_b) = map( 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:
		return []
	pc = nv_line.cross_plane_line( line.new_p2(pa, pb) )
	return [pa, pc] if in_a else [pc, pb]

def eye_clip(eye2g, wh2eye, sc_sz, pa, pb):

点pa, pbを通る線分を視界の範囲内でクリッピングして返す


	(w, h, d) = sc_sz
	n = 4
	p4 = [ [0,0,-d], [w,0,-d], [w,h,-d], [0,h,-d] ]
	p4 = map( lambda p: lstx.tr(wh2eye, 'l2g', p), p4 )
	for i in range(n):
		nv = v.p3_to_nv( [0,0,0], p4[i], p4[(i+1)%n] )
		r = nv_clip(nv, pa, pb)
		if not r:
			return []
		(pa, pb) = r
	return [pa, pb]

def draw(data, eye2g, wh2eye, sc_sz, video):
	col = v.all(255) # white
	for d in data:
		ps = []
		idxs = []
		(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 )

		p2s = map( 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:
			(ax, ay, _) = pa
			(bx, by, _) = pb
			video.line( int(ax), int(ay), int(bx), int(by), col )
# EOF


cg.py




cross.py

#!/usr/bin/env python

import math
import mt
import v
import line
import ax
import lstx

# return dic keys 't', 'p', 'nv'

def plane_z0(l, d):
	# z=0 (x-y)

	(vx, vy, vz) = l.v
	(px, py, pz) = l.p

	if vz == 0:
		return {}

	t = -float(pz) / vz
	if t <= 0:
		return {}

	p = l.on_line_p(t)
	return { 't': t, 'nv': v.z1, 'p': p }

z=0 のxy平面(法線は[0,0,1])と直線lとの交点を求める


def plane_z0_xy_cond(l, d):
	xy_cond_f = d.get( 'xy_cond_f', lambda x, y: True )
	r = plane_z0(l, d)
	if not r:
		return {}
	(x, y, _) = r.get('p')
	return r if xy_cond_f(x, y) else {}

def square(l, d):
	# [-1,-1,0] --- [1,1,0]
	d['xy_cond_f'] = lambda x, y: abs(x) <= 1 and abs(y) <= 1
	return plane_z0_xy_cond(l, d)

[1,1,0],[-1,1,0],[-1,-1,0],[1,-1,0]を頂点とする四角(法線は[0,0,1])と直線lとの交点を求める


def circle(l, d):
	# center zero, r=1, z=0
	d['xy_cond_f'] = lambda x, y: x*x + y*y <= 1
	return plane_z0_xy_cond(l, d)

中心[0,0,0] 半径1の円(法線は[0,0,1])と直線lとの交点を求める


def triangle(l, d):
	# (0,1,0),(-1,0,0),(1,0,0)
	d['xy_cond_f'] = lambda x, y: x+y<=1 and -x+y<=1 and y>=0
	return plane_z0_xy_cond(l, d)

def poly_n(l, d):
	n = d.get('n', 3)
	deg = 360.0 / n
	rad = mt.deg_to_rad( deg * 0.5 )
	h = math.cos(rad)
	w = math.sin(rad)
	for i in range(n):
		axi = ax.slide_y(-1).compo( ax.zoom([w,h,1]) ).compo( ax.rot_z(i*deg) )
		r = triangle( axi.tr('g2l', l), d )
		if r:
			return r
	return {}

def ball(l, d):
	# r = 1, c = zero
	# l = p + v t
	# v^2 t^2 + 2 v p t + p^2 = 1^2
	a = sum( v.op2('*', l.v, l.v) )
	b = 2 * sum( v.op2('*', l.v, l.p) )
	c = sum( v.op2('*', l.p, l.p) ) - 1
	ts = mt.quadratic_formula(a, b, c)
	ts = sorted( filter( lambda v: v > 0, ts ) )
	if not ts:
		return {}
	t = ts[0]
	p = l.on_line_p(t)
	nv = v.unit(p)
	return { 't': t, 'nv': nv, 'p': p }

中心[0,0,0] 半径1の球(法線は外向き)と直線lとの交点を求める


def cross(l, d):
	dic = {
		'square': square,
		'circle': circle,
		'triangle': triangle,
		'poly_n': poly_n,
		'ball': ball,
	}
	f = dic.get( d.get('kind') )
	if not f:
		return {}
	crs = f(l, d)
	if not crs:
		return {}
	if 'p' not in crs:
		crs['p'] = l.on_line_p( crs.get('t') )
	if 'l' not in crs:
		crs['l'] = line.new( crs.get('p'), crs.get('nv') )
	return crs

各種物体と直線lとの交点を求める


def cross_g(l_g, d):
	l2g = d.get('l2g')
	l = lstx.tr( l2g, 'g2l', l_g )
	crs = cross(l, d)
	if not crs:
		return {}
	crs['l'] = nvl_g = lstx.tr( l2g, 'l2g', crs.get('l') )
	crs['p'] = nvl_g.p
	crs['nv'] = nvl_g.v
	return crs

各種物体と直線l_g(グローバル座標系)との交点を求める
ローカル座標系との変換は d の 'l2g' を使う

# EOF


rt.py

#!/usr/bin/env python

import ut
import v
import line
import lstx
import cross

def map_col(d, crs):
	def_col = d.get( 'def_col', v.all(128) )
	return def_col

物体の情報dと、交点の情報crsから、交点の色を返す
この段階では、物体のデフォルト色を返すのみ


def get_col(data, l_g):
	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)

物体群dataと直線l_g(グローバル座標系)との交点の色を返す


def draw(data, eye2g, wh2eye, sc_sz, video):
	(w, h, d_) = sc_sz
	wh2g = lstx.opt( wh2eye + [ eye2g ] )
	cnt = ut.new_cnt_wh(w, h, 'wh')
	while cnt.up():
		(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)
		if col:
			video.set(ix, iy, col)
# EOF


工事中