/
meshview.py
175 lines (166 loc) · 5.97 KB
/
meshview.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#!/usr/bin/python -t
# -*- coding: utf-8 -*-
"""meshview.py
Saturday, September 14 2013
"""
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.QtCore import Qt as qt
from PyQt4.QtOpenGL import *
import OpenGL.GL as gl
import numpy as np
from glview import GLView
from bbox import BBox
class MeshView(GLView):
"""A tool mesh viewer.
"""
def __init__(self, parent):
super(MeshView, self).__init__(parent)
self._mesh = None
# copy of GLView.rotFactor
self.baseRotFactor = self.rotFactor
self._shadeMode = 'smooth'
self._showNormals = False
def initGL(self):
super(MeshView, self).initGL()
self.frontView()
def wheelEvent(self, e):
"""Zoom in/out and adjust the mouse rotation factor.
"""
super(MeshView, self).wheelEvent(e)
self._setRotFactor()
# TODO:
# * Works well but is probably going to need some tweaking.
# * added rotFactor lower bound of 0.05
def _setRotFactor(self, bbox=None):
"""Adjust the mouse rotation factor.
The factor is computed based on how much of the mesh is visible and
how deep into the scene it extends.
"""
if self._mesh:
w, h, d = (bbox or self.getMeshSceneBBox()).size()
# mesh is fully in view
if self.sceneHeight >= h and self.sceneWidth > w:
if d > w+h:
self.rotFactor = max(self.baseRotFactor * ((w+h) / d),
0.05)
else:
self.rotFactor = self.baseRotFactor
# partially in view
else:
self.rotFactor = max(self.baseRotFactor
* self.sceneHeight / max(w, h) * 0.5,
0.05)
def setMesh(self, mesh):
self._mesh = mesh
self.setRotCenter(mesh.bbox().center())
if self._shadeMode == 'smooth':
self._mesh.setSmoothShaded()
elif self._shadeMode == 'flat':
self._mesh.setFlatShaded()
else:
self._mesh.setWireFrame()
self._mesh.toggleNormals(self._showNormals)
def createContextMenu(self):
a = QAction("Fit", self)
self.connect(a, SIGNAL('triggered()'), self.fitMesh)
self.addAction(a)
sep = QAction(self)
sep.setSeparator(True)
self.addAction(sep)
super(MeshView, self).createContextMenu()
# remove redundant fixed views
for a in [x for x in self.actions()
if x.text() in ['Left', 'Right', 'Back']]:
self.removeAction(a)
def mxv(self, m, v):
"""Multiply matrix and vector.
m -- 4x4 matrix
v -- [x, y, z, 1.0]
Return the mapped [x, y, z, 1.0].
"""
out = np.zeros(4)
for r in (0, 1, 2, 3):
result = 0.0
for c in (0, 1, 2, 3):
result += v[c] * m[c, r]
out[r] = result
return out
# TODO: The mesh's bounding box vertices are transformed by the current
# model view matrix. The bounding box of those points is used to fit
# the tool. This isn't as accurate as using the meshes vertices but
# it is faster.
# def fitMesh(self):
# if self._mesh is None:
# return
# mappedVerts = []
# for boxVert in self._mesh.bbox().vertices():
# v = self.mxv(self.modelviewMatrix, boxVert)
# mappedVerts.append(v)
# bbox = BBox.fromVertices(mappedVerts)
# self.fit(QPointF(bbox.p1[0], bbox.p1[1]),
# QPointF(bbox.p2[0], bbox.p2[1]))
# self.updateGL()
# fit mesh vertices version
def getMeshSceneBBox(self):
"""Get the view-aligned bbox of the mesh at its current orientation.
Return a BBox.
"""
mappedVerts = []
for meshVert in self._mesh.sharedVertices():
mappedVerts.append(self.mxv(self.modelviewMatrix,
meshVert + [1.0])[:3])
bbox = BBox.fromVertices(mappedVerts)
return bbox
def fitMesh(self):
if self._mesh is None:
return
bbox = self.getMeshSceneBBox()
# fit calls updateGL()
self.fit(bbox.leftTop(), bbox.rightBottom())
self._setRotFactor(bbox)
def paintGL(self):
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
if self._mesh:
self._mesh.render()
self.renderAxisIndicator()
def topView(self, update=False):
super(MeshView, self).topView(update)
self.fitMesh()
def bottomView(self, update=False):
super(MeshView, self).bottomView(update)
self.fitMesh()
def frontView(self, update=False):
super(MeshView, self).frontView(update)
self.fitMesh()
def isometricView(self, update=False):
super(MeshView, self).isometricView(update)
self.fitMesh()
def keyPressEvent(self, e):
if self._mesh:
if e.key() == qt.Key_F:
self.fitMesh()
return
if e.key() == qt.Key_N:
self._showNormals = not self._showNormals
self._mesh.toggleNormals(self._showNormals)
self.updateGL()
elif e.key() == qt.Key_W:
self._shadeMode = 'wire'
self._mesh.setWireFrame()
self.updateGL()
return
elif e.key() == qt.Key_S:
self._shadeMode = 'smooth'
self._mesh.setSmoothShaded()
self.updateGL()
return
elif e.key() == qt.Key_T:
self._shadeMode = 'flat'
self._mesh.setFlatShaded()
self.updateGL()
return
super(MeshView, self).keyPressEvent(e)
# def mouseMoveEvent(self, e):
# super(MeshView, self).mouseMoveEvent(e)
# print 'pos', self.screenToScene(e.x(), e.y())