|
| 1 | +# Creates two identical panels. Zooming in on the right panel will show |
| 2 | +# a rectangle in the first panel, denoting the zoomed region. |
| 3 | +import numpy as np |
| 4 | +import matplotlib.pyplot as plt |
| 5 | +from matplotlib.patches import Rectangle |
| 6 | + |
| 7 | +# We just subclass Rectangle so that it can be called with an Axes |
| 8 | +# instance, causing the rectangle to update its shape to match the |
| 9 | +# bounds of the Axes |
| 10 | +class UpdatingRect(Rectangle): |
| 11 | + def __call__(self, ax): |
| 12 | + self.set_bounds(*ax.viewLim.bounds) |
| 13 | + ax.figure.canvas.draw_idle() |
| 14 | + |
| 15 | +# A class that will regenerate a fractal set as we zoom in, so that you |
| 16 | +# can actually see the increasing detail. A box in the left panel will show |
| 17 | +# the area to which we are zoomed. |
| 18 | +class MandlebrotDisplay(object): |
| 19 | + def __init__(self, h=500, w=500, niter=50, radius=2., power=2): |
| 20 | + self.height = h |
| 21 | + self.width = w |
| 22 | + self.niter = niter |
| 23 | + self.radius = radius |
| 24 | + self.power = power |
| 25 | + |
| 26 | + def __call__(self, xstart, xend, ystart, yend): |
| 27 | + self.x = np.linspace(xstart, xend, self.width) |
| 28 | + self.y = np.linspace(ystart, yend, self.height).reshape(-1,1) |
| 29 | + c = self.x + 1.0j * self.y |
| 30 | + threshold_time = np.zeros((self.height, self.width)) |
| 31 | + z = np.zeros(threshold_time.shape, dtype=np.complex) |
| 32 | + mask = np.ones(threshold_time.shape, dtype=np.bool) |
| 33 | + for i in xrange(self.niter): |
| 34 | + z[mask] = z[mask]**self.power + c[mask] |
| 35 | + mask = (np.abs(z) < self.radius) |
| 36 | + threshold_time += mask |
| 37 | + return threshold_time |
| 38 | + |
| 39 | + def ax_update(self, ax): |
| 40 | + ax.set_autoscale_on(False) # Otherwise, infinite loop |
| 41 | + |
| 42 | + #Get the number of points from the number of pixels in the window |
| 43 | + dims = ax.axesFrame.get_window_extent().bounds |
| 44 | + self.width = int(dims[2] + 0.5) |
| 45 | + self.height = int(dims[2] + 0.5) |
| 46 | + |
| 47 | + #Get the range for the new area |
| 48 | + xstart,ystart,xdelta,ydelta = ax.viewLim.bounds |
| 49 | + xend = xstart + xdelta |
| 50 | + yend = ystart + ydelta |
| 51 | + |
| 52 | + # Update the image object with our new data and extent |
| 53 | + im = ax.images[-1] |
| 54 | + im.set_data(self.__call__(xstart, xend, ystart, yend)) |
| 55 | + im.set_extent((xstart, xend, ystart, yend)) |
| 56 | + ax.figure.canvas.draw_idle() |
| 57 | + |
| 58 | +md = MandlebrotDisplay() |
| 59 | +Z = md(-2., 0.5, -1.25, 1.25) |
| 60 | + |
| 61 | +fig = plt.figure() |
| 62 | +ax1 = fig.add_subplot(1, 2, 1) |
| 63 | +ax1.imshow(Z, origin='lower', extent=(md.x.min(), md.x.max(), md.y.min(), md.y.max())) |
| 64 | + |
| 65 | +ax2 = fig.add_subplot(1, 2, 2) |
| 66 | +ax2.imshow(Z, origin='lower', extent=(md.x.min(), md.x.max(), md.y.min(), md.y.max())) |
| 67 | + |
| 68 | +rect = UpdatingRect([0, 0], 0, 0, facecolor='None', edgecolor='black') |
| 69 | +rect.set_bounds(*ax2.viewLim.bounds) |
| 70 | +ax1.add_patch(rect) |
| 71 | + |
| 72 | +# Connect for changing the view limits |
| 73 | +ax2.callbacks.connect('xlim_changed', rect) |
| 74 | +ax2.callbacks.connect('ylim_changed', rect) |
| 75 | + |
| 76 | +ax2.callbacks.connect('xlim_changed', md.ax_update) |
| 77 | +ax2.callbacks.connect('ylim_changed', md.ax_update) |
| 78 | + |
| 79 | +plt.show() |
0 commit comments