2004-06-17
[開発ログ]Tk3d実行速度の違い(Windows)
同じWindowsマシンでもcygwinとmswin32で速度が異なる。 tkのバージョンのせいだろうか?
mswin32
>ruby -v ruby 1.8.1 (2003-12-25) [i386-mswin32] ruby -rtk -e 'p Tk::TCL_VERSION' "8.3" loop time(1.344ms)
cygwin
$ ruby -v ruby 1.8.1 (2003-12-25) [i386-cygwin] $ ruby -rtk -e 'p Tk::TK_VERSION' "8.4" loop time(1.648ms)
tk3d.rbを更新
require("tk")
module Tk3d
# poss : [[x1,y1,z1],[x2,y2,z2],[x3,y3,z3],...]
# angle : [ax,ay,az]
def Tk3d.rotate_zyx(poss,angle)
ax = angle[0]
ay = angle[1]
az = angle[2]
poss.collect!{|px,py,pz|
# Z軸回転
xx = px * Math.cos(az) - py * Math.sin(az)
yy = px * Math.sin(az) + py * Math.cos(az)
px = xx
py = yy
# Y軸回転
zz = pz * Math.cos(ay) - px * Math.sin(ay)
xx = pz * Math.sin(ay) + px * Math.cos(ay)
pz = zz
px = xx
# X軸回転
yy = py * Math.cos(ax) - pz * Math.sin(ax)
zz = py * Math.sin(ax) + pz * Math.cos(ax)
py = yy
pz = zz
[px,py,pz]
}
end
def Tk3d.rotate_xyz(poss,angle)
ax = angle[0]
ay = angle[1]
az = angle[2]
poss.collect!{|px,py,pz|
# X軸回転
yy = py * Math.cos(ax) - pz * Math.sin(ax)
zz = py * Math.sin(ax) + pz * Math.cos(ax)
py = yy
pz = zz
# Y軸回転
zz = pz * Math.cos(ay) - px * Math.sin(ay)
xx = pz * Math.sin(ay) + px * Math.cos(ay)
pz = zz
px = xx
# Z軸回転
xx = px * Math.cos(az) - py * Math.sin(az)
yy = px * Math.sin(az) + py * Math.cos(az)
px = xx
py = yy
[px,py,pz]
}
end
def Tk3d.transfer(poss,position)
mx = position[0]
my = position[1]
mz = position[2]
poss.collect!{|pos|
[pos[0]+mx,pos[1]+my,pos[2]+mz]
}
end
def Tk3d.to_screen(poss,cx,cy,screen_z,scale_x=1.0,scale_y=1.0)
npos = []
poss.each{|px,py,pz|
npos << [scale_x*px*screen_z/pz + cx, scale_y*py*screen_z/pz + cy]
}
npos
end
def Tk3d.av_z(poss)
av = 0
poss.each{|px,py,pz|
av += pz
}
av /= poss.size
end
def Tk3d.min_z(poss)
min = poss[0][2]
poss.each{|px,py,pz|
min = pz if pz<min
}
min
end
end
class Tk3dCanvas < TkCanvas
attr_accessor :scale
attr_accessor :camera
attr_accessor :scale
def initialize(parent=nil, keys=nil)
super
@scale = 1.0
end
def quit
@canvas.destroy
end
end
class Tk3dCamera
attr_accessor :ppos
attr_accessor :angle
attr_accessor :length
attr_accessor :position
attr_accessor :screen_z
def initialize(screen_z=1000.0,ppos=nil,ang=nil)
@screen_z = screen_z
@ppos = ppos ? ppos : [0.0,0.0,0.0]
@angle = ang ? ang : [0.0,0.0,0.0]
end
def flush
npos=[[0,0,-@length]]
Tk3d::rotate_xyz(npos,@angle.collect{|v|v*=-1})
npos.flatten!
@position = [@ppos[0]+npos[0], @ppos[1]+npos[1], @ppos[2]+npos[2]]
flag = (@position_last != @position || @angle_last != @angle || @length_last != @length)
@position_last = @position.dup
@angle_last = @angle.dup
@length_last = @length
flag
end
end
class Tk3dOt
attr_reader :z_rate
attr_reader :tag_to_z
def initialize(canvas3d,z_rate=1.0)
@canvas3d = canvas3d
@z_rate = z_rate
@unused = {}
@used = []
@tag_to_z = {}
@prims = {}
# 描画の基準となるウィジェット。このウィジェットの描画順序がこのOtの描画順序となる
@base_prim = TkcPolygon.new(@canvas3d, [])
end
def insert_unused(z,data)
@unused[z] = [] unless @unused[z]
@unused[z] << data
end
def clear_unused
@unused.clear
end
def unused_empty?
@unused.empty?
end
def add_prim(key,value)
@prims[key] = value
end
def get_prim(key)
@prims[key]
end
def get_prim_all
@prims
end
def z_to_neartag(z)
unless a = @used[z..-1]
return nil
end
return a.compact.flatten[0]
end
def draw(max)
c = @canvas3d
n = 0
@unused.each{|z,v|
while !v.empty?
(tag,color,pos2d) = v.shift
c.coords(tag, pos2d)
# @canvas3d.get_prim(tag).fill("#000000")
if old_z = @tag_to_z[tag]
if @used[old_z].size <= 1
@used[old_z] = nil
else
@used[old_z].delete(tag)
end
end
if ltag = z_to_neartag(z)
prim = @prims[ltag]
else
prim = @base_prim
end
@prims[tag].raise(prim)
@tag_to_z[tag] = z
@used[z] = [] unless @used[z]
@used[z] << tag
return if (n += 1) > max
end
@unused.delete(z)
}
end
end
# Tk3dModel
# * モデリングデータ(構成要素)を保持
# * ワールド内での位置と角度を保持
# * OTに描画TAGを登録
class Tk3dModel
attr_accessor :position
attr_accessor :angle
attr_reader :tag_to_data
def initialize(canvas3d, ot, data)
@canvas3d = canvas3d
@ot = ot
@tag_to_data = {}
@data = data
@data.collect!{|lcolor,color,pos|
prim = TkcPolygon.new(@canvas3d, []) do
outline(lcolor)
fill(color)
end
tag = prim.id.to_s
prim.addtag(tag)
@ot.add_prim(tag,prim)
@tag_to_data[tag] = [color,pos]
[tag,color,pos]
}
@position = [0.0,0.0,0.0]
@angle = [0.0,0.0,0.0]
end
def add_ot
camera_angle = @canvas3d.camera.angle
camera_position = @canvas3d.camera.position.collect{|v|v*=-1}
screen_z = @canvas3d.camera.screen_z
cx = TkWinfo.width(@canvas3d)/2 -2
cy = TkWinfo.height(@canvas3d)/2 -2
scale_x = @canvas3d.scale * cx / (@canvas3d.width/2)
scale_y = @canvas3d.scale * cy / (@canvas3d.height/2)
near_z = 0
z_rate = @ot.z_rate
@data.each{|tag,color,pos|
npos = pos.dup
#--ローカル変換
Tk3d::rotate_zyx(npos, @angle)
Tk3d::transfer(npos, @position)
#--ワールド変換
Tk3d::transfer(npos, camera_position)
Tk3d::rotate_zyx(npos, camera_angle)
#--スクリーン投影
if Tk3d::min_z(npos) > near_z#--nearクリップ
pos2d = Tk3d::to_screen(npos, cx, cy, screen_z, scale_x, scale_y)
z = (Tk3d::av_z(npos)*z_rate).to_i
else
pos2d = [0,0,0,0]
z = (near_z*z_rate).to_i
end
@ot.insert_unused(z,[tag,color,pos2d])
}
end
end
if $0 == __FILE__
TkRoot.new.bind("KeyPress-Escape", proc{ @after.stop;TkRoot.new.destroy })
@camera = Tk3dCamera.new(1000)
@camera.length = 500
@canvas3d = Tk3dCanvas.new do
width(200)
height(160)
background("#888888")
pack('fill' => 'both', 'expand' => true)
end
@canvas3d.camera = @camera
@canvas3d.scale = 0.2
@ot_bg = Tk3dOt.new(@canvas3d,0)
@ot = Tk3dOt.new(@canvas3d,8.0)
# Model
@model = []
8.times{|i|
w = -15.0
h = -40.0
@model << m = Tk3dModel.new(@canvas3d,@ot,
[
["","#777777",[[0.0,0.0,0.0],[-w,h,-w],[-w,h, w]]],
["","#888888",[[0.0,0.0,0.0],[-w,h, w],[ w,h, w]]],
["","#888888",[[0.0,0.0,0.0],[ w,h, w],[ w,h,-w]]],
["","#777777",[[0.0,0.0,0.0],[ w,h,-w],[-w,h,-w]]],
["","#999999",[[-w,h,-w],[-w,h,w],[w,h,w],[w,h,-w]]],
]
)
x = i%2==0 ? i/2*40 : -(i+1)/2*40
m.position[0] = x
}
# BG Model
n = 8
w = 64
data = []
n.times{|z|
n.times{|x|
h = 0.0
x1 = x4 = x*w - n*w/2
x2 = x3 = (x+0.8)*w - n*w/2
z1 = z2 = z*w - n*w/2
z3 = z4 = (z+0.8)*w - n*w/2
c = 0xAA - ((x1*x1+z1*z1)/(n*w*4)).to_i.abs
c = 0x00 if c < 0x00
color = sprintf("#%02x%02x%02x",c,c,c)
data << ["",color,[[x1,h,z1],[x2,h,z2],[x3,h,z3],[x4,h,z4]]]
}
}
@bg_model = Tk3dModel.new(@canvas3d,@ot_bg,data)
@camera.angle = [0,0,0]
@camera.ppos = [0,-20,0]
@camera.angle[0] = Math::PI/180.0*10
@camera.angle[1] = Math::PI/180.0*80
draw_max = n*n
TkScale.new {
from(1)
to(n*n)
set(n*n)
orient 'horizontal'
command { |val| draw_max = val }
pack
}
#--------------------
counter = 0
@ct = 0
@t = Time.now
@after = TkAfter.new(10, -1, proc {
if counter%2 == 0
@camera.angle[1] += -Math::PI/180.0*2
end
if @camera.flush
@ot_bg.clear_unused
@bg_model.add_ot
end
if @ot.unused_empty?
ct = 0
@model.each{|m|
ct += 1
next if @ct%1 != ct%1
m.angle[1] += Math::PI/180.0*(rand(4)+8)
m.add_ot
}
@ct += 1
end
@ot.draw(50)
@ot_bg.draw(draw_max)
counter += 1
if counter%20 == 0
puts " loop time(#{Time.now-@t}ms)"
@t = Time.now
end
})
@after.cancel_on_exception = false
@after.start
#--------------------
Tk.mainloop
end