Changeset 8454:3f954e9c61e8 in orange


Ignore:
Timestamp:
07/25/11 19:39:57 (3 years ago)
Author:
matejd <matejd@…>
Branch:
default
Convert:
55c9d443bd43a53682c5f82c06007ff039fe8ac1
Message:

owplot3d: zooming

Location:
orange/OrangeWidgets
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • orange/OrangeWidgets/Prototypes/OWScatterPlot3D.py

    r8445 r8454  
    216216        if self.plot.selection_type == SelectionType.ZOOM: 
    217217            indices = self.plot.get_selection_indices() 
    218             if len(indices) == 0: 
     218            if len(indices) < 1: 
     219                self.plot.selections = [] 
    219220                return 
    220221            X, Y, Z = self.data_array[:, self.x_attr],\ 
  • orange/OrangeWidgets/plot/owplot3d.py

    r8445 r8454  
    6161import sys 
    6262from math import sin, cos, pi 
     63import time 
    6364import struct 
    6465import numpy 
     
    361362        self.scale_x_axis = True 
    362363        self.scale_factor = 100. 
    363         self.initial_scale = numpy.array([1., 1., 1.]) 
    364         self.initial_center = numpy.array([0, 0, 0]) 
     364        self.data_scale = numpy.array([1., 1., 1.]) 
     365        self.data_center = numpy.array([0, 0, 0]) 
    365366 
    366367        # Beside n-gons, symbols should also include cubes, spheres and other stuff. TODO 
     
    381382        self.y_axis_map = None 
    382383        self.z_axis_map = None 
     384 
     385        self.zoom_stack = [] 
     386        self.translation = numpy.array([0, 0, 0]) 
    383387 
    384388    def __del__(self): 
     
    439443 
    440444              vec3 pos = position.xyz; 
    441               //pos += translation; 
     445              pos += translation; 
    442446              pos *= scale; 
    443447              vec4 off_pos = vec4(pos+offset_rotated, 1); 
     
    547551                self.symbol_shader.setUniformValue(self.symbol_shader_transparency, self.transparency / 255.) 
    548552                self.symbol_shader.setUniformValue(self.symbol_shader_scale,        *scale) 
     553                self.symbol_shader.setUniformValue(self.symbol_shader_translation,  *self.translation) 
    549554 
    550555                if self.filled_symbols: 
     
    572577 
    573578                if labels != None: 
     579                    glColor4f(0, 0, 0, 1) 
    574580                    for x, y, z, label in zip(X, Y, Z, labels): 
    575581                        x, y, z = self.transform_data_to_plot((x, y, z)) 
     
    669675        def draw_discrete_axis_values(axis, coord_index, normal, axis_map): 
    670676            start, end = axis 
    671             scale = (max(0, self.scale[coord_index] + self.additional_scale[coord_index])) 
    672             scale *= self.initial_scale[coord_index] 
    673             start_value = (start[coord_index] / scale) + self.initial_center[coord_index] 
    674             end_value =   (end[coord_index] / scale)   + self.initial_center[coord_index] 
     677            start_value = self.transform_plot_to_data(numpy.copy(start))[coord_index] 
     678            end_value = self.transform_plot_to_data(numpy.copy(end))[coord_index] 
    675679            length = end_value - start_value 
    676680            offset = normal*0.8 
     
    700704                glVertex3f(*(position+normal*0.2)) 
    701705                glEnd() 
    702                 value = position[coord_index] /\ 
    703                     (max(0, self.scale[coord_index] + self.additional_scale[coord_index])) 
    704                 value /= self.initial_scale[coord_index] 
    705                 value += self.initial_center[coord_index] 
     706                value = self.transform_plot_to_data(numpy.copy(position))[coord_index] 
    706707                position += offset 
    707708                self.renderText(position[0], 
     
    802803                             self.y_axis+self.unit_z, 
    803804                             self.y_axis] 
    804         normals = [numpy.array([1,0,0]), 
    805                    numpy.array([0,0,1]), 
    806                    numpy.array([-1,0,0]), 
    807                    numpy.array([0,0,-1])] 
     805        normals = [numpy.array([1, 0, 0]), 
     806                   numpy.array([0, 0, 1]), 
     807                   numpy.array([-1,0, 0]), 
     808                   numpy.array([0, 0,-1])] 
    808809        axis = y_axis_translated[rightmost_visible] 
    809810        normal = normals[rightmost_visible] 
     
    870871            symbols = [Symbol.TRIANGLE for _ in range(num_points)] 
    871872 
     873        # We scale and translate data into almost-unit cube centered around (0,0,0) in plot-space. 
     874        # It's almost-unit because the length of its edge is specified with view_cube_edge. 
     875        # This transform is done to ease later calculations and for presentation purposes. 
    872876        min = self.min_x, self.min_y, self.min_z = numpy.min(X), numpy.min(Y), numpy.min(Z) 
    873877        max = self.max_x, self.max_y, self.max_z = numpy.max(X), numpy.max(Y), numpy.max(Z) 
     
    875879        max = numpy.array(max) 
    876880        range_x, range_y, range_z = max-min 
    877         middle_x, middle_y, middle_z = (min+max) / 2. 
    878         self.initial_center = (min + max) / 2  
     881        self.data_center = (min + max) / 2  
     882        self.zoom_stack.append((min, max)) 
    879883 
    880884        scale_x = self.view_cube_edge / range_x 
     
    882886        scale_z = self.view_cube_edge / range_z 
    883887 
    884         self.initial_scale = [scale_x, scale_y, scale_z] 
     888        self.data_scale = numpy.array([scale_x, scale_y, scale_z]) 
     889 
     890        def generate_vertices(symbol): 
     891            # TODO 
     892            pass 
    885893 
    886894        # Generate vertices for shapes and also indices for outlines. 
     
    890898        ai = -1 # Array index (used in color-picking). 
    891899        for x, y, z, (r,g,b,a), size, symbol in zip(X, Y, Z, colors, sizes, symbols): 
    892             x -= self.initial_center[0] 
    893             y -= self.initial_center[1] 
    894             z -= self.initial_center[2] 
     900            x -= self.data_center[0] 
     901            y -= self.data_center[1] 
     902            z -= self.data_center[2] 
    895903            x *= scale_x 
    896904            y *= scale_y 
     
    975983 
    976984    def set_new_zoom(self, x_min, x_max, y_min, y_max, z_min, z_max): 
    977         return 
    978         # TODO 
    979         self.initial_center = [(x_max + x_min) / 2., 
    980                                (y_max + y_min) / 2., 
    981                                (z_max + z_min) / 2.] 
    982         self.initial_center = numpy.array(self.initial_center) 
     985        max = numpy.array([x_max, y_max, z_max]) 
     986        min = numpy.array([x_min, y_min, z_min]) 
    983987        self.selections = [] 
    984         self.updateGL() 
     988        self.zoom_stack.append((min, max)) 
     989        min, max = map(numpy.copy, [min, max]) 
     990        min -= self.data_center 
     991        min *= self.data_scale 
     992        max -= self.data_center 
     993        max *= self.data_scale 
     994        center = (max + min) / 2. 
     995        num_steps = 10 
     996        new_translation = -numpy.array(center) 
     997        # Avoid division by zero by adding a small value (this happens when zooming in 
     998        # on elements with the same value of an attribute). 
     999        size = numpy.array(map(lambda i: i+0.001 if i == 0 else i, max-min)) 
     1000        new_scale = self.view_cube_edge / size 
     1001        translation_step = (new_translation - self.translation) / float(num_steps) 
     1002        scale_step = (new_scale - self.scale) / float(num_steps) 
     1003        # Animate zooming: translate first for a number of steps, 
     1004        # then scale. Make sure it doesn't take too long. 
     1005        start = time.time() 
     1006        for i in range(num_steps): 
     1007            if time.time() - start > 1.: 
     1008                self.translation = new_translation 
     1009                break 
     1010            self.translation = self.translation + translation_step 
     1011            self.updateGL() 
     1012        start = time.time() 
     1013        for i in range(num_steps): 
     1014            if time.time() - start > 1.: 
     1015                self.scale = new_scale 
     1016                break 
     1017            self.scale = self.scale + scale_step 
     1018            self.updateGL() 
     1019 
     1020    def pop_zoom(self): 
     1021        if len(self.zoom_stack) < 2: 
     1022            return 
     1023 
     1024        self.zoom_stack.pop() 
     1025        min, max = self.zoom_stack.pop() 
     1026        self.set_new_zoom(min[0], max[0], min[1], max[1], min[2], max[2]) 
    9851027 
    9861028    def save_to_file(self): 
     
    9951037 
    9961038    def transform_data_to_plot(self, vertex): 
    997         vertex -= self.initial_center 
    998         vertex *= self.initial_scale 
     1039        vertex -= self.data_center 
     1040        vertex *= self.data_scale 
     1041        vertex += self.translation 
    9991042        vertex *= numpy.maximum([0, 0, 0], self.scale + self.additional_scale) 
     1043        return vertex 
     1044 
     1045    def transform_plot_to_data(self, vertex): 
     1046        vertex /= numpy.maximum([0, 0, 0], self.scale + self.additional_scale) 
     1047        vertex -= self.translation 
     1048        vertex /= self.data_scale 
     1049        vertex += self.data_center 
    10001050        return vertex 
    10011051 
     
    10211071                         QVector3D(0, 0, 0), 
    10221072                         QVector3D(0, 1, 0)) 
    1023  
    1024         modelview.scale(*(numpy.maximum([0, 0, 0], self.scale + self.additional_scale))) 
    10251073 
    10261074        proj_model = projection * modelview 
     
    10741122                    self.new_selection = RectangleSelection([pos.x(), pos.y()]) 
    10751123        elif buttons & Qt.RightButton: 
    1076             self.selections = [] 
    1077             self.new_selection = None 
    1078             self.state = PlotState.SCALING 
    1079             self.scaling_init_pos = self.mouse_pos 
    1080             self.additional_scale = [0, 0, 0] 
     1124            if QApplication.keyboardModifiers() & Qt.ShiftModifier: 
     1125                self.selections = [] 
     1126                self.new_selection = None 
     1127                self.state = PlotState.SCALING 
     1128                self.scaling_init_pos = self.mouse_pos 
     1129                self.additional_scale = [0, 0, 0] 
     1130            else: 
     1131                self.pop_zoom() 
    10811132            self.updateGL() 
    10821133        elif buttons & Qt.MiddleButton: 
     
    11251176                #off_y = numpy.cross(self.camera, [1,0,0]) * (dy / self.move_factor) 
    11261177                # TODO: this incidentally works almost fine, but the math is wrong and should be fixed 
    1127                 #self.initial_center += off_x 
     1178                #self.data_center += off_x 
    11281179            else: 
    11291180                self.yaw += dx / self.rotation_factor 
     
    12011252        self.selections = [] 
    12021253        self.legend.clear() 
     1254        self.zoom_stack = [] 
    12031255        self.x_axis_title = self.y_axis_title = self.z_axis_title = '' 
    12041256        self.x_axis_map = self.y_axis_map = self.z_axis_map = None 
Note: See TracChangeset for help on using the changeset viewer.