Changeset 8285:fbd42c63e48f in orange


Ignore:
Timestamp:
05/31/11 23:44:01 (3 years ago)
Author:
matejd <matejd@…>
Branch:
default
Convert:
91e140797da00cc00fd962cd86048a76e5feabe0
Message:

work on OWGraph3D and OWScatterplot3D, recoded rotations, added axis titles

Location:
orange/OrangeWidgets
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • orange/OrangeWidgets/OWGraph3D.py

    r8277 r8285  
    1  
    21from PyQt4.QtCore import * 
    32from PyQt4.QtGui import * 
     
    65import orange 
    76 
    8 import OpenGL 
    9 OpenGL.FORWARD_COMPATIBLE_ONLY = True 
    10   
    11 import OpenGL.GL as gl 
    12 import OpenGL.GLU as glu 
    13 import sys, os 
     7from OpenGL.GL import * 
     8from OpenGL.GLU import * 
     9import sys 
    1410import numpy 
    15  
    16 class BufferObject(object): 
    17     def __init__(self, glId=None, data=None): 
    18         self.glId = glId 
    19         self.data = data 
    20         if glId is None: 
    21             self.glId = self.genBuffer() 
    22              
    23     @classmethod 
    24     def genBuffer(cls): 
    25         id = gl.glGenBuffers(1) 
    26         return id[0] 
    27          
     11from math import sin, cos 
     12 
     13def normalize(vec): 
     14  return vec / numpy.sqrt(numpy.sum(vec** 2)) 
    2815 
    2916class OWGraph3D(QtOpenGL.QGLWidget): 
     
    3118        QtOpenGL.QGLWidget.__init__(self, QtOpenGL.QGLFormat(QtOpenGL.QGL.SampleBuffers), parent) 
    3219        self.commands = [] 
    33          
    34         self.quad = numpy.array([[-1., 1., 0], 
    35                      [1., 1., 0], 
    36                      [1., -1., 0], 
    37                      [-1., -1, 0]], dtype="f") 
    38          
    3920        self.minx = self.miny = self.minz = 0 
    4021        self.maxx = self.maxy = self.maxz = 0 
    41         self.b_box = [0, 0, 0], [0, 0, 0] 
    42         self.camera = numpy.array([0, 0, 0]) 
    43         self.center = numpy.array([2, 0, 0]) 
     22        self.b_box = [numpy.array([0, 0, 0]), numpy.array([0, 0, 0])] 
     23        self.camera = numpy.array([0.6, 0.8, 0])  # Spherical unit vector around the center. This is where camera is looking from. 
     24        self.center = numpy.array([0, 0, 0])      # Camera is looking into this point. 
     25 
     26        # Try to use displays lists for performance. 
     27        self.sphere_dl = glGenLists(1) # TODO: why does this fail? 
     28        if self.sphere_dl != 0: 
     29          gluQuadric  = gluNewQuadric() 
     30          glNewList(self.sphere_dl, GL_COMPILE) 
     31          gluSphere(gluQuadric, 1, 10, 10) 
     32          glEndList() 
     33          gluDeleteQuadric(gluQuadric) 
     34 
     35        # TODO: other shapes 
     36 
     37        self.yaw = self.pitch = 0 
     38        self.rotation_factor = 100. 
     39        self.zoom_factor = 100. 
     40        self.zoom = 10 
     41        self.move_factor = 100. 
     42        self.mouse_pos = [100,100] # TODO: get real mouse position, calculate camera, fix the initial jump 
    4443        self.updateAxes() 
    45          
     44 
     45        self.axisTitleFont = QFont('Helvetica', 10, QFont.Bold) 
     46        self.ticksFont = QFont('Helvetica', 9) 
     47        self.XaxisTitle = '' 
     48        self.YaxisTitle = '' 
     49        self.ZaxisTitle = '' 
     50 
    4651    def initializeGL(self): 
    47         gl.glClearColor(1.0, 1.0, 1.0, 1.0) 
    48         gl.glClearDepth(1.0) 
    49         gl.glDepthFunc(gl.GL_LESS) 
    50         gl.glShadeModel(gl.GL_SMOOTH) 
    51 #        gl.glEnable(gl.GL_DEPTH_TEST) 
    52         gl.glEnable(gl.GL_BLEND) 
    53         gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) 
    54         gl.glMatrixMode(gl.GL_PROJECTION) 
    55         gl.glLoadIdentity() 
    56         glu.gluPerspective(30.0, float(self.width())/float(self.height()), 0.1, 100.0) 
    57 #        glu.gluOrtho2D(0, 10, 0, 10) 
    58         gl.glMatrixMode(gl.GL_MODELVIEW) 
    59          
     52        glClearColor(1.0, 1.0, 1.0, 1.0) 
     53        glClearDepth(1.0) 
     54        glDepthFunc(GL_LESS) 
     55        glShadeModel(GL_SMOOTH) 
     56        glEnable(GL_BLEND) 
     57        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 
     58        glMatrixMode(GL_PROJECTION) 
     59        glLoadIdentity() 
     60        gluPerspective(30.0, float(self.width())/float(self.height()), 0.1, 100.0) 
     61        glMatrixMode(GL_MODELVIEW) 
     62        glLoadIdentity() 
     63  
    6064    def resizeGL(self, w, h): 
    61         gl.glViewport(0, 0, w, h) 
    62         gl.glMatrixMode(gl.GL_PROJECTION) 
    63         gl.glLoadIdentity() 
     65        glViewport(0, 0, w, h) 
     66        glMatrixMode(GL_PROJECTION) 
     67        glLoadIdentity() 
    6468        if h == 0: 
    6569            aspect = 1 
    6670        else: 
    6771            aspect = float(w)/float(h) 
    68         radius = self.radius() 
    69         glu.gluPerspective(30.0, aspect, 0.1, radius * 50) 
    70         gl.glMatrixMode(gl.GL_MODELVIEW) 
     72        gluPerspective(30.0, aspect, 0.1, 100) 
     73        glMatrixMode(GL_MODELVIEW) 
     74        glLoadIdentity() 
    7175 
    7276    def paintGL(self): 
    73         gl.glMatrixMode(gl.GL_MODELVIEW) 
    74         gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) 
    75         gl.glLoadIdentity() 
    76 #        gl.glPushMatrix() 
    77         glu.gluLookAt(self.camera[0], self.camera[1], self.camera[2], 
    78                       self.center[0], self.center[1], self.center[2], 
    79                       0, 1, 0) 
     77        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 
     78        glMatrixMode(GL_MODELVIEW) 
     79        glLoadIdentity() 
     80        gluLookAt( 
     81            self.camera[0]*self.zoom + self.center[0], 
     82            self.camera[1]*self.zoom + self.center[1], 
     83            self.camera[2]*self.zoom + self.center[2], 
     84            self.center[0], 
     85            self.center[1], 
     86            self.center[2], 
     87            0, 1, 0) 
    8088        self.paintAxes() 
    81          
    82         normalSize = numpy.max(self.center - self.b_box[1]) / 100.0   
    83          
    84         gl.glEnable(gl.GL_BLEND) 
    85         gl.glEnable(gl.GL_CULL_FACE) 
    86         gl.glCullFace(gl.GL_BACK) 
    87          
    88         gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) 
    89         gluQuadric  = glu.gluNewQuadric() 
     89 
     90        glEnable(GL_CULL_FACE) 
     91        glCullFace(GL_BACK) 
     92  
     93        if self.sphere_dl == 0: 
     94          gluQuadric  = gluNewQuadric() 
    9095        for cmd, (array, colors, sizes) in self.commands: 
    9196            for (x,y,z), (r, g, b, a), size in zip(array, colors, sizes): 
    92                 gl.glPushMatrix() 
    93                 gl.glTranslatef(x, y, z) 
    94                 gl.glColor4f(r, g, b, a) 
    95                 glu.gluSphere(gluQuadric, normalSize * size, 10, 10) 
    96                 gl.glPopMatrix() 
    97         glu.gluDeleteQuadric(gluQuadric) 
    98                  
    99          
    100     def paintGLScatter(self, array, colors="r", shape=".", size=1): 
    101         for x, y, z in array: 
    102             pass 
    103              
    104          
     97                glPushMatrix() 
     98                glTranslatef(x, y, z) 
     99                glColor4f(r, g, b, a) 
     100                scale = self.normalSize * size 
     101                glScalef(scale, scale, scale) 
     102                if self.sphere_dl == 0: 
     103                  gluSphere(gluQuadric, 1, 10, 10) 
     104                else: 
     105                  glCallList(self.sphere_dl) 
     106                glPopMatrix() 
     107        if self.sphere_dl == 0: 
     108          gluDeleteQuadric(gluQuadric) 
     109 
     110        glDisable(GL_CULL_FACE) 
     111 
     112    def setXaxisTitle(self, title): 
     113      self.XaxisTitle = title 
     114      self.updateGL() 
     115 
     116    def setYaxisTitle(self, title): 
     117      self.YaxisTitle = title 
     118      self.updateGL() 
     119 
     120    def setZaxisTitle(self, title): 
     121      self.ZaxisTitle = title 
     122      self.updateGL() 
     123 
    105124    def paintAxes(self): 
    106         gl.glDisable(gl.GL_CULL_FACE) 
    107         gl.glColor4f(1,1,1,1) 
     125        glDisable(GL_CULL_FACE) 
     126        glColor4f(1,1,1,1) 
    108127        for start, end in [self.x_axis, self.y_axis, self.z_axis]: 
    109             gl.glBegin(gl.GL_LINES) 
    110             gl.glVertex3f(*start) 
    111             gl.glVertex3f(*end) 
    112             gl.glEnd() 
    113              
    114         def paintGrid(planeQuad, sub=7): 
     128            glBegin(GL_LINES) 
     129            glVertex3f(*start) 
     130            glVertex3f(*end) 
     131            glEnd() 
     132 
     133        bb_center = (self.b_box[1] + self.b_box[0]) / 2. 
     134 
     135        # Draw axis labels. 
     136        glColor4f(0,0,0,1) 
     137 
     138        ac = (self.x_axis[0] + self.x_axis[1]) / 2. 
     139        self.renderText(ac[0], ac[1]-0.2, ac[2]-0.2, self.XaxisTitle) 
     140        ac = (self.y_axis[0] + self.y_axis[1]) / 2. 
     141        self.renderText(ac[0], ac[1]-0.2, ac[2]-0.2, self.YaxisTitle) 
     142        ac = (self.z_axis[0] + self.z_axis[1]) / 2. 
     143        self.renderText(ac[0], ac[1]-0.2, ac[2]-0.2, self.ZaxisTitle) 
     144 
     145        outwards = normalize(self.x_axis[0] - bb_center) 
     146        pos = self.x_axis[0] + outwards * 0.2 
     147        self.renderText(pos[0], pos[1], pos[2], '{0:.2}'.format(pos[0])) 
     148 
     149        glColor4f(1,1,1,1) 
     150 
     151        def paintGrid(planeQuad, sub=20): 
    115152            P11, P12, P22, P21 = numpy.asarray(planeQuad) 
    116153            Dx = numpy.linspace(0.0, 1.0, num=sub) 
     
    119156            P1vecV = P21 - P11 
    120157            P2vecV = P22 - P12 
    121             gl.glBegin(gl.GL_LINES) 
     158            glBegin(GL_LINES) 
    122159            for i, dx in enumerate(Dx): 
    123160                start = P11 + P1vecH*dx 
    124161                end = P21 + P2vecH*dx 
    125                 gl.glVertex3f(*start) 
    126                 gl.glVertex3f(*end) 
    127                  
     162                glVertex3f(*start) 
     163                glVertex3f(*end) 
     164 
    128165                start = P11 + P1vecV*dx 
    129166                end = P12 + P2vecV*dx 
    130                 gl.glVertex3f(*start) 
    131                 gl.glVertex3f(*end) 
    132             gl.glEnd() 
    133              
     167                glVertex3f(*start) 
     168                glVertex3f(*end) 
     169            glEnd() 
     170 
    134171        def paintQuad(planeQuad): 
    135172            P11, P12, P21, P22 = numpy.asarray(planeQuad) 
    136             gl.glBegin(gl.GL_QUADS) 
    137             gl.glVertex3f(*P11) 
    138             gl.glVertex3f(*P12) 
    139             gl.glVertex3f(*P21) 
    140             gl.glVertex3f(*P22) 
    141             gl.glEnd() 
    142              
     173            glBegin(GL_QUADS) 
     174            glVertex3f(*P11) 
     175            glVertex3f(*P12) 
     176            glVertex3f(*P21) 
     177            glVertex3f(*P22) 
     178            glEnd() 
     179 
    143180        colorPlane = [0.5, 0.5, 0.5, 0.5] 
    144         colorGrid = [0.0, 0.0, 0.0, 1.0] 
    145          
     181        colorGrid = [0.3, 0.3, 0.3, 1.0] 
     182 
    146183        def paintPlain(planeQuad): 
    147             gl.glColor4f(*colorPlane) 
    148             paintQuad(planeQuad) 
    149             gl.glColor4f(*colorGrid) 
     184            #glColor4f(*colorPlane) 
     185            #paintQuad(planeQuad) 
     186            glColor4f(*colorGrid) 
    150187            paintGrid(planeQuad) 
    151              
    152         def normalize(Vec):             
    153             return Vec / numpy.sqrt(numpy.sum(Vec ** 2)) 
    154              
     188 
    155189        def normalFromPoints(P1, P2, P3): 
    156190            V1 = P2 - P1 
    157191            V2 = P3 - P1 
    158192            return normalize(numpy.cross(V1, V2)) 
    159          
    160         cameraVector = normalize(self.center - self.camera) 
    161         def paintPlainIf(planeQuad, ccw=False): 
     193  
     194        def drawGridVisible(planeQuad, ccw=False): 
    162195            normal = normalFromPoints(*planeQuad[:3]) 
    163             cameraVector = planeQuad[0] - self.camera 
     196            camInSpace = numpy.array([ 
     197              self.center[0] + self.camera[0]*self.zoom, 
     198              self.center[1] + self.camera[1]*self.zoom, 
     199              self.center[2] + self.camera[2]*self.zoom 
     200            ]) 
     201            cameraVector = normalize(planeQuad[0] - camInSpace) 
    164202            cos = numpy.dot(normal, cameraVector) * (-1 if ccw else 1) 
    165203            if cos > 0: 
    166204                paintPlain(planeQuad) 
    167                  
    168         paintPlainIf(self.axisPlaneXY) 
    169         paintPlainIf(self.axisPlaneYZ) 
    170         paintPlainIf(self.axisPlaneXZ) 
    171         paintPlainIf(self.axisPlaneXYBack) 
    172         paintPlainIf(self.axisPlaneYZRight) 
    173         paintPlainIf(self.axisPLaneXZTop) 
    174          
    175         gl.glEnable(gl.GL_CULL_FACE) 
    176          
    177          
     205 
     206        drawGridVisible(self.axisPlaneXY) 
     207        drawGridVisible(self.axisPlaneYZ) 
     208        drawGridVisible(self.axisPlaneXZ) 
     209        drawGridVisible(self.axisPlaneXYBack) 
     210        drawGridVisible(self.axisPlaneYZRight) 
     211        drawGridVisible(self.axisPLaneXZTop) 
     212 
     213        glEnable(GL_CULL_FACE) 
     214 
    178215    def updateAxes(self): 
    179216        x_axis = [[self.minx, self.miny, self.minz], 
     
    186223        self.y_axis = y_axis = numpy.array(y_axis) 
    187224        self.z_axis = z_axis = numpy.array(z_axis) 
    188          
     225 
    189226        self.unit_x = unit_x = numpy.array([self.maxx - self.minx, 0, 0]) 
    190227        self.unit_y = unit_y = numpy.array([0, self.maxy - self.miny, 0]) 
    191228        self.unit_z = unit_z = numpy.array([0, 0, self.maxz - self.minz]) 
    192          
     229  
    193230        A = y_axis[1] 
    194231        B = y_axis[1] + unit_x 
    195232        C = x_axis[1] 
    196233        D = x_axis[0] 
    197          
     234 
    198235        E = A + unit_z 
    199236        F = B + unit_z 
    200237        G = C + unit_z 
    201238        H = D + unit_z 
    202          
     239 
    203240        self.axisPlaneXY = [A, B, C, D] 
    204241        self.axisPlaneYZ = [A, D, H, E] 
    205242        self.axisPlaneXZ = [D, C, G, H] 
    206          
     243 
    207244        self.axisPlaneXYBack = [H, G, F, E] 
    208245        self.axisPlaneYZRight = [B, F, G, C] 
    209246        self.axisPLaneXZTop = [E, F, B, A] 
    210          
    211     def plot3d(self, x, y, z, format="."): 
    212         pass 
    213      
     247 
    214248    def scatter(self, X, Y, Z=0, c="b", s=20, **kwargs): 
    215249        array = [[x, y, z] for x,y,z in zip(X, Y, Z)] 
     
    222256        else: 
    223257            colors = c 
    224              
     258  
    225259        if isinstance(s, int): 
    226260            s = [s for _ in array] 
    227              
     261 
    228262        self.commands.append(("scatter", (array, colors, s))) 
    229263        max, min = numpy.max(array, axis=0), numpy.min(array, axis=0) 
    230         self.b_box = max, min 
     264        self.b_box = [max, min] 
    231265        self.minx, self.miny, self.minz = min 
    232266        self.maxx, self.maxy, self.maxz = max 
    233267        self.center = (min + max) / 2  
     268        self.normalSize = numpy.max(self.center - self.b_box[1]) / 100. 
    234269        self.updateAxes() 
    235270        self.updateGL() 
    236          
    237          
     271  
    238272    def mousePressEvent(self, event): 
    239         if event.button() == Qt.LeftButton: 
    240             self._last_camera = numpy.array(self.camera) 
    241             self._lastMousePos = glu.gluUnProject(event.x(), event.y(), 0) 
    242              
    243          
     273      self.mouse_pos = event.pos() 
     274 
    244275    def mouseMoveEvent(self, event): 
    245         if event.buttons() & Qt.LeftButton: 
    246             x, y, z = glu.gluUnProject(event.x(), event.y(), 0) 
    247              
    248             radius = self.radius() 
    249              
    250             def normalize(P): 
    251                 return P / numpy.sqrt(numpy.sum(P ** 2)) 
    252              
    253             def toSphere(P, r=radius): 
    254                 #Project P to the centered sphere 
    255                 P = P - self.center 
    256                 P = normalize(P) 
    257                 return self.center + (P * radius) 
    258                  
    259             diff = numpy.array([x, y, z]) - numpy.array(self._lastMousePos) 
    260             pos =  diff + self._last_camera 
    261              
    262             camera = numpy.array(self._last_camera) 
    263             center = numpy.array(self.center) 
    264             dist = numpy.sqrt(numpy.sum((camera - center) ** 2)) #distance from the center 
    265             camera = (pos - center) / numpy.sqrt(numpy.sum((pos - center) ** 2)) 
    266             camera = camera * dist 
    267             camera = center + camera 
    268             self.camera[:] = camera 
    269              
    270             self._lastMousePos = x, y, z 
    271             self._last_camera = numpy.array(self.camera)  
    272             self.updateGL() 
    273              
    274              
    275     def radius(self): 
    276         return numpy.max(self.center - self.b_box[1]) * 2   
    277          
    278              
     276      if event.buttons() & Qt.MiddleButton: 
     277        pos = event.pos() 
     278        dx = pos.x() - self.mouse_pos.x() 
     279        dy = pos.y() - self.mouse_pos.y() 
     280        if QApplication.keyboardModifiers() & Qt.ShiftModifier: 
     281          off = numpy.cross(self.center - self.camera, [0,1,0]) * (dx / self.move_factor) 
     282          self.center -= off 
     283        else: 
     284          self.yaw += dx /  self.rotation_factor 
     285          self.pitch += dy / self.rotation_factor 
     286          self.camera = [ 
     287            sin(self.pitch)*cos(self.yaw), 
     288            cos(self.pitch), 
     289            sin(self.pitch)*sin(self.yaw)] 
     290        self.mouse_pos = pos 
     291        self.updateGL() 
     292 
    279293    def wheelEvent(self, event): 
    280         if event.orientation() == Qt.Vertical: 
    281             degrees = event.delta() / 8 
    282             steps = degrees / 15 
    283             cameraToCenter = self.camera - self.center 
    284             radius = numpy.sqrt(numpy.sum((self.center - self.b_box[0]) ** 2)) 
    285             cameraToCenter = cameraToCenter / numpy.sqrt(numpy.sum(cameraToCenter ** 2)) 
    286             diff = cameraToCenter * (radius / 360) * degrees * 8 
    287             self.camera = self.camera + diff 
    288             self.updateGL() 
    289      
     294      if event.orientation() == Qt.Vertical: 
     295        self.zoom += event.delta() / self.zoom_factor 
     296        if self.zoom < 2: 
     297          self.zoom = 2 
     298        self.updateGL() 
     299 
    290300    def clear(self): 
    291301        self.commands = [] 
    292      
    293      
     302 
    294303if __name__ == "__main__": 
    295304    app = QApplication(sys.argv) 
    296305    w = OWGraph3D() 
    297306    w.show() 
    298      
     307  
    299308    from random import random 
    300309    rand = lambda :random() - 0.5 
     
    308317    z = array[:, 2] 
    309318    colors = [palette[int(ex.getclass())] for ex in data] 
    310     colors = [[c.red()/255., c.green()/255., c.blue()/255.] for c in colors] 
     319    colors = [[c.red()/255., c.green()/255., c.blue()/255., 0.8] for c in colors] 
    311320 
    312321#    x = [rand()*2 for i in range(N)] 
     
    314323#    z = [-3 + rand() for i in range(N)] 
    315324#    colors = "b" 
    316     w.scatter3d(x, y, z, c=colors) 
     325    w.scatter(x, y, z, c=colors) 
    317326    app.exec_() 
    318          
  • orange/OrangeWidgets/Prototypes/OWScatterPlot3D.py

    r8277 r8285  
    1 """<name> 3D Scatter Plot</name> 
     1"""<name> 3D Scatterplot</name> 
    22""" 
    33 
     
    1212class OWScatterPlot3D(OWWidget): 
    1313    contextHandlers = {"": DomainContextHandler("", ["xAttr", "yAttr", "zAttr"])} 
    14      
     14  
    1515    def __init__(self, parent=None, signalManager=None, name="Scatter Plot 3D"): 
    1616        OWWidget.__init__(self, parent, signalManager, name) 
    17          
     17 
    1818        self.inputs = [("Examples", ExampleTable, self.setData), ("Subset Examples", ExampleTable, self.setSubsetData)] 
    1919        self.outputs = [] 
    20          
     20 
    2121        self.xAttr = 0 
    2222        self.yAttr = 0 
    2323        self.zAttr = 0 
    24          
     24 
    2525        self.colorAttr = None 
    2626        self.sizeAttr = None 
    2727        self.labelAttr = None 
    28          
     28 
    2929        self.pointSize = 5 
    3030        self.alphaValue = 255 
    31          
    32         # ### 
    33         # GUI 
    34         # ### 
    35          
    36 #        box = OWGUI.widgetBox(self.controlArea, "Axes", addSpace=True) 
     31 
    3732        self.xAttrCB = OWGUI.comboBox(self.controlArea, self, "xAttr", box="X-axis Attribute", 
    38                                       tooltip="Attribute to plot on X axis.", 
    39                                       callback=self.onAxisChange 
    40                                       ) 
    41          
     33            tooltip="Attribute to plot on X axis.", 
     34            callback=self.onAxisChange 
     35            ) 
     36 
    4237        self.yAttrCB = OWGUI.comboBox(self.controlArea, self, "yAttr", box="Y-axis Attribute", 
    43                                       tooltip="Attribute to plot on Y axis.", 
    44                                       callback=self.onAxisChange 
    45                                       ) 
    46          
     38            tooltip="Attribute to plot on Y axis.", 
     39            callback=self.onAxisChange 
     40            ) 
     41 
    4742        self.zAttrCB = OWGUI.comboBox(self.controlArea, self, "zAttr", box="Z-axis Attribute", 
    48                                       tooltip="Attribute to plot on Z axis.", 
    49                                       callback=self.onAxisChange 
    50                                       ) 
    51          
     43            tooltip="Attribute to plot on Z axis.", 
     44            callback=self.onAxisChange 
     45            ) 
     46 
    5247        self.colorAttrCB = OWGUI.comboBox(self.controlArea, self, "colorAttr", box="Point color", 
    53                                           tooltip="Attribute to use for point color", 
    54                                           callback=self.onAxisChange) 
    55          
     48            tooltip="Attribute to use for point color", 
     49            callback=self.onAxisChange) 
     50 
    5651        self.sizeAttrCB = OWGUI.comboBox(self.controlArea, self, "sizeAttr", box="Point Size", 
    57                                          tooltip="Attribute to use for pointSize", 
    58                                          callback=self.onAxisChange, 
    59                                          ) 
     52            tooltip="Attribute to use for pointSize", 
     53            callback=self.onAxisChange, 
     54            ) 
     55 
    6056        OWGUI.hSlider(self.controlArea, self, "pointSize", box="Max. point size", 
    61                       minValue=1, maxValue=10, 
    62                       tooltip="Maximum point size", 
    63                       callback=self.onAxisChange 
    64                       ) 
    65   
    66         self.alphaSlider = OWGUI.hSlider(self.controlArea, self, "alphaValue", box="Transparency", 
    67                                          minValue=10, maxValue=255, 
    68                                          tooltip="Point transparency value", 
    69                                          callback=self.onAxisChange) 
    70          
    71         #TODO: jittering options 
    72          
     57            minValue=1, maxValue=10, 
     58            tooltip="Maximum point size", 
     59            callback=self.onAxisChange 
     60            ) 
     61 
     62        OWGUI.hSlider(self.controlArea, self, "alphaValue", box="Transparency", 
     63            minValue=10, maxValue=255, 
     64            tooltip="Point transparency value", 
     65            callback=self.onAxisChange) 
     66 
     67        # TODO: jittering options 
     68        # TODO: find out what's with the TODO above 
     69        # TODO: add ortho/perspective checkbox (or perhaps not?) 
     70        # TODO: add grid enable/disable options 
     71 
    7372        OWGUI.rubber(self.controlArea) 
    74          
     73 
    7574        self.graph = OWGraph3D(self) 
    7675        self.mainArea.layout().addWidget(self.graph) 
    77          
     76 
    7877        self.data = None 
    7978        self.subsetData = None 
    80          
    8179        self.resize(800, 600) 
    82      
    83      
     80 
    8481    def setData(self, data=None): 
    85         self.closeContext("") 
    86         self.data = data 
    87         self.xAttrCB.clear() 
    88         self.yAttrCB.clear() 
    89         self.zAttrCB.clear() 
    90         self.colorAttrCB.clear() 
    91         self.sizeAttrCB.clear() 
    92         if self.data is not None: 
    93             self.allAttrs = data.domain.variables + data.domain.getmetas().values() 
    94             self.axisCandidateAttrs = [attr for attr in self.allAttrs if attr.varType in [orange.VarTypes.Continuous, orange.VarTypes.Discrete]] 
    95              
    96             self.colorAttrCB.addItem("<None>") 
    97             self.sizeAttrCB.addItem("<None>") 
    98             icons = OWGUI.getAttributeIcons()  
    99             for attr in self.axisCandidateAttrs: 
    100                 self.xAttrCB.addItem(icons[attr.varType], attr.name) 
    101                 self.yAttrCB.addItem(icons[attr.varType], attr.name) 
    102                 self.zAttrCB.addItem(icons[attr.varType], attr.name) 
    103                 self.colorAttrCB.addItem(icons[attr.varType], attr.name) 
    104                 self.sizeAttrCB.addItem(icons[attr.varType], attr.name) 
    105              
    106             array, c, w = self.data.toNumpyMA() 
    107             if len(c): 
    108                 array = numpy.hstack((array, c.reshape(-1,1))) 
    109             self.dataArray = array 
    110              
    111             self.xAttr, self.yAttr, self.zAttr = numpy.min([[0, 1, 2], [len(self.axisCandidateAttrs) - 1]*3], axis=0) 
    112             self.colorAttr = max(len(self.axisCandidateAttrs) - 1, 0) 
    113               
    114             self.openContext("", data) 
    115              
    116          
     82      self.closeContext("") 
     83      self.data = data 
     84      self.xAttrCB.clear() 
     85      self.yAttrCB.clear() 
     86      self.zAttrCB.clear() 
     87      self.colorAttrCB.clear() 
     88      self.sizeAttrCB.clear() 
     89      if self.data is not None: 
     90        self.allAttrs = data.domain.variables + data.domain.getmetas().values() 
     91        self.axisCandidateAttrs = [attr for attr in self.allAttrs if attr.varType in [orange.VarTypes.Continuous, orange.VarTypes.Discrete]] 
     92 
     93        self.colorAttrCB.addItem("<None>") 
     94        self.sizeAttrCB.addItem("<None>") 
     95        icons = OWGUI.getAttributeIcons()  
     96        for attr in self.axisCandidateAttrs: 
     97          self.xAttrCB.addItem(icons[attr.varType], attr.name) 
     98          self.yAttrCB.addItem(icons[attr.varType], attr.name) 
     99          self.zAttrCB.addItem(icons[attr.varType], attr.name) 
     100          self.colorAttrCB.addItem(icons[attr.varType], attr.name) 
     101          self.sizeAttrCB.addItem(icons[attr.varType], attr.name) 
     102 
     103        array, c, w = self.data.toNumpyMA() 
     104        if len(c): 
     105          array = numpy.hstack((array, c.reshape(-1,1))) 
     106        self.dataArray = array 
     107 
     108        self.xAttr, self.yAttr, self.zAttr = numpy.min([[0, 1, 2], [len(self.axisCandidateAttrs) - 1]*3], axis=0) 
     109        self.colorAttr = max(len(self.axisCandidateAttrs) - 1, 0) 
     110 
     111        self.openContext("", data) 
     112 
    117113    def setSubsetData(self, data=None): 
    118         self.subsetData = data 
    119          
    120          
     114      self.subsetData = data 
     115 
    121116    def handleNewSignals(self): 
     117      self.updateGraph() 
     118 
     119    def onAxisChange(self): 
     120      if self.data is not None: 
    122121        self.updateGraph() 
    123          
    124          
    125     def onAxisChange(self): 
    126         if self.data is not None: 
    127             self.updateGraph() 
    128              
     122 
    129123    def updateGraph(self): 
    130         if self.data is None: 
    131             return 
    132          
    133         xInd, yInd, zInd = self.getAxesIndices() 
    134         X, Y, Z, mask = self.getAxisData(xInd, yInd, zInd) 
    135          
    136         if self.colorAttr > 0: 
    137             colorAttr = self.axisCandidateAttrs[self.colorAttr -1] 
    138             C = self.dataArray[:, self.colorAttr - 1] 
    139          
    140             if colorAttr.varType == orange.VarTypes.Discrete: 
    141                 palette = OWColorPalette.ColorPaletteHSV(len(colorAttr.values)) 
    142                 colors = [palette[int(value)] for value in C.ravel()] 
    143                 colors = [[c.red()/255., c.green()/255., c.blue()/255., self.alphaValue/255.] for c in colors] 
    144             else: 
    145                 palette = OWColorPalette.ColorPaletteBW() 
    146                 maxC, minC = numpy.max(C), numpy.min(C) 
    147                 C = (C - minC) / (maxC - minC) 
    148                 colors = [palette[value] for value in C.ravel()] 
    149                 colors = [[c.red()/255., c.green()/255., c.blue()/255., self.alphaValue/255.] for c in colors] 
     124      if self.data is None: 
     125        return 
     126 
     127      xInd, yInd, zInd = self.getAxesIndices() 
     128      X, Y, Z, mask = self.getAxisData(xInd, yInd, zInd) 
     129 
     130      if self.colorAttr > 0: 
     131        colorAttr = self.axisCandidateAttrs[self.colorAttr - 1] 
     132        C = self.dataArray[:, self.colorAttr - 1] 
     133        if colorAttr.varType == orange.VarTypes.Discrete: 
     134          palette = OWColorPalette.ColorPaletteHSV(len(colorAttr.values)) 
     135          colors = [palette[int(value)] for value in C.ravel()] 
     136          colors = [[c.red()/255., c.green()/255., c.blue()/255., self.alphaValue/255.] for c in colors] 
    150137        else: 
    151             colors = "b" 
    152              
    153         if self.sizeAttr > 0: 
    154             sizeAttr = self.axisCandidateAttrs[self.sizeAttr - 1] 
    155             S = self.dataArray[:, self.sizeAttr - 1] 
    156             if sizeAttr.varType == orange.VarTypes.Discrete: 
    157                 sizes = [(v + 1) * len(sizeAttr.values) / (11 - self.pointSize) for v in S] 
    158             else: 
    159                 min, max = numpy.min(S), numpy.max(S) 
    160                 sizes = [(v - min) * self.pointSize / (max-min) for v in S] 
     138          palette = OWColorPalette.ColorPaletteBW() 
     139          maxC, minC = numpy.max(C), numpy.min(C) 
     140          C = (C - minC) / (maxC - minC) 
     141          colors = [palette[value] for value in C.ravel()] 
     142          colors = [[c.red()/255., c.green()/255., c.blue()/255., self.alphaValue/255.] for c in colors] 
     143      else: 
     144        colors = "b" 
     145 
     146      if self.sizeAttr > 0: 
     147        sizeAttr = self.axisCandidateAttrs[self.sizeAttr - 1] 
     148        S = self.dataArray[:, self.sizeAttr - 1] 
     149        if sizeAttr.varType == orange.VarTypes.Discrete: 
     150          sizes = [(v + 1) * len(sizeAttr.values) / (11 - self.pointSize) for v in S] 
    161151        else: 
    162             sizes = 1 
    163          
    164         self.graph.clear() 
    165         self.graph.scatter(X, Y, Z, colors, sizes) 
    166          
    167          
     152          min, max = numpy.min(S), numpy.max(S) 
     153          sizes = [(v - min) * self.pointSize / (max-min) for v in S] 
     154      else: 
     155        sizes = 1 
     156 
     157      self.graph.clear() 
     158      self.graph.scatter(X, Y, Z, colors, sizes) 
     159      self.graph.setXaxisTitle(self.axisCandidateAttrs[self.xAttr].name) 
     160      self.graph.setYaxisTitle(self.axisCandidateAttrs[self.yAttr].name) 
     161      self.graph.setZaxisTitle(self.axisCandidateAttrs[self.zAttr].name) 
     162 
    168163    def getAxisData(self, xInd, yInd, zInd): 
    169         array = self.dataArray 
    170         X, Y, Z = array[:, xInd], array[:, yInd], array[:, zInd] 
    171         return X, Y, Z, None 
    172      
     164      array = self.dataArray 
     165      X, Y, Z = array[:, xInd], array[:, yInd], array[:, zInd] 
     166      return X, Y, Z, None 
     167 
    173168    def getAxesIndices(self): 
    174         return self.xAttr, self.yAttr, self.zAttr 
    175          
    176          
     169      return self.xAttr, self.yAttr, self.zAttr 
     170 
    177171if __name__ == "__main__": 
    178     app = QApplication(sys.argv) 
    179     w = OWScatterPlot3D() 
    180     data = orange.ExampleTable("../../doc/datasets/iris") 
    181     w.setData(data) 
    182     w.handleNewSignals() 
    183     w.show() 
    184     app.exec_() 
    185          
    186          
    187          
    188              
    189      
    190          
    191          
    192          
     172  app = QApplication(sys.argv) 
     173  w = OWScatterPlot3D() 
     174  data = orange.ExampleTable("../../doc/datasets/iris") 
     175  w.setData(data) 
     176  w.handleNewSignals() 
     177  w.show() 
     178  app.exec_() 
Note: See TracChangeset for help on using the changeset viewer.