source: trunk/GSASIIplot.py @ 335

Last change on this file since 335 was 335, checked in by vondreele, 12 years ago

Add delete Pawley list; scratch "leBail" from things
mods to texture display stuff
refactor instrument parameters GUI as sizer based - no grid table
some mods to peak fit output

  • Property svn:keywords set to Date Author Revision URL Id
File size: 105.0 KB
Line 
1#GSASII plotting routines
2########### SVN repository information ###################
3# $Date: 2011-07-07 18:53:57 +0000 (Thu, 07 Jul 2011) $
4# $Author: vondreele $
5# $Revision: 335 $
6# $URL: trunk/GSASIIplot.py $
7# $Id: GSASIIplot.py 335 2011-07-07 18:53:57Z vondreele $
8########### SVN repository information ###################
9import math
10import time
11import copy
12import os.path
13import numpy as np
14import numpy.linalg as nl
15import wx
16import wx.aui
17import wx.glcanvas
18import matplotlib as mpl
19import mpl_toolkits.mplot3d.axes3d as mp3d
20import GSASIIpath
21import GSASIIgrid as G2gd
22import GSASIIimage as G2img
23import GSASIIpwd as G2pwd
24import GSASIIIO as G2IO
25import GSASIIpwdGUI as G2pdG
26import GSASIIimgGUI as G2imG
27import GSASIIphsGUI as G2phG
28import GSASIIlattice as G2lat
29import GSASIIspc as G2spc
30import pytexture as ptx
31from  OpenGL.GL import *
32from OpenGL.GLU import *
33from OpenGL.GLUT import *
34from OpenGL.GLE import *
35from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
36from matplotlib.backends.backend_wxagg import NavigationToolbar2Wx as Toolbar
37
38# useful degree trig functions
39sind = lambda x: math.sin(x*math.pi/180.)
40cosd = lambda x: math.cos(x*math.pi/180.)
41tand = lambda x: math.tan(x*math.pi/180.)
42asind = lambda x: 180.*math.asin(x)/math.pi
43acosd = lambda x: 180.*math.acos(x)/math.pi
44atan2d = lambda x,y: 180.*math.atan2(y,x)/math.pi
45atand = lambda x: 180.*math.atan(x)/math.pi
46# numpy versions
47npsind = lambda x: np.sin(x*np.pi/180.)
48npcosd = lambda x: np.cos(x*np.pi/180.)
49npacosd = lambda x: 180.*np.arccos(x)/np.pi
50npasind = lambda x: 180.*np.arcsin(x)/np.pi
51npatand = lambda x: 180.*np.arctan(x)/np.pi
52npatan2d = lambda x,y: 180.*np.arctan2(x,y)/np.pi
53   
54class G2PlotMpl(wx.Panel):   
55    def __init__(self,parent,id=-1,dpi=None,**kwargs):
56        wx.Panel.__init__(self,parent,id=id,**kwargs)
57        mpl.rcParams['legend.fontsize'] = 8
58        self.figure = mpl.figure.Figure(dpi=dpi,figsize=(5,7))
59        self.canvas = Canvas(self,-1,self.figure)
60        self.toolbar = Toolbar(self.canvas)
61
62        self.toolbar.Realize()
63       
64        sizer=wx.BoxSizer(wx.VERTICAL)
65        sizer.Add(self.canvas,1,wx.EXPAND)
66        sizer.Add(self.toolbar,0,wx.LEFT|wx.EXPAND)
67        self.SetSizer(sizer)
68       
69class G2PlotOgl(wx.Panel):
70    def __init__(self,parent,id=-1,dpi=None,**kwargs):
71        self.figure = wx.Panel.__init__(self,parent,id=id,**kwargs)
72        self.canvas = wx.glcanvas.GLCanvas(self,-1,**kwargs)
73        self.camera = {}
74        sizer=wx.BoxSizer(wx.VERTICAL)
75        sizer.Add(self.canvas,1,wx.EXPAND)
76        self.SetSizer(sizer)
77       
78class G2Plot3D(wx.Panel):
79    def __init__(self,parent,id=-1,dpi=None,**kwargs):
80        wx.Panel.__init__(self,parent,id=id,**kwargs)
81        self.figure = mpl.figure.Figure(dpi=dpi,figsize=(6,6))
82        self.canvas = Canvas(self,-1,self.figure)
83        self.toolbar = Toolbar(self.canvas)
84
85        self.toolbar.Realize()
86       
87        sizer=wx.BoxSizer(wx.VERTICAL)
88        sizer.Add(self.canvas,1,wx.EXPAND)
89        sizer.Add(self.toolbar,0,wx.LEFT|wx.EXPAND)
90        self.SetSizer(sizer)
91                             
92class G2PlotNoteBook(wx.Panel):
93    def __init__(self,parent,id=-1):
94        wx.Panel.__init__(self,parent,id=id)
95        #so one can't delete a plot page!!
96        self.nb = wx.aui.AuiNotebook(self, \
97            style=wx.aui.AUI_NB_DEFAULT_STYLE ^ wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB)
98        sizer = wx.BoxSizer()
99        sizer.Add(self.nb,1,wx.EXPAND)
100        self.SetSizer(sizer)
101        self.status = parent.CreateStatusBar()
102        self.status.SetFieldsCount(2)
103        self.status.SetStatusWidths([150,-1])
104        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
105       
106        self.plotList = []
107           
108    def addMpl(self,name=""):
109        page = G2PlotMpl(self.nb)
110        self.nb.AddPage(page,name)
111       
112        self.plotList.append(name)
113       
114        return page.figure
115       
116    def add3D(self,name=""):
117        page = G2Plot3D(self.nb)
118        self.nb.AddPage(page,name)
119       
120        self.plotList.append(name)
121       
122        return page.figure
123       
124    def addOgl(self,name=""):
125        page = G2PlotOgl(self.nb)
126        self.nb.AddPage(page,name)
127       
128        self.plotList.append(name)
129       
130        return page.figure
131       
132    def Delete(self,name):
133        try:
134            item = self.plotList.index(name)
135            del self.plotList[item]
136            self.nb.DeletePage(item)
137        except ValueError:          #no plot of this name - do nothing
138            return     
139               
140    def clear(self):
141        while self.nb.GetPageCount():
142            self.nb.DeletePage(0)
143        self.plotList = []
144        self.status.DestroyChildren()
145       
146    def Rename(self,oldName,newName):
147        try:
148            item = self.plotList.index(oldName)
149            self.plotList[item] = newName
150            self.nb.SetPageText(item,newName)
151        except ValueError:          #no plot of this name - do nothing
152            return     
153       
154    def OnPageChanged(self,event):       
155        if self.plotList:
156            self.status.SetStatusText('Better to select this from GSAS-II data tree',1)
157        self.status.DestroyChildren()                           #get rid of special stuff on status bar
158       
159def PlotSngl(self,newPlot=False):
160    '''Single crystal structure factor plotting package - displays zone of reflections as rings proportional
161        to F, F**2, etc. as requested
162    '''
163    from matplotlib.patches import Circle
164    global HKL,HKLF
165
166    def OnSCMotion(event):
167        xpos = event.xdata
168        if xpos:
169            xpos = round(xpos)                                        #avoid out of frame mouse position
170            ypos = round(event.ydata)
171            zpos = Data['Layer']
172            if '100' in Data['Zone']:
173                HKLtxt = '(%3d,%3d,%3d)'%(zpos,xpos,ypos)
174            elif '010' in Data['Zone']:
175                HKLtxt = '(%3d,%3d,%3d)'%(xpos,zpos,ypos)
176            elif '001' in Data['Zone']:
177                HKLtxt = '(%3d,%3d,%3d)'%(xpos,ypos,zpos)
178            Page.canvas.SetToolTipString(HKLtxt)
179            self.G2plotNB.status.SetFields(['HKL = '+HKLtxt,''])
180               
181    def OnSCPick(event):
182        zpos = Data['Layer']
183        pos = event.artist.center
184        if '100' in Data['Zone']:
185            Page.canvas.SetToolTipString('(picked:(%3d,%3d,%3d))'%(zpos,pos[0],pos[1]))
186            hkl = np.array([zpos,pos[0],pos[1]])
187        elif '010' in Data['Zone']:
188            Page.canvas.SetToolTipString('(picked:(%3d,%3d,%3d))'%(pos[0],zpos,pos[1]))
189            hkl = np.array([pos[0],zpos,pos[1]])
190        elif '001' in Data['Zone']:
191            Page.canvas.SetToolTipString('(picked:(%3d,%3d,%3d))'%(pos[0],pos[1],zpos))
192            hkl = np.array([pos[0],pos[1],zpos])
193        h,k,l = hkl
194        hklf = HKLF[np.where(np.all(HKL-hkl == [0,0,0],axis=1))]
195        if len(hklf):
196            Fosq,sig,Fcsq = hklf[0]
197            HKLtxt = '(%3d,%3d,%3d %.2f %.3f %.2f %.2f)'%(h,k,l,Fosq,sig,Fcsq,(Fosq-Fcsq)/(scale*sig))
198            self.G2plotNB.status.SetFields(['','HKL, Fosq, sig, Fcsq, delFsq/sig = '+HKLtxt])
199                                 
200    def OnSCKeyPress(event):
201        print event.key
202
203    try:
204        plotNum = self.G2plotNB.plotList.index('Structure Factors')
205        Page = self.G2plotNB.nb.GetPage(plotNum)
206        if not newPlot:
207            Plot = Page.figure.gca()          #get previous powder plot & get limits
208            xylim = Plot.get_xlim(),Plot.get_ylim()
209        Page.figure.clf()
210        Plot = Page.figure.gca()          #get a fresh plot after clf()
211    except ValueError:
212        Plot = self.G2plotNB.addMpl('Structure Factors').gca()
213        plotNum = self.G2plotNB.plotList.index('Structure Factors')
214        Page = self.G2plotNB.nb.GetPage(plotNum)
215#        Page.canvas.mpl_connect('key_press_event', OnSCKeyPress)
216        Page.canvas.mpl_connect('pick_event', OnSCPick)
217        Page.canvas.mpl_connect('motion_notify_event', OnSCMotion)
218    Page.SetFocus()
219   
220    Plot.set_aspect(aspect='equal')
221    HKLref = self.PatternTree.GetItemPyData(self.Sngl)
222    Data = self.PatternTree.GetItemPyData( \
223        G2gd.GetPatternTreeItemId(self,self.Sngl, 'HKL Plot Controls'))
224    Type = Data['Type']           
225    scale = Data['Scale']
226    HKLmax = Data['HKLmax']
227    HKLmin = Data['HKLmin']
228    FosqMax = Data['FoMax']
229    FoMax = math.sqrt(FosqMax)
230    ifFc = Data['ifFc']
231    xlabel = ['k, h=','h, k=','h, l=']
232    ylabel = ['l','l','k']
233    zones = ['100','010','001']
234    pzone = [[1,2],[0,2],[0,1]]
235    izone = zones.index(Data['Zone'])
236    Plot.set_title(self.PatternTree.GetItemText(self.Sngl)[5:])
237    HKL = []
238    HKLF = []
239    for H,Fosq,sig,Fcsq,x,x,x in HKLref:
240        HKL.append(H)
241        HKLF.append([Fosq,sig,Fcsq])
242        if H[izone] == Data['Layer']:
243            B = 0
244            if Type == 'Fosq':
245                A = scale*Fosq/FosqMax
246                B = scale*Fcsq/FosqMax
247                C = abs(A-B)
248            elif Type == 'Fo':
249                A = scale*math.sqrt(max(0,Fosq))/FoMax
250                B = scale*math.sqrt(max(0,Fcsq))/FoMax
251                C = abs(A-B)
252            elif Type == '|DFsq|/sig':
253                A = abs(Fosq-Fcsq)/(scale*sig)
254            elif Type == '|DFsq|>sig':
255                A = abs(Fosq-Fcsq)/(scale*sig)
256                if A < 1.0: A = 0                   
257            elif Type == '|DFsq|>3sig':
258                A = abs(Fosq-Fcsq)/(scale*sig)
259                if A < 3.0: A = 0                   
260            xy = (H[pzone[izone][0]],H[pzone[izone][1]])
261            if A > 0.0:
262                Plot.add_artist(Circle(xy,radius=A,ec='g',fc='w',picker=3))
263            if B:
264                Plot.add_artist(Circle(xy,radius=B,ec='b',fc='w'))
265                radius = C
266                if radius > 0:
267                    if A > B:
268                        Plot.add_artist(Circle(xy,radius=radius,ec='g',fc='g'))
269                    else:                   
270                        Plot.add_artist(Circle(xy,radius=radius,ec='r',fc='r'))
271    HKL = np.array(HKL,dtype=np.int)
272    HKLF = np.array(HKLF)
273    Plot.set_xlabel(xlabel[izone]+str(Data['Layer']),fontsize=12)
274    Plot.set_ylabel(ylabel[izone],fontsize=12)
275    Plot.set_xlim((HKLmin[pzone[izone][0]],HKLmax[pzone[izone][0]]))
276    Plot.set_ylim((HKLmin[pzone[izone][1]],HKLmax[pzone[izone][1]]))
277    if not newPlot:
278        Page.toolbar.push_current()
279        Plot.set_xlim(xylim[0])
280        Plot.set_ylim(xylim[1])
281        xylim = []
282        Page.toolbar.push_current()
283        Page.toolbar.draw()
284    else:
285        Page.canvas.draw()
286       
287def PlotPatterns(self,newPlot=False):
288    '''Powder pattern plotting package - displays single or multiple powder patterns as intensity vs
289    2-theta or q (future TOF). Can display multiple patterns as "waterfall plots" or contour plots. Log I
290    plotting available.
291    '''
292    global HKL
293   
294    def OnPick(event):
295        if self.itemPicked is not None: return
296        PatternId = self.PatternId
297        try:
298            Values,Names = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.PatternId, 'Instrument Parameters'))[1::2]
299        except TypeError:
300            return
301        Parms = dict(zip(Names,Values))
302        try:
303            wave = Parms['Lam']
304        except KeyError:
305            wave = Parms['Lam1']
306        PickId = self.PickId
307        pick = event.artist
308        mouse = event.mouseevent       
309        xpos = pick.get_xdata()
310        ypos = pick.get_ydata()
311        ind = event.ind
312        xy = list(zip(np.take(xpos,ind),np.take(ypos,ind))[0])
313        if self.PatternTree.GetItemText(PickId) == 'Peak List':
314            if ind.all() != [0]:                                    #picked a data point
315                if 'C' in Parms['Type']:                            #CW data - TOF later in an elif
316                    ins = [Parms[x] for x in ['U','V','W','X','Y']]
317                    if self.qPlot:                              #qplot - convert back to 2-theta
318                        xy[0] = 2.0*asind(xy[0]*wave/(4*math.pi))
319                    sig = ins[0]*tand(xy[0]/2.0)**2+ins[1]*tand(xy[0]/2.0)+ins[2]
320                    gam = ins[3]/cosd(xy[0]/2.0)+ins[4]*tand(xy[0]/2.0)           
321                    data = self.PatternTree.GetItemPyData(self.PickId)
322                    XY = [xy[0],0, xy[1],1, sig,0, gam,0]       #default refine intensity 1st
323                data.append(XY)
324                G2pdG.UpdatePeakGrid(self,data)
325                PlotPatterns(self)
326            else:                                                   #picked a peak list line
327                self.itemPicked = pick
328        elif self.PatternTree.GetItemText(PickId) == 'Limits':
329            if ind.all() != [0]:                                    #picked a data point
330                LimitId = G2gd.GetPatternTreeItemId(self,PatternId, 'Limits')
331                data = self.PatternTree.GetItemPyData(LimitId)
332                if 'C' in Parms['Type']:                            #CW data - TOF later in an elif
333                    if self.qPlot:                              #qplot - convert back to 2-theta
334                        xy[0] = 2.0*asind(xy[0]*wave/(4*math.pi))
335                if mouse.button==1:
336                    data[1][0] = min(xy[0],data[1][1])
337                if mouse.button==3:
338                    data[1][1] = max(xy[0],data[1][0])
339                self.PatternTree.SetItemPyData(LimitId,data)
340                G2pdG.UpdateLimitsGrid(self,data)
341                PlotPatterns(self)
342            else:                                                   #picked a limit line
343                self.itemPicked = pick
344       
345    def OnPlotKeyPress(event):
346        newPlot = False
347        if event.key == 'w':
348            if self.Weight:
349                self.Weight = False
350            else:
351                self.Weight = True
352            print 'plot weighting:',self.Weight
353        elif event.key == 'n':
354            if self.Contour:
355                pass
356            else:
357                if self.logPlot:
358                    self.logPlot = False
359                else:
360                    self.Offset[0] = 0
361                    self.logPlot = True
362        elif event.key == 'u':
363            if self.Contour:
364                self.Cmax = min(1.0,self.Cmax*1.2)
365            elif self.logPlot:
366                pass
367            elif self.Offset[0] < 100.:
368                self.Offset[0] += 1.
369        elif event.key == 'd':
370            if self.Contour:
371                self.Cmax = max(0.0,self.Cmax*0.8)
372            elif self.logPlot:
373                pass
374            elif self.Offset[0] > 0.:
375                self.Offset[0] -= 1.
376        elif event.key == 'l':
377            self.Offset[1] -= 1.
378        elif event.key == 'r':
379            self.Offset[1] += 1.
380        elif event.key == 'o':
381            self.Offset = [0,0]
382        elif event.key == 'c':
383            newPlot = True
384            if self.Contour:
385                self.Contour = False
386            else:
387                self.Contour = True
388                self.SinglePlot = False
389                self.Offset = [0.,0.]
390        elif event.key == 'q':
391            newPlot = True
392            if self.qPlot:
393                self.qPlot = False
394            else:
395                self.qPlot = True
396        elif event.key == 's':
397            if self.Contour:
398                choice = [m for m in mpl.cm.datad.keys() if not m.endswith("_r")]
399                choice.sort()
400                dlg = wx.SingleChoiceDialog(self,'Select','Color scheme',choice)
401                if dlg.ShowModal() == wx.ID_OK:
402                    sel = dlg.GetSelection()
403                    self.ContourColor = choice[sel]
404                else:
405                    self.ContourColor = 'Paired'
406                dlg.Destroy()
407            else:               
408                if self.SinglePlot:
409                    self.SinglePlot = False
410                else:
411                    self.SinglePlot = True
412        elif event.key == '+':
413            if self.PickId:
414                self.PickId = False
415        elif event.key == 'i':                  #for smoothing contour plot
416            choice = ['nearest','bilinear','bicubic','spline16','spline36','hanning',
417               'hamming','hermite','kaiser','quadric','catrom','gaussian','bessel',
418               'mitchell','sinc','lanczos']
419            dlg = wx.SingleChoiceDialog(self,'Select','Interpolation',choice)
420            if dlg.ShowModal() == wx.ID_OK:
421                sel = dlg.GetSelection()
422                self.Interpolate = choice[sel]
423            else:
424                self.Interpolate = 'nearest'
425            dlg.Destroy()
426           
427        PlotPatterns(self,newPlot=newPlot)
428       
429    def OnKeyBox(event):
430        if self.G2plotNB.nb.GetSelection() == self.G2plotNB.plotList.index('Powder Patterns'):
431            event.key = cb.GetValue()[0]
432            cb.SetValue(' key press')
433            OnPlotKeyPress(event)
434                       
435    def OnMotion(event):
436        xpos = event.xdata
437        if xpos:                                        #avoid out of frame mouse position
438            ypos = event.ydata
439            Page.canvas.SetCursor(wx.CROSS_CURSOR)
440            try:
441                Values,Names = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.PatternId, 'Instrument Parameters'))[1::2]
442                Parms = dict(zip(Names,Values))
443                try:
444                    wave = Parms['Lam']
445                except KeyError:
446                    wave = Parms['Lam1']
447                if self.qPlot:
448                    xpos = 2.0*asind(xpos*wave/(4*math.pi))
449                dsp = 0.0
450                if abs(xpos) > 0.:                  #avoid possible singularity at beam center
451                    dsp = wave/(2.*sind(abs(xpos)/2.0))
452                if self.Contour:
453                    self.G2plotNB.status.SetStatusText('2-theta =%9.3f d =%9.5f pattern ID =%5d'%(xpos,dsp,int(ypos)),1)
454                else:
455                    self.G2plotNB.status.SetStatusText('2-theta =%9.3f d =%9.5f Intensity =%9.1f'%(xpos,dsp,ypos),1)
456                if self.itemPicked:
457                    Page.canvas.SetToolTipString('%9.3f'%(xpos))
458                if self.PickId and self.PatternTree.GetItemText(self.PickId) in ['Index Peak List','Unit Cells List']:
459                    found = []
460                    if len(HKL):
461                        view = Page.toolbar._views.forward()[0][:2]
462                        wid = view[1]-view[0]
463                        found = HKL[np.where(np.fabs(HKL.T[5]-xpos) < 0.002*wid)]
464                    if len(found):
465                        h,k,l = found[0][:3] 
466                        Page.canvas.SetToolTipString('%d,%d,%d'%(int(h),int(k),int(l)))
467                    else:
468                        Page.canvas.SetToolTipString('')
469
470            except TypeError:
471                self.G2plotNB.status.SetStatusText('Select PWDR powder pattern first',1)
472                                                   
473    def OnRelease(event):
474        if self.itemPicked is None: return
475        Values,Names = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.PatternId, 'Instrument Parameters'))[1::2]
476        Parms = dict(zip(Names,Values))
477        try:
478            wave = Parms['Lam']
479        except KeyError:
480            wave = Parms['Lam1']
481        xpos = event.xdata
482        if xpos:                                        #avoid out of frame mouse position
483            lines = []
484            for line in self.Lines: lines.append(line.get_xdata()[0])
485            lineNo = lines.index(self.itemPicked.get_xdata()[0])
486            if  lineNo in [0,1]:
487                LimitId = G2gd.GetPatternTreeItemId(self,self.PatternId, 'Limits')
488                data = self.PatternTree.GetItemPyData(LimitId)
489#                print 'limits',xpos
490                if self.qPlot:
491                    data[1][lineNo] = 2.0*asind(wave*xpos/(4*math.pi))
492                else:
493                    data[1][lineNo] = xpos
494                self.PatternTree.SetItemPyData(LimitId,data)
495                if self.PatternTree.GetItemText(self.PickId) == 'Limits':
496                    G2pdG.UpdateLimitsGrid(self,data)
497            else:
498                PeakId = G2gd.GetPatternTreeItemId(self,self.PatternId, 'Peak List')
499                data = self.PatternTree.GetItemPyData(PeakId)
500#                print 'peaks',xpos
501                if event.button == 3:
502                    del data[lineNo-2]
503                else:
504                    if self.qPlot:
505                        data[lineNo-2][0] = 2.0*asind(wave*xpos/(4*math.pi))
506                    else:
507                        data[lineNo-2][0] = xpos
508                self.PatternTree.SetItemPyData(PeakId,data)
509                G2pdG.UpdatePeakGrid(self,data)
510        PlotPatterns(self)
511        self.itemPicked = None   
512
513    xylim = []
514    try:
515        plotNum = self.G2plotNB.plotList.index('Powder Patterns')
516        Page = self.G2plotNB.nb.GetPage(plotNum)
517        if not newPlot:
518            Plot = Page.figure.gca()          #get previous powder plot & get limits
519            xylim = Plot.get_xlim(),Plot.get_ylim()
520        Page.figure.clf()
521        Plot = Page.figure.gca()          #get a fresh plot after clf()
522    except ValueError:
523        newPlot = True
524        self.Cmax = 1.0
525        Plot = self.G2plotNB.addMpl('Powder Patterns').gca()
526        plotNum = self.G2plotNB.plotList.index('Powder Patterns')
527        Page = self.G2plotNB.nb.GetPage(plotNum)
528        Page.canvas.mpl_connect('key_press_event', OnPlotKeyPress)
529        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
530        Page.canvas.mpl_connect('pick_event', OnPick)
531        Page.canvas.mpl_connect('button_release_event', OnRelease)
532    Page.SetFocus()
533    self.G2plotNB.status.DestroyChildren()
534    if self.Contour:
535        Choice = (' key press','d: lower contour max','u: raise contour max',
536            'i: interpolation method','s: color scheme','c: contour off')
537    else:
538        if self.logPlot:
539            Choice = (' key press','n: log(I) off','l: offset left','r: offset right',
540                'c: contour on','q: toggle q plot','s: toggle single plot','+: no selection')
541        else:
542            Choice = (' key press','l: offset left','r: offset right','d: offset down',
543                'u: offset up','o: reset offset','n: log(I) on','c: contour on',
544                'q: toggle q plot','s: toggle single plot','+: no selection')
545    cb = wx.ComboBox(self.G2plotNB.status,style=wx.CB_DROPDOWN|wx.CB_READONLY,
546        choices=Choice)
547    cb.Bind(wx.EVT_COMBOBOX, OnKeyBox)
548    cb.SetValue(' key press')
549   
550    PickId = self.PickId
551    PatternId = self.PatternId
552    colors=['b','g','r','c','m','k']
553    Lines = []
554    if self.SinglePlot:
555        Pattern = self.PatternTree.GetItemPyData(PatternId)
556        Pattern.append(self.PatternTree.GetItemText(PatternId))
557        PlotList = [Pattern,]
558        ParmList = [self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,
559            self.PatternId, 'Instrument Parameters'))[1],]
560    else:       
561        PlotList = []
562        ParmList = []
563        item, cookie = self.PatternTree.GetFirstChild(self.root)
564        while item:
565            if 'PWDR' in self.PatternTree.GetItemText(item):
566                Pattern = self.PatternTree.GetItemPyData(item)
567                if len(Pattern) < 3:                    # put name on end if needed
568                    Pattern.append(self.PatternTree.GetItemText(item))
569                PlotList.append(Pattern)
570                ParmList.append(self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,
571                    item,'Instrument Parameters'))[1])
572            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
573    Ymax = 1.0
574    lenX = 0
575    HKL = np.array(self.HKL)
576    for Pattern in PlotList:
577        xye = Pattern[1]
578        Ymax = max(Ymax,max(xye[1]))
579    offset = self.Offset[0]*Ymax/100.0
580    Title = 'Powder Patterns: '+os.path.split(self.GSASprojectfile)[1]
581    if self.logPlot:
582        Title = 'log('+Title+')'
583    Plot.set_title(Title)
584    if self.qPlot:
585        Plot.set_xlabel(r'$q, \AA^{-1}$',fontsize=14)
586    else:       
587        Plot.set_xlabel(r'$\mathsf{2\theta}$',fontsize=14)
588    Plot.set_ylabel('Intensity',fontsize=12)
589    if self.Contour:
590        ContourZ = []
591        ContourY = []
592        Nseq = 0
593    for N,Pattern in enumerate(PlotList):
594        Parms = ParmList[N]
595        ifpicked = False
596        LimitId = 0
597        xye = np.array(Pattern[1])
598        if PickId:
599            ifpicked = Pattern[2] == self.PatternTree.GetItemText(PatternId)
600            LimitId = G2gd.GetPatternTreeItemId(self,PatternId, 'Limits')
601        if self.qPlot:
602            Id = G2gd.GetPatternTreeItemId(self,self.root, Pattern[2])
603            X = 4*np.pi*npsind(xye[0]/2.0)/Parms[1]
604        else:
605            X = xye[0]
606        if not lenX:
607            lenX = len(X)           
608        Y = xye[1]+offset*N
609        if LimitId:
610            limits = np.array(self.PatternTree.GetItemPyData(LimitId))
611            if self.qPlot:
612                limits = 4*np.pi*npsind(limits/2.0)/Parms[1]
613            Lines.append(Plot.axvline(limits[1][0],color='g',dashes=(5,5),picker=3.))   
614            Lines.append(Plot.axvline(limits[1][1],color='r',dashes=(5,5),picker=3.))                   
615        if self.Contour:
616           
617            if lenX == len(X):
618                ContourY.append(N)
619                ContourZ.append(Y)
620                ContourX = X
621                Nseq += 1
622                Plot.set_ylabel('Data sequence',fontsize=12)
623        else:
624            X += self.Offset[1]*.005*N
625            if ifpicked:
626                Z = xye[3]+offset*N
627                W = xye[4]+offset*N
628                D = xye[5]+offset*N-Ymax*.02
629                if self.Weight:
630                    W2 = np.sqrt(xye[2])
631                    D *= W2-Ymax*.02
632                if self.logPlot:
633                    Plot.semilogy(X,Y,colors[N%6]+'+',picker=3.,clip_on=False,nonposy='mask')
634                    Plot.semilogy(X,Z,colors[(N+1)%6],picker=False,nonposy='mask')
635                    Plot.semilogy(X,W,colors[(N+2)%6],picker=False,nonposy='mask')
636                else:
637                    Plot.plot(X,Y,colors[N%6]+'+',picker=3.,clip_on=False)
638                    Plot.plot(X,Z,colors[(N+1)%6],picker=False)
639                    Plot.plot(X,W,colors[(N+2)%6],picker=False)
640                    Plot.plot(X,D,colors[(N+3)%6],picker=False)
641                    Plot.axhline(0.,color=wx.BLACK)
642                Page.canvas.SetToolTipString('')
643                if self.PatternTree.GetItemText(PickId) == 'Peak List':
644                    tip = 'On data point: Pick peak - L or R MB. On line: L-move, R-delete'
645                    Page.canvas.SetToolTipString(tip)
646                    data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PatternId, 'Peak List'))
647                    for item in data:
648                        if self.qPlot:
649                            Lines.append(Plot.axvline(4*math.pi*sind(item[0]/2.)/Parms[1],color=colors[N%6],picker=2.))
650                        else:
651                            Lines.append(Plot.axvline(item[0],color=colors[N%6],picker=2.))
652                if self.PatternTree.GetItemText(PickId) == 'Limits':
653                    tip = 'On data point: Lower limit - L MB; Upper limit - R MB. On limit: MB down to move'
654                    Page.canvas.SetToolTipString(tip)
655                    data = self.LimitsTable.GetData()
656            else:
657                if self.logPlot:
658                    Plot.semilogy(X,Y,colors[N%6],picker=False,nonposy='clip')
659                else:
660                    Plot.plot(X,Y,colors[N%6],picker=False)
661    if PickId and self.PatternTree.GetItemText(PickId) in ['Index Peak List','Unit Cells List']:
662        Values,Names = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PatternId, 'Instrument Parameters'))[1::2]
663        Parms = dict(zip(Names,Values))
664        try:
665            wave = Parms['Lam']
666        except KeyError:
667            wave = Parms['Lam1']
668        peaks = np.array((self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PatternId, 'Index Peak List'))))
669        for peak in peaks:
670            if self.qPlot:
671                Plot.axvline(4*np.pi*sind(peak[0]/2.0)/wave,color='b')
672            else:
673                Plot.axvline(peak[0],color='b')
674        for hkl in self.HKL:
675            if self.qPlot:
676                Plot.axvline(4*np.pi*sind(hkl[5]/2.0)/wave,color='r',dashes=(5,5))
677            else:
678                Plot.axvline(hkl[5],color='r',dashes=(5,5))
679    if self.Contour:
680        acolor = mpl.cm.get_cmap(self.ContourColor)
681        Img = Plot.imshow(ContourZ,cmap=acolor,vmin=0,vmax=Ymax*self.Cmax,interpolation=self.Interpolate, 
682            extent=[ContourX[0],ContourX[-1],ContourY[0],ContourY[-1]],aspect='auto',origin='lower')
683        Page.figure.colorbar(Img)
684    else:
685        self.Lines = Lines
686    if not newPlot:
687        Page.toolbar.push_current()
688        Plot.set_xlim(xylim[0])
689        Plot.set_ylim(xylim[1])
690        xylim = []
691        Page.toolbar.push_current()
692        Page.toolbar.draw()
693    else:
694        Page.canvas.draw()
695    self.Pwdr = True
696   
697def PlotISFG(self,newPlot=False,type=''):
698    ''' PLotting package for PDF analysis; displays I(q), S(q), F(q) and G(r) as single
699    or multiple plots with waterfall and contour plots as options
700    '''
701    if not type:
702        type = self.G2plotNB.plotList[self.G2plotNB.nb.GetSelection()]
703    if type not in ['I(Q)','S(Q)','F(Q)','G(R)']:
704        return
705    superMinusOne = unichr(0xaf)+unichr(0xb9)
706   
707    def OnPlotKeyPress(event):
708        newPlot = False
709        if event.key == 'u':
710            if self.Contour:
711                self.Cmax = min(1.0,self.Cmax*1.2)
712            elif self.Offset[0] < 100.:
713                self.Offset[0] += 1.
714        elif event.key == 'd':
715            if self.Contour:
716                self.Cmax = max(0.0,self.Cmax*0.8)
717            elif self.Offset[0] > 0.:
718                self.Offset[0] -= 1.
719        elif event.key == 'l':
720            self.Offset[1] -= 1.
721        elif event.key == 'r':
722            self.Offset[1] += 1.
723        elif event.key == 'o':
724            self.Offset = [0,0]
725        elif event.key == 'c':
726            newPlot = True
727            if self.Contour:
728                self.Contour = False
729            else:
730                self.Contour = True
731                self.SinglePlot = False
732                self.Offset = [0.,0.]
733        elif event.key == 's':
734            if self.Contour:
735                choice = [m for m in mpl.cm.datad.keys() if not m.endswith("_r")]
736                choice.sort()
737                dlg = wx.SingleChoiceDialog(self,'Select','Color scheme',choice)
738                if dlg.ShowModal() == wx.ID_OK:
739                    sel = dlg.GetSelection()
740                    self.ContourColor = choice[sel]
741                else:
742                    self.ContourColor = 'Paired'
743                dlg.Destroy()
744            else:               
745                if self.SinglePlot:
746                    self.SinglePlot = False
747                else:
748                    self.SinglePlot = True
749        elif event.key == 'i':                  #for smoothing contour plot
750            choice = ['nearest','bilinear','bicubic','spline16','spline36','hanning',
751               'hamming','hermite','kaiser','quadric','catrom','gaussian','bessel',
752               'mitchell','sinc','lanczos']
753            dlg = wx.SingleChoiceDialog(self,'Select','Interpolation',choice)
754            if dlg.ShowModal() == wx.ID_OK:
755                sel = dlg.GetSelection()
756                self.Interpolate = choice[sel]
757            else:
758                self.Interpolate = 'nearest'
759            dlg.Destroy()
760        elif event.key == 't' and not self.Contour:
761            if self.Legend:
762                self.Legend = False
763            else:
764                self.Legend = True
765           
766           
767        PlotISFG(self,newPlot=newPlot,type=type)
768       
769    def OnKeyBox(event):
770        if self.G2plotNB.nb.GetSelection() == self.G2plotNB.plotList.index(type):
771            event.key = cb.GetValue()[0]
772            cb.SetValue(' key press')
773            OnPlotKeyPress(event)
774                       
775    def OnMotion(event):
776        xpos = event.xdata
777        if xpos:                                        #avoid out of frame mouse position
778            ypos = event.ydata
779            Page.canvas.SetCursor(wx.CROSS_CURSOR)
780            try:
781                if self.Contour:
782                    self.G2plotNB.status.SetStatusText('R =%.3fA pattern ID =%5d'%(xpos,int(ypos)),1)
783                else:
784                    self.G2plotNB.status.SetStatusText('R =%.3fA %s =%.2f'%(xpos,type,ypos),1)                   
785            except TypeError:
786                self.G2plotNB.status.SetStatusText('Select '+type+' pattern first',1)
787   
788    xylim = []
789    try:
790        plotNum = self.G2plotNB.plotList.index(type)
791        Page = self.G2plotNB.nb.GetPage(plotNum)
792        if not newPlot:
793            Plot = Page.figure.gca()          #get previous plot & get limits
794            xylim = Plot.get_xlim(),Plot.get_ylim()
795        Page.figure.clf()
796        Plot = Page.figure.gca()
797    except ValueError:
798        newPlot = True
799        self.Cmax = 1.0
800        Plot = self.G2plotNB.addMpl(type).gca()
801        plotNum = self.G2plotNB.plotList.index(type)
802        Page = self.G2plotNB.nb.GetPage(plotNum)
803        Page.canvas.mpl_connect('key_press_event', OnPlotKeyPress)
804        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
805   
806    Page.SetFocus()
807    self.G2plotNB.status.DestroyChildren()
808    if self.Contour:
809        Choice = (' key press','d: lower contour max','u: raise contour max',
810            'i: interpolation method','s: color scheme','c: contour off')
811    else:
812        Choice = (' key press','l: offset left','r: offset right','d: offset down','u: offset up',
813            'o: reset offset','t: toggle legend','c: contour on','s: toggle single plot')
814    cb = wx.ComboBox(self.G2plotNB.status,style=wx.CB_DROPDOWN|wx.CB_READONLY,
815        choices=Choice)
816    cb.Bind(wx.EVT_COMBOBOX, OnKeyBox)
817    cb.SetValue(' key press')
818    PatternId = self.PatternId
819    PickId = self.PickId
820    Plot.set_title(type)
821    if type == 'G(R)':
822        Plot.set_xlabel(r'$R,\AA$',fontsize=14)
823    else:
824        Plot.set_xlabel(r'$Q,\AA$'+superMinusOne,fontsize=14)
825    Plot.set_ylabel(r''+type,fontsize=14)
826    colors=['b','g','r','c','m','k']
827    name = self.PatternTree.GetItemText(PatternId)[4:]
828    Pattern = []   
829    if self.SinglePlot:
830        name = self.PatternTree.GetItemText(PatternId)
831        name = type+name[4:]
832        Id = G2gd.GetPatternTreeItemId(self,PatternId,name)
833        Pattern = self.PatternTree.GetItemPyData(Id)
834        if Pattern:
835            Pattern.append(name)
836        PlotList = [Pattern,]
837    else:
838        PlotList = []
839        item, cookie = self.PatternTree.GetFirstChild(self.root)
840        while item:
841            if 'PDF' in self.PatternTree.GetItemText(item):
842                name = type+self.PatternTree.GetItemText(item)[4:]
843                Id = G2gd.GetPatternTreeItemId(self,item,name)
844                Pattern = self.PatternTree.GetItemPyData(Id)
845                if Pattern:
846                    Pattern.append(name)
847                    PlotList.append(Pattern)
848            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
849    PDFdata = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PatternId, 'PDF Controls'))
850    numbDen = G2pwd.GetNumDensity(PDFdata['ElList'],PDFdata['Form Vol'])
851    Xb = [0.,10.]
852    Yb = [0.,-40.*np.pi*numbDen]
853    Ymax = 1.0
854    lenX = 0
855    for Pattern in PlotList:
856        xye = Pattern[1]
857        Ymax = max(Ymax,max(xye[1]))
858    offset = self.Offset[0]*Ymax/100.0
859    if self.Contour:
860        ContourZ = []
861        ContourY = []
862        Nseq = 0
863    for N,Pattern in enumerate(PlotList):
864        xye = Pattern[1]
865        if PickId:
866            ifpicked = Pattern[2] == self.PatternTree.GetItemText(PatternId)
867        X = xye[0]
868        if not lenX:
869            lenX = len(X)           
870        Y = xye[1]+offset*N
871        if self.Contour:
872            if lenX == len(X):
873                ContourY.append(N)
874                ContourZ.append(Y)
875                ContourX = X
876                Nseq += 1
877                Plot.set_ylabel('Data sequence',fontsize=12)
878        else:
879            X = xye[0]+self.Offset[1]*.005*N
880            if ifpicked:
881                Plot.plot(X,Y,colors[N%6]+'+',picker=3.,clip_on=False)
882                Page.canvas.SetToolTipString('')
883            else:
884                if self.Legend:
885                    Plot.plot(X,Y,colors[N%6],picker=False,label='Azm:'+Pattern[2].split('=')[1])
886                else:
887                    Plot.plot(X,Y,colors[N%6],picker=False)
888            if type == 'G(R)':
889                Plot.plot(Xb,Yb,color='k',dashes=(5,5))
890            elif type == 'F(Q)':
891                Plot.axhline(0.,color=wx.BLACK)
892            elif type == 'S(Q)':
893                Plot.axhline(1.,color=wx.BLACK)
894    if self.Contour:
895        acolor = mpl.cm.get_cmap(self.ContourColor)
896        Img = Plot.imshow(ContourZ,cmap=acolor,vmin=0,vmax=Ymax*self.Cmax,interpolation=self.Interpolate, 
897            extent=[ContourX[0],ContourX[-1],ContourY[0],ContourY[-1]],aspect='auto',origin='lower')
898        Page.figure.colorbar(Img)
899    elif self.Legend:
900        Plot.legend(loc='best')
901    if not newPlot:
902        Page.toolbar.push_current()
903        Plot.set_xlim(xylim[0])
904        Plot.set_ylim(xylim[1])
905        xylim = []
906        Page.toolbar.push_current()
907        Page.toolbar.draw()
908    else:
909        Page.canvas.draw()
910       
911def PlotXY(self,XY,newPlot=False,type=''):
912    '''simple plot of xy data, used for diagnostic purposes
913    '''
914    def OnMotion(event):
915        xpos = event.xdata
916        if xpos:                                        #avoid out of frame mouse position
917            ypos = event.ydata
918            Page.canvas.SetCursor(wx.CROSS_CURSOR)
919            try:
920                self.G2plotNB.status.SetStatusText('X =%9.3f %s =%9.3f'%(xpos,type,ypos),1)                   
921            except TypeError:
922                self.G2plotNB.status.SetStatusText('Select '+type+' pattern first',1)
923
924    try:
925        plotNum = self.G2plotNB.plotList.index(type)
926        Page = self.G2plotNB.nb.GetPage(plotNum)
927        if not newPlot:
928            Plot = Page.figure.gca()
929            xylim = Plot.get_xlim(),Plot.get_ylim()
930        Page.figure.clf()
931        Plot = Page.figure.gca()
932    except ValueError:
933        newPlot = True
934        Plot = self.G2plotNB.addMpl(type).gca()
935        plotNum = self.G2plotNB.plotList.index(type)
936        Page = self.G2plotNB.nb.GetPage(plotNum)
937        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
938   
939    Page.SetFocus()
940    self.G2plotNB.status.DestroyChildren()
941    Plot.set_title(type)
942    Plot.set_xlabel(r'X',fontsize=14)
943    Plot.set_ylabel(r''+type,fontsize=14)
944    colors=['b','g','r','c','m','k']
945    Ymax = 1.0
946    lenX = 0
947    X,Y = XY[:2]
948    Ymax = max(Ymax,max(Y))
949    Plot.plot(X,Y,'k',picker=False)
950    if not newPlot:
951        Page.toolbar.push_current()
952        Plot.set_xlim(xylim[0])
953        Plot.set_ylim(xylim[1])
954        xylim = []
955        Page.toolbar.push_current()
956        Page.toolbar.draw()
957    else:
958        Page.canvas.draw()
959
960def PlotPowderLines(self):
961    ''' plotting of powder lines (i.e. no powder pattern) as sticks
962    '''
963    global HKL
964
965    def OnMotion(event):
966        xpos = event.xdata
967        if xpos:                                        #avoid out of frame mouse position
968            Page.canvas.SetCursor(wx.CROSS_CURSOR)
969            self.G2plotNB.status.SetFields(['','2-theta =%9.3f '%(xpos,)])
970            if self.PickId and self.PatternTree.GetItemText(self.PickId) in ['Index Peak List','Unit Cells List']:
971                found = []
972                if len(HKL):
973                    view = Page.toolbar._views.forward()[0][:2]
974                    wid = view[1]-view[0]
975                    found = HKL[np.where(np.fabs(HKL.T[5]-xpos) < 0.002*wid)]
976                if len(found):
977                    h,k,l = found[0][:3] 
978                    Page.canvas.SetToolTipString('%d,%d,%d'%(int(h),int(k),int(l)))
979                else:
980                    Page.canvas.SetToolTipString('')
981
982    try:
983        plotNum = self.G2plotNB.plotList.index('Powder Lines')
984        Page = self.G2plotNB.nb.GetPage(plotNum)
985        Page.figure.clf()
986        Plot = Page.figure.gca()
987    except ValueError:
988        Plot = self.G2plotNB.addMpl('Powder Lines').gca()
989        plotNum = self.G2plotNB.plotList.index('Powder Lines')
990        Page = self.G2plotNB.nb.GetPage(plotNum)
991        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
992       
993    Page.SetFocus()
994    Plot.set_title('Powder Pattern Lines')
995    Plot.set_xlabel(r'$\mathsf{2\theta}$',fontsize=14)
996    PickId = self.PickId
997    PatternId = self.PatternId
998    peaks = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PatternId, 'Index Peak List'))
999    for peak in peaks:
1000        Plot.axvline(peak[0],color='b')
1001    HKL = np.array(self.HKL)
1002    for hkl in self.HKL:
1003        Plot.axvline(hkl[5],color='r',dashes=(5,5))
1004    xmin = peaks[0][0]
1005    xmax = peaks[-1][0]
1006    delt = xmax-xmin
1007    xlim = [max(0,xmin-delt/20.),min(180.,xmax+delt/20.)]
1008    Plot.set_xlim(xlim)
1009    Page.canvas.draw()
1010    Page.toolbar.push_current()
1011
1012def PlotPeakWidths(self):
1013    ''' Plotting of instrument broadening terms as function of 2-theta (future TOF)
1014    Seen when "Instrument Parameters" chosen from powder pattern data tree
1015    '''
1016    PatternId = self.PatternId
1017    limitID = G2gd.GetPatternTreeItemId(self,PatternId, 'Limits')
1018    if limitID:
1019        limits = self.PatternTree.GetItemPyData(limitID)
1020    else:
1021        return
1022    instParms = self.PatternTree.GetItemPyData( \
1023        G2gd.GetPatternTreeItemId(self,PatternId, 'Instrument Parameters'))
1024    if instParms[0][0] in ['PXC','PNC']:
1025        lam = instParms[1][1]
1026        if len(instParms[1]) == 13:
1027            GU,GV,GW,LX,LY = instParms[0][6:11]
1028        else:
1029            GU,GV,GW,LX,LY = instParms[0][4:9]
1030    peakID = G2gd.GetPatternTreeItemId(self,PatternId, 'Peak List')
1031    if peakID:
1032        peaks = self.PatternTree.GetItemPyData(peakID)
1033    else:
1034        peaks = []
1035   
1036    try:
1037        plotNum = self.G2plotNB.plotList.index('Peak Widths')
1038        Page = self.G2plotNB.nb.GetPage(plotNum)
1039        Page.figure.clf()
1040        Plot = Page.figure.gca()
1041    except ValueError:
1042        Plot = self.G2plotNB.addMpl('Peak Widths').gca()
1043        plotNum = self.G2plotNB.plotList.index('Peak Widths')
1044        Page = self.G2plotNB.nb.GetPage(plotNum)
1045    Page.SetFocus()
1046   
1047    Page.canvas.SetToolTipString('')
1048    colors=['b','g','r','c','m','k']
1049    Xmin,Xmax = limits[1]
1050    Xmin = min(0.5,max(Xmin,1))
1051    Xmin /= 2
1052    Xmax /= 2
1053    nPts = 100
1054    delt = (Xmax-Xmin)/nPts
1055    thetas = []
1056    for i in range(nPts):
1057        thetas.append(Xmin+i*delt)
1058    X = []
1059    Y = []
1060    Z = []
1061    W = []
1062    V = []
1063    sig = lambda Th,U,V,W: 1.17741*math.sqrt(U*tand(Th)**2+V*tand(Th)+W)*math.pi/18000.
1064    gam = lambda Th,X,Y: (X/cosd(Th)+Y*tand(Th))*math.pi/18000.
1065    gamFW = lambda s,g: math.exp(math.log(s**5+2.69269*s**4*g+2.42843*s**3*g**2+4.47163*s**2*g**3+0.07842*s*g**4+g**5)/5.)
1066    gamFW2 = lambda s,g: math.sqrt(s**2+(0.4654996*g)**2)+.5345004*#Ubaldo Bafile - private communication
1067    for theta in thetas:
1068        X.append(4.0*math.pi*sind(theta)/lam)              #q
1069        s = sig(theta,GU,GV,GW)
1070        g = gam(theta,LX,LY)
1071        G = gamFW(g,s)
1072        H = gamFW2(g,s)
1073        Y.append(s/tand(theta))
1074        Z.append(g/tand(theta))
1075        W.append(G/tand(theta))
1076        V.append(H/tand(theta))
1077    Plot.set_title('Instrument and sample peak widths')
1078    Plot.set_ylabel(r'$\Delta q/q, \Delta d/d$',fontsize=14)
1079    Plot.set_xlabel(r'$q, \AA^{-1}$',fontsize=14)
1080    Plot.plot(X,Y,color='r',label='Gaussian')
1081    Plot.plot(X,Z,color='g',label='Lorentzian')
1082    Plot.plot(X,W,color='b',label='G+L')
1083    Plot.plot(X,V,color='k',label='G+L2')
1084    X = []
1085    Y = []
1086    Z = []
1087    W = []
1088    V = []
1089    for peak in peaks:
1090        X.append(4.0*math.pi*sind(peak[0]/2.0)/lam)
1091        try:
1092            s = 1.17741*math.sqrt(peak[4])*math.pi/18000.
1093        except ValueError:
1094            s = 0.01
1095        g = peak[6]*math.pi/18000.
1096        G = gamFW(g,s)
1097        H = gamFW2(g,s)
1098        Y.append(s/tand(peak[0]/2.))
1099        Z.append(g/tand(peak[0]/2.))
1100        W.append(G/tand(peak[0]/2.))
1101        V.append(H/tand(peak[0]/2.))
1102    Plot.plot(X,Y,'+',color='r',label='G peak')
1103    Plot.plot(X,Z,'+',color='g',label='L peak')
1104    Plot.plot(X,W,'+',color='b',label='G+L peak')
1105    Plot.plot(X,V,'+',color='k',label='G+L2 peak')
1106    Plot.legend(loc='best')
1107    Page.canvas.draw()
1108   
1109def PlotStrain(self,data):
1110    '''Plot 3D microstrain figure. In this instance data is for a phase
1111    '''
1112    PatternId = self.PatternId
1113    generalData = data['General']
1114    SGData = generalData['SGData']
1115    MuStrKeys = G2spc.MustrainNames(SGData)
1116    cell = generalData['Cell'][1:]
1117    A,B = G2lat.cell2AB(cell[:6])
1118    Vol = cell[6]
1119    useList = data['Histograms']
1120    for item in useList:
1121        if useList[item]['Show']:
1122            break
1123    else:
1124        self.G2plotNB.Delete('Microstrain')
1125        return            #nothing to show!!
1126   
1127    numPlots = len(useList)
1128   
1129    try:
1130        plotNum = self.G2plotNB.plotList.index('Microstrain')
1131        Page = self.G2plotNB.nb.GetPage(plotNum)
1132        Page.figure.clf()
1133        Plot = mp3d.Axes3D(Page.figure)
1134        if not Page.IsShown():
1135            Page.Show()
1136    except ValueError:
1137        Plot = mp3d.Axes3D(self.G2plotNB.add3D('Microstrain'))
1138        plotNum = self.G2plotNB.plotList.index('Microstrain')
1139        Page = self.G2plotNB.nb.GetPage(plotNum)
1140    Page.SetFocus()
1141    self.G2plotNB.status.SetStatusText('Adjust frame size to get desired aspect ratio',1)
1142   
1143    for item in useList:
1144        if useList[item]['Show']:
1145            muStrain = useList[item]['Mustrain']
1146            PHI = np.linspace(0.,360.,30,True)
1147            PSI = np.linspace(0.,180.,30,True)
1148            X = np.outer(npsind(PHI),npsind(PSI))
1149            Y = np.outer(npcosd(PHI),npsind(PSI))
1150            Z = np.outer(np.ones(np.size(PHI)),npcosd(PSI))
1151            if muStrain[0] == 'isotropic':
1152                muiso = muStrain[1][0]*math.pi/0.018      #centidegrees to radians!
1153                X *= muiso
1154                Y *= muiso
1155                Z *= muiso               
1156               
1157            elif muStrain[0] == 'uniaxial':
1158                def uniaxMustrain(xyz,muiso,muaniso,axes):
1159                    cp = abs(np.dot(xyz,axes))
1160                    S = muiso+muaniso*cp
1161                    return S*xyz*math.pi/0.018      #centidegrees to radians!
1162                muiso,muaniso = muStrain[1][:2]
1163                axes = np.inner(A,np.array(muStrain[3]))
1164                axes /= nl.norm(axes)
1165                Shkl = np.array(muStrain[1])
1166                Shape = X.shape[0]
1167                XYZ = np.dstack((X,Y,Z))
1168                XYZ = np.nan_to_num(np.apply_along_axis(uniaxMustrain,2,XYZ,muiso,muaniso,axes))
1169                X,Y,Z = np.dsplit(XYZ,3)
1170                X = X[:,:,0]
1171                Y = Y[:,:,0]
1172                Z = Z[:,:,0]
1173               
1174            elif muStrain[0] == 'generalized':
1175                def genMustrain(xyz,SGData,A,Shkl):
1176                    uvw = np.inner(A.T,xyz)
1177                    Strm = np.array(G2spc.MustrainCoeff(uvw,SGData))
1178                    sum = np.sqrt(np.sum(np.multiply(Shkl,Strm)))*math.pi/0.018      #centidegrees to radians!
1179                    return sum*xyz
1180                Shkl = np.array(muStrain[4])
1181                if np.any(Shkl):
1182                    Shape = X.shape[0]
1183                    XYZ = np.dstack((X,Y,Z))
1184                    XYZ = np.nan_to_num(np.apply_along_axis(genMustrain,2,XYZ,SGData,A,Shkl))
1185                    X,Y,Z = np.dsplit(XYZ,3)
1186                    X = X[:,:,0]
1187                    Y = Y[:,:,0]
1188                    Z = Z[:,:,0]
1189                   
1190            if np.any(X) and np.any(Y) and np.any(Z):
1191                Plot.plot_surface(X,Y,Z,rstride=1,cstride=1,color='g')
1192               
1193            Plot.set_xlabel('X')
1194            Plot.set_ylabel('Y')
1195            Plot.set_zlabel('Z')
1196    Page.canvas.draw()
1197   
1198def PlotTexture(self,data,newPlot=False,Start=False):
1199    '''Pole figure, inverse pole figure(?), 3D pole distribution and 3D inverse pole distribution(?)
1200    plotting; Need way to select 
1201    pole figure or pole distribution to be displayed - do in key enter menu
1202    dict generalData contains all phase info needed which is in data
1203    '''
1204
1205    shModels = ['cylindrical','none','shear - 2/m','rolling - mmm']
1206    SamSym = dict(zip(shModels,['0','-1','2/m','mmm']))
1207    PatternId = self.PatternId
1208    generalData = data['General']
1209    SGData = generalData['SGData']
1210    textureData = generalData['SH Texture']
1211    if not textureData['Order']:
1212        self.G2plotNB.Delete('Texture')
1213        return                  #no plot!!
1214    SHData = generalData['SH Texture']
1215    SHCoef = SHData['SH Coeff'][1]
1216    cell = generalData['Cell'][1:7]
1217    Amat,Bmat = G2lat.cell2AB(cell)
1218    sq2 = 1.0/math.sqrt(2.0)
1219   
1220    def rp2xyz(r,p):
1221        z = npcosd(r)
1222        xy = np.sqrt(1.-z**2)
1223        return xy*npsind(p),xy*npcosd(p),z
1224           
1225    def OnMotion(event):
1226        SHData = data['General']['SH Texture']
1227        if event.xdata and event.ydata:                 #avoid out of frame errors
1228            xpos = event.xdata
1229            ypos = event.ydata
1230            if 'Inverse' in SHData['PlotType']:
1231                r = xpos**2+ypos**2
1232                if r <= 1.0:
1233                    if 'equal' in self.Projection: 
1234                        r,p = 2.*npasind(np.sqrt(r)*sq2),npatan2d(ypos,xpos)
1235                    else:
1236                        r,p = 2.*npatand(np.sqrt(r)),npatan2d(ypos,xpos)
1237                    ipf = G2lat.invpolfcal(IODFln,SGData,np.array([r,]),np.array([p,]))
1238                    xyz = np.inner(Amat.T,np.array([rp2xyz(r,p)]))
1239                    y,x,z = list(xyz/np.max(np.abs(xyz)))
1240                   
1241                    self.G2plotNB.status.SetFields(['',
1242                        'psi =%9.3f, beta =%9.3f, MRD =%9.3f xyz=%5.2f,%5.2f,%5.2f'%(r,p,ipf,x,y,z)])
1243                                   
1244            elif 'Axial' in SHData['PlotType']:
1245                pass
1246               
1247            else:                       #ordinary pole figure
1248                z = xpos**2+ypos**2
1249                if z <= 1.0:
1250                    z = np.sqrt(z)
1251                    if 'equal' in self.Projection: 
1252                        r,p = 2.*npasind(z*sq2),npatan2d(ypos,xpos)
1253                    else:
1254                        r,p = 2.*npatand(z),npatan2d(ypos,xpos)
1255                    pf = G2lat.polfcal(ODFln,SamSym[textureData['Model']],np.array([r,]),np.array([p,]))
1256                    self.G2plotNB.status.SetFields(['','phi =%9.3f, gam =%9.3f, MRD =%9.3f'%(r,p,pf)])
1257                   
1258    try:
1259        plotNum = self.G2plotNB.plotList.index('Texture')
1260        Page = self.G2plotNB.nb.GetPage(plotNum)
1261        Page.figure.clf()
1262        Plot = Page.figure.gca()
1263        if not Page.IsShown():
1264            Page.Show()
1265    except ValueError:
1266        Plot = self.G2plotNB.addMpl('Texture').gca()
1267        plotNum = self.G2plotNB.plotList.index('Texture')
1268        Page = self.G2plotNB.nb.GetPage(plotNum)
1269        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
1270
1271    Page.SetFocus()
1272    self.G2plotNB.status.SetFields(['',''])   
1273    PH = np.array(SHData['PFhkl'])
1274    phi,beta = G2lat.CrsAng(PH,cell,SGData)
1275    ODFln = G2lat.Flnh(Start,SHCoef,phi,beta,SGData)
1276    PX = np.array(SHData['PFxyz'])
1277    gam = atan2d(PX[0],PX[1])
1278    xy = math.sqrt(PX[0]**2+PX[1]**2)
1279    xyz = math.sqrt(PX[0]**2+PX[1]**2+PX[2]**2)
1280    psi = asind(xy/xyz)
1281    IODFln = G2lat.Glnh(Start,SHCoef,psi,gam,SamSym[textureData['Model']])
1282    if 'Axial' in SHData['PlotType']:
1283        X = np.linspace(0,90.0,26)
1284        Y = G2lat.polfcal(ODFln,SamSym[textureData['Model']],X,0.0)
1285        Plot.plot(X,Y,color='k',label=str(SHData['PFhkl']))
1286        Plot.legend(loc='best')
1287        Plot.set_title('Axial distribution for HKL='+str(SHData['PFhkl']))
1288        Plot.set_xlabel(r'$\psi$',fontsize=16)
1289        Plot.set_ylabel('MRD',fontsize=14)
1290       
1291    else:       
1292        npts = 201
1293        if 'Inverse' in SHData['PlotType']:
1294            X,Y = np.meshgrid(np.linspace(1.,-1.,npts),np.linspace(-1.,1.,npts))
1295            R,P = np.sqrt(X**2+Y**2).flatten(),npatan2d(X,Y).flatten()
1296            if 'equal' in self.Projection:
1297                R = np.where(R <= 1.,2.*npasind(R*sq2),0.0)
1298            else:
1299                R = np.where(R <= 1.,2.*npatand(R),0.0)
1300            Z = np.zeros_like(R)
1301            Z = G2lat.invpolfcal(IODFln,SGData,R,P)
1302            Z = np.reshape(Z,(npts,npts))
1303            CS = Plot.contour(Y,X,Z,aspect='equal')
1304            Plot.clabel(CS,fontsize=9,inline=1)
1305            Img = Plot.imshow(Z.T,aspect='equal',cmap=self.ContourColor,extent=[-1,1,-1,1])
1306            if newPlot:
1307#                Page.figure.colorbar(Img)    #colorbar fails - crashes gsasii
1308                newPlot = False
1309            Plot.set_title('Inverse pole figure for XYZ='+str(SHData['PFxyz']))
1310            Plot.set_xlabel(self.Projection.capitalize()+' projection')
1311                       
1312        else:
1313            X,Y = np.meshgrid(np.linspace(1.,-1.,npts),np.linspace(-1.,1.,npts))
1314            R,P = np.sqrt(X**2+Y**2).flatten(),npatan2d(X,Y).flatten()
1315            if 'equal' in self.Projection:
1316                R = np.where(R <= 1.,2.*npasind(R*sq2),0.0)
1317            else:
1318                R = np.where(R <= 1.,2.*npatand(R),0.0)
1319            Z = np.zeros_like(R)
1320            Z = G2lat.polfcal(ODFln,SamSym[textureData['Model']],R,P)
1321            Z = np.reshape(Z,(npts,npts))
1322            CS = Plot.contour(Y,X,Z,aspect='equal')
1323            Plot.clabel(CS,fontsize=9,inline=1)
1324            Img = Plot.imshow(Z.T,aspect='equal',cmap=self.ContourColor,extent=[-1,1,-1,1])
1325            if newPlot:
1326#                Page.figure.colorbar(Img)    #colorbar fails - crashes gsasii
1327                newPlot = False
1328            Plot.set_title('Pole figure for HKL='+str(SHData['PFhkl']))
1329            Plot.set_xlabel(self.Projection.capitalize()+' projection')
1330    Page.canvas.draw()
1331
1332           
1333def PlotExposedImage(self,newPlot=False,event=None):
1334    '''General access module for 2D image plotting
1335    '''
1336    plotNo = self.G2plotNB.nb.GetSelection()
1337    if self.G2plotNB.nb.GetPageText(plotNo) == '2D Powder Image':
1338        PlotImage(self,newPlot,event,newImage=True)
1339    elif self.G2plotNB.nb.GetPageText(plotNo) == '2D Integration':
1340        PlotIntegration(self,newPlot,event)
1341
1342def PlotImage(self,newPlot=False,event=None,newImage=True):
1343    '''Plot of 2D detector images as contoured plot. Also plot calibration ellipses,
1344    masks, etc.
1345    '''
1346    from matplotlib.patches import Ellipse,Arc,Circle,Polygon
1347    import numpy.ma as ma
1348    Dsp = lambda tth,wave: wave/(2.*sind(tth/2.))
1349    global Data,Masks
1350    colors=['b','g','r','c','m','k']
1351    Data = self.PatternTree.GetItemPyData(
1352        G2gd.GetPatternTreeItemId(self,self.Image, 'Image Controls'))
1353    Masks = self.PatternTree.GetItemPyData(
1354        G2gd.GetPatternTreeItemId(self,self.Image, 'Masks'))
1355
1356    def OnImMotion(event):
1357        Page.canvas.SetToolTipString('')
1358        sizexy = Data['size']
1359        if event.xdata and event.ydata:                 #avoid out of frame errors
1360            Page.canvas.SetCursor(wx.CROSS_CURSOR)
1361            item = self.itemPicked
1362            pixelSize = Data['pixelSize']
1363            scalex = 1000./pixelSize[0]
1364            scaley = 1000./pixelSize[1]
1365            if item and self.PatternTree.GetItemText(self.PickId) == 'Image Controls':
1366                if 'Text' in str(item):
1367                    Page.canvas.SetToolTipString('%8.3f %8.3fmm'%(event.xdata,event.ydata))
1368                else:
1369                    xcent,ycent = Data['center']
1370                    xpos = event.xdata-xcent
1371                    ypos = event.ydata-ycent
1372                    tth,azm = G2img.GetTthAzm(event.xdata,event.ydata,Data)
1373                    if 'line3' in  str(item) or 'line4' in str(item) and not Data['fullIntegrate']:
1374                        Page.canvas.SetToolTipString('%6d deg'%(azm))
1375                    elif 'line1' in  str(item) or 'line2' in str(item):
1376                        Page.canvas.SetToolTipString('%8.3fdeg'%(tth))                           
1377            else:
1378                xpos = event.xdata
1379                ypos = event.ydata
1380                xpix = xpos*scalex
1381                ypix = ypos*scaley
1382                Int = 0
1383                if (0 <= xpix <= sizexy[0]) and (0 <= ypix <= sizexy[1]):
1384                    Int = self.ImageZ[ypix][xpix]
1385                tth,azm,dsp = G2img.GetTthAzmDsp(xpos,ypos,Data)
1386                Q = 2.*math.pi/dsp
1387                if self.setPoly:
1388                    self.G2plotNB.status.SetFields(['','Polygon mask pick - LB next point, RB close polygon'])
1389                else:
1390                    self.G2plotNB.status.SetFields(\
1391                        ['','Detector 2-th =%9.3fdeg, dsp =%9.3fA, Q = %6.5fA-1, azm = %7.2fdeg, I = %6d'%(tth,dsp,Q,azm,Int)])
1392
1393    def OnImPlotKeyPress(event):
1394        try:
1395            PickName = self.PatternTree.GetItemText(self.PickId)
1396        except TypeError:
1397            return
1398        if PickName == 'Masks':
1399            Xpos = event.xdata
1400            if not Xpos:            #got point out of frame
1401                return
1402            Ypos = event.ydata
1403            if event.key == 's':
1404                Masks['Points'].append([Xpos,Ypos,1.])
1405            elif event.key == 'r':
1406                tth = G2img.GetTth(Xpos,Ypos,Data)
1407                Masks['Rings'].append([tth,0.1])
1408            elif event.key == 'a':
1409                tth,azm = G2img.GetTthAzm(Xpos,Ypos,Data)
1410                azm = int(azm)               
1411                Masks['Arcs'].append([tth,[azm-5,azm+5],0.1])
1412            elif event.key == 'p':
1413                self.setPoly = True
1414                Masks['Polygons'].append([])
1415                self.G2plotNB.status.SetFields(['','Polygon mask active - LB pick next point, RB close polygon'])
1416            G2imG.UpdateMasks(self,Masks)
1417        elif PickName == 'Image Controls':
1418            if event.key == 'c':
1419                Xpos = event.xdata
1420                if not Xpos:            #got point out of frame
1421                    return
1422                Ypos = event.ydata
1423                dlg = wx.MessageDialog(self,'Are you sure you want to change the center?',
1424                    'Center change',style=wx.OK|wx.CANCEL)
1425                try:
1426                    if dlg.ShowModal() == wx.ID_OK:
1427                        print 'move center to: ',Xpos,Ypos
1428                        Data['center'] = [Xpos,Ypos]
1429                        G2imG.UpdateImageControls(self,Data,Masks)
1430                finally:
1431                    dlg.Destroy()
1432            elif event.key == 'l':
1433                if self.logPlot:
1434                    self.logPlot = False
1435                else:
1436                    self.logPlot = True
1437        PlotImage(self,newImage=True)
1438           
1439    def OnKeyBox(event):
1440        if self.G2plotNB.nb.GetSelection() == self.G2plotNB.plotList.index('2D Powder Image'):
1441            event.key = cb.GetValue()[0]
1442            cb.SetValue(' key press')
1443            if event.key in 'l':
1444                OnImPlotKeyPress(event)
1445                       
1446    def OnImPick(event):
1447        if self.PatternTree.GetItemText(self.PickId) not in ['Image Controls','Masks']:
1448            return
1449        if self.setPoly:
1450            polygon = Masks['Polygons'][-1]
1451            xpos,ypos = event.mouseevent.xdata,event.mouseevent.ydata
1452            if xpos and ypos:                       #point inside image
1453                if len(polygon) > 2 and event.mouseevent.button == 3:
1454                    x0,y0 = polygon[0]
1455                    polygon.append([x0,y0])
1456                    self.setPoly = False
1457                    self.G2plotNB.status.SetFields(['','Polygon closed - RB drag a vertex to change shape'])
1458                else:
1459                    self.G2plotNB.status.SetFields(['','New polygon point: %.1f,%.1f'%(xpos,ypos)])
1460                    polygon.append([xpos,ypos])
1461                G2imG.UpdateMasks(self,Masks)
1462        else:
1463            if self.itemPicked is not None: return
1464            self.itemPicked = event.artist
1465            self.mousePicked = event.mouseevent
1466       
1467    def OnImRelease(event):
1468        try:
1469            PickName = self.PatternTree.GetItemText(self.PickId)
1470        except TypeError:
1471            return
1472        if PickName not in ['Image Controls','Masks']:
1473            return
1474        pixelSize = Data['pixelSize']
1475        scalex = 1000./pixelSize[0]
1476        scaley = 1000./pixelSize[1]
1477        pixLimit = Data['pixLimit']
1478        if self.itemPicked is None and PickName == 'Image Controls':
1479#            sizexy = Data['size']
1480            Xpos = event.xdata
1481            if not (Xpos and self.ifGetRing):                   #got point out of frame
1482                return
1483            Ypos = event.ydata
1484            if Ypos and not Page.toolbar._active:         #make sure zoom/pan not selected
1485                if event.button == 1:
1486                    Xpix = Xpos*scalex
1487                    Ypix = Ypos*scaley
1488                    xpos,ypos,I,J = G2img.ImageLocalMax(self.ImageZ,pixLimit,Xpix,Ypix)
1489                    if I and J:
1490                        xpos += .5                              #shift to pixel center
1491                        ypos += .5
1492                        xpos /= scalex                          #convert to mm
1493                        ypos /= scaley
1494                        Data['ring'].append([xpos,ypos])
1495                elif event.button == 3:
1496                    self.dataFrame.GetStatusBar().SetStatusText('Calibrating...')
1497                    if G2img.ImageCalibrate(self,Data):
1498                        self.dataFrame.GetStatusBar().SetStatusText('Calibration successful - Show ring picks to check')
1499                        print 'Calibration successful'
1500                    else:
1501                        self.dataFrame.GetStatusBar().SetStatusText('Calibration failed - Show ring picks to diagnose')
1502                        print 'Calibration failed'
1503                    self.ifGetRing = False
1504                    G2imG.UpdateImageControls(self,Data,Masks)
1505                    return
1506                PlotImage(self,newImage=False)
1507            return
1508        else:
1509            xpos = event.xdata
1510            if xpos:                                        #avoid out of frame mouse position
1511                ypos = event.ydata
1512                if self.ifGetRing:                          #delete a calibration ring pick
1513                    xypos = [xpos,ypos]
1514                    rings = Data['ring']
1515                    for ring in rings:
1516                        if np.allclose(ring,xypos,.01,0):
1517                            rings.remove(ring)                                                                       
1518                else:
1519                    tth,azm,dsp = G2img.GetTthAzmDsp(xpos,ypos,Data)
1520                    itemPicked = str(self.itemPicked)
1521                    if 'Line2D' in itemPicked and PickName == 'Image Controls':
1522                        if 'line1' in itemPicked:
1523                            Data['IOtth'][0] = max(tth,0.001)
1524                        elif 'line2' in itemPicked:
1525                            Data['IOtth'][1] = tth
1526                        elif 'line3' in itemPicked:
1527                            Data['LRazimuth'][0] = int(azm)
1528                            if Data['fullIntegrate']:
1529                                Data['LRazimuth'][1] = Data['LRazimuth'][0]+360
1530                        elif 'line4' in itemPicked and not Data['fullIntegrate']:
1531                            Data['LRazimuth'][1] = int(azm)
1532                           
1533                        if Data['LRazimuth'][0] > Data['LRazimuth'][1]:
1534                            Data['LRazimuth'][0] -= 360
1535                           
1536                        azLim = np.array(Data['LRazimuth'])   
1537                        if np.any(azLim>360):
1538                            azLim -= 360
1539                            Data['LRazimuth'] = list(azLim)
1540                           
1541                        if  Data['IOtth'][0] > Data['IOtth'][1]:
1542                            Data['IOtth'][0],Data['IOtth'][1] = Data['IOtth'][1],Data['IOtth'][0]
1543                           
1544                        self.InnerTth.SetValue("%8.2f" % (Data['IOtth'][0]))
1545                        self.OuterTth.SetValue("%8.2f" % (Data['IOtth'][1]))
1546                        self.Lazim.SetValue("%6d" % (Data['LRazimuth'][0]))
1547                        self.Razim.SetValue("%6d" % (Data['LRazimuth'][1]))
1548                    elif 'Circle' in itemPicked and PickName == 'Masks':
1549                        spots = Masks['Points']
1550                        newPos = itemPicked.split(')')[0].split('(')[2].split(',')
1551                        newPos = np.array([float(newPos[0]),float(newPos[1])])
1552                        for spot in spots:
1553                            if np.allclose(np.array([spot[:2]]),newPos):
1554                                spot[:2] = xpos,ypos
1555                        G2imG.UpdateMasks(self,Masks)
1556                    elif 'Line2D' in itemPicked and PickName == 'Masks':
1557                        Obj = self.itemPicked.findobj()
1558                        rings = Masks['Rings']
1559                        arcs = Masks['Arcs']
1560                        polygons = Masks['Polygons']
1561                        for ring in self.ringList:
1562                            if Obj == ring[0]:
1563                                rN = ring[1]
1564                                if ring[2] == 'o':
1565                                    rings[rN][0] = G2img.GetTth(xpos,ypos,Data)-rings[rN][1]/2.
1566                                else:
1567                                    rings[rN][0] = G2img.GetTth(xpos,ypos,Data)+rings[rN][1]/2.
1568                        for arc in self.arcList:
1569                            if Obj == arc[0]:
1570                                aN = arc[1]
1571                                if arc[2] == 'o':
1572                                    arcs[aN][0] = G2img.GetTth(xpos,ypos,Data)-arcs[aN][2]/2
1573                                elif arc[2] == 'i':
1574                                    arcs[aN][0] = G2img.GetTth(xpos,ypos,Data)+arcs[aN][2]/2
1575                                elif arc[2] == 'l':
1576                                    arcs[aN][1][0] = int(G2img.GetAzm(xpos,ypos,Data))
1577                                else:
1578                                    arcs[aN][1][1] = int(G2img.GetAzm(xpos,ypos,Data))
1579                        for poly in self.polyList:
1580                            if Obj == poly[0]:
1581                                ind = self.itemPicked.contains(self.mousePicked)[1]['ind'][0]
1582                                oldPos = np.array([self.mousePicked.xdata,self.mousePicked.ydata])
1583                                pN = poly[1]
1584                                for i,xy in enumerate(polygons[pN]):
1585                                    if np.allclose(np.array([xy]),oldPos,atol=1.0):
1586                                        polygons[pN][i] = xpos,ypos
1587                        G2imG.UpdateMasks(self,Masks)
1588#                    else:                  #keep for future debugging
1589#                        print str(self.itemPicked),event.xdata,event.ydata,event.button
1590                PlotImage(self,newImage=True)
1591            self.itemPicked = None
1592           
1593    try:
1594        plotNum = self.G2plotNB.plotList.index('2D Powder Image')
1595        Page = self.G2plotNB.nb.GetPage(plotNum)
1596        if not newPlot:
1597            Plot = Page.figure.gca()          #get previous powder plot & get limits
1598            xylim = Plot.get_xlim(),Plot.get_ylim()
1599        if newImage:
1600            Page.figure.clf()
1601            Plot = Page.figure.gca()          #get a fresh plot after clf()
1602       
1603    except ValueError:
1604        Plot = self.G2plotNB.addMpl('2D Powder Image').gca()
1605        plotNum = self.G2plotNB.plotList.index('2D Powder Image')
1606        Page = self.G2plotNB.nb.GetPage(plotNum)
1607        Page.canvas.mpl_connect('key_press_event', OnImPlotKeyPress)
1608        Page.canvas.mpl_connect('motion_notify_event', OnImMotion)
1609        Page.canvas.mpl_connect('pick_event', OnImPick)
1610        Page.canvas.mpl_connect('button_release_event', OnImRelease)
1611        xylim = []
1612    if not event:                       #event from GUI TextCtrl - don't want focus to change to plot!!!
1613        Page.SetFocus()
1614    Title = self.PatternTree.GetItemText(self.Image)[4:]
1615    self.G2plotNB.status.DestroyChildren()
1616    if self.logPlot:
1617        Title = 'log('+Title+')'
1618    Plot.set_title(Title)
1619    try:
1620        if self.PatternTree.GetItemText(self.PickId) in ['Image Controls',]:
1621            if self.logPlot:
1622                Choice = (' key press','l: log(I) off')
1623            else:
1624                Choice = (' key press','l: log(I) on')
1625            cb = wx.ComboBox(self.G2plotNB.status,style=wx.CB_DROPDOWN|wx.CB_READONLY,
1626                choices=Choice)
1627            cb.Bind(wx.EVT_COMBOBOX, OnKeyBox)
1628            cb.SetValue(' key press')
1629    except TypeError:
1630        pass
1631    size,imagefile = self.PatternTree.GetItemPyData(self.Image)
1632    if imagefile != self.oldImagefile:
1633        imagefile = G2IO.CheckImageFile(self,imagefile)
1634        if not imagefile:
1635            self.G2plotNB.Delete('2D Powder Image')
1636            return
1637        self.PatternTree.SetItemPyData(self.Image,[size,imagefile])
1638        self.ImageZ = G2IO.GetImageData(self,imagefile,imageOnly=True)
1639#        print self.ImageZ.shape,self.ImageZ.size,Data['size'] #might be useful debugging line
1640        self.oldImagefile = imagefile
1641
1642    imScale = 1
1643    if len(self.ImageZ) > 1024:
1644        imScale = len(self.ImageZ)/1024
1645    sizexy = Data['size']
1646    pixelSize = Data['pixelSize']
1647    scalex = 1000./pixelSize[0]
1648    scaley = 1000./pixelSize[1]
1649    Xmax = sizexy[0]*pixelSize[0]/1000.
1650    Ymax = sizexy[1]*pixelSize[1]/1000.
1651    xlim = (0,Xmax)
1652    ylim = (Ymax,0)
1653    Imin,Imax = Data['range'][1]
1654    acolor = mpl.cm.get_cmap(Data['color'])
1655    xcent,ycent = Data['center']
1656    Plot.set_xlabel('Image x-axis, mm',fontsize=12)
1657    Plot.set_ylabel('Image y-axis, mm',fontsize=12)
1658    #do threshold mask - "real" mask - others are just bondaries
1659    Zlim = Masks['Thresholds'][1]
1660    wx.BeginBusyCursor()
1661    try:
1662           
1663        if newImage:                   
1664            MA = ma.masked_greater(ma.masked_less(self.ImageZ,Zlim[0]),Zlim[1])
1665            MaskA = ma.getmaskarray(MA)
1666            A = G2img.ImageCompress(MA,imScale)
1667            AM = G2img.ImageCompress(MaskA,imScale)
1668            if self.logPlot:
1669                A = np.log(A)
1670                AM = np.log(AM)
1671                Imin,Imax = [np.amin(A),np.amax(A)]
1672            ImgM = Plot.imshow(AM,aspect='equal',cmap='Reds',
1673                interpolation='nearest',vmin=0,vmax=2,extent=[0,Xmax,Xmax,0])
1674            Img = Plot.imshow(A,aspect='equal',cmap=acolor,
1675                interpolation='nearest',vmin=Imin,vmax=Imax,extent=[0,Xmax,Ymax,0])
1676            if self.setPoly:
1677                Img.set_picker(True)
1678   
1679        Plot.plot(xcent,ycent,'x')
1680        if Data['showLines']:
1681            LRAzim = Data['LRazimuth']                  #NB: integers
1682            Nazm = Data['outAzimuths']
1683            delAzm = float(LRAzim[1]-LRAzim[0])/Nazm
1684            AzmthOff = Data['azmthOff']
1685            IOtth = Data['IOtth']
1686            wave = Data['wavelength']
1687            dspI = wave/(2.0*sind(IOtth[0]/2.0))
1688            ellI = G2img.GetEllipse(dspI,Data)           #=False if dsp didn't yield an ellipse (ugh! a parabola or a hyperbola)
1689            dspO = wave/(2.0*sind(IOtth[1]/2.0))
1690            ellO = G2img.GetEllipse(dspO,Data)           #Ditto & more likely for outer ellipse
1691            Azm = np.array(range(LRAzim[0],LRAzim[1]+1))-AzmthOff
1692            if ellI:
1693                xyI = []
1694                for azm in Azm:
1695                    xyI.append(G2img.GetDetectorXY(dspI,azm-90.,Data))
1696                xyI = np.array(xyI)
1697                arcxI,arcyI = xyI.T
1698                Plot.plot(arcxI,arcyI,picker=3)
1699            if ellO:
1700                xyO = []
1701                for azm in Azm:
1702                    xyO.append(G2img.GetDetectorXY(dspO,azm-90.,Data))
1703                xyO = np.array(xyO)
1704                arcxO,arcyO = xyO.T
1705                Plot.plot(arcxO,arcyO,picker=3)
1706            if ellO and ellI:
1707                Plot.plot([arcxI[0],arcxO[0]],[arcyI[0],arcyO[0]],picker=3)
1708                Plot.plot([arcxI[-1],arcxO[-1]],[arcyI[-1],arcyO[-1]],picker=3)
1709            for i in range(Nazm):
1710                cake = LRAzim[0]+i*delAzm
1711                ind = np.searchsorted(Azm,cake)
1712                Plot.plot([arcxI[ind],arcxO[ind]],[arcyI[ind],arcyO[ind]],color='k',dashes=(5,5))
1713                   
1714        for xring,yring in Data['ring']:
1715            Plot.plot(xring,yring,'r+',picker=3)
1716        if Data['setRings']:
1717#            rings = np.concatenate((Data['rings']),axis=0)
1718            N = 0
1719            for ring in Data['rings']:
1720                xring,yring = np.array(ring).T[:2]
1721                Plot.plot(xring,yring,'+',color=colors[N%6])
1722                N += 1           
1723        for ellipse in Data['ellipses']:
1724            cent,phi,[width,height],col = ellipse
1725            Plot.add_artist(Ellipse([cent[0],cent[1]],2*width,2*height,phi,ec=col,fc='none'))
1726            Plot.text(cent[0],cent[1],'+',color=col,ha='center',va='center')
1727        #masks - mask lines numbered after integration limit lines
1728        spots = Masks['Points']
1729        rings = Masks['Rings']
1730        arcs = Masks['Arcs']
1731        polygons = Masks['Polygons']
1732        for x,y,d in spots:
1733            Plot.add_artist(Circle((x,y),radius=d/2,fc='none',ec='r',picker=3))
1734        self.ringList = []
1735        for iring,(tth,thick) in enumerate(rings):
1736            wave = Data['wavelength']
1737            x1,y1 = np.hsplit(np.array(G2img.makeIdealRing(G2img.GetEllipse(Dsp(tth+thick/2.,wave),Data))),2)
1738            x2,y2 = np.hsplit(np.array(G2img.makeIdealRing(G2img.GetEllipse(Dsp(tth-thick/2.,wave),Data))),2)
1739            self.ringList.append([Plot.plot(x1,y1,'r',picker=3),iring,'o'])           
1740            self.ringList.append([Plot.plot(x2,y2,'r',picker=3),iring,'i'])
1741        self.arcList = []
1742        for iarc,(tth,azm,thick) in enumerate(arcs):           
1743            wave = Data['wavelength']
1744            x1,y1 = np.hsplit(np.array(G2img.makeIdealRing(G2img.GetEllipse(Dsp(tth+thick/2.,wave),Data),azm)),2)
1745            x2,y2 = np.hsplit(np.array(G2img.makeIdealRing(G2img.GetEllipse(Dsp(max(0.01,tth-thick/2.),wave),Data),azm)),2)
1746            self.arcList.append([Plot.plot(x1,y1,'r',picker=3),iarc,'o'])           
1747            self.arcList.append([Plot.plot(x2,y2,'r',picker=3),iarc,'i'])
1748            self.arcList.append([Plot.plot([x1[0],x2[0]],[y1[0],y2[0]],'r',picker=3),iarc,'l'])
1749            self.arcList.append([Plot.plot([x1[-1],x2[-1]],[y1[-1],y2[-1]],'r',picker=3),iarc,'u'])
1750        self.polyList = []
1751        for ipoly,polygon in enumerate(polygons):
1752            x,y = np.hsplit(np.array(polygon),2)
1753            self.polyList.append([Plot.plot(x,y,'r+',picker=10),ipoly])
1754            Plot.plot(x,y,'r')           
1755        if newImage:
1756            colorBar = Page.figure.colorbar(Img)
1757        Plot.set_xlim(xlim)
1758        Plot.set_ylim(ylim)
1759        if not newPlot and xylim:
1760            Page.toolbar.push_current()
1761            Plot.set_xlim(xylim[0])
1762            Plot.set_ylim(xylim[1])
1763            xylim = []
1764            Page.toolbar.push_current()
1765            Page.toolbar.draw()
1766        else:
1767            Page.canvas.draw()
1768    finally:
1769        wx.EndBusyCursor()
1770       
1771def PlotIntegration(self,newPlot=False,event=None):
1772    '''Plot of 2D image after image integration with 2-theta and azimuth as coordinates
1773    '''
1774           
1775    def OnMotion(event):
1776        Page.canvas.SetToolTipString('')
1777        Page.canvas.SetCursor(wx.CROSS_CURSOR)
1778        azm = event.ydata
1779        tth = event.xdata
1780        if azm and tth:
1781            self.G2plotNB.status.SetFields(\
1782                ['','Detector 2-th =%9.3fdeg, azm = %7.2fdeg'%(tth,azm)])
1783                               
1784    try:
1785        plotNum = self.G2plotNB.plotList.index('2D Integration')
1786        Page = self.G2plotNB.nb.GetPage(plotNum)
1787        if not newPlot:
1788            Plot = Page.figure.gca()          #get previous plot & get limits
1789            xylim = Plot.get_xlim(),Plot.get_ylim()
1790        Page.figure.clf()
1791        Plot = Page.figure.gca()          #get a fresh plot after clf()
1792       
1793    except ValueError:
1794        Plot = self.G2plotNB.addMpl('2D Integration').gca()
1795        plotNum = self.G2plotNB.plotList.index('2D Integration')
1796        Page = self.G2plotNB.nb.GetPage(plotNum)
1797        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
1798        Page.views = False
1799        view = False
1800    if not event:
1801        Page.SetFocus()
1802       
1803    Data = self.PatternTree.GetItemPyData(
1804        G2gd.GetPatternTreeItemId(self,self.Image, 'Image Controls'))
1805    image = self.Integrate[0]
1806    xsc = self.Integrate[1]
1807    ysc = self.Integrate[2]
1808    Imin,Imax = Data['range'][1]
1809    acolor = mpl.cm.get_cmap(Data['color'])
1810    Plot.set_title(self.PatternTree.GetItemText(self.Image)[4:])
1811    Plot.set_ylabel('azimuth',fontsize=12)
1812    Plot.set_xlabel('2-theta',fontsize=12)
1813    Img = Plot.imshow(image,cmap=acolor,vmin=Imin,vmax=Imax,interpolation='nearest', \
1814        extent=[ysc[0],ysc[-1],xsc[-1],xsc[0]],aspect='auto')
1815    colorBar = Page.figure.colorbar(Img)
1816    if Data['setRings'] and Data['rings']:
1817        rings = np.concatenate((Data['rings']),axis=0)
1818        for xring,yring,dsp in rings:
1819            x,y = G2img.GetTthAzm(xring,yring,Data)
1820            Plot.plot(x,y,'r+')
1821    if Data['ellipses']:           
1822        for ellipse in Data['ellipses']:
1823            ring = np.array(G2img.makeIdealRing(ellipse[:3])) #skip color
1824            x,y = np.hsplit(ring,2)
1825            tth,azm = G2img.GetTthAzm(x,y,Data)
1826            azm = np.where(azm < 0.,azm+360,azm)
1827            Plot.plot(tth,azm,'b,')
1828    if not newPlot:
1829        Page.toolbar.push_current()
1830        Plot.set_xlim(xylim[0])
1831        Plot.set_ylim(xylim[1])
1832        xylim = []
1833        Page.toolbar.push_current()
1834        Page.toolbar.draw()
1835    else:
1836        Page.canvas.draw()
1837               
1838def PlotTRImage(self,tax,tay,taz,newPlot=False):
1839    '''a test plot routine - not normally used
1840    ''' 
1841           
1842    def OnMotion(event):
1843        Page.canvas.SetToolTipString('')
1844        Page.canvas.SetCursor(wx.CROSS_CURSOR)
1845        azm = event.xdata
1846        tth = event.ydata
1847        if azm and tth:
1848            self.G2plotNB.status.SetFields(\
1849                ['','Detector 2-th =%9.3fdeg, azm = %7.2fdeg'%(tth,azm)])
1850                               
1851    try:
1852        plotNum = self.G2plotNB.plotList.index('2D Transformed Powder Image')
1853        Page = self.G2plotNB.nb.GetPage(plotNum)
1854        if not newPlot:
1855            Plot = Page.figure.gca()          #get previous plot & get limits
1856            xylim = Plot.get_xlim(),Plot.get_ylim()
1857        Page.figure.clf()
1858        Plot = Page.figure.gca()          #get a fresh plot after clf()
1859       
1860    except ValueError:
1861        Plot = self.G2plotNB.addMpl('2D Transformed Powder Image').gca()
1862        plotNum = self.G2plotNB.plotList.index('2D Transformed Powder Image')
1863        Page = self.G2plotNB.nb.GetPage(plotNum)
1864        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
1865        Page.views = False
1866        view = False
1867    Page.SetFocus()
1868       
1869    Data = self.PatternTree.GetItemPyData(
1870        G2gd.GetPatternTreeItemId(self,self.Image, 'Image Controls'))
1871    Imin,Imax = Data['range'][1]
1872    step = (Imax-Imin)/5.
1873    V = np.arange(Imin,Imax,step)
1874    acolor = mpl.cm.get_cmap(Data['color'])
1875    Plot.set_title(self.PatternTree.GetItemText(self.Image)[4:])
1876    Plot.set_xlabel('azimuth',fontsize=12)
1877    Plot.set_ylabel('2-theta',fontsize=12)
1878    Plot.contour(tax,tay,taz,V,cmap=acolor)
1879    if Data['showLines']:
1880        IOtth = Data['IOtth']
1881        if Data['fullIntegrate']:
1882            LRAzim = [-180,180]
1883        else:
1884            LRAzim = Data['LRazimuth']                  #NB: integers
1885        Plot.plot([LRAzim[0],LRAzim[1]],[IOtth[0],IOtth[0]],picker=True)
1886        Plot.plot([LRAzim[0],LRAzim[1]],[IOtth[1],IOtth[1]],picker=True)
1887        if not Data['fullIntegrate']:
1888            Plot.plot([LRAzim[0],LRAzim[0]],[IOtth[0],IOtth[1]],picker=True)
1889            Plot.plot([LRAzim[1],LRAzim[1]],[IOtth[0],IOtth[1]],picker=True)
1890    if Data['setRings']:
1891        rings = np.concatenate((Data['rings']),axis=0)
1892        for xring,yring,dsp in rings:
1893            x,y = G2img.GetTthAzm(xring,yring,Data)
1894            Plot.plot(y,x,'r+')           
1895    if Data['ellipses']:           
1896        for ellipse in Data['ellipses']:
1897            ring = np.array(G2img.makeIdealRing(ellipse[:3])) #skip color
1898            x,y = np.hsplit(ring,2)
1899            tth,azm = G2img.GetTthAzm(x,y,Data)
1900            Plot.plot(azm,tth,'b,')
1901    if not newPlot:
1902        Page.toolbar.push_current()
1903        Plot.set_xlim(xylim[0])
1904        Plot.set_ylim(xylim[1])
1905        xylim = []
1906        Page.toolbar.push_current()
1907        Page.toolbar.draw()
1908    else:
1909        Page.canvas.draw()
1910       
1911def PlotStructure(self,data):
1912    '''Crystal structure plotting package. Can show structures as balls, sticks, lines,
1913    thermal motion ellipsoids and polyhedra
1914    '''
1915    generalData = data['General']
1916    cell = generalData['Cell'][1:7]
1917    Amat,Bmat = G2lat.cell2AB(cell)         #Amat - crystal to cartesian, Bmat - inverse
1918    A4mat = np.concatenate((np.concatenate((Amat,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
1919    B4mat = np.concatenate((np.concatenate((Bmat,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
1920    Mydir = generalData['Mydir']
1921    atomData = data['Atoms']
1922    drawingData = data['Drawing']
1923    drawAtoms = drawingData['Atoms']
1924    cx,ct,cs = drawingData['atomPtrs']
1925    Wt = [255,255,255]
1926    Rd = [255,0,0]
1927    Gr = [0,255,0]
1928    Bl = [0,0,255]
1929    uBox = np.array([[0,0,0],[1,0,0],[1,1,0],[0,1,0],[0,0,1],[1,0,1],[1,1,1],[0,1,1]])
1930    uEdges = np.array([
1931        [uBox[0],uBox[1]],[uBox[0],uBox[3]],[uBox[0],uBox[4]],[uBox[1],uBox[2]], 
1932        [uBox[2],uBox[3]],[uBox[1],uBox[5]],[uBox[2],uBox[6]],[uBox[3],uBox[7]], 
1933        [uBox[4],uBox[5]],[uBox[5],uBox[6]],[uBox[6],uBox[7]],[uBox[7],uBox[4]]])
1934    uColors = [Rd,Gr,Bl,Wt, Wt,Wt,Wt,Wt, Wt,Wt,Wt,Wt]
1935    altDown = False
1936    shiftDown = False
1937    ctrlDown = False
1938   
1939    def OnKeyBox(event):
1940        import Image
1941        Draw()                          #make sure plot is fresh!!
1942        mode = cb.GetValue()
1943        Fname = Mydir+'\\'+generalData['Name']+'.'+mode
1944        size = Page.canvas.GetSize()
1945        glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
1946        if mode in ['jpeg',]:
1947            Pix = glReadPixels(0,0,size[0],size[1],GL_RGBA, GL_UNSIGNED_BYTE)
1948            im = Image.new("RGBA", (size[0],size[1]))
1949        else:
1950            Pix = glReadPixels(0,0,size[0],size[1],GL_RGB, GL_UNSIGNED_BYTE)
1951            im = Image.new("RGB", (size[0],size[1]))
1952        im.fromstring(Pix)
1953        im.save(Fname,mode)
1954        cb.SetValue(' Save as:')
1955        self.G2plotNB.status.SetStatusText('Drawing saved to: '+Fname,1)
1956   
1957    def GetTruePosition(xy):
1958        View = glGetIntegerv(GL_VIEWPORT)
1959        Proj = glGetDoublev(GL_PROJECTION_MATRIX)
1960        Model = glGetDoublev(GL_MODELVIEW_MATRIX)
1961        Zmax = 1.
1962        for i,atom in enumerate(drawAtoms):
1963            x,y,z = atom[cx:cx+3]
1964            X,Y,Z = gluProject(x,y,z,Model,Proj,View)
1965            XY = [int(X),int(View[3]-Y)]
1966            if np.allclose(xy,XY,atol=10) and Z < Zmax:
1967                Zmax = Z
1968                SetSelectedAtoms(i)
1969                   
1970    def OnMouseDown(event):
1971        xy = event.GetPosition()
1972        if event.ShiftDown():
1973            GetTruePosition(xy)
1974        else:
1975            drawingData['Rotation'][3] = xy
1976        Draw()
1977       
1978    def OnMouseMove(event):
1979        newxy = event.GetPosition()
1980        page = getSelection()
1981        if event.ControlDown() and drawingData['showABC']:
1982            if event.LeftIsDown():
1983                SetTestRot(newxy)
1984            elif event.RightIsDown():
1985                SetTestPos(newxy)
1986            elif event.MiddleIsDown():
1987                SetTestRotZ(newxy)
1988            x,y,z = drawingData['testPos'][0]
1989            self.G2plotNB.status.SetStatusText('moving test point %.4f,%.4f,%.4f'%(x,y,z),1)
1990               
1991               
1992        if event.Dragging() and not event.ControlDown():
1993            if event.LeftIsDown():
1994                SetRotation(newxy)
1995                angX,angY,angZ = drawingData['Rotation'][:3]
1996                self.G2plotNB.status.SetStatusText('New rotation: %.2f, %.2f ,%.2f'%(angX,angY,angZ),1)
1997            elif event.RightIsDown():
1998                SetTranslation(newxy)
1999                Tx,Ty,Tz = drawingData['viewPoint'][0]
2000                self.G2plotNB.status.SetStatusText('New view point: %.4f, %.4f, %.4f'%(Tx,Ty,Tz),1)
2001            elif event.MiddleIsDown():
2002                SetRotationZ(newxy)
2003                angX,angY,angZ = drawingData['Rotation'][:3]
2004                self.G2plotNB.status.SetStatusText('New rotation: %.2f, %.2f, %.2f'%(angX,angY,angZ),1)
2005        Draw()
2006       
2007    def OnMouseWheel(event):
2008        drawingData['cameraPos'] += event.GetWheelRotation()/24
2009        drawingData['cameraPos'] = max(10,min(500,drawingData['cameraPos']))
2010        self.G2plotNB.status.SetStatusText('New camera distance: %.2f'%(drawingData['cameraPos']),1)
2011        page = getSelection()
2012        if page:
2013            if self.dataDisplay.GetPageText(page) == 'Draw Options':
2014                panel = self.dataDisplay.GetPage(page).GetChildren()[0].GetChildren()
2015                names = [child.GetName() for child in panel]
2016                panel[names.index('cameraPos')].SetLabel('Camera Position: '+'%.2f'%(drawingData['cameraPos']))
2017                panel[names.index('cameraSlider')].SetValue(drawingData['cameraPos'])
2018        Draw()
2019       
2020    def getSelection():
2021        try:
2022            return self.dataDisplay.GetSelection()
2023        except AttributeError:
2024            print self.dataDisplay.GetLabel()
2025            self.G2plotNB.status.SetStatusText('Select this from Phase data window!')
2026            return 0
2027           
2028    def SetViewPointText(VP):
2029        page = getSelection()
2030        if page:
2031            if self.dataDisplay.GetPageText(page) == 'Draw Options':
2032                panel = self.dataDisplay.GetPage(page).GetChildren()[0].GetChildren()
2033                names = [child.GetName() for child in panel]
2034                panel[names.index('viewPoint')].SetValue('%.3f, %.3f, %.3f'%(VP[0],VP[1],VP[2]))
2035           
2036    def ClearSelectedAtoms():
2037        page = getSelection()
2038        if page:
2039            if self.dataDisplay.GetPageText(page) == 'Draw Atoms':
2040                self.dataDisplay.GetPage(page).ClearSelection()      #this is the Atoms grid in Draw Atoms
2041            elif self.dataDisplay.GetPageText(page) == 'Atoms':
2042                self.dataDisplay.GetPage(page).ClearSelection()      #this is the Atoms grid in Atoms
2043                   
2044    def SetSelectedAtoms(ind):
2045        page = getSelection()
2046        if page:
2047            if self.dataDisplay.GetPageText(page) == 'Draw Atoms':
2048                self.dataDisplay.GetPage(page).SelectRow(ind)      #this is the Atoms grid in Draw Atoms
2049            elif self.dataDisplay.GetPageText(page) == 'Atoms':
2050                Id = drawAtoms[ind][-2]
2051                for i,atom in enumerate(atomData):
2052                    if atom[-1] == Id:
2053                        self.dataDisplay.GetPage(page).SelectRow(i)      #this is the Atoms grid in Atoms
2054                 
2055    def GetSelectedAtoms():
2056        page = getSelection()
2057        Ind = []
2058        if page:
2059            if self.dataDisplay.GetPageText(page) == 'Draw Atoms':
2060                Ind = self.dataDisplay.GetPage(page).GetSelectedRows()      #this is the Atoms grid in Draw Atoms
2061            elif self.dataDisplay.GetPageText(page) == 'Atoms':
2062                Ind = self.dataDisplay.GetPage(page).GetSelectedRows()      #this is the Atoms grid in Atoms
2063        return Ind
2064                                       
2065    def OnKey(event):           #on key UP!!
2066        keyCode = event.GetKeyCode()
2067        if keyCode > 255:
2068            keyCode = 0
2069        key,xyz = chr(keyCode),event.GetPosition()
2070        indx = drawingData['selectedAtoms']
2071        if key in ['c','C']:
2072            drawingData['viewPoint'] = [[.5,.5,.5],[0,0]]
2073            drawingData['testPos'] = [[-.1,-.1,-.1],[0.0,0.0,0.0],[0,0]]
2074            drawingData['Rotation'] = [0.0,0.0,0.0,[]]
2075            SetViewPointText(drawingData['viewPoint'][0])
2076        elif key in ['n','N']:
2077            drawAtoms = drawingData['Atoms']
2078            pI = drawingData['viewPoint'][1]
2079            if indx:
2080                pI[0] = indx[pI[1]]
2081                Tx,Ty,Tz = drawAtoms[pI[0]][cx:cx+3]
2082                pI[1] += 1
2083                if pI[1] >= len(indx):
2084                    pI[1] = 0
2085            else:
2086                Tx,Ty,Tz = drawAtoms[pI[0]][cx:cx+3]               
2087                pI[0] += 1
2088                if pI[0] >= len(drawAtoms):
2089                    pI[0] = 0
2090            drawingData['viewPoint'] = [[Tx,Ty,Tz],pI]
2091            SetViewPointText(drawingData['viewPoint'][0])
2092               
2093        elif key in ['p','P']:
2094            drawAtoms = drawingData['Atoms']
2095            pI = drawingData['viewPoint'][1]
2096            if indx:
2097                pI[0] = indx[pI[1]]
2098                Tx,Ty,Tz = drawAtoms[pI[0]][cx:cx+3]
2099                pI[1] -= 1
2100                if pI[1] < 0:
2101                    pI[1] = len(indx)-1
2102            else:
2103                Tx,Ty,Tz = drawAtoms[pI[0]][cx:cx+3]               
2104                pI[0] -= 1
2105                if pI[0] < 0:
2106                    pI[0] = len(drawAtoms)-1
2107            drawingData['viewPoint'] = [[Tx,Ty,Tz],pI]
2108            SetViewPointText(drawingData['viewPoint'][0])           
2109        Draw()
2110           
2111    def SetBackground():
2112        R,G,B,A = Page.camera['backColor']
2113        glClearColor(R,G,B,A)
2114        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
2115       
2116    def SetLights():
2117        glEnable(GL_DEPTH_TEST)
2118        glShadeModel(GL_SMOOTH)
2119        glEnable(GL_LIGHTING)
2120        glEnable(GL_LIGHT0)
2121        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,0)
2122        glLightfv(GL_LIGHT0,GL_AMBIENT,[1,1,1,.8])
2123        glLightfv(GL_LIGHT0,GL_DIFFUSE,[1,1,1,1])
2124       
2125    def SetTranslation(newxy):
2126        Tx,Ty,Tz = drawingData['viewPoint'][0]
2127        anglex,angley,anglez,oldxy = drawingData['Rotation']
2128        Rx = G2lat.rotdMat(anglex,0)
2129        Ry = G2lat.rotdMat(angley,1)
2130        Rz = G2lat.rotdMat(anglez,2)
2131        dxy = list(newxy-oldxy)+[0,]
2132        dxy = np.inner(Bmat,np.inner(Rz,np.inner(Ry,np.inner(Rx,dxy))))
2133        Tx -= dxy[0]*0.01
2134        Ty += dxy[1]*0.01
2135        Tz -= dxy[2]*0.01
2136        drawingData['Rotation'][3] = newxy
2137        drawingData['viewPoint'][0] =  Tx,Ty,Tz
2138        SetViewPointText([Tx,Ty,Tz])
2139       
2140    def SetTestPos(newxy):
2141        Tx,Ty,Tz = drawingData['testPos'][0]
2142        anglex,angley,anglez,oldxy = drawingData['Rotation']
2143        Rx = G2lat.rotdMat(anglex,0)
2144        Ry = G2lat.rotdMat(angley,1)
2145        Rz = G2lat.rotdMat(anglez,2)
2146        dxy = list(newxy-oldxy)+[0,]
2147        dxy = np.inner(Rz,np.inner(Ry,np.inner(Rx,dxy)))
2148        Tx += dxy[0]*0.001
2149        Ty -= dxy[1]*0.001
2150        Tz += dxy[2]*0.001
2151        drawingData['Rotation'][3] = newxy
2152        drawingData['testPos'][0] =  Tx,Ty,Tz
2153       
2154    def SetTestRot(newxy):
2155        Txyz = np.array(drawingData['testPos'][0])
2156        oldxy = drawingData['testPos'][2]
2157        Ax,Ay,Az = drawingData['testPos'][1]
2158        Vxyz = np.array(drawingData['viewPoint'][0])
2159        Dxyz = np.inner(Amat,Txyz-Vxyz)
2160        dxy = list(newxy-oldxy)+[0,]
2161        Ax += dxy[1]*0.01
2162        Ay += dxy[0]*0.01
2163        Rx = G2lat.rotdMat(Ax,0)
2164        Ry = G2lat.rotdMat(Ay,1)
2165        Dxyz = np.inner(Ry,np.inner(Rx,Dxyz))       
2166        Dxyz = np.inner(Bmat,Dxyz)+Vxyz
2167        drawingData['testPos'][1] = [Ax,Ay,Az]
2168        drawingData['testPos'][2] = newxy
2169        drawingData['testPos'][0] = Dxyz
2170       
2171    def SetTestRotZ(newxy):
2172        Txyz = np.array(drawingData['testPos'][0])
2173        oldxy = drawingData['testPos'][2]
2174        Ax,Ay,Az = drawingData['testPos'][1]
2175        Vxyz = np.array(drawingData['viewPoint'][0])
2176        Dxyz = np.inner(Amat,Txyz-Vxyz)       
2177        dxy = list(newxy-oldxy)+[0,]
2178        Az += (dxy[0]+dxy[1])*.01
2179        Rz = G2lat.rotdMat(Az,2)
2180        Dxyz = np.inner(Rz,Dxyz)       
2181        Dxyz = np.inner(Bmat,Dxyz)+Vxyz
2182        drawingData['testPos'][1] = [Ax,Ay,Az]
2183        drawingData['testPos'][2] = newxy
2184        drawingData['testPos'][0] = Dxyz
2185                             
2186    def SetRotation(newxy):       
2187        anglex,angley,anglez,oldxy = drawingData['Rotation']
2188        dxy = newxy-oldxy
2189        anglex += dxy[1]*.25
2190        angley += dxy[0]*.25
2191        oldxy = newxy
2192        drawingData['Rotation'] = [anglex,angley,anglez,oldxy]
2193       
2194    def SetRotationZ(newxy):                       
2195        anglex,angley,anglez,oldxy = drawingData['Rotation']
2196        dxy = newxy-oldxy
2197        anglez += (dxy[0]+dxy[1])*.25
2198        oldxy = newxy
2199        drawingData['Rotation'] = [anglex,angley,anglez,oldxy]
2200       
2201    def RenderBox():
2202        glEnable(GL_COLOR_MATERIAL)
2203        glLineWidth(2)
2204        glEnable(GL_BLEND)
2205        glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)
2206        glEnable(GL_LINE_SMOOTH)
2207        glBegin(GL_LINES)
2208        for line,color in zip(uEdges,uColors):
2209            glColor3ubv(color)
2210            glVertex3fv(line[0])
2211            glVertex3fv(line[1])
2212        glEnd()
2213        glColor4ubv([0,0,0,0])
2214        glDisable(GL_LINE_SMOOTH)
2215        glDisable(GL_BLEND)
2216        glDisable(GL_COLOR_MATERIAL)
2217       
2218    def RenderUnitVectors(x,y,z):
2219        xyz = np.array([x,y,z])
2220        glEnable(GL_COLOR_MATERIAL)
2221        glLineWidth(1)
2222        glPushMatrix()
2223        glTranslate(x,y,z)
2224        glScalef(1/cell[0],1/cell[1],1/cell[2])
2225        glBegin(GL_LINES)
2226        for line,color in zip(uEdges,uColors)[:3]:
2227            glColor3ubv(color)
2228            glVertex3fv(line[0])
2229            glVertex3fv(line[1])
2230        glEnd()
2231        glPopMatrix()
2232        glColor4ubv([0,0,0,0])
2233        glDisable(GL_COLOR_MATERIAL)
2234               
2235    def RenderSphere(x,y,z,radius,color):
2236        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color)
2237        glPushMatrix()
2238        glTranslate(x,y,z)
2239        glMultMatrixf(B4mat.T)
2240        q = gluNewQuadric()
2241        gluSphere(q,radius,20,10)
2242        glPopMatrix()
2243       
2244    def RenderEllipsoid(x,y,z,ellipseProb,E,R4,color):
2245        s1,s2,s3 = E
2246        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color)
2247        glPushMatrix()
2248        glTranslate(x,y,z)
2249        glMultMatrixf(B4mat.T)
2250        glMultMatrixf(R4.T)
2251        glEnable(GL_NORMALIZE)
2252        glScale(s1,s2,s3)
2253        q = gluNewQuadric()
2254        gluSphere(q,ellipseProb,20,10)
2255        glDisable(GL_NORMALIZE)
2256        glPopMatrix()
2257       
2258    def RenderBonds(x,y,z,Bonds,radius,color,slice=20):
2259        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color)
2260        glPushMatrix()
2261        glTranslate(x,y,z)
2262        glMultMatrixf(B4mat.T)
2263        for bond in Bonds:
2264            glPushMatrix()
2265            Dx = np.inner(Amat,bond)
2266            Z = np.sqrt(np.sum(Dx**2))
2267            azm = atan2d(-Dx[1],-Dx[0])
2268            phi = acosd(Dx[2]/Z)
2269            glRotate(-azm,0,0,1)
2270            glRotate(phi,1,0,0)
2271            q = gluNewQuadric()
2272            gluCylinder(q,radius,radius,Z,slice,2)
2273            glPopMatrix()           
2274        glPopMatrix()
2275               
2276    def RenderLines(x,y,z,Bonds,color):
2277        xyz = np.array([x,y,z])
2278        glEnable(GL_COLOR_MATERIAL)
2279        glLineWidth(1)
2280        glColor3fv(color)
2281        glPushMatrix()
2282        glBegin(GL_LINES)
2283        for bond in Bonds:
2284            glVertex3fv(xyz)
2285            glVertex3fv(xyz+bond)
2286        glEnd()
2287        glColor4ubv([0,0,0,0])
2288        glPopMatrix()
2289        glDisable(GL_COLOR_MATERIAL)
2290       
2291    def RenderPolyhedra(x,y,z,Faces,color):
2292        glPushMatrix()
2293        glTranslate(x,y,z)
2294        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color)
2295        glShadeModel(GL_SMOOTH)
2296        glMultMatrixf(B4mat.T)
2297        for face,norm in Faces:
2298            glPolygonMode(GL_FRONT_AND_BACK,GL_FILL)
2299            glFrontFace(GL_CW)
2300            glNormal3fv(norm)
2301            glBegin(GL_TRIANGLES)
2302            for vert in face:
2303                glVertex3fv(vert)
2304            glEnd()
2305        glPopMatrix()
2306       
2307    def RenderBackbone(Backbone,BackboneColor,radius):
2308        glPushMatrix()
2309        glMultMatrixf(B4mat.T)
2310        glEnable(GL_COLOR_MATERIAL)
2311        glShadeModel(GL_SMOOTH)
2312        gleSetJoinStyle(TUBE_NORM_EDGE | TUBE_JN_ANGLE | TUBE_JN_CAP)
2313        glePolyCylinder(Backbone,BackboneColor,radius)
2314        glPopMatrix()       
2315        glDisable(GL_COLOR_MATERIAL)
2316       
2317    def RenderLabel(x,y,z,label,r):       
2318        glPushMatrix()
2319        glTranslate(x,y,z)
2320        glMultMatrixf(B4mat.T)
2321        glDisable(GL_LIGHTING)
2322        glColor3f(0,1.,0)
2323        glRasterPos3f(r,r,r)
2324        for c in list(label):
2325            glutBitmapCharacter(GLUT_BITMAP_8_BY_13,ord(c))
2326        glEnable(GL_LIGHTING)
2327        glPopMatrix()
2328                           
2329    def Draw():
2330        import numpy.linalg as nl
2331        Ind = GetSelectedAtoms()
2332        VS = np.array(Page.canvas.GetSize())
2333        aspect = float(VS[0])/float(VS[1])
2334        cPos = drawingData['cameraPos']
2335        Zclip = drawingData['Zclip']*cPos/200.
2336        anglex,angley,anglez = drawingData['Rotation'][:3]
2337        Tx,Ty,Tz = drawingData['viewPoint'][0]
2338        cx,ct,cs = drawingData['atomPtrs']
2339        bondR = drawingData['bondRadius']
2340        G,g = G2lat.cell2Gmat(cell)
2341        GS = G
2342        GS[0][1] = GS[1][0] = math.sqrt(GS[0][0]*GS[1][1])
2343        GS[0][2] = GS[2][0] = math.sqrt(GS[0][0]*GS[2][2])
2344        GS[1][2] = GS[2][1] = math.sqrt(GS[1][1]*GS[2][2])
2345        ellipseProb = G2lat.criticalEllipse(drawingData['ellipseProb']/100.)
2346       
2347        SetBackground()
2348        glInitNames()
2349        glPushName(0)
2350       
2351        glMatrixMode(GL_PROJECTION)
2352        glLoadIdentity()
2353        glViewport(0,0,VS[0],VS[1])
2354        gluPerspective(20.,aspect,cPos-Zclip,cPos+Zclip)
2355        gluLookAt(0,0,cPos,0,0,0,0,1,0)
2356        SetLights()           
2357           
2358        glMatrixMode(GL_MODELVIEW)
2359        glLoadIdentity()
2360        glRotate(anglez,0,0,1)
2361        glRotate(anglex,cosd(anglez),-sind(anglez),0)
2362        glRotate(angley,sind(anglez),cosd(anglez),0)
2363        glMultMatrixf(A4mat.T)
2364        glTranslate(-Tx,-Ty,-Tz)
2365        if drawingData['unitCellBox']:
2366            RenderBox()
2367        if drawingData['showABC']:
2368            x,y,z = drawingData['testPos'][0]
2369#            if altDown:
2370#                self.G2plotNB.status.SetStatusText('moving test point %.4f,%.4f,%.4f'%(x,y,z),1)
2371#            else:
2372#                self.G2plotNB.status.SetStatusText('test point %.4f,%.4f,%.4f'%(x,y,z),1)           
2373            RenderUnitVectors(x,y,z)
2374        Backbone = []
2375        BackboneColor = []
2376        time0 = time.time()
2377        for iat,atom in enumerate(drawingData['Atoms']):
2378            x,y,z = atom[cx:cx+3]
2379            Bonds = atom[-2]
2380            Faces = atom[-1]
2381            try:
2382                atNum = generalData['AtomTypes'].index(atom[ct])
2383            except ValueError:
2384                atNum = -1
2385            CL = atom[cs+2]
2386            color = np.array(CL)/255.
2387            if iat in Ind:
2388                color = np.array(Gr)/255.
2389            radius = 0.5
2390            if atom[cs] != '':
2391                glLoadName(atom[-3])                   
2392            if 'balls' in atom[cs]:
2393                vdwScale = drawingData['vdwScale']
2394                ballScale = drawingData['ballScale']
2395                if atNum < 0:
2396                    radius = 0.2
2397                elif 'H' == atom[ct]:
2398                    if drawingData['showHydrogen']:
2399                        if 'vdW' in atom[cs] and atNum >= 0:
2400                            radius = vdwScale*generalData['vdWRadii'][atNum]
2401                        else:
2402                            radius = ballScale*drawingData['sizeH']
2403                    else:
2404                        radius = 0.0
2405                else:
2406                    if 'vdW' in atom[cs]:
2407                        radius = vdwScale*generalData['vdWRadii'][atNum]
2408                    else:
2409                        radius = ballScale*generalData['BondRadii'][atNum]
2410                RenderSphere(x,y,z,radius,color)
2411                if 'sticks' in atom[cs]:
2412                    RenderBonds(x,y,z,Bonds,bondR,color)
2413            elif 'ellipsoids' in atom[cs]:
2414                RenderBonds(x,y,z,Bonds,bondR,color)
2415                if atom[cs+3] == 'A':                   
2416                    Uij = atom[cs+5:cs+11]
2417                    U = np.multiply(G2spc.Uij2U(Uij),GS)
2418                    U = np.inner(Amat,np.inner(U,Amat).T)
2419                    E,R = nl.eigh(U)
2420                    R4 = np.concatenate((np.concatenate((R,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
2421                    E = np.sqrt(E)
2422                    if atom[ct] == 'H' and not drawingData['showHydrogen']:
2423                        pass
2424                    else:
2425                        RenderEllipsoid(x,y,z,ellipseProb,E,R4,color)                   
2426                else:
2427                    if atom[ct] == 'H' and not drawingData['showHydrogen']:
2428                        pass
2429                    else:
2430                        radius = ellipseProb*math.sqrt(abs(atom[cs+4]))
2431                        RenderSphere(x,y,z,radius,color)
2432            elif 'lines' in atom[cs]:
2433                radius = 0.1
2434                RenderLines(x,y,z,Bonds,color)
2435#                RenderBonds(x,y,z,Bonds,0.05,color,6)
2436            elif atom[cs] == 'sticks':
2437                radius = 0.1
2438                RenderBonds(x,y,z,Bonds,bondR,color)
2439            elif atom[cs] == 'polyhedra':
2440                RenderPolyhedra(x,y,z,Faces,color)
2441            elif atom[cs] == 'backbone':
2442                if atom[ct-1].split()[0] in ['C','N']:
2443                    Backbone.append(list(np.inner(Amat,np.array([x,y,z]))))
2444                    BackboneColor.append(list(color))
2445                   
2446            if atom[cs+1] == 'type':
2447                RenderLabel(x,y,z,atom[ct],radius)
2448            elif atom[cs+1] == 'name':
2449                RenderLabel(x,y,z,atom[ct-1],radius)
2450            elif atom[cs+1] == 'number':
2451                RenderLabel(x,y,z,str(iat+1),radius)
2452            elif atom[cs+1] == 'residue' and atom[ct-1] == 'CA':
2453                RenderLabel(x,y,z,atom[ct-4],radius)
2454            elif atom[cs+1] == '1-letter' and atom[ct-1] == 'CA':
2455                RenderLabel(x,y,z,atom[ct-3],radius)
2456            elif atom[cs+1] == 'chain' and atom[ct-1] == 'CA':
2457                RenderLabel(x,y,z,atom[ct-2],radius)
2458        if Backbone:
2459            RenderBackbone(Backbone,BackboneColor,bondR)
2460#        print time.time()-time0
2461        Page.canvas.SwapBuffers()
2462       
2463    def OnSize(event):
2464        Draw()
2465       
2466    def OnFocus(event):
2467        Draw()
2468       
2469    try:
2470        plotNum = self.G2plotNB.plotList.index(generalData['Name'])
2471        Page = self.G2plotNB.nb.GetPage(plotNum)       
2472    except ValueError:
2473        Plot = self.G2plotNB.addOgl(generalData['Name'])
2474        plotNum = self.G2plotNB.plotList.index(generalData['Name'])
2475        Page = self.G2plotNB.nb.GetPage(plotNum)
2476        Page.views = False
2477        view = False
2478        altDown = False
2479    Page.SetFocus()
2480    cb = wx.ComboBox(self.G2plotNB.status,style=wx.CB_DROPDOWN|wx.CB_READONLY,
2481        choices=(' save as:','jpeg','tiff','bmp'))
2482    cb.Bind(wx.EVT_COMBOBOX, OnKeyBox)
2483    cb.SetValue(' save as:')
2484    Page.canvas.Bind(wx.EVT_MOUSEWHEEL, OnMouseWheel)
2485    Page.canvas.Bind(wx.EVT_LEFT_DOWN, OnMouseDown)
2486    Page.canvas.Bind(wx.EVT_RIGHT_DOWN, OnMouseDown)
2487    Page.canvas.Bind(wx.EVT_MIDDLE_DOWN, OnMouseDown)
2488    Page.canvas.Bind(wx.EVT_KEY_UP, OnKey)
2489    Page.canvas.Bind(wx.EVT_MOTION, OnMouseMove)
2490    Page.canvas.Bind(wx.EVT_SIZE, OnSize)
2491    Page.canvas.Bind(wx.EVT_SET_FOCUS, OnFocus)
2492    Page.camera['position'] = drawingData['cameraPos']
2493    Page.camera['viewPoint'] = np.inner(Amat,drawingData['viewPoint'][0])
2494    Page.camera['backColor'] = np.array(list(drawingData['backColor'])+[0,])/255.
2495    Page.canvas.SetCurrent()
2496    Draw()
2497       
Note: See TracBrowser for help on using the repository browser.