Changeset 8359:a7adcc1ba07c in orange


Ignore:
Timestamp:
07/06/11 00:16:09 (3 years ago)
Author:
matejd <matejd@…>
Branch:
default
Convert:
b9d5585e3a9bfddafcd458ddf0399887c2a95947
Message:

Added shapes (symbols), additional work on legend

File:
1 edited

Legend:

Unmodified
Added
Removed
  • orange/OrangeWidgets/OWGraph3D.py

    r8352 r8359  
    11""" 
     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. 
    238""" 
    339 
     
    844import orange 
    945 
     46import OpenGL 
     47OpenGL.ERROR_CHECKING = True 
     48OpenGL.ERROR_LOGGING = True 
     49#OpenGL.FULL_LOGGING = True 
    1050from OpenGL.GL import * 
    1151from OpenGL.GLU import * 
     
    1454import sys 
    1555import numpy 
    16 from math import sin, cos 
     56from math import sin, cos, pi 
    1757 
    1858# Import undefined functions, override some wrappers. 
     
    4585glEnableVertexAttribArray = gl.glEnableVertexAttribArray 
    4686glGetProgramiv = gl.glGetProgramiv 
     87glDrawElements = gl.glDrawElements 
     88glDrawArrays = gl.glDrawArrays 
    4789 
    4890 
     
    82124 
    83125        self.vertex_buffers = [] 
     126        self.index_buffers = [] 
    84127        self.vaos = [] 
    85128 
    86129        self.ortho = False 
    87130        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 
    89139 
    90140    def __del__(self): 
     141        # TODO: delete shaders and vertex buffer 
    91142        glDeleteProgram(self.color_shader) 
    92143 
     
    109160            attribute vec4 color; 
    110161 
    111             uniform mat4 projection; 
    112             uniform mat4 modelview; 
    113             uniform vec4 overriden_color; 
    114             uniform bool override_color; 
     162            uniform bool face_symbols; 
    115163 
    116164            varying vec4 var_color; 
    117165 
    118166            void main(void) { 
    119               //gl_Position = projection * modelview * position; 
    120  
    121167              // Calculate inverse of rotations (in this case, inverse 
    122168              // is actually just transpose), so that polygons face 
     
    136182              invs[2][2] = gl_ModelViewMatrix[2][2]; 
    137183 
    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 
    139190              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; 
    144192            } 
    145193            ''' 
     
    185233        self.color_shader_override_color = glGetUniformLocation(self.color_shader, 'override_color') 
    186234        self.color_shader_overriden_color = glGetUniformLocation(self.color_shader, 'overriden_color') 
     235        self.color_shader_face_symbols = glGetUniformLocation(self.color_shader, 'face_symbols') 
    187236        linked = c_int() 
    188237        glGetProgramiv(self.color_shader, GL_LINK_STATUS, byref(linked)) 
     
    223272        glDisable(GL_CULL_FACE) 
    224273 
    225         for cmd, vao in self.commands: 
     274        for cmd, vao, vao_outline in self.commands: 
    226275            if cmd == 'scatter': 
    227276                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) 
    238286                glUseProgram(0) 
    239287 
     
    244292        glMatrixMode(GL_PROJECTION) 
    245293        glLoadIdentity() 
    246         glOrtho(0, self.width(), 0, self.height(), -1, 1) 
     294        glOrtho(0, self.width(), self.height(), 0, -1, 1) 
    247295        glMatrixMode(GL_MODELVIEW) 
    248296        glLoadIdentity() 
     297 
     298        x, y = self.legend_position 
     299        w, h = self.legend_size 
     300        t = self.legend_border_thickness 
    249301 
    250302        glDisable(GL_DEPTH_TEST) 
    251303        glColor4f(*self.legend_border_color) 
    252304        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) 
    257309        glEnd() 
    258310 
    259311        glColor4f(1, 1, 1, 1) 
    260312        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+w-t, y+t) 
     315        glVertex2f(x+w-t, y+h-t) 
     316        glVertex2f(x+t,   y+h-t) 
    265317        glEnd() 
    266318 
     
    461513        self.axis_plane_xz_top = [E, F, B, A] 
    462514 
    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): 
    464516        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]} 
    469521            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] 
    473523  
    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? 
    476530 
    477531        max, min = numpy.max(array, axis=0), numpy.min(array, axis=0) 
     
    482536        self.normal_size = numpy.max(self.center - self.b_box[1]) / 100. 
    483537 
     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 
    484554        vao = c_int() 
    485555        glGenVertexArrays(1, byref(vao)) 
     
    498568        glEnableVertexAttribArray(2) 
    499569 
    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  
    506570        # It's important to keep a reference to vertices around, 
    507571        # data uploaded to GPU seem to get corrupted otherwise. 
    508572        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) 
    511576 
    512577        glBindVertexArray(0) 
    513578        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) 
    514610 
    515611        vao.num_vertices = len(vertices) / (vertex_size / 4) 
     612        vao_outline.num_indices = vao.num_vertices * 2 / 3 
    516613        self.vertex_buffers.append(vertex_buffer) 
     614        self.index_buffers.append(index_buffer) 
    517615        self.vaos.append(vao) 
    518         self.commands.append(("scatter", vao)) 
     616        self.vaos.append(vao_outline) 
     617        self.commands.append(("scatter", vao, vao_outline)) 
    519618        self.update_axes() 
    520619        self.updateGL() 
     
    524623 
    525624    def mouseMoveEvent(self, event): 
    526       if event.buttons() & Qt.MiddleButton: 
    527625        pos = event.pos() 
    528626        dx = pos.x() - self.mouse_pos.x() 
    529627        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 
    542650        self.mouse_pos = pos 
    543651        self.updateGL() 
    544652 
     653    def mouseReleaseEvent(self, event): 
     654        self.dragging_legend = False 
     655 
    545656    def wheelEvent(self, event): 
    546       if event.orientation() == Qt.Vertical: 
    547         self.zoom -= event.delta() / self.zoom_factor 
    548         if self.zoom < 2: 
    549           self.zoom = 2 
    550         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() 
    551662 
    552663    def clear(self): 
Note: See TracChangeset for help on using the changeset viewer.