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