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

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

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

Line 
1import os
2
3from plot.owplot3d import *
4from plot.owopenglrenderer import VertexBuffer
5from plot import OWPoint
6
7from Orange.data.preprocess.scaling import ScaleLinProjData3D, get_variable_values_sorted
8import orange
9Discrete = orange.VarTypes.Discrete
10Continuous = orange.VarTypes.Continuous
11
12class OWLinProj3DPlot(OWPlot3D, ScaleLinProjData3D):
13    def __init__(self, widget, parent=None, name='None'):
14        self.name = name
15        OWPlot3D.__init__(self, parent)
16        ScaleLinProjData3D.__init__(self)
17
18        self.camera_fov = 22.
19        self.camera_in_center = False
20        self.show_axes = self.show_chassis = self.show_grid = False
21
22        self.point_width = 6
23        self.animate_plot = False
24        self.animate_points = False
25        self.antialias_plot = False
26        self.antialias_points = False
27        self.antialias_lines = False
28        self.auto_adjust_performance = False
29        self.show_filled_symbols = False
30        self.use_antialiasing = False
31        self.sendSelectionOnUpdate = False
32        self.setCanvasBackground = self.setCanvasColor
33        self.showValueLines = 0
34
35        self._point_width_to_symbol_scale = 1.5
36
37        if 'linear' in self.name.lower():
38            self._arrow_lines = []
39            self.mouseover_callback = self._update_arrow_values
40
41    def set_data(self, data, subset_data=None, **args):
42        if data == None:
43            return
44        ScaleLinProjData3D.setData(self, data, subset_data, **args)
45        OWPlot3D.initializeGL(self)
46
47        if hasattr(self, '_value_lines_shader'):
48            return
49
50        self.makeCurrent()
51        self.before_draw_callback = self.before_draw
52
53        ## Value lines shader
54        self._value_lines_shader = QtOpenGL.QGLShaderProgram()
55        self._value_lines_shader.addShaderFromSourceFile(QtOpenGL.QGLShader.Vertex,
56            os.path.join(os.path.dirname(__file__), 'value-lines.vs'))
57        self._value_lines_shader.addShaderFromSourceFile(QtOpenGL.QGLShader.Fragment,
58            os.path.join(os.path.dirname(__file__), 'value-lines.fs'))
59
60        self._value_lines_shader.bindAttributeLocation('position', 0)
61        self._value_lines_shader.bindAttributeLocation('color', 1)
62        self._value_lines_shader.bindAttributeLocation('normal', 2)
63
64        if not self._value_lines_shader.link():
65            print('Failed to link value-lines shader!')
66
67    setData = set_data
68
69    def before_draw(self):
70        view = QMatrix4x4()
71        view.lookAt(
72            self.camera * self.camera_distance,
73            QVector3D(0, 0, 0),
74            QVector3D(0, 1, 0))
75        self.view = view
76
77        glEnable(GL_DEPTH_TEST)
78        glDisable(GL_BLEND)
79        glMatrixMode(GL_PROJECTION)
80        glLoadIdentity()
81        glMultMatrixd(numpy.array(self.projection.data(), dtype=float))
82        glMatrixMode(GL_MODELVIEW)
83        glLoadIdentity()
84        glMultMatrixd(numpy.array(self.view.data(), dtype=float))
85        glMultMatrixd(numpy.array(self.model.data(), dtype=float))
86
87        self.renderer.set_transform(self.model, self.view, self.projection)
88
89        if self.showAnchors:
90            for anchor in self.anchor_data:
91                x, y, z, label = anchor
92
93                self.qglColor(self._theme.axis_values_color)
94                self.renderText(x*1.2, y*1.2, z*1.2, label)
95
96                glDepthMask(GL_FALSE)
97                self.renderer.draw_line(
98                    QVector3D(0, 0, 0),
99                    QVector3D(x, y, z),
100                    color=self._theme.axis_color)
101                glDepthMask(GL_TRUE)
102
103        if self.tooltipKind == 0 and self._arrow_lines:
104            glEnable(GL_DEPTH_TEST)
105            glLineWidth(2)
106            for x, y, z, value, factor, color in self._arrow_lines:
107                self.renderer.draw_line(
108                    QVector3D(0, 0, 0),
109                    QVector3D(x*factor, y*factor, z*factor),
110                    color=color)
111                if not isinstance(value, str):
112                    value = ('%.3f' % (value if self.tooltipValue == 0 else factor)).rstrip('0').rstrip('.')
113                self.renderText(
114                    x * 1.1, y * 1.1, z * 1.1,
115                    value,
116                    font=self._theme.labels_font)
117            glLineWidth(1)
118
119        self._draw_value_lines()
120
121    def _draw_value_lines(self):
122        if self.showValueLines:
123            self._value_lines_shader.bind()
124            self._value_lines_shader.setUniformValue('projection', self.projection)
125            self._value_lines_shader.setUniformValue('modelview', self.view * self.model)
126            self._value_lines_shader.setUniformValue('value_line_length', float(self.valueLineLength))
127            self._value_lines_shader.setUniformValue('plot_scale', self.plot_scale)
128            self._value_lines_buffer.draw(GL_LINES)
129            self._value_lines_shader.release()
130
131    def updateData(self, labels=None, setAnchors=0, **args):
132        self.clear()
133        self.clear_plot_transformations()
134
135        if labels == None:
136            labels = [anchor[3] for anchor in self.anchor_data]
137
138        if not self.have_data or len(labels) < 3:
139            self.anchor_data = []
140            self.update()
141            return
142
143        if setAnchors or (args.has_key('XAnchors') and args.has_key('YAnchors') and args.has_key('ZAnchors')):
144            self.setAnchors(args.get('XAnchors'), args.get('YAnchors'), args.get('ZAnchors'), labels)
145
146        indices = [self.attribute_name_index[anchor[3]] for anchor in self.anchor_data]
147        valid_data = self.getValidList(indices)
148        trans_proj_data = self.create_projection_as_numeric_array(indices, validData=valid_data,
149            scaleFactor=1.0, normalize=self.normalize_examples, jitterSize=-1,
150            useAnchorData=1, removeMissingData=0)
151        if trans_proj_data == None:
152            return
153
154        proj_data = trans_proj_data.T
155        proj_data[0:3] += 0.5
156        if self.data_has_discrete_class:
157            proj_data[3] = self.no_jittering_scaled_data[self.attribute_name_index[self.data_domain.classVar.name]]
158        self.set_plot_data(proj_data, None)
159        self.proj_data = proj_data
160        self.symbol_scale = self.point_width*self._point_width_to_symbol_scale
161        self.hide_outside = False
162        self.fade_outside = False
163
164        color_index = symbol_index = size_index = label_index = -1
165        color_discrete = False
166        x_discrete = self.data_domain[self.anchor_data[0][3]].varType == Discrete
167        y_discrete = self.data_domain[self.anchor_data[1][3]].varType == Discrete
168        z_discrete = self.data_domain[self.anchor_data[2][3]].varType == Discrete
169
170        if self.data_has_discrete_class:
171            self.discrete_palette.setNumberOfColors(len(self.data_domain.classVar.values))
172
173        use_different_symbols = self.useDifferentSymbols and self.data_has_discrete_class and\
174            len(self.data_domain.classVar.values) <= len(Symbol)
175
176        if use_different_symbols:
177            symbol_index = 3
178            num_symbols_used = len(self.data_domain.classVar.values)
179        else:
180            num_symbols_used = -1
181
182        if self.useDifferentColors and self.data_has_discrete_class:
183            color_discrete = True
184            color_index = 3
185
186        colors = []
187        if color_discrete:
188            for i in range(len(self.data_domain.classVar.values)):
189                c = self.discrete_palette[i]
190                colors.append(c)
191
192        self.set_features(0, 1, 2, color_index, symbol_index, size_index, label_index,
193                          colors, num_symbols_used,
194                          x_discrete, y_discrete, z_discrete)
195
196        def_color = QColor(150, 150, 150)
197        def_symbol = 0
198        def_size = 10
199
200        if color_discrete:
201            num = len(self.data_domain.classVar.values)
202            values = get_variable_values_sorted(self.data_domain.classVar)
203            for ind in range(num):
204                symbol = ind if use_different_symbols else def_symbol
205                self.legend().add_item(self.data_domain.classVar.name, values[ind], OWPoint(symbol, self.discrete_palette[ind], def_size))
206
207        if use_different_symbols and not color_discrete:
208            num = len(self.data_domain.classVar.values)
209            values = get_variable_values_sorted(self.data_domain.classVar)
210            for ind in range(num):
211                self.legend().add_item(self.data_domain.classVar.name, values[ind], OWPoint(ind, def_color, def_size))
212
213        self.legend().set_orientation(Qt.Vertical)
214        self.legend().max_size = QSize(400, 400)
215        if self.legend().pos().x() == 0:
216            self.legend().setPos(QPointF(100, 100))
217        self.legend().update_items()
218        self.legend().setVisible(self.show_legend)
219
220        x_positions = proj_data[0]-0.5
221        y_positions = proj_data[1]-0.5
222        z_positions = proj_data[2]-0.5
223        XAnchors = [anchor[0] for anchor in self.anchor_data]
224        YAnchors = [anchor[1] for anchor in self.anchor_data]
225        ZAnchors = [anchor[2] for anchor in self.anchor_data]
226        data_size = len(self.raw_data)
227
228        value_lines = []
229        for i in range(data_size):
230            if not valid_data[i]:
231                continue
232            if self.useDifferentColors and self.data_has_discrete_class:
233                color = self.discrete_palette.getRGB(self.original_data[self.data_class_index][i])
234            else:
235                color = (0, 0, 0)
236
237            len_anchor_data = len(self.anchor_data)
238            x = numpy.array([x_positions[i]] * len_anchor_data)
239            y = numpy.array([y_positions[i]] * len_anchor_data)
240            z = numpy.array([z_positions[i]] * len_anchor_data)
241            dists = numpy.sqrt((XAnchors - x)**2 + (YAnchors - y)**2 + (ZAnchors - z)**2)
242            x_directions = 0.03 * (XAnchors - x) / dists
243            y_directions = 0.03 * (YAnchors - y) / dists
244            z_directions = 0.03 * (ZAnchors - z) / dists
245            example_values = [self.no_jittering_scaled_data[attr_ind, i] for attr_ind in indices]
246
247            for j in range(len_anchor_data):
248                value_lines.extend([x_positions[i], y_positions[i], z_positions[i],
249                                    color[0]/255.,
250                                    color[1]/255.,
251                                    color[2]/255.,
252                                    0., 0., 0.,
253                                    x_positions[i], y_positions[i], z_positions[i],
254                                    color[0]/255.,
255                                    color[1]/255.,
256                                    color[2]/255.,
257                                    x_directions[j]*example_values[j],
258                                    y_directions[j]*example_values[j],
259                                    z_directions[j]*example_values[j]])
260
261        self._value_lines_buffer = VertexBuffer(numpy.array(value_lines, numpy.float32),
262            [(3, GL_FLOAT),
263             (3, GL_FLOAT),
264             (3, GL_FLOAT)])
265
266        self.update()
267
268    def updateGraph(self, attrList=None, setAnchors=0, insideColors=None, **args):
269        pass
270
271    def setCanvasColor(self, c):
272        pass
273
274    def getSelectionsAsExampleTables(self, attrList, useAnchorData=1, addProjectedPositions=0):
275        return (None, None) # TODO: this is disabled for now
276
277        if not self.have_data:
278            return (None, None)
279
280        selected = self.get_selected_indices()
281
282        if addProjectedPositions == 0 and not numpy.any(selected):
283            return (None, self.raw_data)
284        if (useAnchorData and len(self.anchor_data) < 3) or len(attrList) < 3:
285            return (None, None)
286
287        x_attr = orange.FloatVariable("X Positions")
288        y_attr = orange.FloatVariable("Y Positions")
289        z_attr = orange.FloatVariable("Z Positions")
290
291        if addProjectedPositions == 1:
292            domain = orange.Domain([x_attr, y_attr, z_attr] + [v for v in self.data_domain.variables])
293        elif addProjectedPositions == 2:
294            domain = orange.Domain(self.data_domain)
295            domain.addmeta(orange.newmetaid(), x_attr)
296            domain.addmeta(orange.newmetaid(), y_attr)
297            domain.addmeta(orange.newmetaid(), z_attr)
298        else:
299            domain = orange.Domain(self.data_domain)
300
301        domain.addmetas(self.data_domain.getmetas())
302
303        if useAnchorData:
304            indices = [self.attribute_name_index[val[3]] for val in self.anchor_data]
305        else:
306            indices = [self.attribute_name_index[label] for label in attrList]
307        valid_data = self.getValidList(indices)
308        if len(valid_data) == 0:
309            return (None, None)
310
311        array = self.create_projection_as_numeric_array(attrList, scaleFactor=self.scaleFactor, useAnchorData=useAnchorData, removeMissingData=0)
312        if array == None:
313            return (None, None)
314
315        unselected = numpy.logical_not(selected)
316        selected_indices, unselected_indices = list(selected), list(unselected)
317
318        if addProjectedPositions:
319            selected = orange.ExampleTable(domain, self.raw_data.selectref(selected_indices))
320            unselected = orange.ExampleTable(domain, self.raw_data.selectref(unselected_indices))
321            selected_index = 0
322            unselected_index = 0
323            for i in range(len(selected_indices)):
324                if selected_indices[i]:
325                    selected[selected_index][x_attr] = array[i][0]
326                    selected[selected_index][y_attr] = array[i][1]
327                    selected[selected_index][z_attr] = array[i][2]
328                    selected_index += 1
329                else:
330                    unselected[unselected_index][x_attr] = array[i][0]
331                    unselected[unselected_index][y_attr] = array[i][1]
332                    unselected[unselected_index][z_attr] = array[i][2]
333        else:
334            selected = self.raw_data.selectref(selected_indices)
335            unselected = self.raw_data.selectref(unselected_indices)
336
337        if len(selected) == 0:
338            selected = None
339        if len(unselected) == 0:
340            unselected = None
341        return (selected, unselected)
342
343    def removeAllSelections(self):
344        pass
345
346    def update_point_size(self):
347        self.symbol_scale = self.point_width*self._point_width_to_symbol_scale
348        self.update()
349
350    def update_alpha_value(self):
351        self.update()
352
353    def update_legend(self):
354        self.update()
355
356    def replot(self):
357        pass
358
359    def saveToFile(self):
360        pass
361
362    def _update_arrow_values(self, index):
363        if not self.have_data:
364            return
365
366        if self.tooltipKind == 1:
367            shown_attrs = [anchor[3] for anchor in self.anchor_data]
368            self.show_tooltip(self.get_example_tooltip_text(self.raw_data[index], shown_attrs))
369            return
370        elif self.tooltipKind == 2:
371            self.show_tooltip(self.get_example_tooltip_text(self.raw_data[index]))
372            return
373
374        if index == self._last_index:
375            return
376        self._last_index = index
377        self._arrow_lines = []
378        example = self.original_data.T[index]
379        for x, y, z, attribute in self.anchor_data:
380            value = example[self.attribute_name_index[attribute]]
381            if value < 0:
382                x = y = z = 0
383            max_value = self.attr_values[attribute][1]
384            factor = value / max_value
385            if self.data_domain[attribute].varType == Discrete:
386                value = get_variable_values_sorted(self.data_domain[attribute])[int(value)]
387            if self.useDifferentColors and self.data_has_discrete_class:
388                color = self.discrete_palette.getColor(example[self.data_class_index])
389            else:
390                color = QColor(0, 0, 0)
391            self._arrow_lines.append([x, y, z, value, factor, color])
392        self._mouseover_called = True
393        self.update()
394
395    def get_example_tooltip_text(self, example, indices=None, maxIndices=20):
396        if indices and type(indices[0]) == str:
397            indices = [self.attributeNameIndex[i] for i in indices]
398        if not indices: 
399            indices = range(len(self.dataDomain.attributes))
400
401        # don't show the class value twice
402        if example.domain.classVar:
403            classIndex = self.attributeNameIndex[example.domain.classVar.name]
404            while classIndex in indices:
405                indices.remove(classIndex)     
406
407        text = "<b>Attributes:</b><br>"
408        for index in indices[:maxIndices]:
409            attr = self.attributeNames[index]
410            if attr not in example.domain:  text += "&nbsp;"*4 + "%s = ?<br>" % (Qt.escape(attr))
411            elif example[attr].isSpecial(): text += "&nbsp;"*4 + "%s = ?<br>" % (Qt.escape(attr))
412            else:                           text += "&nbsp;"*4 + "%s = %s<br>" % (Qt.escape(attr), Qt.escape(str(example[attr])))
413        if len(indices) > maxIndices:
414            text += "&nbsp;"*4 + " ... <br>"
415
416        if example.domain.classVar:
417            text = text[:-4]
418            text += "<hr><b>Class:</b><br>"
419            if example.getclass().isSpecial(): text += "&nbsp;"*4 + "%s = ?<br>" % (Qt.escape(example.domain.classVar.name))
420            else:                              text += "&nbsp;"*4 + "%s = %s<br>" % (Qt.escape(example.domain.classVar.name), Qt.escape(str(example.getclass())))
421
422        if len(example.domain.getmetas()) != 0:
423            text = text[:-4]
424            text += "<hr><b>Meta attributes:</b><br>"
425            # show values of meta attributes
426            for key in example.domain.getmetas():
427                try: text += "&nbsp;"*4 + "%s = %s<br>" % (Qt.escape(example.domain[key].name), Qt.escape(str(example[key])))
428                except: pass
429        return text[:-4]        # remove the last <br>
430
431    def mousePressEvent(self, event):
432        if not self.have_data:
433            return
434
435        # Filter events (no panning or scaling)
436        if event.buttons() & Qt.LeftButton or\
437           (event.buttons() & Qt.RightButton and not QApplication.keyboardModifiers() & Qt.ShiftModifier) or\
438           (event.buttons() & Qt.MiddleButton and not QApplication.keyboardModifiers() & Qt.ShiftModifier):
439            OWPlot3D.mousePressEvent(self, event)
440
441    def mouseMoveEvent(self, event):
442        if not self.have_data:
443            return
444
445        pos = event.pos()
446
447        self._last_index = -1
448        self._mouseover_called = False
449        self._check_mouseover(pos)
450        if not self._mouseover_called and 'linear' in self.name.lower():
451            before = len(self._arrow_lines)
452            self._arrow_lines = []
453            if before > 0:
454                self.update()
455
456        OWPlot3D.mouseMoveEvent(self, event)
457
458    def mouseReleaseEvent(self, event):
459        if not self.have_data:
460            return
461
462        OWPlot3D.mouseReleaseEvent(self, event)
Note: See TracBrowser for help on using the repository browser.