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

RevLine 
[8735]1'''
[8732]2<name>Sphereviz 3D</name>
[11474]3<icon>icons/Radviz.svg</icon>
[8732]4<priority>2000</priority>
[8735]5'''
[8732]6
[8928]7import os
[8847]8from math import pi
9from random import choice
10
[8732]11from plot.owplot3d import *
[8928]12from plot.owopenglrenderer import VertexBuffer
[8735]13from plot.primitives import parse_obj
[8732]14from OWLinProjQt import *
[8747]15from OWLinProj3DPlot import OWLinProj3DPlot
[8735]16
[8742]17import orange
18Discrete = orange.VarTypes.Discrete
19Continuous = orange.VarTypes.Continuous
20
[8747]21class OWSphereviz3DPlot(OWLinProj3DPlot):
[8771]22    def __init__(self, widget, parent=None, name='SpherevizPlot'):
[8747]23        OWLinProj3DPlot.__init__(self, widget, parent, name)
[8732]24
[8847]25        self.camera_angle = 90
26        self.camera_type = 0 # Default, center, attribute
[9045]27        self.show_anchor_grid = False
[8847]28
[8810]29    def _build_anchor_grid(self):
30        lines = []
31        num_parts = 30
[8975]32        anchors = numpy.array([a[:3] for a in self.anchor_data])
[8810]33        for anchor in self.anchor_data:
[8975]34            a0 = numpy.array(anchor[:3])
[8810]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
[8928]44        self._grid_buffer = VertexBuffer(numpy.array(lines, numpy.float32), [(3, GL_FLOAT)])
[8810]45
[8928]46    def update_data(self, labels=None, setAnchors=0, **args):
[8810]47        OWLinProj3DPlot.updateData(self, labels, setAnchors, **args)
48
49        if self.anchor_data:
50            self._build_anchor_grid()
51
52        self.updateGL()
[8742]53
[8928]54    updateData = update_data
55
[9015]56    def setData(self, data, subset_data=None, **args):
57        OWLinProj3DPlot.set_data(self, data, subset_data, **args)
[8732]58
[8810]59        # No need to generate backgroud grid sphere geometry more than once
[8928]60        if hasattr(self, '_sphere_buffer'):
[8732]61            return
62
[8928]63        self.makeCurrent()
64
[8810]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])
[8732]88
[8928]89        self._sphere_buffer = VertexBuffer(numpy.array(lines, numpy.float32), [(3, GL_FLOAT)])
[8732]90
[8928]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'))
[8732]96
[8928]97        self._sphere_shader.bindAttributeLocation('position', 0)
[8732]98
[8928]99        if not self._sphere_shader.link():
[8732]100            print('Failed to link sphere shader!')
101
[8928]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]])
[8810]109
[8928]110        self._cone_buffer = VertexBuffer(numpy.array(vertices, numpy.float32),
111            [(3, GL_FLOAT),
112             (3, GL_FLOAT)])
[8810]113
[8928]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'))
[8810]119
[8928]120        self._cone_shader.bindAttributeLocation('position', 0)
121        self._cone_shader.bindAttributeLocation('normal', 1)
[8810]122
[8928]123        if not self._cone_shader.link():
124            print('Failed to link cone shader!')
[8810]125
[8928]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'))
[8810]132
[8928]133        self._grid_shader.bindAttributeLocation('position', 0)
[8810]134
[8928]135        if not self._grid_shader.link():
[8810]136            print('Failed to link grid shader!')
137
[8969]138        self.before_draw_callback = self.before_draw
[8732]139
[8847]140    def update_camera_type(self):
141        if self.camera_type == 2:
142            self._random_anchor = choice(self.anchor_data)
143        self.update()
144
[8747]145    def before_draw(self):
[8967]146        view = QMatrix4x4()
[8847]147        if self.camera_type == 2:
[8967]148            view.lookAt(
[8847]149                QVector3D(self._random_anchor[0], self._random_anchor[1], self._random_anchor[2]),
[8975]150                self.camera,
[8847]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:
[8967]157            view.lookAt(
[8747]158                QVector3D(0, 0, 0),
[8975]159                self.camera * self.camera_distance,
[8747]160                QVector3D(0, 1, 0))
161            projection = QMatrix4x4()
[8847]162            projection.perspective(self.camera_angle, float(self.width()) / self.height(),
[8837]163                                   0.01, 5.)
[8747]164            self.projection = projection
[8742]165        else:
[8967]166            view.lookAt(
[8975]167                self.camera * self.camera_distance,
[8747]168                QVector3D(0, 0, 0),
169                QVector3D(0, 1, 0))
[8967]170        self.view = view
[8748]171
[8928]172        self._draw_sphere()
[8732]173
[8810]174        # Qt text rendering classes still use deprecated OpenGL functionality
[8748]175        glEnable(GL_DEPTH_TEST)
176        glDisable(GL_BLEND)
[8810]177        glMatrixMode(GL_PROJECTION)
178        glLoadIdentity()
179        glMultMatrixd(numpy.array(self.projection.data(), dtype=float))
180        glMatrixMode(GL_MODELVIEW)
181        glLoadIdentity()
[8967]182        glMultMatrixd(numpy.array(self.view.data(), dtype=float))
183        glMultMatrixd(numpy.array(self.model.data(), dtype=float))
[8748]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
[8967]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)
[8748]204
[8928]205                self._cone_shader.bind()
206                self._cone_shader.setUniformValue('projection', self.projection)
[8967]207                self._cone_shader.setUniformValue('modelview', self.view * model)
[8928]208                self._cone_buffer.draw()
209                self._cone_shader.release()
[8748]210
[8810]211                self.qglColor(self._theme.axis_values_color)
[8748]212                self.renderText(x*1.2, y*1.2, z*1.2, label)
213
[8928]214            if self.anchor_data and not hasattr(self, '_grid_buffer'):
[8810]215                self._build_anchor_grid()
216
217            # Draw grid between anchors
[9045]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()
[8748]225
[8751]226        self._draw_value_lines()
227
[8928]228    def _draw_sphere(self):
[8732]229        glDisable(GL_DEPTH_TEST)
230        glEnable(GL_BLEND)
231        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
232
[8928]233        self._sphere_shader.bind()
234        self._sphere_shader.setUniformValue('projection', self.projection)
[8967]235        self._sphere_shader.setUniformValue('modelview', self.view * self.model)
[8975]236        self._sphere_shader.setUniformValue('cam_position', self.camera * self.camera_distance)
[8928]237        self._sphere_shader.setUniformValue('use_transparency', self.camera_type == 0)
238        self._sphere_buffer.draw(GL_LINES)
239        self._sphere_shader.release()
[8732]240
[8743]241    def mouseMoveEvent(self, event):
[8847]242        self.invert_mouse_x = self.camera_type != 0
[8747]243        OWLinProj3DPlot.mouseMoveEvent(self, event)
[8732]244
245class OWSphereviz3D(OWLinProjQt):
246    def __init__(self, parent=None, signalManager=None):
247        OWLinProjQt.__init__(self, parent, signalManager, "Sphereviz 3D", graphClass=OWSphereviz3DPlot)
248
[9546]249        self.inputs = [("Data", ExampleTable, self.setData, Default),
250                       ("Data Subset", ExampleTable, self.setSubsetData),
251                       ("Features", AttributeList, self.setShownAttributes),
[8732]252                       ("Evaluation Results", orngTest.ExperimentResults, self.setTestResults),
253                       ("VizRank Learner", orange.Learner, self.setVizRankLearner)]
[9546]254        self.outputs = [("Selected Data", ExampleTable),
255                        ("Other Data", ExampleTable),
256                        ("Features", AttributeList)]
[8732]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.