disp3D 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/README.rdoc +73 -1
  2. data/VERSION +1 -1
  3. data/disp3D.gemspec +97 -0
  4. data/example/stl_viewer/app_model.rb +8 -0
  5. data/example/stl_viewer/document.rb +15 -0
  6. data/example/stl_viewer/document_ctrl.rb +62 -0
  7. data/example/stl_viewer/gl_ctrl.rb +15 -0
  8. data/example/stl_viewer/main.rb +120 -0
  9. data/example/stl_viewer/mesh_info.rb +12 -0
  10. data/example/stl_viewer/stl_viewer.rb +9 -0
  11. data/lib/camera.rb +49 -35
  12. data/lib/disp3D.rb +19 -3
  13. data/lib/dsl.rb +18 -0
  14. data/lib/gl_view.rb +80 -0
  15. data/lib/glut_window.rb +60 -0
  16. data/lib/light.rb +49 -0
  17. data/lib/manipulator.rb +92 -21
  18. data/lib/node.rb +15 -9
  19. data/lib/node_arrows.rb +68 -0
  20. data/lib/node_collection.rb +14 -3
  21. data/lib/node_leaf.rb +80 -0
  22. data/lib/node_lines.rb +4 -7
  23. data/lib/node_points.rb +9 -11
  24. data/lib/node_polylines.rb +34 -0
  25. data/lib/node_tea_pod.rb +9 -2
  26. data/lib/node_text.rb +17 -0
  27. data/lib/node_tris.rb +24 -0
  28. data/lib/picked_result.rb +15 -0
  29. data/lib/picker.rb +56 -0
  30. data/lib/qt_widget_gl.rb +80 -0
  31. data/lib/scene_graph.rb +5 -0
  32. data/lib/stl.rb +92 -0
  33. data/lib/util.rb +18 -0
  34. data/test/test_data/binary_test.stl +0 -0
  35. data/test/test_data/cube-ascii.stl +86 -0
  36. data/test/test_dsl.rb +8 -0
  37. data/test/test_glut_window.rb +113 -0
  38. data/test/test_qtgl.rb +26 -0
  39. data/test/test_stl.rb +12 -0
  40. data/test/test_tea_pod.rb +13 -2
  41. metadata +41 -19
  42. data/lib/helloworld.rb +0 -112
  43. data/lib/view.rb +0 -47
  44. data/test/test_line.rb +0 -8
  45. data/test/test_lines.rb +0 -17
  46. data/test/test_point.rb +0 -25
  47. data/test/test_points.rb +0 -27
data/README.rdoc CHANGED
@@ -1,6 +1,78 @@
1
1
  = disp3D
2
+ 本ライブラリは、Qtと組み合わせて、3次元の表示が簡単に可能なフレームワークを提供する目的で開発されています。ターゲットは、簡単なアプリ、プロトタイピング、学術利用などを想定しています。
2
3
 
3
- This library provide 3D GUI framework.
4
+ 以下のような特徴を持つようなライブラリを目指します。
5
+ - シンプルで使いやすく!
6
+ - Rubyでの開発が可能
7
+ - windows,mac,linuxなどプラットフォームを問わない
8
+
9
+ また、ゲーム、CGや大規模モデルなどは当面の目標からは外れます。
10
+
11
+ == インストール
12
+ ライブラリは以下の3つのgemに依存しています。
13
+ - gmath3D
14
+ - ruby-opengl
15
+ - qtbindings()
16
+
17
+ === gmath3D
18
+ gemからインストールが可能です
19
+
20
+ gem install GMath3D
21
+
22
+ === ruby-opengl
23
+ openglを扱う上では、必須となります。
24
+
25
+ gemでのインストールが失敗する可能性があります。
26
+ https://github.com/toshi0328/ruby-opengl
27
+ の内容をビルド、インストールします。
28
+
29
+ === qtbindings
30
+ Qtを利用する場合に必要となります。
31
+
32
+ gemでのインストールが失敗する可能性があります。
33
+ https://github.com/toshi0328/qtbindings
34
+ の内容をビルド、インストールします。
35
+
36
+ == 使い方
37
+ === 例
38
+ 1.点や線を3次元ビュー上に追加し、表示する
39
+ 開発中
40
+
41
+ 2.Qtアプリケーションとして開発をする
42
+ 開発中
43
+
44
+ 3.STLファイルを読み込み、表示する
45
+ 開発中
46
+
47
+ 4.表示要素をマウス操作で取得する
48
+ 開発中
49
+
50
+ === データ構造
51
+ 中心となるクラスは、GLViewおよびSceneGraphです。
52
+ 執筆中
53
+
54
+ == 今後の課題
55
+ === 開発項目
56
+ - デモアプリ(STLビューワー)
57
+  - QTでのアプリケーション設計
58
+  - tri_nodeの法線方向指定
59
+ - インディケート処理の追加
60
+ - trackball manipulatorを洗練させる
61
+  - 回転中心を変えるとか・・・
62
+ - DSLの設計
63
+ - 自動テスト構築
64
+ - Windowsでの利用(なぜか、インストールでエラー・・・)
65
+ - ライティングの工夫、表示の洗練
66
+ - ドキュメント整理(html?)
67
+ - テクスチャの貼り付け
68
+ - データの可視化としての応用
69
+ - アニメーション
70
+ - モバイル応用
71
+
72
+ === 他ライブラリとの組み合わせ
73
+ - 3次元画像処理,pclの利用
74
+ - 医用応用として、DICOMパーサー(Ruby-DICOM)の利用
75
+ - 2次元画像処理、RMagicに任せる・・・(Ruby-DICOMとのつなぎなど)
4
76
 
5
77
  == Copyright
6
78
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
data/disp3D.gemspec ADDED
@@ -0,0 +1,97 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{disp3D}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Toshiyasu Shimizu"]
12
+ s.date = %q{2011-11-03}
13
+ s.description = %q{disp3D provide 3D GUI framework}
14
+ s.email = %q{toshi0328@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "disp3D.gemspec",
28
+ "example/stl_viewer/app_model.rb",
29
+ "example/stl_viewer/document.rb",
30
+ "example/stl_viewer/document_ctrl.rb",
31
+ "example/stl_viewer/gl_ctrl.rb",
32
+ "example/stl_viewer/main.rb",
33
+ "example/stl_viewer/mesh_info.rb",
34
+ "example/stl_viewer/stl_viewer.rb",
35
+ "lib/camera.rb",
36
+ "lib/disp3D.rb",
37
+ "lib/dsl.rb",
38
+ "lib/gl_view.rb",
39
+ "lib/glut_window.rb",
40
+ "lib/light.rb",
41
+ "lib/manipulator.rb",
42
+ "lib/node.rb",
43
+ "lib/node_arrows.rb",
44
+ "lib/node_collection.rb",
45
+ "lib/node_leaf.rb",
46
+ "lib/node_lines.rb",
47
+ "lib/node_points.rb",
48
+ "lib/node_polylines.rb",
49
+ "lib/node_tea_pod.rb",
50
+ "lib/node_text.rb",
51
+ "lib/node_tris.rb",
52
+ "lib/picked_result.rb",
53
+ "lib/picker.rb",
54
+ "lib/qt_widget_gl.rb",
55
+ "lib/scene_graph.rb",
56
+ "lib/stl.rb",
57
+ "lib/util.rb",
58
+ "test/helper.rb",
59
+ "test/test_data/binary_test.stl",
60
+ "test/test_data/cube-ascii.stl",
61
+ "test/test_dsl.rb",
62
+ "test/test_glut_window.rb",
63
+ "test/test_qtgl.rb",
64
+ "test/test_stl.rb",
65
+ "test/test_tea_pod.rb"
66
+ ]
67
+ s.homepage = %q{http://github.com/toshi0328/disp3D}
68
+ s.licenses = ["MIT"]
69
+ s.require_paths = ["lib"]
70
+ s.rubygems_version = %q{1.6.2}
71
+ s.summary = %q{disp3D provide 3D GUI framework.}
72
+
73
+ if s.respond_to? :specification_version then
74
+ s.specification_version = 3
75
+
76
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
77
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
78
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
79
+ s.add_development_dependency(%q<gmath3D>, [">= 0"])
80
+ s.add_development_dependency(%q<ruby-opengl>, ["= 0.60.1"])
81
+ s.add_development_dependency(%q<qtbindings>, ["= 4.6.3.4"])
82
+ else
83
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
84
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
85
+ s.add_dependency(%q<gmath3D>, [">= 0"])
86
+ s.add_dependency(%q<ruby-opengl>, ["= 0.60.1"])
87
+ s.add_dependency(%q<qtbindings>, ["= 4.6.3.4"])
88
+ end
89
+ else
90
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
91
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
92
+ s.add_dependency(%q<gmath3D>, [">= 0"])
93
+ s.add_dependency(%q<ruby-opengl>, ["= 0.60.1"])
94
+ s.add_dependency(%q<qtbindings>, ["= 4.6.3.4"])
95
+ end
96
+ end
97
+
@@ -0,0 +1,8 @@
1
+ require 'stl_viewer'
2
+
3
+ #TODO all model should have @dirty flg...
4
+ class AppModel
5
+ def initialize
6
+ @dirty = false
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ require 'stl_viewer'
2
+
3
+ class Document
4
+ def initialize
5
+ @tri_mesh_info_list = []
6
+ # @dirty = false
7
+ end
8
+
9
+ def add_tri_mesh!(tri_mesh)
10
+ # @dirty = true
11
+ mesh_info = MeshInfo.new(tri_mesh)
12
+ @tri_mesh_info_list.push(mesh_info)
13
+ return mesh_info
14
+ end
15
+ end
@@ -0,0 +1,62 @@
1
+ require 'stl_viewer'
2
+
3
+ class DocumentCtrl
4
+ attr_reader :document
5
+
6
+ def initialize(main_window)
7
+ @document = nil # this is THE ROOT DOCUMENT of this application
8
+ @main_window = main_window
9
+ @gl_ctrl = GLCtrl.new(main_window.gl_widget)
10
+ end
11
+
12
+ def new
13
+ if (@document.nil? || may_be_save)
14
+ @document = Document.new()
15
+ end
16
+ end
17
+
18
+ def open
19
+ #TODO implement!
20
+ end
21
+
22
+ def save
23
+ #TODO implement! return true if success
24
+ return true
25
+ end
26
+
27
+ def add_stl
28
+ if(@document.nil?)
29
+ new()
30
+ end
31
+
32
+ file_name = Qt::FileDialog.getOpenFileName(@main_window)
33
+ if !file_name.nil?
34
+ stl = STL.new()
35
+ if stl.parse(file_name)
36
+ added = @document.add_tri_mesh!(stl.tri_mesh)
37
+ if !added.nil?
38
+ @gl_ctrl.add2scenegraph(added)
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+ def may_be_save
46
+ if @document.dirty
47
+ ret = Qt::MessageBox::warning(@main_window, tr("STL Viewer"),
48
+ tr("The document has been modified. \n" +
49
+ "Do you want to save your change?"),
50
+ Qt::MessageBox::Yes | Qt::MessageBox::Defaut,
51
+ Qt::MessageBox::No,
52
+ Qt::MessageBox::Cancel | Qt::MessageBox::Escape)
53
+ if ret == Qt::MessageBox::Yes
54
+ return save()
55
+ elsif ret == Qt::MessageBox::Cancel
56
+ return false
57
+ end
58
+ end
59
+ return true
60
+ end
61
+
62
+ end
@@ -0,0 +1,15 @@
1
+ require 'stl_viewer'
2
+
3
+ class GLCtrl
4
+ def initialize(gl_widget)
5
+ @gl_widget = gl_widget
6
+ end
7
+
8
+ def add2scenegraph(mesh_info)
9
+ mesh_info.mesh_node = Disp3D::NodeTris.new(mesh_info.mesh_geom)
10
+ mesh_info.mesh_node.material_color = [1,0,0,1]
11
+ @gl_widget.world_scene_graph.add(mesh_info.mesh_node)
12
+ @gl_widget.fit
13
+ @gl_widget.updateGL
14
+ end
15
+ end
@@ -0,0 +1,120 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '../..', 'lib'))
3
+
4
+ require 'Qt'
5
+ require 'qt_widget_gl'
6
+ require 'stl_viewer'
7
+
8
+ class STLViewerWindow < Qt::MainWindow
9
+ attr_reader :gl_widget
10
+
11
+ slots 'new()',
12
+ 'open()',
13
+ 'save()',
14
+ 'add_stl()',
15
+ 'about()',
16
+ 'aboutQt()'
17
+
18
+ def initialize(parent = nil)
19
+ super
20
+
21
+ w = Qt::Widget.new
22
+ setCentralWidget(w)
23
+
24
+ topFiller = Qt::Widget.new
25
+ topFiller.setSizePolicy(Qt::SizePolicy::Expanding, Qt::SizePolicy::Expanding)
26
+
27
+ @gl_widget = QtWidgetGL.new(self)
28
+ @gl_widget.width = 600
29
+ @gl_widget.height = 400
30
+
31
+ bottomFiller = Qt::Widget.new
32
+ bottomFiller.setSizePolicy(Qt::SizePolicy::Expanding, Qt::SizePolicy::Expanding)
33
+
34
+ vbox = Qt::VBoxLayout.new
35
+ vbox.margin = 5
36
+ vbox.addWidget(topFiller)
37
+ vbox.addWidget(@gl_widget)
38
+ vbox.addWidget(bottomFiller)
39
+ w.layout = vbox
40
+
41
+ createActions()
42
+ createMenus()
43
+
44
+ setWindowTitle(tr("STL Viewer"))
45
+ setMinimumSize(160, 160)
46
+
47
+ # controllers
48
+ @doc_ctrl = DocumentCtrl.new(self)
49
+ end
50
+
51
+ def createActions()
52
+ @newAct = Qt::Action.new(tr("&New..."), self)
53
+ @newAct.shortcut = Qt::KeySequence.new( tr("Ctrl+N") )
54
+ @newAct.statusTip = tr("Create new file")
55
+ connect(@newAct, SIGNAL('triggered()'), self, SLOT('new()'))
56
+
57
+ @openAct = Qt::Action.new(tr("&Open..."), self)
58
+ @openAct.shortcut = Qt::KeySequence.new( tr("Ctrl+O") )
59
+ @openAct.statusTip = tr("Open an existing file")
60
+ connect(@openAct, SIGNAL('triggered()'), self, SLOT('open()'))
61
+
62
+ @saveAct = Qt::Action.new(tr("&Save"), self)
63
+ @saveAct.shortcut = Qt::KeySequence.new( tr("Ctrl+S") )
64
+ @saveAct.statusTip = tr("Save the document to disk")
65
+ connect(@saveAct, SIGNAL('triggered()'), self, SLOT('save()'))
66
+
67
+ @addStlAct = Qt::Action.new(tr("&Add STL"), self)
68
+ @addStlAct.shortcut = Qt::KeySequence.new( tr("Ctrl+A") )
69
+ @addStlAct.statusTip = tr("Add STL file")
70
+ connect(@addStlAct, SIGNAL('triggered()'), self, SLOT('add_stl()'))
71
+
72
+ @aboutAct = Qt::Action.new(tr("&About"), self)
73
+ @aboutAct.statusTip = tr("Show the application's About box")
74
+ connect(@aboutAct, SIGNAL('triggered()'), self, SLOT('about()'))
75
+
76
+ @aboutQtAct = Qt::Action.new(tr("About &Qt"), self)
77
+ @aboutQtAct.statusTip = tr("Show the Qt library's About box")
78
+ connect(@aboutQtAct, SIGNAL('triggered()'), $qApp, SLOT('aboutQt()'))
79
+ end
80
+
81
+ def createMenus()
82
+ @fileMenu = menuBar().addMenu(tr("&File"))
83
+ @fileMenu.addAction(@newAct)
84
+ @fileMenu.addAction(@openAct)
85
+ @fileMenu.addAction(@saveAct)
86
+ @fileMenu.addAction(@addStlAct)
87
+
88
+ @helpMenu = menuBar().addMenu(tr("&Help"))
89
+ @helpMenu.addAction(@aboutAct)
90
+ @helpMenu.addAction(@aboutQtAct)
91
+ end
92
+
93
+ def new()
94
+ @doc_ctrl.new
95
+ end
96
+
97
+ def open()
98
+ @doc_ctrl.open
99
+ end
100
+
101
+ def save()
102
+ @doc_ctrl.save
103
+ end
104
+
105
+ def add_stl()
106
+ @doc_ctrl.add_stl
107
+ end
108
+
109
+ def about()
110
+ Qt::MessageBox.about(self, tr("About Menu"),
111
+ tr("STL Viewer, Demo application using <b>disp3D</b> library."))
112
+ end
113
+ end
114
+
115
+ # start application
116
+ app = Qt::Application.new(ARGV)
117
+ window = STLViewerWindow.new
118
+ window.show
119
+ app.exec
120
+
@@ -0,0 +1,12 @@
1
+ require 'stl_viewer'
2
+
3
+ class MeshInfo
4
+ attr_reader :mesh_geom
5
+ attr_accessor :mesh_node
6
+
7
+ def initialize(mesh)
8
+ @mesh_geom = mesh
9
+ @mesh_node = nil
10
+ end
11
+
12
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+
3
+ require 'app_model'
4
+
5
+ require 'document'
6
+ require 'mesh_info'
7
+
8
+ require 'document_ctrl'
9
+ require 'gl_ctrl'
data/lib/camera.rb CHANGED
@@ -2,56 +2,70 @@ require 'disp3D'
2
2
 
3
3
  module Disp3D
4
4
  class Camera
5
- attr_accessor :rotX
6
- attr_accessor :rotY
5
+ attr_accessor :rotation
6
+ attr_accessor :translate
7
+ attr_accessor :center
8
+ attr_accessor :scale
9
+ attr_accessor :is_orth
7
10
 
8
- LIGHT_POSITION = [0.25, 1.0, 0.25, 0.0]
9
- LIGHT_DIFFUSE = [1.0, 1.0, 1.0]
10
- LIGHT_AMBIENT = [0.25, 0.25, 0.25]
11
- LIGHT_SPECULAR = [1.0, 1.0, 1.0]
12
-
13
- MAT_DIFFUSE = [0.0, 0.0, 0.0]
14
- MAT_AMBIENT = [0.25, 0.25, 0.25]
15
- MAT_SPECULAR = [1.0, 1.0, 1.0]
16
- MAT_SHININESS = [32.0]
11
+ def initialize()
12
+ @rotation = Quat.from_axis(Vector3.new(1,0,0),0)
13
+ @translate = nil
14
+ @eye = Vector3.new(0,0,5)
15
+ @center = Vector3.new(0,0,0)
16
+ @scale = 1
17
+ @angle = 30
18
+ @is_orgh = false
19
+ @near = 0.1
20
+ @far = 100.0
21
+ end
17
22
 
18
23
  def reshape(w,h)
19
- GL.Viewport(0,0,w,h)
20
-
24
+ GL.Viewport(0.0,0.0,w,h)
21
25
  GL.MatrixMode(GL::GL_PROJECTION)
22
26
  GL.LoadIdentity()
23
- GLU.Perspective(45.0, w.to_f()/h.to_f(), 0.1, 100.0)
24
- # GL.Ortho(-1,1,-1,1,2,4)
27
+ set_screen(w,h)
25
28
  end
26
29
 
27
30
  def display()
28
31
  GL.MatrixMode(GL::GL_MODELVIEW)
29
32
  GL.LoadIdentity()
30
- GLU.LookAt(0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
31
-
32
- GL.Lightfv(GL::GL_LIGHT0, GL::GL_POSITION, LIGHT_POSITION)
33
- GL.Lightfv(GL::GL_LIGHT0, GL::GL_DIFFUSE, LIGHT_DIFFUSE)
34
- GL.Lightfv(GL::GL_LIGHT0, GL::GL_AMBIENT, LIGHT_AMBIENT)
35
- GL.Lightfv(GL::GL_LIGHT0, GL::GL_SPECULAR, LIGHT_SPECULAR)
33
+ GLU.LookAt(@eye.x, @eye.y, @eye.z, @center.x, @center.y, @center.z, 0.0, 1.0, 0.0)
36
34
 
37
- GL.Materialfv(GL::GL_FRONT, GL::GL_DIFFUSE, MAT_DIFFUSE)
38
- GL.Materialfv(GL::GL_FRONT, GL::GL_AMBIENT, MAT_AMBIENT)
39
- GL.Materialfv(GL::GL_FRONT, GL::GL_SPECULAR, MAT_SPECULAR)
40
- GL.Materialfv(GL::GL_FRONT, GL::GL_SHININESS, MAT_SHININESS)
35
+ GL.Translate(translate.x, translate.y, translate.z) if(@translate)
36
+ rot_mat = Matrix.from_quat(@rotation)
37
+ rot_mat_array = [
38
+ [rot_mat[0,0], rot_mat[0,1], rot_mat[0,2], 0],
39
+ [rot_mat[1,0], rot_mat[1,1], rot_mat[1,2], 0],
40
+ [rot_mat[2,0], rot_mat[2,1], rot_mat[2,2], 0],
41
+ [0,0,0,1]]
41
42
 
42
- GL.Clear(GL::GL_COLOR_BUFFER_BIT | GL::GL_DEPTH_BUFFER_BIT)
43
-
44
- GL.Rotate(@rotX, 1, 0, 0)
45
- GL.Rotate(@rotY, 0, 1, 0)
43
+ GL.MultMatrix(rot_mat_array)
44
+ GL.Scale(@scale, @scale, @scale)
46
45
  end
47
46
 
48
- def initialize()
49
- @rotY = 0
50
- @rotX = 0
51
- GL.Enable(GL::GL_LIGHTING)
52
- GL.Enable(GL::GL_LIGHT0)
47
+ def fit(radius, width, height)
48
+ eye_z = radius / Math.sin(@angle/2.0*Math::PI/180.0)
49
+ @eye = Vector3.new(0,0,eye_z)
50
+ @near = radius - 1
51
+ @far = eye_z + radius
52
+ if @is_orgh
53
+ min_screen = [width, height].min
54
+ @scale = (min_screen.to_f/2.0)/radius
55
+ else
56
+ @scale = 1.0
57
+ end
58
+ set_screen(width,height)
59
+ end
53
60
 
54
- GLUT.ReshapeFunc(method(:reshape).to_proc())
61
+ def set_screen(w,h)
62
+ @aspect = w.to_f()/h.to_f()
63
+ if @is_orgh
64
+ GL.Ortho(-w/2.0, w/2.0, -h/2.0, h/2.0, -@far*@scale*10, @far*@scale*10)
65
+ else
66
+ GLU.Perspective(@angle, @aspect, @near, @far)
67
+ end
55
68
  end
69
+
56
70
  end
57
71
  end