~ubuntu-branches/ubuntu/utopic/python-chaco/utopic

« back to all changes in this revision

Viewing changes to enthought/chaco/grid_mapper.py

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2011-07-08 20:38:02 UTC
  • mfrom: (7.2.3 sid)
  • Revision ID: james.westby@ubuntu.com-20110708203802-5t32e0ldv441yh90
Tags: 4.0.0-1
* New upstream release
* debian/control:
  - Depend on python-traitsui (Closes: #633604)
  - Bump Standards-Version to 3.9.2
* Update debian/watch file
* Remove debian/patches/* -- no longer needed

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""
2
 
Defines the GridMapper class, which maps from a 2-D region in data space
3
 
into a structured (gridded) 1-D output space.
4
 
"""
5
 
 
6
 
# Major library imports
7
 
from numpy import transpose
8
 
 
9
 
# Enthought library imports
10
 
from enthought.traits.api import Bool, DelegatesTo, Instance, Float, Property
11
 
 
12
 
# Local relative imports
13
 
from abstract_mapper import AbstractMapper
14
 
from base_1d_mapper import Base1DMapper
15
 
from data_range_2d import DataRange2D
16
 
from linear_mapper import LinearMapper
17
 
from log_mapper import LogMapper
18
 
 
19
 
 
20
 
class GridMapper(AbstractMapper):
21
 
    """
22
 
    Maps a 2-D data space to and from screen space by specifying a 2-tuple in
23
 
    data space or by specifying a pair of screen coordinates.
24
 
    
25
 
    The mapper concerns itself only with metric and not with orientation. So, to
26
 
    "flip" a screen space orientation, swap the appropriate screen space
27
 
    values for **x_low_pos**, **x_high_pos**, **y_low_pos**, and **y_high_pos**.
28
 
    """
29
 
 
30
 
    # The data-space bounds of the mapper. 
31
 
    range = Instance(DataRange2D)
32
 
   
33
 
    # The screen space position of the lower bound of the horizontal axis.
34
 
    x_low_pos = Float(0.0)
35
 
    
36
 
    # The screen space position of the upper bound of the horizontal axis. 
37
 
    x_high_pos  = Float(1.0)
38
 
   
39
 
    # The screen space position of the lower bound of the vertical axis. 
40
 
    y_low_pos = Float(0.0)
41
 
    
42
 
    # The screen space position of the upper bound of the vertical axis. 
43
 
    y_high_pos  = Float(1.0)
44
 
    
45
 
    # Convenience property for low and high positions in one structure.
46
 
    # Must be a tuple (x_low_pos, x_high_pos, y_low_pos, y_high_pos).
47
 
    screen_bounds = Property
48
 
 
49
 
    # Should the mapper stretch the dataspace when its screen space bounds are
50
 
    # modified (default), or should it preserve the screen-to-data ratio and
51
 
    # resize the data bounds?  If the latter, it will only try to preserve
52
 
    # the ratio if both screen and data space extents are non-zero.
53
 
    stretch_data_x = DelegatesTo("_xmapper", prefix="stretch_data")
54
 
    stretch_data_y = DelegatesTo("_ymapper", prefix="stretch_data")
55
 
 
56
 
    #------------------------------------------------------------------------
57
 
    # Private Traits
58
 
    #------------------------------------------------------------------------
59
 
 
60
 
    _updating_submappers = Bool(False)
61
 
    
62
 
    _xmapper = Instance(Base1DMapper)
63
 
    _ymapper = Instance(Base1DMapper)
64
 
    
65
 
 
66
 
    #------------------------------------------------------------------------
67
 
    # Public methods
68
 
    #------------------------------------------------------------------------
69
 
 
70
 
    def __init__(self, x_type="linear", y_type="linear", range=None, **kwargs):
71
 
        # TODO: This is currently an implicit assumption, i.e. that the range
72
 
        # will be passed in to the constructor.  It would be impossible to 
73
 
        # create the xmapper and ymapper otherwise.  However, this should be
74
 
        # changed so that the mappers get created or modified in response to
75
 
        # the .range attribute changing, instead of requiring the range to
76
 
        # be passed in at construction time.
77
 
        self.range = range
78
 
 
79
 
        if "_xmapper" not in kwargs:
80
 
            if x_type == "linear":
81
 
                self._xmapper = LinearMapper(range=self.range.x_range)
82
 
            elif x_type == "log":
83
 
                self._xmapper = LogMapper(range=self.range.x_range)
84
 
            else:
85
 
                raise ValueError("Invalid x axis type: %s" % x_type)
86
 
        else:
87
 
            self._xmapper = kwargs.pop("_xmapper")
88
 
 
89
 
        if "_ymapper" not in kwargs:
90
 
            if y_type == "linear":
91
 
                self._ymapper = LinearMapper(range=self.range.y_range)
92
 
            elif y_type == "log":
93
 
                self._ymapper = LogMapper(range=self.range.y_range)
94
 
            else:
95
 
                raise ValueError("Invalid y axis type: %s" % y_type)
96
 
        else:
97
 
            self._ymapper = kwargs.pop("_ymapper")
98
 
 
99
 
        # Now that the mappers are created, we can go to the normal HasTraits
100
 
        # constructor, which might set values that depend on us having a valid
101
 
        # range and mappers.
102
 
        super(GridMapper, self).__init__(**kwargs)
103
 
        
104
 
 
105
 
    def map_screen(self, data_pts):
106
 
        """ map_screen(data_pts) -> screen_array
107
 
 
108
 
        Maps values from data space into screen space.
109
 
        """
110
 
        xs, ys = transpose(data_pts)
111
 
        screen_xs = self._xmapper.map_screen(xs)
112
 
        screen_ys = self._ymapper.map_screen(ys)
113
 
        return zip(screen_xs,screen_ys)
114
 
        
115
 
    def map_data(self, screen_pts):
116
 
        """ map_data(screen_pts) -> data_vals
117
 
 
118
 
        Maps values from screen space into data space. 
119
 
        """
120
 
        screen_xs, screen_ys = transpose(screen_pts)
121
 
        xs = self._xmapper.map_data(screen_xs)
122
 
        ys = self._ymapper.map_data(screen_ys)
123
 
        return zip(xs,ys)
124
 
 
125
 
    def map_data_array(self, screen_pts):
126
 
        return self.map_data(screen_pts)
127
 
 
128
 
 
129
 
    #------------------------------------------------------------------------
130
 
    # Private Methods
131
 
    #------------------------------------------------------------------------
132
 
 
133
 
    def _update_bounds(self):
134
 
        self._updating_submappers = True
135
 
        self._xmapper.screen_bounds = (self.x_low_pos, self.x_high_pos)
136
 
        self._ymapper.screen_bounds = (self.y_low_pos, self.y_high_pos)
137
 
        self._updating_submappers = False
138
 
        self.updated = True
139
 
 
140
 
    def _update_range(self):
141
 
        self.updated = True
142
 
 
143
 
 
144
 
    #------------------------------------------------------------------------
145
 
    # Property handlers
146
 
    #------------------------------------------------------------------------
147
 
 
148
 
    def _range_changed(self, old, new):
149
 
        if old is not None:
150
 
            old.on_trait_change(self._update_range, "updated", remove=True)
151
 
        if new is not None:
152
 
            new.on_trait_change(self._update_range, "updated")
153
 
            if self._xmapper is not None:
154
 
                self._xmapper.range = new.x_range
155
 
            if self._ymapper is not None:
156
 
                self._ymapper.range = new.y_range
157
 
            self._update_range()
158
 
 
159
 
    def _x_low_pos_changed(self):
160
 
        self._xmapper.low_pos = self.x_low_pos
161
 
 
162
 
    def _x_high_pos_changed(self):
163
 
        self._xmapper.high_pos = self.x_high_pos
164
 
 
165
 
    def _y_low_pos_changed(self):
166
 
        self._ymapper.low_pos = self.y_low_pos
167
 
 
168
 
    def _y_high_pos_changed(self):
169
 
        self._ymapper.high_pos = self.y_high_pos
170
 
 
171
 
    def _set_screen_bounds(self, new_bounds):
172
 
        # TODO: figure out a way to not need to do this check:
173
 
        if self.screen_bounds == new_bounds:
174
 
            return
175
 
        self.set(x_low_pos = new_bounds[0], trait_change_notify=False)
176
 
        self.set(x_high_pos = new_bounds[1], trait_change_notify=False)
177
 
        self.set(y_low_pos = new_bounds[2], trait_change_notify=False)
178
 
        self.set(y_high_pos = new_bounds[3], trait_change_notify=False)
179
 
        self._update_bounds( )
180
 
 
181
 
    def _get_screen_bounds(self):
182
 
        return (self.x_low_pos, self.x_high_pos, 
183
 
                self.y_low_pos, self.y_high_pos)
184
 
 
185
 
    def _updated_fired_for__xmapper(self):
186
 
        if not self._updating_submappers:
187
 
            self.updated = True
188
 
    
189
 
    def _updated_fired_for__ymapper(self):
190
 
        if not self._updating_submappers:
191
 
            self.updated = True
192
 
 
193