Coverage for tatlin/lib/gl/views.py: 94%
157 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-20 05:56 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-20 05:56 +0000
1# -*- coding: utf-8 -*-
2# Copyright (C) 2011 Denis Kobozev
3#
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 2 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program; if not, write to the Free Software Foundation,
16# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19from OpenGL.GL import * # type:ignore
20from OpenGL.GLU import * # type:ignore
21from OpenGL.GLUT import * # type:ignore
24class ViewMode(object):
25 """
26 Base class for projection transformations.
27 """
29 ZOOM_MIN = 0.1
30 ZOOM_MAX = 800
32 def __init__(self):
33 self._stack = []
34 self._save_vars = []
36 self.supports_ortho = False
38 def push_state(self):
39 """
40 Save state variables.
41 """
42 for var in self._save_vars:
43 self._stack.append(getattr(self, var))
45 def pop_state(self):
46 """
47 Restore state variables.
48 """
49 for var in reversed(self._save_vars):
50 setattr(self, var, self._stack.pop())
52 def reset_state(self):
53 """
54 Reset internal state to initial values.
55 """
56 self.pop_state()
57 self.push_state()
59 def begin(self):
60 """
61 Set up projection transformations.
62 """
63 raise NotImplementedError("method not implemented")
65 def end(self):
66 """
67 Tear down projection transformations.
68 """
69 raise NotImplementedError("method not implemented")
71 def zoom(self, delta_x, delta_y):
72 if delta_y > 0:
73 self.zoom_factor = min(self.zoom_factor * 1.2, self.ZOOM_MAX)
74 elif delta_y < 0:
75 self.zoom_factor = max(self.zoom_factor * 0.83, self.ZOOM_MIN)
78class View2D(ViewMode):
79 """
80 Orthographic projection transformations (2D mode).
81 """
83 NEAR = -100.0
84 FAR = 100.0
85 PAN_FACTOR = 4
87 def __init__(self):
88 super(View2D, self).__init__()
90 self.x, self.y, self.z = 0.0, 0.0, 0.0
91 self.zoom_factor = 5.0
92 self.elevation = -90.0 # for compatibility with 3d view
93 self.azimuth = 0.0
95 self._save_vars.extend(["x", "y", "z", "zoom_factor", "azimuth"])
96 self.push_state()
98 self.w, self.h = None, None
100 def begin(self, w, h):
101 self.w, self.h = w, h
102 glMatrixMode(GL_PROJECTION) # select projection matrix
103 glPushMatrix() # save the current projection matrix
104 glLoadIdentity() # set up orthographic projection
105 glOrtho(0, w, 0, h, self.NEAR, self.FAR)
106 glMatrixMode(GL_MODELVIEW) # select the modelview matrix
107 glPushMatrix() # save the current modelview matrix
108 glLoadIdentity() # replace the modelview matrix with identity
110 def end(self):
111 """
112 Switch back to perspective projection.
113 """
114 self.w, self.h = None, None
115 # projection and modelview matrix stacks are separate, and can be
116 # popped in any order
117 glMatrixMode(GL_PROJECTION)
118 glPopMatrix() # restore the projection matrix
119 glMatrixMode(GL_MODELVIEW)
120 glPopMatrix() # restore the modelview matrix
122 def display_transform(self):
123 self._center_on_origin()
124 glTranslate(self.x, self.y, self.z)
125 glRotate(self.azimuth, 0.0, 0.0, 1.0)
126 glScale(self.zoom_factor, self.zoom_factor, self.zoom_factor)
128 def _center_on_origin(self):
129 """
130 Center orthographic projection box on (0, 0, 0).
131 """
132 glMatrixMode(GL_PROJECTION)
133 glLoadIdentity()
134 x, y = self.w / 2, self.h / 2 # type:ignore
135 glOrtho(-x, x, -y, y, self.NEAR, self.FAR)
136 glMatrixMode(GL_MODELVIEW)
138 def ui_transform(self, length):
139 glTranslate(length + 20.0, length + 20.0, 0.0)
140 glRotate(self.azimuth, 0.0, 0.0, 1.0)
142 def rotate(self, delta_x, delta_y):
143 self.azimuth += delta_x
145 def pan(self, delta_x, delta_y):
146 self.x += delta_x * self.PAN_FACTOR
147 self.y -= delta_y * self.PAN_FACTOR
149 def zoom(self, delta_x, delta_y):
150 old_zoom = self.zoom_factor
151 super(View2D, self).zoom(delta_x, delta_y)
153 # adjust panning for new zoom level
154 self.x *= self.zoom_factor / old_zoom
155 self.y *= self.zoom_factor / old_zoom
158class View3D(ViewMode):
159 """
160 Perspective projection transformations (3D mode).
161 """
163 FOVY = 80.0
164 ZOOM_ORTHO_ADJ = 4.5
165 NEAR = 1
166 FAR = 100000
168 def __init__(self):
169 super(View3D, self).__init__()
171 self.x, self.y, self.z = 0.0, 180.0, -20.0
172 self.zoom_factor = 1.0
173 self.azimuth = 0.0
174 self.elevation = -20.0
175 self.offset_x = self.offset_y = 0.0
177 self.supports_ortho = True
178 self.ortho = False
180 self._save_vars.extend(
181 [
182 "x",
183 "y",
184 "z",
185 "zoom_factor",
186 "azimuth",
187 "elevation",
188 "offset_x",
189 "offset_y",
190 ]
191 )
192 self.push_state()
194 def begin(self, w, h):
195 glMatrixMode(GL_PROJECTION)
196 glPushMatrix()
197 glLoadIdentity()
199 if self.ortho:
200 x, y = w / 2, h / 2
201 glOrtho(-x, x, -y, y, -self.FAR, self.FAR)
202 else:
203 gluPerspective(self.FOVY, w / h, self.NEAR, self.FAR)
205 glMatrixMode(GL_MODELVIEW)
206 glPushMatrix()
207 glLoadIdentity()
209 def end(self):
210 glMatrixMode(GL_PROJECTION)
211 glPopMatrix() # restore the projection matrix
212 glMatrixMode(GL_MODELVIEW)
213 glPopMatrix() # restore the modelview matrix
215 def display_transform(self):
216 glRotate(-90, 1.0, 0.0, 0.0) # make z point up
217 glTranslate(0.0, self.y, 0.0) # move away from the displayed object
219 # zoom
220 f = self.zoom_factor
221 if self.ortho:
222 # adjust zoom for orthographic projection in which the object's
223 # distance from the camera has no effect on its apparent size
224 f *= self.ZOOM_ORTHO_ADJ
225 glScale(f, f, f)
227 # pan and rotate
228 glTranslate(self.x, 0.0, self.z)
229 glRotate(-self.elevation, 1.0, 0.0, 0.0)
230 glRotate(self.azimuth, 0.0, 0.0, 1.0)
231 self._draw_rotation_center_bead()
232 glTranslate(self.offset_x, self.offset_y, 0)
234 def _draw_rotation_center_bead(self):
235 glEnable(GL_LIGHTING)
236 glEnable(GL_LIGHT0)
237 glEnable(GL_LIGHT1)
238 glShadeModel(GL_SMOOTH)
240 # material properties (white plastic)
241 glMaterial(GL_FRONT, GL_AMBIENT, (0.0, 0.0, 0.0, 1.0))
242 glMaterial(GL_FRONT, GL_DIFFUSE, (0.55, 0.55, 0.55, 1.0))
243 glMaterial(GL_FRONT, GL_SPECULAR, (0.7, 0.7, 0.7, 1.0))
244 glMaterial(GL_FRONT, GL_SHININESS, 32.0)
246 # lights properties
247 glLight(GL_LIGHT0, GL_AMBIENT, (0.3, 0.3, 0.3, 1.0))
248 glLight(GL_LIGHT0, GL_DIFFUSE, (0.3, 0.3, 0.3, 1.0))
249 glLight(GL_LIGHT1, GL_DIFFUSE, (0.3, 0.3, 0.3, 1.0))
251 # lights position
252 glLightfv(GL_LIGHT0, GL_POSITION, (20.0, 20.0, 20.0))
253 glLightfv(GL_LIGHT1, GL_POSITION, (-20.0, -20.0, 20.0))
255 glColor(1.0, 0.0, 0.0)
256 glutSolidSphere(0.8, 100, 100)
258 glDisable(GL_LIGHT1)
259 glDisable(GL_LIGHT0)
260 glDisable(GL_LIGHTING)
262 def ui_transform(self, length):
263 glRotate(-90, 1.0, 0.0, 0.0) # make z point up
264 glTranslate(length + 20.0, 0.0, length + 20.0)
265 glRotatef(-self.elevation, 1.0, 0.0, 0.0)
266 glRotatef(self.azimuth, 0.0, 0.0, 1.0)
268 def rotate(self, delta_x, delta_y):
269 self.azimuth += delta_x
270 self.elevation -= delta_y
272 def pan(self, delta_x, delta_y):
273 self.x += delta_x / self.zoom_factor
274 self.z -= delta_y / self.zoom_factor
276 def offset(self, delta_x, delta_y):
277 self.offset_x += delta_x / self.zoom_factor
278 self.offset_y -= delta_y / self.zoom_factor