def __add_xy_values_as_polygon__(self, xy: list, color: str, closed=False, fill=False, alpha=0.2, line_width=2):
     xy = PlotterInterface.get_xy_from_timestamp_to_date_number(xy)
     polygon = Polygon(np.array(xy), closed=closed, fill=fill)
     polygon.set_visible(True)
     polygon.set_color(color)
     polygon.set_alpha(alpha)
     polygon.set_linewidth(line_width)
     self.axes_for_candlesticks.add_patch(polygon)
 def __plot_single_fibonacci_wave__(self, fib_wave: FibonacciWave, color: str, suffix: str = ''):
     if self.sys_config.config.fibonacci_detail_print:
         fib_wave.print(suffix)
     xy = fib_wave.get_xy_parameter()
     xy = PlotterInterface.get_xy_from_timestamp_to_date_number(xy)
     fib_polygon = Polygon(np.array(xy), closed=False, fill=False)
     fib_polygon.set_visible(True)
     fib_polygon.set_color(color)
     fib_polygon.set_linewidth(1)
     self.axes_for_candlesticks.add_patch(fib_polygon)
     fib_wave_patch = FibonacciWavePatch(fib_wave, fib_polygon)
     self.fibonacci_patch_container.add_patch(fib_wave_patch)
     fib_wave_patch.add_retracement_patch_list_to_axis(self.axes_for_candlesticks)
 def __add_to_ranges_polygon_dic__(self, polygon: Polygon, for_main: bool, range: PatternRange):
     polygon.set_visible(False)
     polygon.set_color('r' if for_main else 'k')
     polygon.set_linewidth(1)
     self.axes_for_candlesticks.add_patch(polygon)
     for ticks in range.tick_list:
         if for_main:
             if ticks.f_var not in self.ranges_polygon_dic_list:
                 self.ranges_polygon_dic_list[ticks.f_var] = [polygon]
             else:
                 self.ranges_polygon_dic_list[ticks.f_var].append(polygon)
         else:
             if ticks.f_var not in self.ranges_opposite_polygon_dic_list:
                 self.ranges_opposite_polygon_dic_list[ticks.f_var] = [polygon]
             else:
                 self.ranges_opposite_polygon_dic_list[ticks.f_var].append(polygon)
Ejemplo n.º 4
0
class WidthPolygon(object):
    def __init__(self, ax, x, y, *args, **kwargs):

        self.ax, self.x, self.y = ax, x, y
        self.data = list(zip(x,
                             y))  # [(x[0],y[0])] + zip(x,y) + [(x[-1],y[-1])]

        self.polygon = Polygon(self.data, *args, **kwargs)
        self.ax.add_patch(self.polygon)
        self.continuum, = self.ax.plot([x[0], x[-1]], [y[0], y[-1]],
                                       color='black',
                                       lw=1)

    def set_data(self, x, y):

        self.data = list(zip(x, y))
        self.polygon.set_xy(self.data)
        self.continuum.set_data([[x[0], x[-1]], [y[0], y[-1]]])

    def set_color(self, color):

        self.polygon.set_facecolor(color)

    def set_visible(self, visible):

        self.polygon.set_visible(visible)
        self.continuum.set_visible(visible)

    def __setattr__(self, name, value):

        if name == 'zoom_ignore':

            self.polygon.zoom_ignore = value
            self.continuum.zoom_ignore = value

        else:

            object.__setattr__(self, name, value)

    def delete(self):

        self.ax.patches.remove(self.polygon)
        self.ax.lines.remove(self.continuum)
Ejemplo n.º 5
0
class MplPolygonalROI(AbstractMplRoi):
    """
    Matplotlib ROI for polygon selections

    Parameters
    ----------
    axes : `~matplotlib.axes.Axes`
        The Matplotlib axes to draw to.
    roi : `~glue.core.roi.Roi`, optional
        If specified, this ROI will be used and updated, otherwise a new one
        will be created.
    """

    _roi_cls = PolygonalROI

    def __init__(self, axes, roi=None):

        super(MplPolygonalROI, self).__init__(axes, roi=roi)

        self.plot_opts = {'edgecolor': PATCH_COLOR,
                          'facecolor': PATCH_COLOR,
                          'alpha': 0.3}

        self._patch = Polygon(np.array(list(zip([0, 1], [0, 1]))), zorder=100)
        self._patch.set_visible(False)
        self._axes.add_patch(self._patch)

    def _sync_patch(self):
        if self._roi.defined():
            x, y = self._roi.to_polygon()
            self._patch.set_xy(list(zip(x + [x[0]],
                                        y + [y[0]])))
            self._patch.set_visible(True)
            self._patch.set(**self.plot_opts)
        else:
            self._patch.set_visible(False)

    def start_selection(self, event, scrubbing=False):

        if event.inaxes != self._axes:
            return False

        if scrubbing or event.key == SCRUBBING_KEY:
            if not self._roi.defined():
                return False
            elif not self._roi.contains(event.xdata, event.ydata):
                return False

        self._store_previous_roi()
        self._store_background()

        if scrubbing or event.key == SCRUBBING_KEY:
            self._scrubbing = True
            self._cx = event.xdata
            self._cy = event.ydata
        else:
            self.reset()
            self._roi.add_point(event.xdata, event.ydata)

        self._mid_selection = True

        self._sync_patch()
        self._draw()

    def update_selection(self, event):

        if not self._mid_selection or event.inaxes != self._axes:
            return False

        if event.key == SCRUBBING_KEY:
            if not self._roi.defined():
                return False

        if self._scrubbing:
            self._roi.move_to(event.xdata - self._cx,
                              event.ydata - self._cy)
            self._cx = event.xdata
            self._cy = event.ydata
        else:
            self._roi.add_point(event.xdata, event.ydata)

        self._sync_patch()
        self._draw()

    def finalize_selection(self, event):
        self._scrubbing = False
        self._mid_selection = False
        self._patch.set_visible(False)
        self._draw()
Ejemplo n.º 6
0
class ScatterPlotWidget(wx.Panel, ManageBusyCursorMixin):
    def __init__(self, parent, scatt_id, scatt_mgr, transpose, id=wx.ID_ANY):
        # TODO should not be transpose and scatt_id but x, y
        wx.Panel.__init__(self, parent, id)
        # bacause of aui (if floatable it can not take cursor from parent)
        ManageBusyCursorMixin.__init__(self, window=self)

        self.parent = parent
        self.full_extend = None
        self.mode = None

        self._createWidgets()
        self._doLayout()
        self.scatt_id = scatt_id
        self.scatt_mgr = scatt_mgr

        self.cidpress = None
        self.cidrelease = None

        self.rend_dt = {}

        self.transpose = transpose

        self.inverse = False

        self.SetSize((200, 100))
        self.Layout()

        self.base_scale = 1.2
        self.Bind(wx.EVT_CLOSE, lambda event: self.CleanUp())

        self.plotClosed = Signal("ScatterPlotWidget.plotClosed")
        self.cursorMove = Signal("ScatterPlotWidget.cursorMove")

        self.contex_menu = ScatterPlotContextMenu(plot=self)

        self.ciddscroll = None

        self.canvas.mpl_connect('motion_notify_event', self.Motion)
        self.canvas.mpl_connect('button_press_event', self.OnPress)
        self.canvas.mpl_connect('button_release_event', self.OnRelease)
        self.canvas.mpl_connect('draw_event', self.DrawCallback)
        self.canvas.mpl_connect('figure_leave_event', self.OnCanvasLeave)

    def DrawCallback(self, event):
        self.polygon_drawer.DrawCallback(event)
        self.axes.draw_artist(self.zoom_rect)

    def _createWidgets(self):

        # Create the mpl Figure and FigCanvas objects.
        # 5x4 inches, 100 dots-per-inch
        #
        self.dpi = 100
        self.fig = Figure((1.0, 1.0), dpi=self.dpi)
        self.fig.autolayout = True

        self.canvas = FigCanvas(self, -1, self.fig)

        self.axes = self.fig.add_axes([0.0, 0.0, 1, 1])

        pol = Polygon(list(zip([0], [0])), animated=True)
        self.axes.add_patch(pol)
        self.polygon_drawer = PolygonDrawer(self.axes, pol=pol, empty_pol=True)

        self.zoom_wheel_coords = None
        self.zoom_rect_coords = None
        self.zoom_rect = Polygon(list(zip([0], [0])), facecolor='none')
        self.zoom_rect.set_visible(False)
        self.axes.add_patch(self.zoom_rect)

    def ZoomToExtend(self):
        if self.full_extend:
            self.axes.axis(self.full_extend)
            self.canvas.draw()

    def SetMode(self, mode):
        self._deactivateMode()
        if mode == 'zoom':
            self.ciddscroll = self.canvas.mpl_connect('scroll_event',
                                                      self.ZoomWheel)
            self.mode = 'zoom'
        elif mode == 'zoom_extend':
            self.mode = 'zoom_extend'
        elif mode == 'pan':
            self.mode = 'pan'
        elif mode:
            self.polygon_drawer.SetMode(mode)

    def SetSelectionPolygonMode(self, activate):
        self.polygon_drawer.SetSelectionPolygonMode(activate)

    def _deactivateMode(self):
        self.mode = None
        self.polygon_drawer.SetMode(None)

        if self.ciddscroll:
            self.canvas.mpl_disconnect(self.ciddscroll)

        self.zoom_rect.set_visible(False)
        self._stopCategoryEdit()

    def GetCoords(self):

        coords = self.polygon_drawer.GetCoords()
        if coords is None:
            return

        if self.transpose:
            for c in coords:
                tmp = c[0]
                c[0] = c[1]
                c[1] = tmp

        return coords

    def SetEmpty(self):
        return self.polygon_drawer.SetEmpty()

    def OnRelease(self, event):
        if not self.mode == "zoom":
            return
        self.zoom_rect.set_visible(False)
        self.ZoomRectangle(event)
        self.canvas.draw()

    def OnPress(self, event):
        'on button press we will see if the mouse is over us and store some data'
        if not event.inaxes:
            return
        if self.mode == "zoom_extend":
            self.ZoomToExtend()

        if event.xdata and event.ydata:
            self.zoom_wheel_coords = {'x': event.xdata, 'y': event.ydata}
            self.zoom_rect_coords = {'x': event.xdata, 'y': event.ydata}
        else:
            self.zoom_wheel_coords = None
            self.zoom_rect_coords = None

    def _stopCategoryEdit(self):
        'disconnect all the stored connection ids'

        if self.cidpress:
            self.canvas.mpl_disconnect(self.cidpress)
        if self.cidrelease:
            self.canvas.mpl_disconnect(self.cidrelease)
        # self.canvas.mpl_disconnect(self.cidmotion)

    def _doLayout(self):

        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
        self.main_sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        self.SetSizer(self.main_sizer)
        self.main_sizer.Fit(self)

    def Plot(self, cats_order, scatts, ellipses, styles):
        """Redraws the figure
        """

        callafter_list = []

        if self.full_extend:
            cx = self.axes.get_xlim()
            cy = self.axes.get_ylim()
            c = cx + cy
        else:
            c = None

        q = Queue()
        _rendDtMemmapsToFiles(self.rend_dt)
        p = Process(target=MergeImg,
                    args=(cats_order, scatts, styles, self.rend_dt, q))
        p.start()
        merged_img, self.full_extend, self.rend_dt = q.get()
        p.join()

        _rendDtFilesToMemmaps(self.rend_dt)
        merged_img = np.memmap(filename=merged_img['dt'],
                               shape=merged_img['sh'])

        #merged_img, self.full_extend = MergeImg(cats_order, scatts, styles, None)
        self.axes.clear()
        self.axes.axis('equal')

        if self.transpose:
            merged_img = np.transpose(merged_img, (1, 0, 2))

        img = imshow(self.axes,
                     merged_img,
                     extent=[int(ceil(x)) for x in self.full_extend],
                     origin='lower',
                     interpolation='nearest',
                     aspect="equal")

        callafter_list.append([self.axes.draw_artist, [img]])
        callafter_list.append([grass.try_remove, [merged_img.filename]])

        for cat_id in cats_order:
            if cat_id == 0:
                continue
            if cat_id not in ellipses:
                continue

            e = ellipses[cat_id]
            if not e:
                continue

            colors = styles[cat_id]['color'].split(":")
            if self.transpose:
                e['theta'] = 360 - e['theta'] + 90
                if e['theta'] >= 360:
                    e['theta'] = abs(360 - e['theta'])

                e['pos'] = [e['pos'][1], e['pos'][0]]

            ellip = Ellipse(xy=e['pos'],
                            width=e['width'],
                            height=e['height'],
                            angle=e['theta'],
                            edgecolor="w",
                            linewidth=1.5,
                            facecolor='None')
            self.axes.add_artist(ellip)
            callafter_list.append([self.axes.draw_artist, [ellip]])

            color = [
                int(v) / 255.0 for v in styles[cat_id]['color'].split(":")[:3]
            ]

            ellip = Ellipse(xy=e['pos'],
                            width=e['width'],
                            height=e['height'],
                            angle=e['theta'],
                            edgecolor=color,
                            linewidth=1,
                            facecolor='None')

            self.axes.add_artist(ellip)
            callafter_list.append([self.axes.draw_artist, [ellip]])

            center = Line2D(
                [e['pos'][0]],
                [e['pos'][1]],
                marker='x',
                markeredgecolor='w',
                # markerfacecolor=color,
                markersize=2)
            self.axes.add_artist(center)
            callafter_list.append([self.axes.draw_artist, [center]])

        callafter_list.append([self.fig.canvas.blit, []])

        if c:
            self.axes.axis(c)
        wx.CallAfter(lambda: self.CallAfter(callafter_list))

    def CallAfter(self, funcs_list):
        while funcs_list:
            fcn, args = funcs_list.pop(0)
            fcn(*args)

        self.canvas.draw()

    def CleanUp(self):
        self.plotClosed.emit(scatt_id=self.scatt_id)
        self.Destroy()

    def ZoomWheel(self, event):
        # get the current x and y limits
        if not event.inaxes:
            return
        # tcaswell
        # http://stackoverflow.com/questions/11551049/matplotlib-plot-zooming-with-scroll-wheel
        cur_xlim = self.axes.get_xlim()
        cur_ylim = self.axes.get_ylim()

        xdata = event.xdata
        ydata = event.ydata
        if event.button == 'up':
            scale_factor = 1 / self.base_scale
        elif event.button == 'down':
            scale_factor = self.base_scale
        else:
            scale_factor = 1

        extend = (xdata - (xdata - cur_xlim[0]) * scale_factor,
                  xdata + (cur_xlim[1] - xdata) * scale_factor,
                  ydata - (ydata - cur_ylim[0]) * scale_factor,
                  ydata + (cur_ylim[1] - ydata) * scale_factor)

        self.axes.axis(extend)

        self.canvas.draw()

    def ZoomRectangle(self, event):
        # get the current x and y limits
        if not self.mode == "zoom":
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        cur_xlim = self.axes.get_xlim()
        cur_ylim = self.axes.get_ylim()

        x1, y1 = event.xdata, event.ydata
        x2 = deepcopy(self.zoom_rect_coords['x'])
        y2 = deepcopy(self.zoom_rect_coords['y'])

        if x1 == x2 or y1 == y2:
            return

        if x1 > x2:
            tmp = x1
            x1 = x2
            x2 = tmp

        if y1 > y2:
            tmp = y1
            y1 = y2
            y2 = tmp

        self.axes.axis((x1, x2, y1, y2))
        # self.axes.set_xlim(x1, x2)#, auto = True)
        # self.axes.set_ylim(y1, y2)#, auto = True)
        self.canvas.draw()

    def Motion(self, event):
        self.PanMotion(event)
        self.ZoomRectMotion(event)

        if event.inaxes is None:
            return

        self.cursorMove.emit(x=event.xdata,
                             y=event.ydata,
                             scatt_id=self.scatt_id)

    def OnCanvasLeave(self, event):
        self.cursorMove.emit(x=None, y=None, scatt_id=self.scatt_id)

    def PanMotion(self, event):
        'on mouse movement'
        if not self.mode == "pan":
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        cur_xlim = self.axes.get_xlim()
        cur_ylim = self.axes.get_ylim()

        x, y = event.xdata, event.ydata

        mx = (x - self.zoom_wheel_coords['x']) * 0.6
        my = (y - self.zoom_wheel_coords['y']) * 0.6

        extend = (cur_xlim[0] - mx, cur_xlim[1] - mx, cur_ylim[0] - my,
                  cur_ylim[1] - my)

        self.zoom_wheel_coords['x'] = x
        self.zoom_wheel_coords['y'] = y

        self.axes.axis(extend)

        # self.canvas.copy_from_bbox(self.axes.bbox)
        # self.canvas.restore_region(self.background)
        self.canvas.draw()

    def ZoomRectMotion(self, event):
        if not self.mode == "zoom":
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        x1, y1 = event.xdata, event.ydata
        self.zoom_rect.set_visible(True)
        x2 = self.zoom_rect_coords['x']
        y2 = self.zoom_rect_coords['y']

        self.zoom_rect.xy = ((x1, y1), (x1, y2), (x2, y2), (x2, y1), (x1, y1))

        # self.axes.draw_artist(self.zoom_rect)
        self.canvas.draw()
Ejemplo n.º 7
0
class ScatterPlotWidget(wx.Panel, ManageBusyCursorMixin):

    def __init__(self, parent, scatt_id, scatt_mgr, transpose,
                 id=wx.ID_ANY):
        # TODO should not be transpose and scatt_id but x, y
        wx.Panel.__init__(self, parent, id)
        # bacause of aui (if floatable it can not take cursor from parent)
        ManageBusyCursorMixin.__init__(self, window=self)

        self.parent = parent
        self.full_extend = None
        self.mode = None

        self._createWidgets()
        self._doLayout()
        self.scatt_id = scatt_id
        self.scatt_mgr = scatt_mgr

        self.cidpress = None
        self.cidrelease = None

        self.rend_dt = {}

        self.transpose = transpose

        self.inverse = False

        self.SetSize((200, 100))
        self.Layout()

        self.base_scale = 1.2
        self.Bind(wx.EVT_CLOSE, lambda event: self.CleanUp())

        self.plotClosed = Signal("ScatterPlotWidget.plotClosed")
        self.cursorMove = Signal("ScatterPlotWidget.cursorMove")

        self.contex_menu = ScatterPlotContextMenu(plot=self)

        self.ciddscroll = None

        self.canvas.mpl_connect('motion_notify_event', self.Motion)
        self.canvas.mpl_connect('button_press_event', self.OnPress)
        self.canvas.mpl_connect('button_release_event', self.OnRelease)
        self.canvas.mpl_connect('draw_event', self.DrawCallback)
        self.canvas.mpl_connect('figure_leave_event', self.OnCanvasLeave)

    def DrawCallback(self, event):
        self.polygon_drawer.DrawCallback(event)
        self.axes.draw_artist(self.zoom_rect)

    def _createWidgets(self):

        # Create the mpl Figure and FigCanvas objects.
        # 5x4 inches, 100 dots-per-inch
        #
        self.dpi = 100
        self.fig = Figure((1.0, 1.0), dpi=self.dpi)
        self.fig.autolayout = True

        self.canvas = FigCanvas(self, -1, self.fig)

        self.axes = self.fig.add_axes([0.0, 0.0, 1, 1])

        pol = Polygon(list(zip([0], [0])), animated=True)
        self.axes.add_patch(pol)
        self.polygon_drawer = PolygonDrawer(self.axes, pol=pol, empty_pol=True)

        self.zoom_wheel_coords = None
        self.zoom_rect_coords = None
        self.zoom_rect = Polygon(list(zip([0], [0])), facecolor='none')
        self.zoom_rect.set_visible(False)
        self.axes.add_patch(self.zoom_rect)

    def ZoomToExtend(self):
        if self.full_extend:
            self.axes.axis(self.full_extend)
            self.canvas.draw()

    def SetMode(self, mode):
        self._deactivateMode()
        if mode == 'zoom':
            self.ciddscroll = self.canvas.mpl_connect(
                'scroll_event', self.ZoomWheel)
            self.mode = 'zoom'
        elif mode == 'zoom_extend':
            self.mode = 'zoom_extend'
        elif mode == 'pan':
            self.mode = 'pan'
        elif mode:
            self.polygon_drawer.SetMode(mode)

    def SetSelectionPolygonMode(self, activate):
        self.polygon_drawer.SetSelectionPolygonMode(activate)

    def _deactivateMode(self):
        self.mode = None
        self.polygon_drawer.SetMode(None)

        if self.ciddscroll:
            self.canvas.mpl_disconnect(self.ciddscroll)

        self.zoom_rect.set_visible(False)
        self._stopCategoryEdit()

    def GetCoords(self):

        coords = self.polygon_drawer.GetCoords()
        if coords is None:
            return

        if self.transpose:
            for c in coords:
                tmp = c[0]
                c[0] = c[1]
                c[1] = tmp

        return coords

    def SetEmpty(self):
        return self.polygon_drawer.SetEmpty()

    def OnRelease(self, event):
        if not self.mode == "zoom":
            return
        self.zoom_rect.set_visible(False)
        self.ZoomRectangle(event)
        self.canvas.draw()

    def OnPress(self, event):
        'on button press we will see if the mouse is over us and store some data'
        if not event.inaxes:
            return
        if self.mode == "zoom_extend":
            self.ZoomToExtend()

        if event.xdata and event.ydata:
            self.zoom_wheel_coords = {'x': event.xdata, 'y': event.ydata}
            self.zoom_rect_coords = {'x': event.xdata, 'y': event.ydata}
        else:
            self.zoom_wheel_coords = None
            self.zoom_rect_coords = None

    def _stopCategoryEdit(self):
        'disconnect all the stored connection ids'

        if self.cidpress:
            self.canvas.mpl_disconnect(self.cidpress)
        if self.cidrelease:
            self.canvas.mpl_disconnect(self.cidrelease)
        # self.canvas.mpl_disconnect(self.cidmotion)

    def _doLayout(self):

        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
        self.main_sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        self.SetSizer(self.main_sizer)
        self.main_sizer.Fit(self)

    def Plot(self, cats_order, scatts, ellipses, styles):
        """Redraws the figure
        """

        callafter_list = []

        if self.full_extend:
            cx = self.axes.get_xlim()
            cy = self.axes.get_ylim()
            c = cx + cy
        else:
            c = None

        q = Queue()
        _rendDtMemmapsToFiles(self.rend_dt)
        p = Process(target=MergeImg, args=(cats_order, scatts, styles,
                                           self.rend_dt, q))
        p.start()
        merged_img, self.full_extend, self.rend_dt = q.get()
        p.join()

        _rendDtFilesToMemmaps(self.rend_dt)
        merged_img = np.memmap(
            filename=merged_img['dt'],
            shape=merged_img['sh'])

        #merged_img, self.full_extend = MergeImg(cats_order, scatts, styles, None)
        self.axes.clear()
        self.axes.axis('equal')

        if self.transpose:
            merged_img = np.transpose(merged_img, (1, 0, 2))

        img = imshow(self.axes, merged_img,
                     extent=[int(ceil(x)) for x in self.full_extend],
                     origin='lower',
                     interpolation='nearest',
                     aspect="equal")

        callafter_list.append([self.axes.draw_artist, [img]])
        callafter_list.append([grass.try_remove, [merged_img.filename]])

        for cat_id in cats_order:
            if cat_id == 0:
                continue
            if cat_id not in ellipses:
                continue

            e = ellipses[cat_id]
            if not e:
                continue

            colors = styles[cat_id]['color'].split(":")
            if self.transpose:
                e['theta'] = 360 - e['theta'] + 90
                if e['theta'] >= 360:
                    e['theta'] = abs(360 - e['theta'])

                e['pos'] = [e['pos'][1], e['pos'][0]]

            ellip = Ellipse(xy=e['pos'],
                            width=e['width'],
                            height=e['height'],
                            angle=e['theta'],
                            edgecolor="w",
                            linewidth=1.5,
                            facecolor='None')
            self.axes.add_artist(ellip)
            callafter_list.append([self.axes.draw_artist, [ellip]])

            color = map(
                lambda v: int(v) / 255.0,
                styles[cat_id]['color'].split(":"))

            ellip = Ellipse(xy=e['pos'],
                            width=e['width'],
                            height=e['height'],
                            angle=e['theta'],
                            edgecolor=color,
                            linewidth=1,
                            facecolor='None')

            self.axes.add_artist(ellip)
            callafter_list.append([self.axes.draw_artist, [ellip]])

            center = Line2D([e['pos'][0]], [e['pos'][1]],
                            marker='x',
                            markeredgecolor='w',
                            # markerfacecolor=color,
                            markersize=2)
            self.axes.add_artist(center)
            callafter_list.append([self.axes.draw_artist, [center]])

        callafter_list.append([self.fig.canvas.blit, []])

        if c:
            self.axes.axis(c)
        wx.CallAfter(lambda: self.CallAfter(callafter_list))

    def CallAfter(self, funcs_list):
        while funcs_list:
            fcn, args = funcs_list.pop(0)
            fcn(*args)

        self.canvas.draw()

    def CleanUp(self):
        self.plotClosed.emit(scatt_id=self.scatt_id)
        self.Destroy()

    def ZoomWheel(self, event):
        # get the current x and y limits
        if not event.inaxes:
            return
        # tcaswell
        # http://stackoverflow.com/questions/11551049/matplotlib-plot-zooming-with-scroll-wheel
        cur_xlim = self.axes.get_xlim()
        cur_ylim = self.axes.get_ylim()

        xdata = event.xdata
        ydata = event.ydata
        if event.button == 'up':
            scale_factor = 1 / self.base_scale
        elif event.button == 'down':
            scale_factor = self.base_scale
        else:
            scale_factor = 1

        extend = (xdata - (xdata - cur_xlim[0]) * scale_factor,
                  xdata + (cur_xlim[1] - xdata) * scale_factor,
                  ydata - (ydata - cur_ylim[0]) * scale_factor,
                  ydata + (cur_ylim[1] - ydata) * scale_factor)

        self.axes.axis(extend)

        self.canvas.draw()

    def ZoomRectangle(self, event):
        # get the current x and y limits
        if not self.mode == "zoom":
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        cur_xlim = self.axes.get_xlim()
        cur_ylim = self.axes.get_ylim()

        x1, y1 = event.xdata, event.ydata
        x2 = deepcopy(self.zoom_rect_coords['x'])
        y2 = deepcopy(self.zoom_rect_coords['y'])

        if x1 == x2 or y1 == y2:
            return

        if x1 > x2:
            tmp = x1
            x1 = x2
            x2 = tmp

        if y1 > y2:
            tmp = y1
            y1 = y2
            y2 = tmp

        self.axes.axis((x1, x2, y1, y2))
        # self.axes.set_xlim(x1, x2)#, auto = True)
        # self.axes.set_ylim(y1, y2)#, auto = True)
        self.canvas.draw()

    def Motion(self, event):
        self.PanMotion(event)
        self.ZoomRectMotion(event)

        if event.inaxes is None:
            return

        self.cursorMove.emit(
            x=event.xdata,
            y=event.ydata,
            scatt_id=self.scatt_id)

    def OnCanvasLeave(self, event):
        self.cursorMove.emit(x=None, y=None, scatt_id=self.scatt_id)

    def PanMotion(self, event):
        'on mouse movement'
        if not self.mode == "pan":
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        cur_xlim = self.axes.get_xlim()
        cur_ylim = self.axes.get_ylim()

        x, y = event.xdata, event.ydata

        mx = (x - self.zoom_wheel_coords['x']) * 0.6
        my = (y - self.zoom_wheel_coords['y']) * 0.6

        extend = (
            cur_xlim[0] - mx,
            cur_xlim[1] - mx,
            cur_ylim[0] - my,
            cur_ylim[1] - my)

        self.zoom_wheel_coords['x'] = x
        self.zoom_wheel_coords['y'] = y

        self.axes.axis(extend)

        # self.canvas.copy_from_bbox(self.axes.bbox)
        # self.canvas.restore_region(self.background)
        self.canvas.draw()

    def ZoomRectMotion(self, event):
        if not self.mode == "zoom":
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        x1, y1 = event.xdata, event.ydata
        self.zoom_rect.set_visible(True)
        x2 = self.zoom_rect_coords['x']
        y2 = self.zoom_rect_coords['y']

        self.zoom_rect.xy = ((x1, y1), (x1, y2), (x2, y2), (x2, y1), (x1, y1))

        # self.axes.draw_artist(self.zoom_rect)
        self.canvas.draw()
Ejemplo n.º 8
0
class MplPolygonalROI(AbstractMplRoi):
    """
    Defines and displays polygonal ROIs on matplotlib plots

    Attributes:

        plot_opts: Dictionary instance
                   A dictionary of plot keywords that are passed to
                   the patch representing the ROI. These control
                   the visual properties of the ROI
    """
    def __init__(self, axes, roi=None):
        """
        :param axes: A matplotlib Axes object to attach the graphical ROI to
        """
        AbstractMplRoi.__init__(self, axes, roi=roi)
        self.plot_opts = {
            'edgecolor': PATCH_COLOR,
            'facecolor': PATCH_COLOR,
            'alpha': 0.3
        }

        self._setup_patch()

    def _setup_patch(self):
        self._patch = Polygon(np.array(list(zip([0, 1], [0, 1]))))
        self._patch.set_zorder(100)
        self._patch.set(**self.plot_opts)
        self._axes.add_patch(self._patch)
        self._patch.set_visible(False)
        self._sync_patch()

    def _roi_factory(self):
        return PolygonalROI()

    def _sync_patch(self):
        # Update geometry
        if not self._roi.defined():
            self._patch.set_visible(False)
        else:
            x, y = self._roi.to_polygon()
            self._patch.set_xy(list(zip(x + [x[0]], y + [y[0]])))
            self._patch.set_visible(True)

        # Update appearance
        self._patch.set(**self.plot_opts)

        # Refresh
        self._axes.figure.canvas.draw()

    def start_selection(self, event, scrubbing=False):

        if event.inaxes != self._axes:
            return False

        if scrubbing or event.key == SCRUBBING_KEY:
            if not self._roi.defined():
                return False
            elif not self._roi.contains(event.xdata, event.ydata):
                return False

        self._roi_store()

        if scrubbing or event.key == SCRUBBING_KEY:
            self._scrubbing = True
            self._cx = event.xdata
            self._cy = event.ydata
        else:
            self.reset()
            self._roi.add_point(event.xdata, event.ydata)

        self._mid_selection = True
        self._sync_patch()

    def update_selection(self, event):

        if not self._mid_selection or event.inaxes != self._axes:
            return False

        if event.key == SCRUBBING_KEY:
            if not self._roi.defined():
                return False

        if self._scrubbing:
            self._roi.move_to(event.xdata - self._cx, event.ydata - self._cy)
            self._cx = event.xdata
            self._cy = event.ydata
        else:
            self._roi.add_point(event.xdata, event.ydata)

        self._sync_patch()

    def finalize_selection(self, event):
        self._scrubbing = False
        self._mid_selection = False
        self._patch.set_visible(False)
        self._axes.figure.canvas.draw()
Ejemplo n.º 9
0
Archivo: roi.py Proyecto: saimn/glue
class MplPolygonalROI(AbstractMplRoi):

    """
    Defines and displays polygonal ROIs on matplotlib plots

    Attributes:

        plot_opts: Dictionary instance
                   A dictionary of plot keywords that are passed to
                   the patch representing the ROI. These control
                   the visual properties of the ROI
    """

    def __init__(self, axes):
        """
        :param axes: A matplotlib Axes object to attach the graphical ROI to
        """
        AbstractMplRoi.__init__(self, axes)
        self.plot_opts = {'edgecolor': PATCH_COLOR, 'facecolor': PATCH_COLOR,
                          'alpha': 0.3}

        self._setup_patch()

    def _setup_patch(self):
        self._patch = Polygon(np.array(list(zip([0, 1], [0, 1]))))
        self._patch.set_zorder(100)
        self._patch.set(**self.plot_opts)
        self._axes.add_patch(self._patch)
        self._patch.set_visible(False)
        self._sync_patch()

    def _roi_factory(self):
        return PolygonalROI()

    def _sync_patch(self):
        # Update geometry
        if not self._roi.defined():
            self._patch.set_visible(False)
        else:
            x, y = self._roi.to_polygon()
            self._patch.set_xy(list(zip(x + [x[0]],
                                        y + [y[0]])))
            self._patch.set_visible(True)

        # Update appearance
        self._patch.set(**self.plot_opts)

        # Refresh
        self._axes.figure.canvas.draw()

    def start_selection(self, event):

        if event.inaxes != self._axes:
            return False

        if event.key == SCRUBBING_KEY:
            if not self._roi.defined():
                return False
            elif not self._roi.contains(event.xdata, event.ydata):
                return False

        self._roi_store()

        if event.key == SCRUBBING_KEY:
            self._scrubbing = True
            self._cx = event.xdata
            self._cy = event.ydata
        else:
            self.reset()
            self._roi.add_point(event.xdata, event.ydata)

        self._mid_selection = True
        self._sync_patch()

    def update_selection(self, event):

        if not self._mid_selection or event.inaxes != self._axes:
            return False

        if event.key == SCRUBBING_KEY:
            if not self._roi.defined():
                return False

        if self._scrubbing:
            self._roi.move_to(event.xdata - self._cx,
                              event.ydata - self._cy)
            self._cx = event.xdata
            self._cy = event.ydata
        else:
            self._roi.add_point(event.xdata, event.ydata)

        self._sync_patch()

    def finalize_selection(self, event):
        self._scrubbing = False
        self._mid_selection = False
        self._patch.set_visible(False)
        self._axes.figure.canvas.draw()
Ejemplo n.º 10
0
Archivo: roi.py Proyecto: glue-viz/glue
class MplPolygonalROI(AbstractMplRoi):
    """
    Matplotlib ROI for polygon selections

    Parameters
    ----------
    axes : `~matplotlib.axes.Axes`
        The Matplotlib axes to draw to.
    roi : `~glue.core.roi.Roi`, optional
        If specified, this ROI will be used and updated, otherwise a new one
        will be created.
    """

    _roi_cls = PolygonalROI

    def __init__(self, axes, roi=None):

        super(MplPolygonalROI, self).__init__(axes, roi=roi)

        self.plot_opts = {'edgecolor': PATCH_COLOR,
                          'facecolor': PATCH_COLOR,
                          'alpha': 0.3}

        self._patch = Polygon(np.array(list(zip([0, 1], [0, 1]))), zorder=100)
        self._patch.set_visible(False)
        self._axes.add_patch(self._patch)

    def _sync_patch(self):
        if self._roi.defined():
            x, y = self._roi.to_polygon()
            self._patch.set_xy(list(zip(x + [x[0]],
                                        y + [y[0]])))
            self._patch.set_visible(True)
            self._patch.set(**self.plot_opts)
        else:
            self._patch.set_visible(False)

    def start_selection(self, event, scrubbing=False):

        if event.inaxes != self._axes:
            return False

        if scrubbing or event.key == SCRUBBING_KEY:
            if not self._roi.defined():
                return False
            elif not self._roi.contains(event.xdata, event.ydata):
                return False

        self._store_previous_roi()
        self._store_background()

        if scrubbing or event.key == SCRUBBING_KEY:
            self._scrubbing = True
            self._cx = event.xdata
            self._cy = event.ydata
        else:
            self.reset()
            self._roi.add_point(event.xdata, event.ydata)

        self._mid_selection = True

        self._sync_patch()
        self._draw()

    def update_selection(self, event):

        if not self._mid_selection or event.inaxes != self._axes:
            return False

        if event.key == SCRUBBING_KEY:
            if not self._roi.defined():
                return False

        if self._scrubbing:
            self._roi.move_to(event.xdata - self._cx,
                              event.ydata - self._cy)
            self._cx = event.xdata
            self._cy = event.ydata
        else:
            self._roi.add_point(event.xdata, event.ydata)

        self._sync_patch()
        self._draw()

    def finalize_selection(self, event):
        self._scrubbing = False
        self._mid_selection = False
        self._patch.set_visible(False)
        self._draw()
Ejemplo n.º 11
0
class PolygonEditor(object):
    '''
    This edits the polygons drawn on the map
    '''

    show_verts = True
    epsilon = 3  #threshold

    def __init__(self, axis, canvas):
        '''
        initialises the editable polygon object
        '''
        self.axis = axis
        self.polygon = None
        self.line = None
        self.xy_values = np.array([])
        self._ind = None
        self.background = None  #background copying

        self._callback_ids = list()
        self._callback_ids.append(
            canvas.mpl_connect('draw_event', self.draw_callback))
        self._callback_ids.append(
            canvas.mpl_connect('button_press_event',
                               self.button_press_callback))
        self._callback_ids.append(
            canvas.mpl_connect('button_release_event',
                               self.button_release_callback))
        self._callback_ids.append(
            canvas.mpl_connect('motion_notify_event',
                               self.motion_notify_callback))
        self.canvas = canvas

    def add_point(self, xval, yval):
        """ adds an new point to the selection list and redraws the selection tool"""
        if self.xy_values.shape[0] == 0:
            self.xy_values = np.array([
                (xval, yval),
            ])
        else:
            self.xy_values = np.concatenate((self.xy_values, [
                [xval, yval],
            ]),
                                            axis=0)
        self.refresh()

    def refresh(self):
        """ This method looks at the list of points available and depending on the number of
        points in the list creates a point or line or a polygon and draws them"""
        if self.xy_values.shape[0] == 0:  # No points available
            self.reset_line()
            self.reset_polygon()
        elif self.xy_values.shape[0] <= 2:  # point or line for 1 or 2 points
            xval, yval = zip(*self.xy_values)
            if self.line == None:
                self.line = Line2D(xval,
                                   yval,
                                   marker='o',
                                   markerfacecolor='r',
                                   animated=True)
                self.axis.add_line(self.line)
            else:
                self.line.set_data(zip(*self.xy_values))
            self.reset_polygon()
        else:  # more than 2 points if polygon is not created then creates one and draws
            if self.polygon == None:
                self.polygon = Polygon(self.xy_values,
                                       animated=True,
                                       alpha=polygon_alpha)
                self.polygon.add_callback(self.polygon_changed)
                self.axis.add_patch(self.polygon)
            else:
                self.polygon.xy = self.xy_values
            self.line.set_data(zip(*self.xy_values))
        self.draw_callback(None)
        self.canvas.draw()

    def reset_line(self):
        """ resets the line i.e removes the line from the axes and resets to None """
        if self.line != None:
            self.line.remove()
            self.line = None

    def reset_polygon(self):
        """ resets the polygon ie. removes the polygon from the axis and reset to None """
        if self.polygon != None:
            self.polygon.remove()
            self.polygon = None

    def draw_line(self):
        """ draws the line if available """
        if self.line != None:
            self.axis.draw_artist(self.line)

    def draw_polygon(self):
        """ draws polygon if available"""
        if self.polygon != None:
            self.axis.draw_artist(self.polygon)

    def disable(self):
        """ disables the events and the selection """
        self.set_visibility(False)
        for callback_id in self._callback_ids:
            self.canvas.mpl_disconnect(callback_id)
        self.canvas = None

    def enable(self):
        """ enables the selection """
        self.set_visibility(True)

    def set_visibility(self, status):
        """ sets the visibility of the selection object """
        if self.polygon != None:
            self.polygon.set_visible(status)
        if self.line != None:
            self.line.set_visible(status)
        self.canvas.blit(self.axis.bbox)

    def draw_callback(self, dummy_event):
        """ this method draws the selection object """
        self.background = self.canvas.copy_from_bbox(self.axis.bbox)
        self.draw_polygon()
        self.draw_line()
        self.canvas.blit(self.axis.bbox)

    def polygon_changed(self, poly):
        """ redraws the polygon """
        vis = self.line.get_visible()
        Artist.update_from(self.line, poly)
        self.line.set_visible(vis)

    def get_index_under_point(self, event):
        """ gets the index of the point under the event (mouse click) """
        if self.xy_values.shape[0] == 0:
            return None
        xy_values = self.xy_values
        xt_values, yt_values = xy_values[:, 0], xy_values[:, 1]
        dist = np.sqrt((xt_values - event.xdata)**2 +
                       (yt_values - event.ydata)**2)
        indseq = np.nonzero(np.equal(dist, np.amin(dist)))[0]
        ind = indseq[0]
        if dist[ind] >= self.epsilon:
            ind = None
        return ind

    def button_press_callback(self, event):
        """ callback to mouse press event """
        if not self.show_verts:
            return
        if event.inaxes == None:
            return
        if event.button != 1:
            return
        self._ind = self.get_index_under_point(event)
        if self._ind == None:
            self.insert_datapoint(event)

    def button_release_callback(self, event):
        """ callback to mouse release event """
        if not self.show_verts:
            return
        if event.button == 2:
            self.insert_datapoint(event)
            return
        if event.button == 3:
            self.delete_datapoint(event)
            return
        if event.button != 1:
            return
        self._ind = None

    def insert_datapoint(self, event):
        """ inserts a new data point between the segment that is closest in polygon """
        if self.xy_values.shape[0] <= 2:
            self.add_point(event.xdata, event.ydata)
        else:
            event_point = event.xdata, event.ydata
            prev_d = dist_point_to_segment(event_point, self.xy_values[0],
                                           self.xy_values[-1])
            prev_i = len(self.xy_values)
            for i in range(len(self.xy_values) - 1):
                seg_start = self.xy_values[i]
                seg_end = self.xy_values[i + 1]
                dist_p_s = dist_point_to_segment(event_point, seg_start,
                                                 seg_end)
                if dist_p_s < prev_d:
                    prev_i = i
                    prev_d = dist_p_s
            self.xy_values = np.array(
                list(self.xy_values[:prev_i + 1]) +
                [(event.xdata, event.ydata)] +
                list(self.xy_values[prev_i + 1:]))
            self.refresh()

    def delete_datapoint(self, event):
        """ deletes the data point under the point in event """
        ind = self.get_index_under_point(event)
        if ind is not None:
            self.xy_values = np.array(
                [tup for i, tup in enumerate(self.xy_values) if i != ind])
            self.refresh()
        self.canvas.draw()

    def motion_notify_callback(self, event):
        """ callback for the mouse motion with button press.
        this is to move the edge points of the polygon"""
        if not self.show_verts:
            return
        if self._ind is None:
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return
        xval, yval = event.xdata, event.ydata

        self.xy_values[self._ind] = xval, yval
        self.refresh()

        self.canvas.restore_region(self.background)
        self.axis.draw_artist(self.polygon)
        self.axis.draw_artist(self.line)
        self.canvas.blit(self.axis.bbox)

    def reset(self):
        """ resets the points in the selection deleting the line and polygon"""
        self.xy_values = np.array([])
        self.reset_line()
        self.reset_polygon()
Ejemplo n.º 12
0
  def ScatterGraph(self):
    AnnotatedArtists = []

    #
    # Compute the scatter plot for this cluster
    #
    NumberOfFrames  = self.Data.GetNumberOfFrames()
    NumberOfObjects = self.Data.GetNumberOfObjects()
    FirstObject     = self.Data.GetFirstObject() - 2 # Include filtered and noise
    LastObject      = self.Data.GetLastObject()
    TasksPerFrame   = self.Data.GetTasksPerFrame()

    SelectedFrame   = self.GUI.GetSelectedFrame()
    SelectedCluster  = self.GUI.GetSelectedCluster()
    SelectedMetricX = self.GUI.GetSelectedMetricX()
    SelectedMetricY = self.GUI.GetSelectedMetricY()
    if self.GUI.in_3D():
      SelectedMetricZ = self.GUI.GetSelectedMetricZ()

#    cursor = Cursor(self.ScatterPlotAxes, useblit=True, color='black', linewidth=1 )

    for current_frame in range(1, NumberOfFrames+1):

      for current_object in range(FirstObject, LastObject+1):
        (r, g, b) = Decorations.RGBColor0_1(current_object)

        # 
        # Compute the scatter plot for this cluster
        # 
        xdata = self.Data.GetClusterData( current_frame, current_object, SelectedMetricX )
        ydata = self.Data.GetClusterData( current_frame, current_object, SelectedMetricY )
        if (len(xdata) == 0) or (len(ydata) == 0):
          continue
        if self.GUI.in_3D():
          zdata = self.Data.GetClusterData( current_frame, current_object, SelectedMetricZ )
          if (len(zdata) == 0):
            continue

        if (self.GUI.in_Trajectory_View()):
          if self.GUI.RatioX():
            xdata = xdata * TasksPerFrame[current_frame-1]
          if self.GUI.RatioY():
            ydata = ydata * TasksPerFrame[current_frame-1]
          if self.GUI.in_3D() and self.GUI.RatioZ():
            zdata = zdata * TasksPerFrame[current_frame-1]

        if self.GUI.in_3D():
          scatter = self.ScatterPlotAxes.scatter( xdata, ydata, zdata, color=(r, g, b), zorder=2, s=50, marker=Decorations.ChooseMarker(current_object), picker=True )
        else:
          scatter = self.ScatterPlotAxes.scatter( xdata, ydata, color=(r, g, b), zorder=2, s=50, marker=Decorations.ChooseMarker(current_object), picker=True )
        thumb = self.GUI.GetThumbAxes(current_frame).scatter( xdata, ydata, color=(r, g, b), zorder=2, s=100, marker=Decorations.ChooseMarker(current_object))
        thumb.set_visible( False )
        self.Thumbs[(current_object, current_frame)] = thumb

        scatter.set_gid( self.Data.PrettyCluster(current_object) )
        scatter.set_visible( False )
        self.Scatters[(current_object, current_frame)] = scatter
        AnnotatedArtists.append( scatter )

        # 
        # Compute the centroid for this cluster
        # 
        centroid_x = nanmean( xdata )
        centroid_y = nanmean( ydata )
        if self.GUI.in_3D():
          centroid_z = nanmean( zdata )
          centroid = self.ScatterPlotAxes.scatter( centroid_x, centroid_y, centroid_z, s=50, color=(r, g, b), edgecolor="black", marker="o", zorder=3, picker=True )
          self.Trajectories[(current_object, current_frame)] = (centroid_x, centroid_y, centroid_z)
        else:
          centroid = self.ScatterPlotAxes.scatter( centroid_x, centroid_y, s=50, color=(r, g, b), edgecolor="black", marker="o", zorder=3, picker=True)
          self.Trajectories[(current_object, current_frame)] = (centroid_x, centroid_y)

        centroid.set_gid( self.Data.PrettyCluster(current_object) )
        centroid.set_visible(False)
        self.Centroids[(current_object, current_frame)] = centroid
        AnnotatedArtists.append( centroid )

        # 
        # Compute the convex hull for this cluster
        # 
        if (AvailableHulls) and self.GUI.in_2D():
          points = np.array( zip(xdata, ydata) )
          if (len(points) < 3):
            continue

          try:
            hull     = ConvexHull( points, qhull_options='Pp' )
            vertices = hull.vertices
          except:
            vertices = []
 
          if (len(vertices) > 0):
            polygon_points = []
            for vertice in vertices:
              polygon_points.append( (points[vertice][0], points[vertice][1]) )
           
            hull_polygon = Polygon(polygon_points, closed=True, alpha=0.5, color=Decorations.RGBColor0_1(current_object), zorder=4, lw=10)
            hull_polygon.set_gid( self.Data.PrettyCluster(current_object) )
            hull_polygon.set_visible(False)
            self.Hulls[(current_object, current_frame)] = hull_polygon
            self.ScatterPlotAxes.add_artist( hull_polygon )
            AnnotatedArtists.append( hull_polygon )

    # Compute the arrows for the trajectories
    for current_object in range(FirstObject, LastObject+1):
      (r, g, b) = Decorations.RGBColor0_1(current_object)
      from_frame = 1
      to_frame   = from_frame + 1
      while (to_frame <= NumberOfFrames):
        tail = (current_object, from_frame)
        head = (current_object, to_frame)

        if not tail in self.Trajectories:
          from_frame = to_frame
          to_frame   = from_frame +1
          continue
        else:
          if not head in self.Trajectories:
            to_frame = to_frame + 1
            continue

        from_x = self.Trajectories[tail][0]
        from_y = self.Trajectories[tail][1]
        to_x   = self.Trajectories[head][0]
        to_y   = self.Trajectories[head][1]
        if self.GUI.in_3D():
          from_z = self.Trajectories[tail][2]
          to_z   = self.Trajectories[head][2]

        if ((to_x - from_x != 0) or (to_y - from_y != 0) or (self.GUI.in_3D() and (to_z - from_z != 0))):
          if (not (math.isnan(from_x) or math.isnan(from_y) or math.isnan(to_x) or math.isnan(to_y))):
            if (self.GUI.in_3D() and (not (math.isnan(from_z) or math.isnan(to_z)))):
              arrow = Arrow3D((from_x,to_x), (from_y, to_y), (from_z, to_z), arrowstyle='-|>', mutation_scale=20, color=(r, g, b), linewidth=1)
            else:
              arrow = FancyArrowPatch((from_x,from_y), (to_x,to_y), arrowstyle='-|>', mutation_scale=20, color=(r, g, b), linewidth=1)
            arrow.set_visible(False)
            self.Arrows[current_object].append(arrow)
            self.ScatterPlotAxes.add_artist(arrow)

        from_frame = to_frame
        to_frame   = from_frame +1

    self.Annotations = DataCursor(AnnotatedArtists)