text: # -*- coding: utf-8 -*-
  """
  Created on Fri Feb  3 22:21:32 2017
  
  @author: yxl
  """
  
  import wx
  from imagepy import IPy
  from imagepy.core.engine import Tool
  import numpy as np
  import pandas as pd
  from numpy.linalg import norm
  from .setting import Setting
  from math import ceil
  
  class Profile:
      """Define the profile class"""
      dtype = 'distance'
      def __init__(self, body=None, unit=None):
          self.body = body if body!=None else []
          self.buf, self.unit = [], unit
  
      def addline(self):
          line = self.buf
          if len(line)!=2 or line[0] !=line[-1]:
              self.body.append(line)
          self.buf = []
  
      def snap(self, x, y, lim):
          minl, idx = 1000, None
          for i in self.body:
              for j in i:
                  d = (j[0]-x)**2+(j[1]-y)**2
                  if d < minl:minl,idx = d,(i, i.index(j))
          return idx if minl**0.5<lim else None
  
      def pick(self, x, y, lim):
          return self.snap(x, y, lim)
  
      def draged(self, ox, oy, nx, ny, i):
          i[0][i[1]] = (nx, ny)
  
      def draw(self, dc, f, **key):
          dc.SetPen(wx.Pen(Setting['color'], width=1, style=wx.SOLID))
          dc.SetTextForeground(Setting['tcolor'])
          linefont = wx.Font(10, wx.FONTFAMILY_DEFAULT, 
                         wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
          dc.SetFont(linefont)
          if len(self.buf)>1:
              dc.DrawLines([f(*i) for i in self.buf])
          for i in self.buf:dc.DrawCircle(f(*i),2)
          for line in self.body:
              dc.DrawLines([f(*i) for i in line])
              for i in line:dc.DrawCircle(f(*i),2)
              pts = np.array(line)
              mid = (pts[:-1]+pts[1:])/2
  
              dxy = (pts[:-1]-pts[1:])
              dis = norm(dxy, axis=1)
              unit = 1 if self.unit is None else self.unit[0]
              for i,j in zip(dis, mid):
                  dc.DrawText('%.2f'%(i*unit), f(*j))
  
      def report(self, title):
          rst, titles = [], ['K']
          for line in self.body:
              pts = np.array(line)
              mid = (pts[:-1]+pts[1:])/2
  
              dxy = (pts[:-1]-pts[1:])
              dxy[:,1][dxy[:,1]==0] = 1
              l = norm(dxy, axis=1)*-np.sign(dxy[:,1])
              rst.append(np.round(np.arccos(dxy[:,0]/l)/np.pi*180,1))
          IPy.show_table(pd.DataFrame(rst, columns=titles), title)
  
  class Plugin(Tool):
      """Define the profile class plugin with the event callback functions"""
      title = 'Profile'
      def __init__(self):
          self.curobj = None
          self.doing = False
          self.odx,self.ody = 0, 0
  
      def mouse_down(self, ips, x, y, btn, **key):
          if key['ctrl'] and key['alt']:
              if isinstance(ips.mark, Profile):
                  ips.mark.report(ips.title)
              return
          lim = 5.0/key['canvas'].scale
          if btn==1:
              if not self.doing:
                  if isinstance(ips.mark, Profile):
                      self.curobj = ips.mark.pick(x, y, lim)
                  if self.curobj!=None:return
  
                  if not isinstance(ips.mark, Profile):
                      ips.mark = Profile(unit=ips.unit)
                      self.doing = True
                  else: ips.mark = None
              if self.doing:
                  ips.mark.buf.append((x,y))
                  ips.mark.buf.append((x,y))
                  self.curobj = (ips.mark.buf, -1)
                  self.odx, self.ody = x,y
          ips.update()
  
      def mouse_up(self, ips, x, y, btn, **key):
          self.curobj = None
          if self.doing:
              ips.mark.addline()
          self.doing = False
          if ips.mark!=None and len(ips.mark.body)==1:
              self.profile(ips.mark.body, ips.img)
          ips.update()
  
      def mouse_move(self, ips, x, y, btn, **key):
          if not isinstance(ips.mark, Profile):return
          lim = 5.0/key['canvas'].scale      
          if btn==None:
              self.cursor = wx.CURSOR_CROSS
              if ips.mark.snap(x, y, lim)!=None:
                  self.cursor = wx.CURSOR_HAND
          elif btn==1:
              ips.mark.draged(self.odx, self.ody, x, y, self.curobj)
              ips.update()
              #PlotFrame.plot(np.random.rand(100))
          self.odx, self.ody = x, y
  
      def profile(self, body, img):
          (x1, y1), (x2, y2) = body[0]
          dx, dy = x2-x1, y2-y1
          n = max(abs(dx), abs(dy)) + 1
          xs = np.linspace(x1, x2, n).round().astype(np.int16)
          ys = np.linspace(y1, y2, n).round().astype(np.int16)
          msk = (xs>=0) * (xs<img.shape[1])
          msk*= (ys>=0) * (ys<img.shape[0])
          ix = np.arange(len(xs))
  
          frame = IPy.plot('Profile', 'Profile - Line', 'pixels coordinate', 'value of pixcels')
          frame.clear()
          if len(img.shape) == 3:
              vs = np.zeros((3,len(xs)), dtype=np.int16)
              vs[:,msk] = img[ys[msk], xs[msk]].T
              frame.add_data(ix, vs[0], (255,0,0), 1)
              frame.add_data(ix, vs[1], (0,255,0), 1)
              frame.add_data(ix, vs[2], (0,0,255), 1)
          else: 
              vs = np.zeros(len(xs), dtype=np.int16)
              vs[msk] = img[ys[msk], xs[msk]]
              frame.add_data(ix, vs, (0,0,0), 1)
          frame.draw()
  
      def mouse_wheel(self, ips, x, y, d, **key):
          pass
  
  if __name__ == '__main__':
      app = wx.App(False)
      frame = PlotFrame(None)
      frame.Show()
      app.MainLoop()