Example #1
0
    def limit_rect(self, args):
        if isinstance(args, tuple):
            rect = Rect(*args)
        elif args is None:
            rect = Rect(-np.inf, -np.inf, np.inf, np.inf)
        else:
            rect = Rect(args)

        if self._limit_rect != rect:
            self._limit_rect = rect
Example #2
0
 def test_update_camera_position_component_marking(self, base_settings,
                                                   image_view, pos):
     roi = np.zeros(base_settings.image.get_channel(0).shape,
                    dtype=np.uint8)
     roi[..., 2:-2, 2:-2, 2:-2] = 1
     base_settings.roi = roi
     image_view.viewer.dims.set_point(1, 0)
     rect = Rect(image_view.viewer_widget.view.camera.get_state()["rect"])
     rect.pos = pos
     rect.size = (100, 100)
     image_view.viewer_widget.view.camera.set_state({"rect": rect})
     image_view.component_mark(1, False)
     assert image_view.viewer.dims.point[1] != 0
     assert image_view.viewer_widget.view.camera.get_state(
     )["rect"].pos != pos
Example #3
0
 def set_state(self, state=None, **kwargs):
     D = state or {}
     if 'rect' not in D:
         return
     for cam in self._linked_cameras:
         r = Rect(D['rect'])
         if cam is self._linked_cameras_no_update:
             continue
         try:
             cam._linked_cameras_no_update = self
             cam_rect = cam.get_state()['rect']
             r.top = cam_rect.top
             r.bottom = cam_rect.bottom
             cam.set_state({'rect': r})
         finally:
             cam._linked_cameras_no_update = None
Example #4
0
    def fit_view(self, rect=None):

        # Lock updates in other threads
        self.shape_collection.lock_updates()

        if not rect:
            rect = Rect(0, 0, 10, 10)
            try:
                rect.left, rect.right = self.shape_collection.bounds(axis=0)
                rect.bottom, rect.top = self.shape_collection.bounds(axis=1)
            except TypeError:
                pass

        self.vispy_canvas.view.camera.rect = rect

        self.shape_collection.unlock_updates()
Example #5
0
    def __init__(self, **kwargs):
        super(RadolanCanvas, self).__init__(keys='interactive', **kwargs)

        # set size ov Canvas
        self.size = 450, 450

        # unfreeze needed to add more elements
        self.unfreeze()

        # add grid central widget
        self.grid = self.central_widget.add_grid()

        # add view to grid
        self.view = self.grid.add_view(row=0, col=0)
        self.view.border_color = (0.5, 0.5, 0.5, 1)

        # add signal emitters
        self.mouse_moved = EventEmitter(source=self, type="mouse_moved")

        # block double clicks
        self.events.mouse_double_click.block()

        # initialize empty RADOLAN image
        img_data = np.zeros((900, 900))

        # initialize colormap, we take cubehelix for now
        # this is the most nice colormap for radar in vispy
        cmap = 'cubehelix'

        # initialize Image Visual with img_data
        # add to view
        self.image = Image(
            img_data,
            method='subdivide',
            #interpolation='bicubic',
            cmap=cmap,
            parent=self.view.scene)

        # add transform to Image
        # (mostly positioning within canvas)
        self.image.transform = STTransform(translate=(0, 0, 0))

        # get radolan ll point coodinate into self.r0
        self.r0 = utils.get_radolan_origin()

        # create cities (Markers and Text Visuals
        self.create_cities()

        # create PanZoomCamera
        self.cam = PanZoomCamera(name="PanZoom",
                                 rect=Rect(0, 0, 900, 900),
                                 aspect=1,
                                 parent=self.view.scene)

        self.view.camera = self.cam

        self._mouse_position = None
        self.freeze()
        # print FPS to console, vispy SceneCanvas internal function
        self.measure_fps()
 def set_state(self, state=None, **kwargs):
     D = state or {}
     if 'rect' not in D:
         return
     for cam in self._linked_cameras:
         r = Rect(D['rect'])
         if cam is self._linked_cameras_no_update:
             continue
         try:
             cam._linked_cameras_no_update = self
             cam_rect = cam.get_state()['rect']
             r.top = cam_rect.top
             r.bottom = cam_rect.bottom
             cam.set_state({'rect':r})
         finally:
             cam._linked_cameras_no_update = None
Example #7
0
    def __init__(self, texdata, parent, overlay=False):
        self._parent2 = parent
        self.overlay = overlay
        #just set a dummy value
        self._shape = (10.0, 22500)
        self.tex = gloo.Texture2D(texdata,
                                  format='luminance',
                                  internalformat='r32f',
                                  interpolation="linear")
        self.get_data = visuals.shaders.Function(norm_luminance)
        self.get_data['vmin'] = -80
        self.get_data['vmax'] = -40
        self.get_data['texture'] = self.tex

        self.bb = Rect((0, 0, 1, 1))

        scene.Image.__init__(self,
                             method='subdivide',
                             grid=(1000, 1),
                             parent=parent)

        #set in the main program
        self.shared_program.frag['get_data'] = self.get_data

        #needs no external color map
        if self.overlay == "r":
            self.set_gl_state('additive')
            self.shared_program.frag[
                'color_transform'] = visuals.shaders.Function(r_cmap)
        elif self.overlay == "g":
            self.set_gl_state('additive')
            self.shared_program.frag[
                'color_transform'] = visuals.shaders.Function(g_cmap)
Example #8
0
    def on_resize(self, event):
        vp = (0, 0, self.physical_size[0], self.physical_size[1])
        self.context.set_viewport(*vp)
        for line in self.lines:
            line.transforms.configure(canvas=self, viewport=vp)

        # Need to update clipping boundaries if the window resizes.
        tr = self.lines[1].transforms.get_transform('visual', 'framebuffer')
        self.clipper.bounds = tr.map(Rect(100, -20, 200, 40))
Example #9
0
    def on_resize(self, event):
        for line in self.lines:
            # let the transform systems know that the window has resized
            line.tr_sys.auto_configure()

        # Need to update clipping boundaries if the window resizes.
        trs = self.lines[1].tr_sys
        tr = trs.document_to_framebuffer * trs.visual_to_document
        self.clipper.bounds = tr.map(Rect(50, -15, 250, 30))
Example #10
0
 def _set_all_x_axes(self, xaxis):
     if xaxis is not None:
         left, right = xaxis
         for pw in self.plot_widgets:
             if pw.linked:
                 rect = Rect(pw.view.camera.rect)
                 rect.left = left
                 rect.right = right
                 pw.view.camera.rect = rect
             else:
                 for c in pw.view.scene.children:
                     if isinstance(c, Histogram) and pw.data is not None:
                         t, data, bins, orientation = pw.data
                         subset = np.where((t >= left)
                                           & (t <= left + right))
                         (rr, tris) = _do_the_histogramming(
                             data[subset], bins, orientation)
                         c.set_data(rr, tris, color=c.color)
Example #11
0
 def _set_all_x_axes(self, xaxis):
     if xaxis is not None:
         left, right = xaxis
         for pw in self.plot_widgets:
             if pw.linked:
                 rect = Rect(pw.view.camera.rect)
                 rect.left = left
                 rect.right = right
                 pw.view.camera.rect = rect
             else:
                 for c in pw.view.scene.children:
                     if isinstance(c, Histogram) and pw.data is not None:
                         t, data, bins, orientation = pw.data
                         subset = np.where((t >= left) &
                                           (t <= left + right))
                         (rr, tris) = _do_the_histogramming(data[subset],
                                                            bins,
                                                            orientation)
                         c.set_data(rr, tris, color=c.color)
Example #12
0
    def _update_child_widget_dim(self):
        # think in terms of (x, y). (row, col) makes code harder to read
        ymax, xmax = self.grid_size
        if ymax <= 0 or xmax <= 0:
            return

        rect = self.rect  # .padded(self.padding + self.margin)
        if rect.width <= 0 or rect.height <= 0:
            return
        if self._need_solver_recreate:
            self._need_solver_recreate = False
            self._recreate_solver()

        # we only need to remove and add the height and width constraints of
        # the solver if they are not the same as the current value
        if rect.height != self._var_h.value:
            if self._height_stay:
                self._solver.remove_constraint(self._height_stay)

            self._var_h.value = rect.height
            self._height_stay = self._solver.add_stay(self._var_h,
                                                      strength=STRONG)

        if rect.width != self._var_w.value:
            if self._width_stay:
                self._solver.remove_constraint(self._width_stay)

            self._var_w.value = rect.width
            self._width_stay = self._solver.add_stay(self._var_w,
                                                     strength=STRONG)

        value_vectorized = np.vectorize(lambda x: x.value)

        for (_, val) in self._grid_widgets.items():
            (row, col, rspan, cspan, widget) = val

            width = np.sum(value_vectorized(
                           self._width_grid[row][col:col+cspan]))
            height = np.sum(value_vectorized(
                            self._height_grid[col][row:row+rspan]))
            if col == 0:
                x = 0
            else:
                x = np.sum(value_vectorized(self._width_grid[row][0:col]))

            if row == 0:
                y = 0
            else:
                y = np.sum(value_vectorized(self._height_grid[col][0:row]))

            if isinstance(widget, ViewBox):
                widget.rect = Rect(x, y, width, height)
            else:
                widget.size = (width, height)
                widget.pos = (x, y)
Example #13
0
    def __init__(self, **kwargs):
        super(DXCanvas, self).__init__(keys='interactive', **kwargs)

        self.size = 450, 450
        self.unfreeze()

        # add grid central widget
        self.grid = self.central_widget.add_grid()

        # add view to grid
        self.view = self.grid.add_view(row=0, col=0)
        self.view.border_color = (0.5, 0.5, 0.5, 1)

        # This is hardcoded now, but maybe handled as the data source changes
        self.img_data = np.zeros((360, 128))

        # initialize colormap, we take cubehelix for now
        # this is the most nice colormap for radar in vispy
        cmap = 'cubehelix'

        # this way we can hold several images on the same scene
        # usable for radar mosaic
        self.images = []
        self.image = PolarImage(
            source=None,
            data=self.img_data,
            method='impostor',
            # interpolation='bicubic',
            cmap=cmap,
            clim=(-32.5, 95),
            parent=self.view.scene)

        self.images.append(self.image)

        # add signal emitters
        self.mouse_moved = EventEmitter(source=self, type="mouse_moved")
        self.key_pressed = EventEmitter(source=self, type="key_pressed")

        # block double clicks
        self.events.mouse_double_click.block()

        # create PanZoomCamera
        # the camera should zoom to the ppi "bounding box"
        self.cam = PanZoomCamera(name="PanZoom",
                                 rect=Rect(0, 0, 256, 256),
                                 aspect=1,
                                 parent=self.view.scene)

        self.view.camera = self.cam

        self._mouse_position = None

        self.freeze()
        self.measure_fps()
Example #14
0
 def zoom(self, factor, center=None):
     # Init some variables
     center = center if (center is not None) else self.center
     assert len(center) in (2, 3, 4)
     # Get scale factor, take scale ratio into account
     if np.isscalar(factor):
         scale = [factor, factor]
     else:
         if len(factor) != 2:
             raise TypeError("factor must be scalar or length-2 sequence.")
         scale = list(factor)
     if self.aspect is not None:
         scale[0] = scale[1]
     # Make a new object (copy), so that allocation will
     # trigger view_changed:
     rect = Rect(self.rect)
     # Get space from given center to edges
     left_space = center[0] - rect.left
     right_space = rect.right - center[0]
     bottom_space = center[1] - rect.bottom
     top_space = rect.top - center[1]
     # Scale these spaces
     rect.left = center[0] - left_space * scale[0]
     rect.right = center[0] + right_space * scale[0]
     rect.bottom = center[1] - bottom_space * scale[1]
     rect.top = center[1] + top_space * scale[1]
     self.rect = rect
     self.currect_scale *= factor
     for text, font_size in zip(self.texts,self.font_sizes):
         text.font_size = font_size / self.currect_scale
Example #15
0
    def rect(self, args):
        if isinstance(args, tuple):
            rect = Rect(*args)
        else:
            rect = Rect(args)

        # THIS DOESN'T WORK. The `rect` object doesn't represent a fixed coordinate system
        # (0 and 1 have different meanings at different zoom levels)

        # Limit how far we can pan and zoom out
        # if self.parent.transform
        # if rect.left >= self._pan_limits[2]:
        #     print("To far right")
        #     return
        # elif rect.left <= self._pan_limits[0]:
        #     print("To far left")
        #     return
        # if self._pan_limits is not None:
        #     new_left = np.clip(rect.left, self._pan_limits[0] - self._zoom_limits[0], self._pan_limits[2])
        #     new_right = np.clip(rect.right, self._pan_limits[0], self._pan_limits[2] + self._zoom_limits[0])
        #     new_bottom = np.clip(rect.bottom, self._pan_limits[1] - self._zoom_limits[0], self._pan_limits[3])
        #     new_top = np.clip(rect.top, self._pan_limits[1], self._pan_limits[3] + self._zoom_limits[1])
        # else:
        #     new_left = rect.left
        #     new_right = rect.right
        #     new_bottom = rect.bottom
        #     new_top = rect.top
        #
        # # Limit how far we can zoom in
        # new_size_x = new_right - new_left
        # new_size_y = new_top - new_bottom
        # if self._zoom_limits is not None:
        #     new_size_x = max(new_size_x, self._zoom_limits[0])
        #     new_size_y = max(new_size_y, self._zoom_limits[1])
        # rect = Rect(new_left, new_bottom, new_size_x, new_size_y)

        if self._rect != rect:
            self._rect = rect
            self.view_changed()
Example #16
0
 def move_to(self, x=None, y=None, z=None, scale=1) -> Navigator:
     if z is not None:
         self.viewer.dims.set_point(0, z)
     with self.camera_state() as state:
         rect = state["rect"]
         size = (rect.width * scale, rect.height * scale)
         center = (default(x, rect.center[0]), default(y, rect.center[1]))
         pos = (
             center[0] - size[0] / 2,
             center[1] - size[1] / 2,
         )
         state["rect"] = Rect(pos, size)
     return self
Example #17
0
    def _update_child_widget_dim(self):
        # think in terms of (x, y). (row, col) makes code harder to read
        ymax, xmax = self.grid_size
        if ymax <= 0 or xmax <= 0:
            return

        rect = self.rect  # .padded(self.padding + self.margin)
        if rect.width <= 0 or rect.height <= 0:
            return
        if self._need_solver_recreate:
            self._need_solver_recreate = False
            self._recreate_solver()

        # we only need to remove and add the height and width constraints of
        # the solver if they are not the same as the current value
        h_changed = abs(rect.height - self._var_h.value()) > 1e-4
        w_changed = abs(rect.width - self._var_w.value()) > 1e-4
        if h_changed:
            self._solver.suggestValue(self._var_h, rect.height)

        if w_changed:
            self._solver.suggestValue(self._var_w, rect.width)
        if h_changed or w_changed:
            self._solver.updateVariables()

        value_vectorized = np.vectorize(lambda x: x.value())

        for (_, val) in self._grid_widgets.items():
            (row, col, rspan, cspan, widget) = val

            width = np.sum(
                value_vectorized(self._width_grid[row][col:col + cspan]))
            height = np.sum(
                value_vectorized(self._height_grid[col][row:row + rspan]))
            if col == 0:
                x = 0
            else:
                x = np.sum(value_vectorized(self._width_grid[row][0:col]))

            if row == 0:
                y = 0
            else:
                y = np.sum(value_vectorized(self._height_grid[col][0:row]))

            if isinstance(widget, ViewBox):
                widget.rect = Rect(x, y, width, height)
            else:
                widget.size = (width, height)
                widget.pos = (x, y)
Example #18
0
 def move_by(self, x=0, y=0, z=0, scale=1) -> Navigator:
     if z:
         new_z = self.viewer.dims.point + z
         self.viewer.dims.set_point(new_z)
     with self.camera_state() as state:
         rect = state["rect"]
         size = (rect.width * scale, rect.height * scale)
         center = (
             rect.center[0] + x,
             rect.center[1] + y,
         )
         pos = (
             center[0] - size[0] / 2,
             center[1] - size[1] / 2,
         )
         state["rect"] = Rect(pos, size)
     return self
Example #19
0
 def _zoom(self):
     self._stop()
     if self.napari_viewer.dims.ndisplay != 2:
         show_info("Zoom in does not work in 3D mode")
     num = self.component_selector.value
     labels = self.labels_layer.value
     bound_info = self.roi_info.bound_info.get(num, None)
     if bound_info is None:
         return
     lower_bound = self._data_to_world(labels, bound_info.lower)
     upper_bound = self._data_to_world(labels, bound_info.upper)
     diff = upper_bound - lower_bound
     frame = diff * 0.2
     if self.napari_viewer.dims.ndisplay == 2:
         rect = Rect(pos=(lower_bound - frame)[-2:][::-1], size=(diff + 2 * frame)[-2:][::-1])
         self.napari_viewer.window.qt_viewer.view.camera.set_state({"rect": rect})
     self._update_point(lower_bound, upper_bound)
Example #20
0
    def zoom(self, factor, center=None):
        """ Zoom in (or out) at the given center

        Parameters
        ----------
        factor : float or tuple
            Fraction by which the scene should be zoomed (e.g. a factor of 2
            causes the scene to appear twice as large).
        center : tuple of 2-4 elements
            The center of the view. If not given or None, use the
            current center.
        """
        assert len(center) in (2, 3, 4)
        # Get scale factor, take scale ratio into account
        if np.isscalar(factor):
            scale = [factor, factor]
        else:
            if len(factor) != 2:
                raise TypeError("factor must be scalar or length-2 sequence.")
            scale = list(factor)
        if self.aspect is not None:
            scale[0] = scale[1]

        # Init some variables
        center = center if (center is not None) else self.center
        # Make a new object (copy), so that allocation will
        # trigger view_changed:
        rect = Rect(self.rect)
        # Get space from given center to edges
        left_space = center[0] - rect.left
        right_space = rect.right - center[0]
        bottom_space = center[1] - rect.bottom
        top_space = rect.top - center[1]
        # Scale these spaces
        rect.left = center[0] - left_space * scale[0]
        rect.right = center[0] + right_space * scale[0]
        rect.bottom = center[1] - bottom_space * scale[1]
        rect.top = center[1] + top_space * scale[1]

        self.limit_zoom(rect)

        self.rect = rect
Example #21
0
 def _shift_if_need(self, labels, bound_info):
     if self.napari_viewer.dims.ndisplay != 2:
         return
     lower_bound = self._data_to_world(labels, bound_info.lower)
     upper_bound = self._data_to_world(labels, bound_info.upper)
     self._update_point(lower_bound, upper_bound)
     l_bound = lower_bound[-2:][::-1]
     u_bound = upper_bound[-2:][::-1]
     rect = Rect(self.napari_viewer.window.qt_viewer.view.camera.get_state()["rect"])
     if rect.contains(*l_bound) and rect.contains(*u_bound):
         return
     size = u_bound - l_bound
     rect.size = tuple(np.max([rect.size, size * 1.2], axis=0))
     pos = rect.pos
     if rect.left > l_bound[0]:
         pos = l_bound[0], pos[1]
     if rect.right < u_bound[0]:
         pos = pos[0] + u_bound[0] - rect.right, pos[1]
     if rect.bottom > l_bound[1]:
         pos = pos[0], l_bound[1]
     if rect.top < u_bound[1]:
         pos = pos[0], pos[1] + (u_bound[1] - rect.top)
     rect.pos = pos
     self.napari_viewer.window.qt_viewer.view.camera.set_state({"rect": rect})
Example #22
0
 def on_resize(self, event):
     scale = 10000
     r1 = self.viewbox_pos.camera.rect
     self.viewbox_fan.camera.rect = Rect(r1.left, r1.bottom * scale,
                                         r1.width, r1.height * scale)
     return
Example #23
0
 def bounds(self, b):
     self._bounds = Rect(b).normalized()
     b = self._bounds
     self.fshader['view'] = (b.left, b.right, b.bottom, b.top)
Example #24
0
    def zoom(self, factor, center=None):
        """ Zoom in (or out) at the given center
        Parameters
        ----------
        factor : float or tuple
            Fraction by which the scene should be zoomed (e.g. a factor of 2
            causes the scene to appear twice as large).
        center : tuple of 2-4 elements
            The center of the view. If not given or None, use the
            current center.
        """
        if self._zoomed:
            return
        assert len(center) in (2, 3, 4)
        # Get scale factor, take scale ratio into account
        if np.isscalar(factor):
            scale = [factor, factor]
        else:
            if len(factor) != 2:
                raise TypeError("factor must be scalar or length-2 sequence.")
            scale = list(factor)
        if self.aspect is not None:
            scale[0] = scale[1]

        # Init some variables
        center = center if (center is not None) else self.center
        # Make a new object (copy), so that allocation will
        # trigger view_changed:
        rect = Rect(self.rect)
        if rect.right - rect.left < 0.1 and scale[0] < 1:
            return
        # Get space from given center to edges
        left_space = center[0] - rect.left
        right_space = rect.right - center[0]
        bottom_space = center[1] - rect.bottom
        top_space = rect.top - center[1]
        # Scale these spaces
        if self._zoom_axis == 'both' or self._zoom_axis == 'x':
            rect.left = center[0] - left_space * scale[0]
            rect.right = center[0] + right_space * scale[0]
        if self._zoom_axis == 'both' or self._zoom_axis == 'y':
            rect.bottom = center[1] - bottom_space * scale[1]
            rect.top = center[1] + top_space * scale[1]
        if self._ybounds is not None:
            if rect.bottom < self._ybounds[0]:
                rect.bottom = self._ybounds[0]
            if rect.top > self._ybounds[1]:
                rect.top = self._ybounds[1]
        if self._xbounds is not None:
            if rect.left < self._xbounds[0]:
                rect.left = self._xbounds[0]
            if rect.right > self._xbounds[1]:
                rect.right = self._xbounds[1]

        self.rect = rect
        self._zoomed = True
        for c in self._linked_panzoom:
            if c._zoomed:
                continue
            new_center = []
            new_center.append(rescale(center[0], self._xbounds[1], c._xbounds[1]))
            new_center.append(rescale(center[1], self._ybounds[1], c._ybounds[1]))
            new_center.extend([0,1])
            c.zoom(factor, np.array(new_center))
        self._zoomed = False
Example #25
0
def test_map_rect():
    r = Rect((2, 7), (13, 19))
    r1 = ST(scale=(2, 2), translate=(-10, 10)).map(r)
    assert r1 == Rect((-6, 24), (26, 38))
Example #26
0
    def __init__(self):
        super(SignalCamera, self).__init__(mag=1)

        self._limit_rect = Rect(0, 0, 1, 1)
Example #27
0
    def zoom(self, factor, center=None):
        """ Zoom in (or out) at the given center
        Parameters
        ----------
        factor : float or tuple
            Fraction by which the scene should be zoomed (e.g. a factor of 2
            causes the scene to appear twice as large).
        center : tuple of 2-4 elements
            The center of the view. If not given or None, use the
            current center.
        """
        if self.zoom is None:
            return
        if self._zoomed:
            return
        assert len(center) in (2, 3, 4)
        # Get scale factor, take scale ratio into account
        if np.isscalar(factor):
            scale = [factor, factor]
        else:
            if len(factor) != 2:
                raise TypeError("factor must be scalar or length-2 sequence.")
            scale = list(factor)
        if self.aspect is not None:
            scale[0] = scale[1]

        # Init some variables
        center = center if (center is not None) else self.center
        # Make a new object (copy), so that allocation will
        # trigger view_changed:
        rect = Rect(self.rect)
        if rect.right - rect.left < 0.1 and scale[0] < 1:
            return
        # Get space from given center to edges
        left_space = center[0] - rect.left
        right_space = rect.right - center[0]
        bottom_space = center[1] - rect.bottom
        top_space = rect.top - center[1]
        # Scale these spaces
        if self._zoom_axis == 'both' or self._zoom_axis == 'x':
            rect.left = center[0] - left_space * scale[0]
            rect.right = center[0] + right_space * scale[0]
        if self._zoom_axis == 'both' or self._zoom_axis == 'y':
            rect.bottom = center[1] - bottom_space * scale[1]
            rect.top = center[1] + top_space * scale[1]
        if self._ybounds is not None:
            if rect.bottom < self._ybounds[0]:
                rect.bottom = self._ybounds[0]
            if rect.top > self._ybounds[1]:
                rect.top = self._ybounds[1]
        if self._xbounds is not None:
            if rect.left < self._xbounds[0]:
                rect.left = self._xbounds[0]
            if rect.right > self._xbounds[1]:
                rect.right = self._xbounds[1]

        self.rect = rect
        self._zoomed = True
        for c in self._linked_panzoom:
            if c._zoomed:
                continue
            new_center = []
            new_center.append(
                rescale(center[0], self._xbounds[1], c._xbounds[1]))
            new_center.append(
                rescale(center[1], self._ybounds[1], c._ybounds[1]))
            new_center.extend([0, 1])
            c.zoom(factor, np.array(new_center))
        self._zoomed = False