source: orange/Orange/OrangeWidgets/VisualizeQt/OWSphereviz3D.py @ 11474:df0622184ee6

Revision 11474:df0622184ee6, 10.4 KB checked in by markotoplak, 12 months ago (diff)

Renamed Visualize Qt to VisualizeQt (so it can be loaded in new canvas).

Line 
1'''
2<name>Sphereviz 3D</name>
3<icon>icons/Radviz.svg</icon>
4<priority>2000</priority>
5'''
6
7import os
8from math import pi
9from random import choice
10
11from plot.owplot3d import *
12from plot.owopenglrenderer import VertexBuffer
13from plot.primitives import parse_obj
14from OWLinProjQt import *
15from OWLinProj3DPlot import OWLinProj3DPlot
16
17import orange
18Discrete = orange.VarTypes.Discrete
19Continuous = orange.VarTypes.Continuous
20
21class OWSphereviz3DPlot(OWLinProj3DPlot):
22    def __init__(self, widget, parent=None, name='SpherevizPlot'):
23        OWLinProj3DPlot.__init__(self, widget, parent, name)
24
25        self.camera_angle = 90
26        self.camera_type = 0 # Default, center, attribute
27        self.show_anchor_grid = False
28
29    def _build_anchor_grid(self):
30        lines = []
31        num_parts = 30
32        anchors = numpy.array([a[:3] for a in self.anchor_data])
33        for anchor in self.anchor_data:
34            a0 = numpy.array(anchor[:3])
35            neighbours = anchors.copy()
36            neighbours = [(((n-a0)**2).sum(), n)  for n in neighbours]
37            neighbours.sort(key=lambda e: e[0])
38            for i in range(1, min(len(anchors), 4)): 
39                difference = neighbours[i][1]-a0
40                for j in range(num_parts):
41                    lines.extend(normalize(a0 + difference*j/float(num_parts)))
42                    lines.extend(normalize(a0 + difference*(j+1)/float(num_parts)))
43
44        self._grid_buffer = VertexBuffer(numpy.array(lines, numpy.float32), [(3, GL_FLOAT)])
45
46    def update_data(self, labels=None, setAnchors=0, **args):
47        OWLinProj3DPlot.updateData(self, labels, setAnchors, **args)
48
49        if self.anchor_data:
50            self._build_anchor_grid()
51
52        self.updateGL()
53
54    updateData = update_data
55
56    def setData(self, data, subset_data=None, **args):
57        OWLinProj3DPlot.set_data(self, data, subset_data, **args)
58
59        # No need to generate backgroud grid sphere geometry more than once
60        if hasattr(self, '_sphere_buffer'):
61            return
62
63        self.makeCurrent()
64
65        lines = []
66        num_parts = 30
67        num_horizontal_rings = 20
68        num_vertical_rings = 24
69        r = 1.
70
71        for i in range(num_horizontal_rings):
72            z_offset = float(i) * 2 / num_horizontal_rings - 1.
73            r = (1. - z_offset**2)**0.5
74            for j in range(num_parts):
75                angle_z_0 = float(j) * 2 * pi / num_parts
76                angle_z_1 = float(j+1) * 2 * pi / num_parts
77                lines.extend([sin(angle_z_0)*r, z_offset, cos(angle_z_0)*r,
78                              sin(angle_z_1)*r, z_offset, cos(angle_z_1)*r])
79
80        for i in range(num_vertical_rings):
81            r = 1.
82            phi = 2 * i * pi / num_vertical_rings
83            for j in range(num_parts):
84                theta_0 = (j) * pi / num_parts
85                theta_1 = (j+1) * pi / num_parts
86                lines.extend([sin(theta_0)*cos(phi)*r, cos(theta_0)*r, sin(theta_0)*sin(phi)*r,
87                              sin(theta_1)*cos(phi)*r, cos(theta_1)*r, sin(theta_1)*sin(phi)*r])
88
89        self._sphere_buffer = VertexBuffer(numpy.array(lines, numpy.float32), [(3, GL_FLOAT)])
90
91        self._sphere_shader = QtOpenGL.QGLShaderProgram()
92        self._sphere_shader.addShaderFromSourceFile(QtOpenGL.QGLShader.Vertex,
93            os.path.join(os.path.dirname(__file__), 'sphere.vs'))
94        self._sphere_shader.addShaderFromSourceFile(QtOpenGL.QGLShader.Fragment,
95            os.path.join(os.path.dirname(__file__), 'sphere.fs'))
96
97        self._sphere_shader.bindAttributeLocation('position', 0)
98
99        if not self._sphere_shader.link():
100            print('Failed to link sphere shader!')
101
102        ## Cones
103        cone_data = parse_obj('cone_hq.obj')
104        vertices = []
105        for v0, v1, v2, n0, n1, n2 in cone_data:
106            vertices.extend([v0[0],v0[1],v0[2], n0[0],n0[1],n0[2],
107                             v1[0],v1[1],v1[2], n1[0],n1[1],n1[2],
108                             v2[0],v2[1],v2[2], n2[0],n2[1],n2[2]])
109
110        self._cone_buffer = VertexBuffer(numpy.array(vertices, numpy.float32),
111            [(3, GL_FLOAT),
112             (3, GL_FLOAT)])
113
114        self._cone_shader = QtOpenGL.QGLShaderProgram()
115        self._cone_shader.addShaderFromSourceFile(QtOpenGL.QGLShader.Vertex,
116            os.path.join(os.path.dirname(__file__), 'cone.vs'))
117        self._cone_shader.addShaderFromSourceFile(QtOpenGL.QGLShader.Fragment,
118            os.path.join(os.path.dirname(__file__), 'cone.fs'))
119
120        self._cone_shader.bindAttributeLocation('position', 0)
121        self._cone_shader.bindAttributeLocation('normal', 1)
122
123        if not self._cone_shader.link():
124            print('Failed to link cone shader!')
125
126        ## Another dummy shader (anchor grid)
127        self._grid_shader = QtOpenGL.QGLShaderProgram()
128        self._grid_shader.addShaderFromSourceFile(QtOpenGL.QGLShader.Vertex,
129            os.path.join(os.path.dirname(__file__), 'grid.vs'))
130        self._grid_shader.addShaderFromSourceFile(QtOpenGL.QGLShader.Fragment,
131            os.path.join(os.path.dirname(__file__), 'grid.fs'))
132
133        self._grid_shader.bindAttributeLocation('position', 0)
134
135        if not self._grid_shader.link():
136            print('Failed to link grid shader!')
137
138        self.before_draw_callback = self.before_draw
139
140    def update_camera_type(self):
141        if self.camera_type == 2:
142            self._random_anchor = choice(self.anchor_data)
143        self.update()
144
145    def before_draw(self):
146        view = QMatrix4x4()
147        if self.camera_type == 2:
148            view.lookAt(
149                QVector3D(self._random_anchor[0], self._random_anchor[1], self._random_anchor[2]),
150                self.camera,
151                QVector3D(0, 1, 0))
152            projection = QMatrix4x4()
153            projection.perspective(self.camera_angle, float(self.width()) / self.height(),
154                                   0.01, 5.)
155            self.projection = projection
156        elif self.camera_type == 1:
157            view.lookAt(
158                QVector3D(0, 0, 0),
159                self.camera * self.camera_distance,
160                QVector3D(0, 1, 0))
161            projection = QMatrix4x4()
162            projection.perspective(self.camera_angle, float(self.width()) / self.height(),
163                                   0.01, 5.)
164            self.projection = projection
165        else:
166            view.lookAt(
167                self.camera * self.camera_distance,
168                QVector3D(0, 0, 0),
169                QVector3D(0, 1, 0))
170        self.view = view
171
172        self._draw_sphere()
173
174        # Qt text rendering classes still use deprecated OpenGL functionality
175        glEnable(GL_DEPTH_TEST)
176        glDisable(GL_BLEND)
177        glMatrixMode(GL_PROJECTION)
178        glLoadIdentity()
179        glMultMatrixd(numpy.array(self.projection.data(), dtype=float))
180        glMatrixMode(GL_MODELVIEW)
181        glLoadIdentity()
182        glMultMatrixd(numpy.array(self.view.data(), dtype=float))
183        glMultMatrixd(numpy.array(self.model.data(), dtype=float))
184
185        if self.showAnchors:
186            for anchor in self.anchor_data:
187                x, y, z, label = anchor
188
189                direction = QVector3D(x, y, z)
190                up = QVector3D(0, 1, 0)
191                right = QVector3D.crossProduct(direction, up).normalized()
192                up = QVector3D.crossProduct(right, direction)
193                rotation = QMatrix4x4()
194                rotation.setColumn(0, QVector4D(right, 0))
195                rotation.setColumn(1, QVector4D(up, 0))
196                rotation.setColumn(2, QVector4D(direction, 0))
197
198                model = QMatrix4x4()
199                model.translate(x, y, z)
200                model = model * rotation
201                model.rotate(-90, 1, 0, 0)
202                model.translate(0, -0.05, 0)
203                model.scale(0.02, 0.02, 0.02)
204
205                self._cone_shader.bind()
206                self._cone_shader.setUniformValue('projection', self.projection)
207                self._cone_shader.setUniformValue('modelview', self.view * model)
208                self._cone_buffer.draw()
209                self._cone_shader.release()
210
211                self.qglColor(self._theme.axis_values_color)
212                self.renderText(x*1.2, y*1.2, z*1.2, label)
213
214            if self.anchor_data and not hasattr(self, '_grid_buffer'):
215                self._build_anchor_grid()
216
217            # Draw grid between anchors
218            if self.show_anchor_grid:
219                self._grid_shader.bind()
220                self._grid_shader.setUniformValue('projection', self.projection)
221                self._grid_shader.setUniformValue('modelview', self.view * self.model)
222                self._grid_shader.setUniformValue('color', self._theme.axis_color)
223                self._grid_buffer.draw(GL_LINES)
224                self._grid_shader.release()
225
226        self._draw_value_lines()
227
228    def _draw_sphere(self):
229        glDisable(GL_DEPTH_TEST)
230        glEnable(GL_BLEND)
231        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
232
233        self._sphere_shader.bind()
234        self._sphere_shader.setUniformValue('projection', self.projection)
235        self._sphere_shader.setUniformValue('modelview', self.view * self.model)
236        self._sphere_shader.setUniformValue('cam_position', self.camera * self.camera_distance)
237        self._sphere_shader.setUniformValue('use_transparency', self.camera_type == 0)
238        self._sphere_buffer.draw(GL_LINES)
239        self._sphere_shader.release()
240
241    def mouseMoveEvent(self, event):
242        self.invert_mouse_x = self.camera_type != 0
243        OWLinProj3DPlot.mouseMoveEvent(self, event)
244
245class OWSphereviz3D(OWLinProjQt):
246    def __init__(self, parent=None, signalManager=None):
247        OWLinProjQt.__init__(self, parent, signalManager, "Sphereviz 3D", graphClass=OWSphereviz3DPlot)
248
249        self.inputs = [("Data", ExampleTable, self.setData, Default),
250                       ("Data Subset", ExampleTable, self.setSubsetData),
251                       ("Features", AttributeList, self.setShownAttributes),
252                       ("Evaluation Results", orngTest.ExperimentResults, self.setTestResults),
253                       ("VizRank Learner", orange.Learner, self.setVizRankLearner)]
254        self.outputs = [("Selected Data", ExampleTable),
255                        ("Other Data", ExampleTable),
256                        ("Features", AttributeList)]
257        self.resize(1000, 600)
258
259if __name__ == '__main__':
260    app = QApplication(sys.argv)
261    viz = OWSphereviz3D()
262    viz.show()
263    data = orange.ExampleTable('../../doc/datasets/iris')
264    viz.setData(data)
265    viz.handleNewSignals()
266    app.exec_()
Note: See TracBrowser for help on using the repository browser.