Changeset 8359:a7adcc1ba07c in orange
 Timestamp:
 07/06/11 00:16:09 (3 years ago)
 Branch:
 default
 Convert:
 b9d5585e3a9bfddafcd458ddf0399887c2a95947
 File:

 1 edited
Legend:
 Unmodified
 Added
 Removed

orange/OrangeWidgets/OWGraph3D.py
r8352 r8359 1 1 """ 2 .. class:: OWGraph3D 3 Base class for 3D graphs. 4 5 .. attribute:: show_legend 6 Determines whether to display the legend or not. 7 8 .. attribute:: ortho 9 If False, perspective projection is used instead. 10 11 .. method:: set_x_axis_title(title) 12 Sets ``title`` as the current title (label) of x axis. 13 14 .. method:: set_y_axis_title(title) 15 Sets ``title`` as the current title (label) of y axis. 16 17 .. method:: set_z_axis_title(title) 18 Sets ``title`` as the current title (label) of z axis. 19 20 .. method:: set_show_x_axis_title(show) 21 Determines whether to show the title of x axis or not. 22 23 .. method:: set_show_y_axis_title(show) 24 Determines whether to show the title of y axis or not. 25 26 .. method:: set_show_z_axis_title(show) 27 Determines whether to show the title of z axis or not. 28 29 .. method:: scatter(X, Y, Z, c, s) 30 Adds scatter data to command buffer. ``X``, ``Y`` and ``Z` 31 should be arrays (of equal length) with example data. 32 ``c`` is optional, can be an array as well (setting 33 colors of each example) or string ('r', 'g' or 'b'). ``s`` 34 optionally sets sizes of individual examples. 35 36 .. method:: clear() 37 Removes everything from the graph. 2 38 """ 3 39 … … 8 44 import orange 9 45 46 import OpenGL 47 OpenGL.ERROR_CHECKING = True 48 OpenGL.ERROR_LOGGING = True 49 #OpenGL.FULL_LOGGING = True 10 50 from OpenGL.GL import * 11 51 from OpenGL.GLU import * … … 14 54 import sys 15 55 import numpy 16 from math import sin, cos 56 from math import sin, cos, pi 17 57 18 58 # Import undefined functions, override some wrappers. … … 45 85 glEnableVertexAttribArray = gl.glEnableVertexAttribArray 46 86 glGetProgramiv = gl.glGetProgramiv 87 glDrawElements = gl.glDrawElements 88 glDrawArrays = gl.glDrawArrays 47 89 48 90 … … 82 124 83 125 self.vertex_buffers = [] 126 self.index_buffers = [] 84 127 self.vaos = [] 85 128 86 129 self.ortho = False 87 130 self.show_legend = True 88 self.legend_border_color = [0.3, 0.3, 0.3, 1] 131 self.legend_border_color = [0.5, 0.5, 0.5, 1] 132 self.legend_border_thickness = 2 133 self.legend_position = [10, 10] 134 self.legend_size = [200, 50] 135 self.dragging_legend = False 136 137 self.face_symbols = True 138 self.filled_symbols = True 89 139 90 140 def __del__(self): 141 # TODO: delete shaders and vertex buffer 91 142 glDeleteProgram(self.color_shader) 92 143 … … 109 160 attribute vec4 color; 110 161 111 uniform mat4 projection; 112 uniform mat4 modelview; 113 uniform vec4 overriden_color; 114 uniform bool override_color; 162 uniform bool face_symbols; 115 163 116 164 varying vec4 var_color; 117 165 118 166 void main(void) { 119 //gl_Position = projection * modelview * position;120 121 167 // Calculate inverse of rotations (in this case, inverse 122 168 // is actually just transpose), so that polygons face … … 136 182 invs[2][2] = gl_ModelViewMatrix[2][2]; 137 183 138 vec3 offset_rotated = invs * offset; 184 vec3 offset_rotated; 185 if (face_symbols) 186 offset_rotated = invs * offset; 187 else 188 offset_rotated = offset; 189 139 190 gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * vec4(position+offset_rotated, 1); 140 if (override_color) 141 var_color = overriden_color; 142 else 143 var_color = color; 191 var_color = color; 144 192 } 145 193 ''' … … 185 233 self.color_shader_override_color = glGetUniformLocation(self.color_shader, 'override_color') 186 234 self.color_shader_overriden_color = glGetUniformLocation(self.color_shader, 'overriden_color') 235 self.color_shader_face_symbols = glGetUniformLocation(self.color_shader, 'face_symbols') 187 236 linked = c_int() 188 237 glGetProgramiv(self.color_shader, GL_LINK_STATUS, byref(linked)) … … 223 272 glDisable(GL_CULL_FACE) 224 273 225 for cmd, vao in self.commands:274 for cmd, vao, vao_outline in self.commands: 226 275 if cmd == 'scatter': 227 276 glUseProgram(self.color_shader) 228 glBindVertexArray(vao.value) 229 glUniform1i(self.color_shader_override_color, 0) 230 glDrawArrays(GL_TRIANGLES, 0, vao.num_vertices) 231 # Draw outlines. 232 glUniform1i(self.color_shader_override_color, 1) 233 glUniform4f(self.color_shader_overriden_color, 0,0,0,1) 234 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) 235 glDrawArrays(GL_TRIANGLES, 0, vao.num_vertices) 236 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) 237 glBindVertexArray(0) 277 glUniform1i(self.color_shader_face_symbols, self.face_symbols) 278 if self.filled_symbols: 279 glBindVertexArray(vao.value) 280 glDrawArrays(GL_TRIANGLES, 0, vao.num_vertices) 281 glBindVertexArray(0) 282 else: 283 glBindVertexArray(vao_outline.value) 284 glDrawElements(GL_LINES, vao_outline.num_indices, GL_UNSIGNED_INT, 0) 285 glBindVertexArray(0) 238 286 glUseProgram(0) 239 287 … … 244 292 glMatrixMode(GL_PROJECTION) 245 293 glLoadIdentity() 246 glOrtho(0, self.width(), 0, self.height(), 1, 1)294 glOrtho(0, self.width(), self.height(), 0, 1, 1) 247 295 glMatrixMode(GL_MODELVIEW) 248 296 glLoadIdentity() 297 298 x, y = self.legend_position 299 w, h = self.legend_size 300 t = self.legend_border_thickness 249 301 250 302 glDisable(GL_DEPTH_TEST) 251 303 glColor4f(*self.legend_border_color) 252 304 glBegin(GL_QUADS) 253 glVertex2f( 10, 10)254 glVertex2f( 10, 100)255 glVertex2f( 200, 100)256 glVertex2f( 200, 10)305 glVertex2f(x, y) 306 glVertex2f(x+w, y) 307 glVertex2f(x+w, y+h) 308 glVertex2f(x, y+h) 257 309 glEnd() 258 310 259 311 glColor4f(1, 1, 1, 1) 260 312 glBegin(GL_QUADS) 261 glVertex2f( 12, 12)262 glVertex2f( 12, 98)263 glVertex2f( 198, 98)264 glVertex2f( 198, 12)313 glVertex2f(x+t, y+t) 314 glVertex2f(x+wt, y+t) 315 glVertex2f(x+wt, y+ht) 316 glVertex2f(x+t, y+ht) 265 317 glEnd() 266 318 … … 461 513 self.axis_plane_xz_top = [E, F, B, A] 462 514 463 def scatter(self, X, Y, Z, c ="b", s=5, **kwargs):515 def scatter(self, X, Y, Z, colors='b', sizes=5, shapes=None, labels=None, **kwargs): 464 516 array = [[x, y, z] for x,y,z in zip(X, Y, Z)] 465 if isinstance(c , str):466 color_map = { "r": [1.0, 0.0, 0.0, 1.0],467 "g": [0.0, 1.0, 0.0, 1.0],468 "b": [0.0, 0.0, 1.0, 1.0]}517 if isinstance(colors, str): 518 color_map = {'r': [1.0, 0.0, 0.0, 1.0], 519 'g': [0.0, 1.0, 0.0, 1.0], 520 'b': [0.0, 0.0, 1.0, 1.0]} 469 521 default = [0.0, 0.0, 1.0, 1.0] 470 colors = [color_map.get(c, default) for _ in array] 471 else: 472 colors = c 522 colors = [color_map.get(colors, default) for _ in array] 473 523 474 if isinstance(s, int): 475 s = [s for _ in array] 524 if isinstance(sizes, int): 525 sizes = [sizes for _ in array] 526 527 if shapes == None: 528 shapes = [0 for _ in array] 529 # TODO: what if shapes are not integers? 476 530 477 531 max, min = numpy.max(array, axis=0), numpy.min(array, axis=0) … … 482 536 self.normal_size = numpy.max(self.center  self.b_box[1]) / 100. 483 537 538 # Generate vertices for shapes and also indices for outlines. 539 vertices = [] 540 outline_indices = [] 541 index = 0 542 for (x,y,z), (r,g,b,a), size, shape in zip(array, colors, sizes, shapes): 543 sO2 = size * self.normal_size / 2. 544 n = 4 if shape % 2 == 0 else 4 545 angle_inc = 2.*pi / n 546 angle = angle_inc / 2. 547 for i in range(n): 548 vertices.extend([x,y,z, 0,0,0, r,g,b,a]) 549 vertices.extend([x,y,z, cos(angle)*sO2, sin(angle)*sO2, 0, r,g,b,a]) 550 angle += angle_inc 551 vertices.extend([x,y,z, cos(angle)*sO2, sin(angle)*sO2, 0, r,g,b,a]) 552 outline_indices.extend([index+1, index+2]) 553 index += 3 484 554 vao = c_int() 485 555 glGenVertexArrays(1, byref(vao)) … … 498 568 glEnableVertexAttribArray(2) 499 569 500 vertices = []501 for (x,y,z), (r,g,b,a), size in zip(array, colors, s):502 vertices.extend([x,y,z, size*self.normal_size,0,0, r,g,b,a])503 vertices.extend([x,y,z, +size*self.normal_size,0,0, r,g,b,a])504 vertices.extend([x,y,z, 0,+size*self.normal_size,0, r,g,b,a])505 506 570 # It's important to keep a reference to vertices around, 507 571 # data uploaded to GPU seem to get corrupted otherwise. 508 572 vertex_buffer.vertices = numpy.array(vertices, 'f') 509 glBufferData(GL_ARRAY_BUFFER, len(vertices)*4, 510 ArrayDatatype.voidDataPointer(vertex_buffer.vertices), GL_STATIC_DRAW) 573 glBufferData(GL_ARRAY_BUFFER, 574 ArrayDatatype.arrayByteCount(vertex_buffer.vertices), 575 ArrayDatatype.voidDataPointer(vertex_buffer.vertices), GL_STATIC_DRAW) 511 576 512 577 glBindVertexArray(0) 513 578 glBindBuffer(GL_ARRAY_BUFFER, 0) 579 glDisableVertexAttribArray(0) 580 glDisableVertexAttribArray(1) 581 glDisableVertexAttribArray(2) 582 583 # Outline: 584 # generate another VAO, keep the same vertex buffer, but use an index buffer 585 # this time. 586 index_buffer = c_int() 587 glGenBuffers(1, byref(index_buffer)) 588 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer.value) 589 index_buffer.indices = numpy.array(outline_indices, 'I') 590 glBufferData(GL_ELEMENT_ARRAY_BUFFER, 591 ArrayDatatype.arrayByteCount(index_buffer.indices), 592 ArrayDatatype.voidDataPointer(index_buffer.indices), GL_STATIC_DRAW) 593 594 vao_outline = c_int() 595 glGenVertexArrays(1, byref(vao_outline)) 596 glBindVertexArray(vao_outline.value) 597 598 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer.value) 599 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer.value) 600 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vertex_size, 0) 601 glEnableVertexAttribArray(0) 602 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertex_size, 3*4) 603 glEnableVertexAttribArray(1) 604 glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, vertex_size, 6*4) 605 glEnableVertexAttribArray(2) 606 607 glBindVertexArray(0) 608 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) 609 glBindBuffer(GL_ARRAY_BUFFER, 0) 514 610 515 611 vao.num_vertices = len(vertices) / (vertex_size / 4) 612 vao_outline.num_indices = vao.num_vertices * 2 / 3 516 613 self.vertex_buffers.append(vertex_buffer) 614 self.index_buffers.append(index_buffer) 517 615 self.vaos.append(vao) 518 self.commands.append(("scatter", vao)) 616 self.vaos.append(vao_outline) 617 self.commands.append(("scatter", vao, vao_outline)) 519 618 self.update_axes() 520 619 self.updateGL() … … 524 623 525 624 def mouseMoveEvent(self, event): 526 if event.buttons() & Qt.MiddleButton:527 625 pos = event.pos() 528 626 dx = pos.x()  self.mouse_pos.x() 529 627 dy = pos.y()  self.mouse_pos.y() 530 if QApplication.keyboardModifiers() & Qt.ShiftModifier: 531 off_x = numpy.cross(self.camera, [0,1,0]) * (dx / self.move_factor) 532 #off_y = numpy.cross(self.camera, [1,0,0]) * (dy / self.move_factor) 533 # TODO: this incidentally works almost fine, but the math is wrong and should be fixed 534 self.center += off_x 535 else: 536 self.yaw += dx / self.rotation_factor 537 self.pitch += dy / self.rotation_factor 538 self.camera = [ 539 sin(self.pitch)*cos(self.yaw), 540 cos(self.pitch), 541 sin(self.pitch)*sin(self.yaw)] 628 629 if event.buttons() & Qt.LeftButton: 630 if self.dragging_legend: 631 self.legend_position[0] += dx 632 self.legend_position[1] += dy 633 elif self.legend_position[0] <= pos.x() <= self.legend_position[0]+self.legend_size[0] and\ 634 self.legend_position[1] <= pos.y() <= self.legend_position[1]+self.legend_size[1]: 635 self.dragging_legend = True 636 elif event.buttons() & Qt.MiddleButton: 637 if QApplication.keyboardModifiers() & Qt.ShiftModifier: 638 off_x = numpy.cross(self.camera, [0,1,0]) * (dx / self.move_factor) 639 #off_y = numpy.cross(self.camera, [1,0,0]) * (dy / self.move_factor) 640 # TODO: this incidentally works almost fine, but the math is wrong and should be fixed 641 self.center += off_x 642 else: 643 self.yaw += dx / self.rotation_factor 644 self.pitch += dy / self.rotation_factor 645 self.camera = [ 646 sin(self.pitch)*cos(self.yaw), 647 cos(self.pitch), 648 sin(self.pitch)*sin(self.yaw)] 649 542 650 self.mouse_pos = pos 543 651 self.updateGL() 544 652 653 def mouseReleaseEvent(self, event): 654 self.dragging_legend = False 655 545 656 def wheelEvent(self, event): 546 if event.orientation() == Qt.Vertical:547 self.zoom = event.delta() / self.zoom_factor548 if self.zoom < 2:549 self.zoom = 2550 self.updateGL()657 if event.orientation() == Qt.Vertical: 658 self.zoom = event.delta() / self.zoom_factor 659 if self.zoom < 2: 660 self.zoom = 2 661 self.updateGL() 551 662 552 663 def clear(self):
Note: See TracChangeset
for help on using the changeset viewer.