Exemplo n.º 1
0
    def map_index(self, screen_pt, threshold=2.0, outside_returns_none=True, \
                  index_only = True):
        """ Maps a screen space point to an index into the plot's index array(s).
        """
        screen_points = self._cached_screen_pts

        if len(screen_points) == 0:
            return None

        data_pt = self.map_data(screen_pt)
        if ((data_pt < self.mapper.range.low) or \
            (data_pt > self.mapper.range.high)) and outside_returns_none:
            return None

        if self._cached_data_pts_sorted is None:
            self._cached_data_argsort = np.argsort(self._cached_data_pts)
            self._cached_data_pts_sorted = self._cached_data_pts[self._cached_data_argsort]

        data = self._cached_data_pts_sorted
        try:
            ndx = reverse_map_1d(data, data_pt, "ascending")
        except IndexError, e:
            if outside_returns_none:
                return None
            else:
                if data_pt < data[0]:
                    return 0
                else:
                    return len(data) - 1
Exemplo n.º 2
0
    def map_index(self, screen_pt, threshold=2.0, outside_returns_none=True,
                  index_only=False):
        """ Maps a screen space point to an index into the plot's index array(s).

        Implements the AbstractPlotRenderer interface.
        """
        data_pt = self.map_data(screen_pt)
        if ((data_pt < self.index_mapper.range.low) or \
            (data_pt > self.index_mapper.range.high)) and outside_returns_none:
            return None
        index_data = self.index.get_data()
        value_data = self.value.get_data()

        if len(value_data) == 0 or len(index_data) == 0:
            return None

        try:
            ndx = reverse_map_1d(index_data, data_pt, self.index.sort_order)
        except IndexError:
            return None

        x = index_data[ndx]
        y = value_data[ndx]

        result = self.map_screen(array([[x,y]]))
        if result is None:
            return None

        sx, sy = result[0]
        if index_only and ((screen_pt[0]-sx) < threshold):
            return ndx
        elif ((screen_pt[0]-sx)**2 + (screen_pt[1]-sy)**2 < threshold*threshold):
            return ndx
        else:
            return None
Exemplo n.º 3
0
    def reverse_map(self, pt, index=0, outside_returns_none=True):
        """Returns the index of *pt* in the data source.

        Parameters
        ----------
        pt : scalar value
            value to find
        index
            ignored for data series with 1-D indices
        outside_returns_none : Boolean
            Whether the method returns None if *pt* is outside the range of
            the data source; if False, the method returns the value of the
            bound that *pt* is outside of.
        """
        if self.sort_order == "none":
            raise NotImplementedError

        # index is ignored for dataseries with 1-dimensional indices
        minval, maxval = self._cached_bounds
        if (pt < minval):
            if outside_returns_none:
                return None
            else:
                return self._min_index
        elif (pt > maxval):
            if outside_returns_none:
                return None
            else:
                return self._max_index
        else:
            return reverse_map_1d(self._data, pt, self.sort_order)
Exemplo n.º 4
0
    def map_index(self, screen_pt, threshold=2.0, outside_returns_none=True, \
                  index_only=False):
        """ Maps a screen space point to an index into the plot's index array(s).

        Implements the AbstractPlotRenderer interface.
        """

        data_pt = self.map_data(screen_pt)
        if ((data_pt < self.index_mapper.range.low) or \
            (data_pt > self.index_mapper.range.high)) and outside_returns_none:
            return None
        index_data = self.index.get_data()
        value_data = self.value.get_data()

        if len(value_data) == 0 or len(index_data) == 0:
            return None

        try:
            ndx = reverse_map_1d(index_data, data_pt, self.index.sort_order)
        except IndexError, e:
            # if reverse_map raises this exception, it means that data_pt is
            # outside the range of values in index_data.
            if outside_returns_none:
                return None
            else:
                if data_pt < index_data[0]:
                    return 0
                else:
                    return len(index_data) - 1
Exemplo n.º 5
0
    def hittest(self, screen_pt, threshold=7.0):
        """
        Tests whether the given screen point is within *threshold* pixels of
        any data points on the line.  If so, then it returns the (x,y) value of
        a data point near the screen point.  If not, then it returns None.

        Note: This only checks data points and *not* the actual line segments
        connecting them.
        """
        ndx = self.map_index(screen_pt, threshold)
        if ndx is not None:
            return (self.index.get_data()[ndx], self.value.get_data()[ndx])
        else:
            data_x = self.map_data(screen_pt)
            xmin, xmax = self.index.get_bounds()
            if xmin <= data_x <= xmax:
                if self.orientation == "h":
                    sy = screen_pt[1]
                else:
                    sy = screen_pt[0]

                interp_val = self.interpolate(data_x)
                interp_y = self.value_mapper.map_screen(interp_val)

                if abs(sy - interp_y) <= threshold:
                    return reverse_map_1d(self.index.get_data(), data_x,
                                          self.index.sort_order)
            return None
Exemplo n.º 6
0
    def hittest(self, screen_pt, threshold=7.0):
        """
        Tests whether the given screen point is within *threshold* pixels of
        any data points on the line.  If so, then it returns the (x,y) value of
        a data point near the screen point.  If not, then it returns None.

        Note: This only checks data points and *not* the actual line segments
        connecting them.
        """
        ndx = self.map_index(screen_pt, threshold)
        if ndx is not None:
            return (self.index.get_data()[ndx], self.value.get_data()[ndx])
        else:
            data_x = self.map_data(screen_pt)
            xmin, xmax = self.index.get_bounds()
            if xmin <= data_x <= xmax:
                if self.orientation == "h":
                    sy = screen_pt[1]
                else:
                    sy = screen_pt[0]

                interp_val = self.interpolate(data_x)
                interp_y = self.value_mapper.map_screen(interp_val)

                if abs(sy - interp_y) <= threshold:
                    return reverse_map_1d(self.index.get_data(), data_x,
                                          self.index.sort_order)
            return None
Exemplo n.º 7
0
    def map_index(self, screen_pt, threshold=0.0, outside_returns_none=True, \
                  index_only = False):
        """ Maps a screen space point to an index into the plot's index array(s).

        Overrides the BaseXYPlot implementation..
        """
        index_data = self.index.get_data()
        value_data = self.value.get_data()

        if len(value_data) == 0 or len(index_data) == 0:
            return None

        if index_only and self.index.sort_order != "none":
            data_pt = self.map_data(screen_pt)[0]
            # The rest of this was copied out of BaseXYPlot.
            # We can't just used BaseXYPlot.map_index because
            # it expect map_data to return a value, not a pair.
            if ((data_pt < self.index_mapper.range.low) or \
                (data_pt > self.index_mapper.range.high)) and outside_returns_none:
                return None

            try:
                ndx = reverse_map_1d(index_data, data_pt,
                                     self.index.sort_order)
            except IndexError, e:
                # if reverse_map raises this exception, it means that data_pt is
                # outside the range of values in index_data.
                if outside_returns_none:
                    return None
                else:
                    if data_pt < index_data[0]:
                        return 0
                    else:
                        return len(index_data) - 1

            if threshold == 0.0:
                # Don't do any threshold testing
                return ndx

            x = index_data[ndx]
            y = value_data[ndx]
            if isnan(x) or isnan(y):
                return None
            sx, sy = self.map_screen([x, y])
            if ((threshold == 0.0) or (screen_pt[0] - sx) < threshold):
                return ndx
            else:
                return None
Exemplo n.º 8
0
    def map_index(self, screen_pt, threshold=0.0, outside_returns_none=True, \
                  index_only = False):
        """ Maps a screen space point to an index into the plot's index array(s).

        Overrides the BaseXYPlot implementation..
        """
        index_data = self.index.get_data()
        value_data = self.value.get_data()

        if len(value_data) == 0 or len(index_data) == 0:
            return None

        if index_only and self.index.sort_order != "none":
            data_pt = self.map_data(screen_pt)[0]
            # The rest of this was copied out of BaseXYPlot.
            # We can't just used BaseXYPlot.map_index because
            # it expect map_data to return a value, not a pair.
            if ((data_pt < self.index_mapper.range.low) or \
                (data_pt > self.index_mapper.range.high)) and outside_returns_none:
                return None

            try:
                ndx = reverse_map_1d(index_data, data_pt, self.index.sort_order)
            except IndexError, e:
                # if reverse_map raises this exception, it means that data_pt is
                # outside the range of values in index_data.
                if outside_returns_none:
                    return None
                else:
                    if data_pt < index_data[0]:
                        return 0
                    else:
                        return len(index_data) - 1

            if threshold == 0.0:
                # Don't do any threshold testing
                return ndx

            x = index_data[ndx]
            y = value_data[ndx]
            if isnan(x) or isnan(y):
                return None
            sx, sy = self.map_screen([x,y])
            if ((threshold == 0.0) or (screen_pt[0]-sx) < threshold):
                return ndx
            else:
                return None
Exemplo n.º 9
0
    def reverse_map(self, pt, index=0, outside_returns_none=True):
        """Returns the index of *pt* in the data source.

        Overrides ArrayDataSource.

        Parameters
        ----------
        pt : (x, y)
            value to find
        index : 0 or 1
            Which of the axes of *pt* the *sort_order* refers to.
        outside_returns_none : Boolean
            Whether the method returns None if *pt* is outside the range of
            the data source; if False, the method returns the value of the
            bound that *pt* is outside of, in the *index* dimension.

        """
        # reverse_map is of limited utility for a PointDataSeries and thus
        # we only perform reverse-mapping if the data is sorted along an axis.

        if self.sort_order == "none":
            # By default, only provide reverse_map if the value data is sorted
            # along one of its axes.
            raise NotImplementedError

        if index != 0 and index != 1:
            raise ValueError, "Index must be 0 or 1."

        # This basically reduces to a scalar data search along self.data[index].
        lowerleft, upperright = self._cached_bounds
        min_val = lowerleft[index]
        max_val = upperright[index]
        val = pt[index]
        if (val < min_val):
            if outside_returns_none:
                return None
            else:
                return self._min_index
        elif (val > max_val):
            if outside_returns_none:
                return None
            else:
                return self._max_index
        else:
            return reverse_map_1d(self._data[:, index], val, self.sort_order)
Exemplo n.º 10
0
    def reverse_map(self, pt, index=0, outside_returns_none=True):
        """Returns the index of *pt* in the data source.

        Overrides ArrayDataSource.

        Parameters
        ----------
        pt : (x, y)
            value to find
        index : 0 or 1
            Which of the axes of *pt* the *sort_order* refers to.
        outside_returns_none : Boolean
            Whether the method returns None if *pt* is outside the range of
            the data source; if False, the method returns the value of the
            bound that *pt* is outside of, in the *index* dimension.

        """
        # reverse_map is of limited utility for a PointDataSeries and thus
        # we only perform reverse-mapping if the data is sorted along an axis.

        if self.sort_order == "none":
            # By default, only provide reverse_map if the value data is sorted
            # along one of its axes.
            raise NotImplementedError

        if index != 0 and index != 1:
            raise ValueError, "Index must be 0 or 1."

        # This basically reduces to a scalar data search along self.data[index].
        lowerleft, upperright= self._cached_bounds
        min_val = lowerleft[index]
        max_val = upperright[index]
        val = pt[index]
        if (val < min_val):
            if outside_returns_none:
                return None
            else:
                return self._min_index
        elif (val > max_val):
            if outside_returns_none:
                return None
            else:
                return self._max_index
        else:
            return reverse_map_1d(self._data[:,index], val, self.sort_order)
Exemplo n.º 11
0
    def interpolate(self, index_value):
        """
        Returns the value of the plot at the given index value in screen space.
        Raises an IndexError when *index_value* exceeds the bounds of indexes on
        the value.
        """

        if self.index is None or self.value is None:
            raise IndexError, "cannot index when data source index or value is None"

        index_data = self.index.get_data()
        value_data = self.value.get_data()

        ndx = reverse_map_1d(index_data, index_value, self.index.sort_order)

        # quick test to see if this value is already in the index array
        if index_value == index_data[ndx]:
            return value_data[ndx]

        # get x and y values to interpolate between
        if index_value < index_data[ndx]:
            x0 = index_data[ndx - 1]
            y0 = value_data[ndx - 1]
            x1 = index_data[ndx]
            y1 = value_data[ndx]
        else:
            x0 = index_data[ndx]
            y0 = value_data[ndx]
            x1 = index_data[ndx + 1]
            y1 = value_data[ndx + 1]

        if x1 != x0:
            slope = float(y1 - y0)/float(x1 - x0)
            dx = index_value - x0
            yp = y0 + slope * dx
        else:
            yp = inf

        return yp
Exemplo n.º 12
0
    def map_index(self,
                  screen_pt,
                  threshold=2.0,
                  outside_returns_none=True,
                  index_only=False):
        """ Maps a screen space point to an index into the plot's index array(s).

        Implements the AbstractPlotRenderer interface.
        """
        data_pt = self.map_data(screen_pt)
        if ((data_pt < self.index_mapper.range.low) or \
            (data_pt > self.index_mapper.range.high)) and outside_returns_none:
            return None
        index_data = self.index.get_data()
        value_data = self.value.get_data()

        if len(value_data) == 0 or len(index_data) == 0:
            return None

        try:
            ndx = reverse_map_1d(index_data, data_pt, self.index.sort_order)
        except IndexError:
            return None

        x = index_data[ndx]
        y = value_data[ndx]

        result = self.map_screen(array([[x, y]]))
        if result is None:
            return None

        sx, sy = result[0]
        if index_only and ((screen_pt[0] - sx) < threshold):
            return ndx
        elif ((screen_pt[0] - sx)**2 +
              (screen_pt[1] - sy)**2 < threshold * threshold):
            return ndx
        else:
            return None
Exemplo n.º 13
0
    def map_index(self, screen_pt, threshold=2.0,
                  outside_returns_none=True, index_only=False):
        """ Maps a screen space point to an index into the plot's index arrays.

        Implements the AbstractPlotRenderer interface.
        The *index_only* parameter is ignored because the index is
        intrinsically 2-D.
        """
        if self.orientation == 'h':
            x_pt,y_pt = self.map_data([screen_pt])[0]
        else:
            x_pt,y_pt = self.map_data([(screen_pt[1],screen_pt[0])])[0]

        if ((x_pt < self.index_mapper.range.low[0]) or
            (x_pt > self.index_mapper.range.high[0]) or
            (y_pt < self.index_mapper.range.low[1]) or
            (y_pt > self.index_mapper.range.high[1])) and outside_returns_none:
            return None, None

        x_index_data, y_index_data = self.index.get_data()

        if x_index_data.get_size() == 0 or y_index_data.get_size() == 0:
            return None, None

        # attempt to map to the x index
        x_data = x_index_data.get_data()
        y_data = y_index_data.get_data()
        try:
            x_ndx = reverse_map_1d(x_data, x_pt, self.index.sort_order[0],
                                   floor_only=True)
        except IndexError, e:
            if outside_returns_none:
                return None, None

            # x index
            if x_pt < x_data[0]:
                x_ndx =  0
            else:
                x_ndx = len(x_data) - 1
Exemplo n.º 14
0
    def interpolate(self, index_value):
        """
        Returns the value of the plot at the given index value in screen space.
        Raises an IndexError when *index_value* exceeds the bounds of indexes on
        the value.
        """

        if self.index is None or self.value is None:
            raise IndexError, "cannot index when data source index or value is None"

        index_data = self.index.get_data()
        value_data = self.value.get_data()

        ndx = reverse_map_1d(index_data, index_value, self.index.sort_order)

        # quick test to see if this value is already in the index array
        if index_value == index_data[ndx]:
            return value_data[ndx]

        # get x and y values to interpolate between
        if index_value < index_data[ndx]:
            x0 = index_data[ndx - 1]
            y0 = value_data[ndx - 1]
            x1 = index_data[ndx]
            y1 = value_data[ndx]
        else:
            x0 = index_data[ndx]
            y0 = value_data[ndx]
            x1 = index_data[ndx + 1]
            y1 = value_data[ndx + 1]

        if x1 != x0:
            slope = float(y1 - y0) / float(x1 - x0)
            dx = index_value - x0
            yp = y0 + slope * dx
        else:
            yp = inf

        return yp
Exemplo n.º 15
0
    def hittest(self, screen_pt, threshold=7.0, return_distance=False):
        """
        Tests whether the given screen point is within *threshold* pixels of
        any data points on the line.  If so, then it returns the (x,y) value of
        a data point near the screen point.  If not, then it returns None.
        """

        # First, check screen_pt is directly on a point in the lineplot
        ndx = self.map_index(screen_pt, threshold)
        if ndx is not None:
            # screen_pt is one of the points in the lineplot
            data_pt = (self.index.get_data()[ndx], self.value.get_data()[ndx])
            if return_distance:
                scrn_pt = self.map_screen(data_pt)
                dist = sqrt((screen_pt[0] - scrn_pt[0])**2 +
                            (screen_pt[1] - scrn_pt[1])**2)
                return (data_pt[0], data_pt[1], dist)
            else:
                return data_pt
        else:
            # We now must check the lines themselves

            # Must check all lines within threshold along the major axis,
            # so determine the bounds of the region of interest in dataspace
            if self.orientation == "h":
                dmax = self.map_data((screen_pt[0] + threshold, screen_pt[1]))
                dmin = self.map_data((screen_pt[0] - threshold, screen_pt[1]))
            else:
                dmax = self.map_data((screen_pt[0], screen_pt[1] + threshold))
                dmin = self.map_data((screen_pt[0], screen_pt[1] - threshold))

            xmin, xmax = self.index.get_bounds()

            # Now compute the bounds of the region of interest as indexes
            if dmin < xmin:
                ndx1 = 0
            elif dmin > xmax:
                ndx1 = len(self.value.get_data()) - 1
            else:
                ndx1 = reverse_map_1d(self.index.get_data(), dmin,
                                      self.index.sort_order)
            if dmax < xmin:
                ndx2 = 0
            elif dmax > xmax:
                ndx2 = len(self.value.get_data()) - 1
            else:
                ndx2 = reverse_map_1d(self.index.get_data(), dmax,
                                      self.index.sort_order)

            start_ndx = max(0, min(
                ndx1 - 1,
                ndx2 - 1,
            ))
            end_ndx = min(
                len(self.value.get_data()) - 1, max(ndx1 + 1, ndx2 + 1))

            # Compute the distances to all points in the range of interest
            start = array([
                self.index.get_data()[start_ndx:end_ndx],
                self.value.get_data()[start_ndx:end_ndx]
            ])
            end = array([
                self.index.get_data()[start_ndx + 1:end_ndx + 1],
                self.value.get_data()[start_ndx + 1:end_ndx + 1]
            ])

            # Convert to screen points
            s_start = transpose(self.map_screen(transpose(start)))
            s_end = transpose(self.map_screen(transpose(end)))

            # t gives the parameter of the closest point to screen_pt
            # on the line going from s_start to s_end
            t = _closest_point(screen_pt, s_start, s_end)

            # Restrict to points on the line segment s_start->s_end
            t = clip(t, 0, 1)

            # Gives the corresponding point on the line
            px, py = _t_to_point(t, s_start, s_end)

            # Calculate distances
            dist = sqrt((px - screen_pt[0])**2 + (py - screen_pt[1])**2)

            # Find the minimum
            n = argmin(dist)
            # And return if it is good
            if dist[n] <= threshold:
                best_pt = self.map_data((px[n], py[n]), all_values=True)

                if return_distance:
                    return [best_pt[0], best_pt[1], dist[n]]
                else:
                    return best_pt

            return None
Exemplo n.º 16
0
    def map_index(self, screen_pt, threshold=2.0, outside_returns_none=True,
                  index_only=False):
        """ Maps a screen space point to an index into the plot's index array(s).

        Implements the AbstractPlotRenderer interface.

        Parameters
        ----------
        screen_pt :
            Screen space point

        threshold : float
            Maximum distance from screen space point to plot data point.
            A value of 0.0 means no threshold (any distance will do).

        outside_returns_none : bool
            If True, a screen space point outside the data range returns None.
            Otherwise, it returns either 0 (outside the lower range) or
            the last index (outside the upper range)

        index_only : bool
            If True, the threshold is measured on the distance between the
            index values, otherwise as Euclidean distance between the (x,y)
            coordinates.
        """

        data_pt = self.map_data(screen_pt)
        if ((data_pt < self.index_mapper.range.low) or
            (data_pt > self.index_mapper.range.high)) and outside_returns_none:
            return None
        index_data = self.index.get_data()
        value_data = self.value.get_data()

        if len(value_data) == 0 or len(index_data) == 0:
            return None

        try:
            # find the closest point to data_pt in index_data
            ndx = reverse_map_1d(index_data, data_pt, self.index.sort_order)
        except IndexError:
            # if reverse_map raises this exception, it means that data_pt is
            # outside the range of values in index_data.
            if outside_returns_none:
                return None
            else:
                if data_pt < index_data[0]:
                    return 0
                else:
                    return len(index_data) - 1

        if threshold == 0.0:
            # Don't do any threshold testing
            return ndx

        x = index_data[ndx]
        y = value_data[ndx]
        if isnan(x) or isnan(y):
            return None

        # transform x,y in a 1x2 array, which is the preferred format of
        # map_screen. this makes it robust against differences in
        # the map_screen methods of logmapper and linearmapper
        # when passed a scalar
        xy = array([[x,y]])
        sx, sy = self.map_screen(xy).T
        if index_only and (threshold == 0.0 or screen_pt[0]-sx < threshold):
            return ndx
        elif ((screen_pt[0]-sx)**2 + (screen_pt[1]-sy)**2
              < threshold*threshold):
            return ndx
        else:
            return None
Exemplo n.º 17
0
    def map_index(self,
                  screen_pt,
                  threshold=2.0,
                  outside_returns_none=True,
                  index_only=False):
        """ Maps a screen space point to an index into the plot's index array(s).

        Implements the AbstractPlotRenderer interface.

        Parameters
        ----------
        screen_pt :
            Screen space point

        threshold : float
            Maximum distance from screen space point to plot data point.
            A value of 0.0 means no threshold (any distance will do).

        outside_returns_none : bool
            If True, a screen space point outside the data range returns None.
            Otherwise, it returns either 0 (outside the lower range) or
            the last index (outside the upper range)

        index_only : bool
            If True, the threshold is measured on the distance between the
            index values, otherwise as Euclidean distance between the (x,y)
            coordinates.
        """

        data_pt = self.map_data(screen_pt)
        if ((data_pt < self.index_mapper.range.low) or
            (data_pt > self.index_mapper.range.high)) and outside_returns_none:
            return None
        index_data = self.index.get_data()
        value_data = self.value.get_data()

        if len(value_data) == 0 or len(index_data) == 0:
            return None

        try:
            # find the closest point to data_pt in index_data
            ndx = reverse_map_1d(index_data, data_pt, self.index.sort_order)
        except IndexError:
            # if reverse_map raises this exception, it means that data_pt is
            # outside the range of values in index_data.
            if outside_returns_none:
                return None
            else:
                if data_pt < index_data[0]:
                    return 0
                else:
                    return len(index_data) - 1

        if threshold == 0.0:
            # Don't do any threshold testing
            return ndx

        x = index_data[ndx]
        y = value_data[ndx]
        if isnan(x) or isnan(y):
            return None

        # transform x,y in a 1x2 array, which is the preferred format of
        # map_screen. this makes it robust against differences in
        # the map_screen methods of logmapper and linearmapper
        # when passed a scalar
        xy = array([[x, y]])
        sx, sy = self.map_screen(xy).T
        if index_only and (threshold == 0.0 or screen_pt[0] - sx < threshold):
            return ndx
        elif ((screen_pt[0] - sx)**2 +
              (screen_pt[1] - sy)**2 < threshold * threshold):
            return ndx
        else:
            return None
Exemplo n.º 18
0
class Base2DPlot(AbstractPlotRenderer):
    """ Base class for 2-D plots.
    """
    #------------------------------------------------------------------------
    # Data-related traits
    #------------------------------------------------------------------------

    # The data source to use for the index coordinate.
    index = Instance(GridDataSource)

    # The data source to use as value points.
    value = Instance(ImageData)

    # Screen mapper for 2-D structured (gridded) index data.
    index_mapper = Instance(GridMapper)

    # Convenience property for accessing the data range of the mapper.
    index_range = Property

    # Convenience property for accessing the plots labels.
    labels = Property

    # The direction that the first array returned by self.index.get_data()
    # maps to.
    #
    # * 'h': index maps to x-direction
    # * 'v': index maps to y-direction
    orientation = Enum("h", "v")

    # Overrides PlotComponent; 2-D plots draw on the 'image' layer,
    # underneath all decorations and annotations, and above only the background
    # fill color.
    draw_layer = "image"

    # Convenience property for accessing the x-direction mappers regardless
    # of orientation.  This provides compatibility with a number of tools.
    x_mapper = Property
    # Convenience property for accessing the y-direction mappers regardless
    # of orientation.  This provides compatibility with a number of tools.
    y_mapper = Property

    # Overall alpha value of the image. Ranges from 0.0 for transparent to 1.0
    # for full intensity.
    alpha = Trait(1.0, Range(0.0, 1.0))

    # Event fired when the index data changes. Subclasses can listen for this
    # event and take appropriate steps (except for requesting a redraw, which
    # is done in this class).
    index_data_changed = Event

    # Event fired when the index mapper changes. Subclasses can listen for this
    # event and take appropriate steps (except for requesting a redraw, which
    # is done in this class).
    index_mapper_changed = Event

    # Event fired when the value data changes. Subclasses can listen for this
    # event and take appropriate steps (except for requesting a redraw, which
    # is done in this class).
    value_data_changed = Event

    #------------------------------------------------------------------------
    # Public methods
    #------------------------------------------------------------------------

    def __init__(self, **kwargs):
        # Handling the setting/initialization of these traits manually because
        # they should be initialized in a certain order.
        kwargs_tmp = {"trait_change_notify": False}
        for trait_name in ("index", "value"):
            if trait_name in kwargs:
                kwargs_tmp[trait_name] = kwargs.pop(trait_name)
        self.set(**kwargs_tmp)
        super(Base2DPlot, self).__init__(**kwargs)
        if self.index is not None:
            self.index.on_trait_change(self._update_index_data,
                                       "data_changed")
        if self.index_mapper:
            self.index_mapper.on_trait_change(self._update_index_mapper,
                                              "updated")
        if self.value is not None:
            self.value.on_trait_change(self._update_value_data,
                                       "data_changed")
        # If we are not resizable, we will not get a bounds update upon layout,
        # so we have to manually update our mappers
        if self.resizable == "":
            self._update_mappers()
        return

    #------------------------------------------------------------------------
    # AbstractPlotRenderer interface
    #------------------------------------------------------------------------

    def map_screen(self, data_pts):
        """ Maps an array of data points into screen space and returns it as
        an array.

        Implements the AbstractPlotRenderer interface.
        """
        # data_pts is Nx2 array
        if len(data_pts) == 0:
            return []
        return asarray(self.index_mapper.map_screen(data_pts))

    def map_data(self, screen_pts):
        """ Maps a screen space point into the "index" space of the plot.

        Implements the AbstractPlotRenderer interface.
        """
        return self.index_mapper.map_data(screen_pts)

    def map_index(self, screen_pt, threshold=2.0,
                  outside_returns_none=True, index_only=False):
        """ Maps a screen space point to an index into the plot's index arrays.

        Implements the AbstractPlotRenderer interface.
        The *index_only* parameter is ignored because the index is
        intrinsically 2-D.
        """
        if self.orientation == 'h':
            x_pt,y_pt = self.map_data([screen_pt])[0]
        else:
            x_pt,y_pt = self.map_data([(screen_pt[1],screen_pt[0])])[0]

        if ((x_pt < self.index_mapper.range.low[0]) or
            (x_pt > self.index_mapper.range.high[0]) or
            (y_pt < self.index_mapper.range.low[1]) or
            (y_pt > self.index_mapper.range.high[1])) and outside_returns_none:
            return None, None

        x_index_data, y_index_data = self.index.get_data()

        if x_index_data.get_size() == 0 or y_index_data.get_size() == 0:
            return None, None

        # attempt to map to the x index
        x_data = x_index_data.get_data()
        y_data = y_index_data.get_data()
        try:
            x_ndx = reverse_map_1d(x_data, x_pt, self.index.sort_order[0],
                                   floor_only=True)
        except IndexError, e:
            if outside_returns_none:
                return None, None

            # x index
            if x_pt < x_data[0]:
                x_ndx =  0
            else:
                x_ndx = len(x_data) - 1

        try:
            y_ndx = reverse_map_1d(y_data, y_pt, self.index.sort_order[1],
                                   floor_only=True)
        except IndexError, e:
            if outside_returns_none:
                return None, None

            # y index
            if y_pt < y_data[0]:
                y_ndx =  0
            else:
                y_ndx = len(y_data) - 1
Exemplo n.º 19
0
    def hittest(self, screen_pt, threshold=7.0, return_distance = False):
        """
        Tests whether the given screen point is within *threshold* pixels of
        any data points on the line.  If so, then it returns the (x,y) value of
        a data point near the screen point.  If not, then it returns None.
        """

        # First, check screen_pt is directly on a point in the lineplot
        ndx = self.map_index(screen_pt, threshold)
        if ndx is not None:
            # screen_pt is one of the points in the lineplot
            data_pt = (self.index.get_data()[ndx], self.value.get_data()[ndx])
            if return_distance:
                scrn_pt = self.map_screen(data_pt)
                dist = sqrt((screen_pt[0] - scrn_pt[0])**2
                            + (screen_pt[1] - scrn_pt[1])**2)
                return (data_pt[0], data_pt[1], dist)
            else:
                return data_pt
        else:
            # We now must check the lines themselves

            # Must check all lines within threshold along the major axis,
            # so determine the bounds of the region of interest in dataspace
            if self.orientation == "h":
                dmax = self.map_data((screen_pt[0]+threshold, screen_pt[1]))
                dmin = self.map_data((screen_pt[0]-threshold, screen_pt[1]))
            else:
                dmax = self.map_data((screen_pt[0], screen_pt[1]+threshold))
                dmin = self.map_data((screen_pt[0], screen_pt[1]-threshold))

            xmin, xmax = self.index.get_bounds()

            # Now compute the bounds of the region of interest as indexes
            if dmin < xmin:
                ndx1 = 0
            elif dmin > xmax:
                ndx1 = len(self.value.get_data())-1
            else:
                ndx1 = reverse_map_1d(self.index.get_data(), dmin,
                                      self.index.sort_order)
            if dmax < xmin:
                ndx2 = 0
            elif dmax > xmax:
                ndx2 = len(self.value.get_data())-1
            else:
                ndx2 = reverse_map_1d(self.index.get_data(), dmax,
                                      self.index.sort_order)

            start_ndx = max(0, min(ndx1-1, ndx2-1,))
            end_ndx = min(len(self.value.get_data())-1, max(ndx1+1, ndx2+1))

            # Compute the distances to all points in the range of interest
            start = array([ self.index.get_data()[start_ndx:end_ndx],
                            self.value.get_data()[start_ndx:end_ndx] ])
            end = array([ self.index.get_data()[start_ndx+1:end_ndx+1],
                            self.value.get_data()[start_ndx+1:end_ndx+1] ])

            # Convert to screen points
            s_start = transpose(self.map_screen(transpose(start)))
            s_end = transpose(self.map_screen(transpose(end)))

            # t gives the parameter of the closest point to screen_pt
            # on the line going from s_start to s_end
            t = _closest_point(screen_pt, s_start, s_end)

            # Restrict to points on the line segment s_start->s_end
            t = clip(t, 0, 1)

            # Gives the corresponding point on the line
            px, py = _t_to_point(t, s_start, s_end)

            # Calculate distances
            dist =  sqrt((px - screen_pt[0])**2 +
                         (py - screen_pt[1])**2)

            # Find the minimum
            n = argmin(dist)
            # And return if it is good
            if dist[n] <= threshold:
                best_pt = self.map_data((px[n], py[n]), all_values=True)

                if return_distance:
                    return [best_pt[0], best_pt[1], dist[n]]
                else:
                    return best_pt

            return None