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

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 

17 

18 

19from OpenGL.GL import * # type:ignore 

20from OpenGL.GLU import * # type:ignore 

21from OpenGL.GLUT import * # type:ignore 

22 

23 

24class ViewMode(object): 

25 """ 

26 Base class for projection transformations. 

27 """ 

28 

29 ZOOM_MIN = 0.1 

30 ZOOM_MAX = 800 

31 

32 def __init__(self): 

33 self._stack = [] 

34 self._save_vars = [] 

35 

36 self.supports_ortho = False 

37 

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

44 

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()) 

51 

52 def reset_state(self): 

53 """ 

54 Reset internal state to initial values. 

55 """ 

56 self.pop_state() 

57 self.push_state() 

58 

59 def begin(self): 

60 """ 

61 Set up projection transformations. 

62 """ 

63 raise NotImplementedError("method not implemented") 

64 

65 def end(self): 

66 """ 

67 Tear down projection transformations. 

68 """ 

69 raise NotImplementedError("method not implemented") 

70 

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) 

76 

77 

78class View2D(ViewMode): 

79 """ 

80 Orthographic projection transformations (2D mode). 

81 """ 

82 

83 NEAR = -100.0 

84 FAR = 100.0 

85 PAN_FACTOR = 4 

86 

87 def __init__(self): 

88 super(View2D, self).__init__() 

89 

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 

94 

95 self._save_vars.extend(["x", "y", "z", "zoom_factor", "azimuth"]) 

96 self.push_state() 

97 

98 self.w, self.h = None, None 

99 

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 

109 

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 

121 

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) 

127 

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) 

137 

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) 

141 

142 def rotate(self, delta_x, delta_y): 

143 self.azimuth += delta_x 

144 

145 def pan(self, delta_x, delta_y): 

146 self.x += delta_x * self.PAN_FACTOR 

147 self.y -= delta_y * self.PAN_FACTOR 

148 

149 def zoom(self, delta_x, delta_y): 

150 old_zoom = self.zoom_factor 

151 super(View2D, self).zoom(delta_x, delta_y) 

152 

153 # adjust panning for new zoom level 

154 self.x *= self.zoom_factor / old_zoom 

155 self.y *= self.zoom_factor / old_zoom 

156 

157 

158class View3D(ViewMode): 

159 """ 

160 Perspective projection transformations (3D mode). 

161 """ 

162 

163 FOVY = 80.0 

164 ZOOM_ORTHO_ADJ = 4.5 

165 NEAR = 1 

166 FAR = 100000 

167 

168 def __init__(self): 

169 super(View3D, self).__init__() 

170 

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 

176 

177 self.supports_ortho = True 

178 self.ortho = False 

179 

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() 

193 

194 def begin(self, w, h): 

195 glMatrixMode(GL_PROJECTION) 

196 glPushMatrix() 

197 glLoadIdentity() 

198 

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) 

204 

205 glMatrixMode(GL_MODELVIEW) 

206 glPushMatrix() 

207 glLoadIdentity() 

208 

209 def end(self): 

210 glMatrixMode(GL_PROJECTION) 

211 glPopMatrix() # restore the projection matrix 

212 glMatrixMode(GL_MODELVIEW) 

213 glPopMatrix() # restore the modelview matrix 

214 

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 

218 

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) 

226 

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) 

233 

234 def _draw_rotation_center_bead(self): 

235 glEnable(GL_LIGHTING) 

236 glEnable(GL_LIGHT0) 

237 glEnable(GL_LIGHT1) 

238 glShadeModel(GL_SMOOTH) 

239 

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) 

245 

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

250 

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

254 

255 glColor(1.0, 0.0, 0.0) 

256 glutSolidSphere(0.8, 100, 100) 

257 

258 glDisable(GL_LIGHT1) 

259 glDisable(GL_LIGHT0) 

260 glDisable(GL_LIGHTING) 

261 

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) 

267 

268 def rotate(self, delta_x, delta_y): 

269 self.azimuth += delta_x 

270 self.elevation -= delta_y 

271 

272 def pan(self, delta_x, delta_y): 

273 self.x += delta_x / self.zoom_factor 

274 self.z -= delta_y / self.zoom_factor 

275 

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