# **************************************************************************
# *
# * Authors: Mohamad Harastani (mohamad.harastani@upmc.fr)
# * Slavica Jonic (slavica.jonic@upmc.fr)
# *
# * This program is free software; you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation; either version 2 of the License, or
# * (at your option) any later version.
# *
# * This program is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program; if not, write to the Free Software
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# * 02111-1307 USA
# *
# * All comments concerning this program package may be sent to the
# * e-mail address 'scipion@cnb.csic.es'
# *
# **************************************************************************
from math import sqrt
# from continuousflex.viewers.plotter_vol import plotArray2D
from continuousflex.viewers.plotter_vol import plotArray2D_xy
STATE_NO_POINTS = 0 # no points have been selected, double-click will add first one
STATE_DRAW_POINTS = 1 # still adding points, double-click will set the last one
STATE_ADJUST_POINTS = 2 # no more points will be added, just adjust the current ones
[docs]class PointPathVol():
""" Graphical manager based on Matplotlib to handle mouse
events to create a path of points.
It also allow to modify the point positions on the path.
"""
def __init__(self, ax, data, pathData, callback=None, tolerance=3, maxPoints=10, LimitL = None, LimitH = None,
s = None, alpha = None):
self.ax = ax
self.data = data
# plotArray2D(ax, self.data)
# To avoid plotting the sidebar twice
if(LimitL):
plotArray2D_xy(ax, self.data, vvmin=LimitL, vvmax=LimitH, s= s, alpha= alpha)
else:
plotArray2D_xy(ax, self.data, s= s, alpha= alpha)
self.callback = callback
self.dragIndex = None
self.tolerance = tolerance
self.maxPoints = maxPoints
self.cidpress = ax.figure.canvas.mpl_connect('button_press_event', self.onClick)
self.cidrelease = ax.figure.canvas.mpl_connect('button_release_event', self.onRelease)
self.cidmotion = ax.figure.canvas.mpl_connect('motion_notify_event', self.onMotion)
self.cidkey = ax.figure.canvas.mpl_connect('key_press_event', self.onKeyPress)
self.pathData = pathData
if pathData.getSize(): # this means there is a path
self.setState(STATE_ADJUST_POINTS)
self.plotPath()
else:
self.setState(STATE_NO_POINTS)
self.path_line = None
self.path_points = None
[docs] def setState(self, state, notify=False):
self.drawing = state
if state == STATE_NO_POINTS:
self.ax.set_title('Double click to select first point of trajectory')
elif state == STATE_DRAW_POINTS:
self.ax.set_title('Click to add points and double click to end trajectory.')
elif state == STATE_ADJUST_POINTS:
self.ax.set_title('Click and drag points to adjust trajectory.')
else:
raise Exception("Invalid PointPath state: %d" % state)
if notify and self.callback:
self.callback()
[docs] def onClick(self, event):
if event.inaxes != self.ax:
return
# ignore click event if toolbar is active
if self.ax.get_navigate_mode():
return
doubleClick = event.dblclick
ex = event.xdata
ey = event.ydata
if self.drawing == STATE_NO_POINTS:
if doubleClick:
self.setState(STATE_DRAW_POINTS)
doubleClick = False
if self.drawing == STATE_DRAW_POINTS:
if doubleClick:
self.setState(STATE_ADJUST_POINTS, notify=True)
else:
point = self.pathData.createEmptyPoint()
point.setX(ex)
point.setY(ey)
self.pathData.addPoint(point)
if self.pathData.getSize() == 1: # first point is added
self.plotPath()
else:
xs, ys = self.getXYData()
self.path_line.set_data(xs, ys)
self.path_points.set_data(xs, ys)
self.ax.figure.canvas.draw()
if self.pathData.getSize() == self.maxPoints:
self.setState(STATE_ADJUST_POINTS, notify=True)
if self.drawing == STATE_ADJUST_POINTS and not doubleClick: # Points moving state
self.dragIndex = None
for i, point in enumerate(self.pathData):
x = point.getX()
y = point.getY()
if sqrt((ex - x) ** 2 + (ey - y) ** 2) < self.tolerance:
self.dragIndex = i
break
[docs] def getXYData(self):
xs = self.pathData.getXData()
ys = self.pathData.getYData()
return xs, ys
[docs] def plotPath(self):
xs, ys = self.getXYData()
self.path_line, = self.ax.plot(xs, ys, alpha=0.75, color='green')
self.path_points, = self.ax.plot(xs, ys, 'o', color='red') # 5 points tolerance, mark line points
[docs] def onMotion(self, event):
if self.dragIndex is None or self.drawing < 2:
return
# ignore click event if toolbar is active
if self.ax.get_navigate_mode():
return
ex, ey = event.xdata, event.ydata
point = self.pathData.getPoint(self.dragIndex)
point.setX(ex)
point.setY(ey)
self.update()
[docs] def onRelease(self, event):
self.dragIndex = None
[docs] def onKeyPress(self, event):
n = self.pathData.getSize()
if event.key == 'ctrl+z' and n > 1:
self.pathData.removeLastPoint()
if n == 1: # Removed last point, change state appropriately
self.setState(STATE_NO_POINTS)
self.update()
[docs] def update(self):
xs, ys = self.getXYData()
self.path_line.set_data(xs, ys)
self.path_points.set_data(xs, ys)
self.ax.figure.canvas.draw()