source: trunk/GSASIIplot.py @ 1475

Last change on this file since 1475 was 1475, checked in by vondreele, 9 years ago

add MCSA tutorial to main gsasII.html web page
change difB tern from d3 to 1/d - works much better
make all TOF/2-theta to dsp & vv conversions with G2lat.Pos2dsp & Dsp2pos routines.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 209.9 KB
Line 
1# -*- coding: utf-8 -*-
2'''
3*GSASIIplot: plotting routines*
4===============================
5
6'''
7########### SVN repository information ###################
8# $Date: 2014-08-22 18:09:47 +0000 (Fri, 22 Aug 2014) $
9# $Author: vondreele $
10# $Revision: 1475 $
11# $URL: trunk/GSASIIplot.py $
12# $Id: GSASIIplot.py 1475 2014-08-22 18:09:47Z vondreele $
13########### SVN repository information ###################
14import math
15import time
16import copy
17import sys
18import os.path
19import numpy as np
20import numpy.ma as ma
21import numpy.linalg as nl
22import wx
23import wx.aui
24import wx.glcanvas
25import matplotlib as mpl
26import mpl_toolkits.mplot3d.axes3d as mp3d
27import GSASIIpath
28GSASIIpath.SetVersionNumber("$Revision: 1475 $")
29import GSASIIgrid as G2gd
30import GSASIIimage as G2img
31import GSASIIpwd as G2pwd
32import GSASIIIO as G2IO
33import GSASIIpwdGUI as G2pdG
34import GSASIIimgGUI as G2imG
35import GSASIIphsGUI as G2phG
36import GSASIIlattice as G2lat
37import GSASIIspc as G2spc
38import GSASIImath as G2mth
39import pytexture as ptx
40from  OpenGL.GL import *
41from OpenGL.GLU import *
42from OpenGL.GLE import *
43import gltext
44from matplotlib.backends.backend_wx import _load_bitmap
45from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
46from matplotlib.backends.backend_wxagg import NavigationToolbar2Wx as Toolbar
47
48# useful degree trig functions
49sind = lambda x: math.sin(x*math.pi/180.)
50cosd = lambda x: math.cos(x*math.pi/180.)
51tand = lambda x: math.tan(x*math.pi/180.)
52asind = lambda x: 180.*math.asin(x)/math.pi
53acosd = lambda x: 180.*math.acos(x)/math.pi
54atan2d = lambda x,y: 180.*math.atan2(y,x)/math.pi
55atand = lambda x: 180.*math.atan(x)/math.pi
56# numpy versions
57npsind = lambda x: np.sin(x*np.pi/180.)
58npcosd = lambda x: np.cos(x*np.pi/180.)
59nptand = lambda x: np.tan(x*np.pi/180.)
60npacosd = lambda x: 180.*np.arccos(x)/np.pi
61npasind = lambda x: 180.*np.arcsin(x)/np.pi
62npatand = lambda x: 180.*np.arctan(x)/np.pi
63npatan2d = lambda x,y: 180.*np.arctan2(x,y)/np.pi
64   
65class G2PlotMpl(wx.Panel):   
66    'needs a doc string'
67    def __init__(self,parent,id=-1,dpi=None,**kwargs):
68        wx.Panel.__init__(self,parent,id=id,**kwargs)
69        mpl.rcParams['legend.fontsize'] = 10
70        self.figure = mpl.figure.Figure(dpi=dpi,figsize=(5,6))
71        self.canvas = Canvas(self,-1,self.figure)
72        self.toolbar = GSASIItoolbar(self.canvas)
73
74        self.toolbar.Realize()
75       
76        sizer=wx.BoxSizer(wx.VERTICAL)
77        sizer.Add(self.canvas,1,wx.EXPAND)
78        sizer.Add(self.toolbar,0,wx.LEFT|wx.EXPAND)
79        self.SetSizer(sizer)
80       
81class G2PlotOgl(wx.Panel):
82    'needs a doc string'
83    def __init__(self,parent,id=-1,dpi=None,**kwargs):
84        self.figure = wx.Panel.__init__(self,parent,id=id,**kwargs)
85        if 'win' in sys.platform:           #Windows (& Mac) already double buffered
86            self.canvas = wx.glcanvas.GLCanvas(self,-1,**kwargs)
87        else:                               #fix from Jim Hester for X systems
88            attribs = (wx.glcanvas.WX_GL_DOUBLEBUFFER,)         
89            self.canvas = wx.glcanvas.GLCanvas(self,-1,attribList=attribs,**kwargs)
90        # create GL context for wx > 2.8
91        i,j= wx.__version__.split('.')[0:2]
92        if int(i)+int(j)/10. > 2.8:
93            self.context = wx.glcanvas.GLContext(self.canvas)
94            self.canvas.SetCurrent(self.context)
95        else:
96            self.context = None
97        self.camera = {}
98        sizer=wx.BoxSizer(wx.VERTICAL)
99        sizer.Add(self.canvas,1,wx.EXPAND)
100        self.SetSizer(sizer)
101       
102class G2Plot3D(wx.Panel):
103    'needs a doc string'
104    def __init__(self,parent,id=-1,dpi=None,**kwargs):
105        wx.Panel.__init__(self,parent,id=id,**kwargs)
106        self.figure = mpl.figure.Figure(dpi=dpi,figsize=(6,6))
107        self.canvas = Canvas(self,-1,self.figure)
108        self.toolbar = GSASIItoolbar(self.canvas)
109
110        self.toolbar.Realize()
111       
112        sizer=wx.BoxSizer(wx.VERTICAL)
113        sizer.Add(self.canvas,1,wx.EXPAND)
114        sizer.Add(self.toolbar,0,wx.LEFT|wx.EXPAND)
115        self.SetSizer(sizer)
116                             
117class G2PlotNoteBook(wx.Panel):
118    'create a tabbed window for plotting'
119    def __init__(self,parent,id=-1):
120        wx.Panel.__init__(self,parent,id=id)
121        #so one can't delete a plot page!!
122        self.nb = wx.aui.AuiNotebook(self, \
123            style=wx.aui.AUI_NB_DEFAULT_STYLE ^ wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB)
124        sizer = wx.BoxSizer()
125        sizer.Add(self.nb,1,wx.EXPAND)
126        self.SetSizer(sizer)
127        self.status = parent.CreateStatusBar()
128        self.status.SetFieldsCount(2)
129        self.status.SetStatusWidths([150,-1])
130        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
131        self.nb.Bind(wx.EVT_KEY_UP,self.OnNotebookKey)
132       
133        self.plotList = []
134           
135    def OnNotebookKey(self,event):
136        '''Called when a keystroke event gets picked up by the notebook window
137        rather the child. This is not expected, but somehow it does sometimes
138        on the Mac and perhaps Linux.
139
140        Assume that the page associated with the currently displayed tab
141        has a child, .canvas; give that child the focus and pass it the event.
142        '''
143        try:
144            Page = self.nb.GetPage(self.nb.GetSelection())
145        except ValueError: # occurs with no plot tabs
146            return
147        try:
148            Page.canvas.SetFocus()
149            wx.PostEvent(Page.canvas,event)
150        except AttributeError:
151            pass
152
153    def addMpl(self,name=""):
154        'Add a tabbed page with a matplotlib plot'
155        page = G2PlotMpl(self.nb)
156        self.nb.AddPage(page,name)
157       
158        self.plotList.append(name)
159       
160        return page.figure
161       
162    def add3D(self,name=""):
163        'Add a tabbed page with a 3D plot'
164        page = G2Plot3D(self.nb)
165        self.nb.AddPage(page,name)
166       
167        self.plotList.append(name)
168       
169        return page.figure
170       
171    def addOgl(self,name=""):
172        'Add a tabbed page with an openGL plot'
173        page = G2PlotOgl(self.nb)
174        self.nb.AddPage(page,name)
175       
176        self.plotList.append(name)
177       
178        return page.figure
179       
180    def Delete(self,name):
181        'delete a tabbed page'
182        try:
183            item = self.plotList.index(name)
184            del self.plotList[item]
185            self.nb.DeletePage(item)
186        except ValueError:          #no plot of this name - do nothing
187            return     
188               
189    def clear(self):
190        'clear all pages from plot window'
191        while self.nb.GetPageCount():
192            self.nb.DeletePage(0)
193        self.plotList = []
194        self.status.DestroyChildren()
195       
196    def Rename(self,oldName,newName):
197        'rename a tab'
198        try:
199            item = self.plotList.index(oldName)
200            self.plotList[item] = newName
201            self.nb.SetPageText(item,newName)
202        except ValueError:          #no plot of this name - do nothing
203            return     
204       
205    def OnPageChanged(self,event):
206        'respond to someone pressing a tab on the plot window'
207        if self.plotList:
208            self.status.SetStatusText('Better to select this from GSAS-II data tree',1)
209        self.status.DestroyChildren()                           #get rid of special stuff on status bar
210       
211class GSASIItoolbar(Toolbar):
212    'needs a doc string'
213    ON_MPL_HELP = wx.NewId()
214    ON_MPL_KEY = wx.NewId()
215    def __init__(self,plotCanvas):
216        Toolbar.__init__(self,plotCanvas)
217        POSITION_OF_CONFIGURE_SUBPLOTS_BTN = 6
218        self.DeleteToolByPos(POSITION_OF_CONFIGURE_SUBPLOTS_BTN)
219        parent = self.GetParent()
220        key = os.path.join(os.path.split(__file__)[0],'key.ico')
221        self.AddSimpleTool(self.ON_MPL_KEY,_load_bitmap(key),'Key press','Select key press')
222        wx.EVT_TOOL(self,self.ON_MPL_KEY,self.OnKey)
223        help = os.path.join(os.path.split(__file__)[0],'help.ico')
224        self.AddSimpleTool(self.ON_MPL_HELP,_load_bitmap(help),'Help on','Show help on')
225        wx.EVT_TOOL(self,self.ON_MPL_HELP,self.OnHelp)
226    def OnHelp(self,event):
227        'needs a doc string'
228        Page = self.GetParent().GetParent()
229        pageNo = Page.GetSelection()
230        bookmark = Page.GetPageText(pageNo)
231        bookmark = bookmark.strip(')').replace('(','_')
232        G2gd.ShowHelp(bookmark,self.TopLevelParent)
233    def OnKey(self,event):
234        'needs a doc string'
235        parent = self.GetParent()
236        if parent.Choice:
237            dlg = wx.SingleChoiceDialog(parent,'Select','Key press',list(parent.Choice))
238            if dlg.ShowModal() == wx.ID_OK:
239                sel = dlg.GetSelection()
240                event.key = parent.Choice[sel][0]
241                parent.keyPress(event)
242            dlg.Destroy()
243           
244################################################################################
245##### PlotSngl
246################################################################################
247           
248def PlotSngl(G2frame,newPlot=False,Data=None,hklRef=None,Title=''):
249    '''Structure factor plotting package - displays zone of reflections as rings proportional
250        to F, F**2, etc. as requested
251    '''
252    from matplotlib.patches import Circle,CirclePolygon
253    global HKL,HKLF
254   
255    def OnSCKeyPress(event):
256        i = zones.index(Data['Zone'])
257        newPlot = False
258        pwdrChoice = {'f':'Fo','s':'Fosq','u':'Unit Fc'}
259        hklfChoice = {'1':'|DFsq|>sig','3':'|DFsq|>3sig','w':'|DFsq|/sig','f':'Fo','s':'Fosq','i':'Unit Fc'}
260        if event.key == 'h':
261            Data['Zone'] = '100'
262            newPlot = True
263        elif event.key == 'k':
264            Data['Zone'] = '010'
265            newPlot = True
266        elif event.key == 'l':
267            Data['Zone'] = '001'
268            newPlot = True
269        elif event.key == 'i':
270            Data['Scale'] *= 1.1
271        elif event.key == 'd':
272            Data['Scale'] /= 1.1
273        elif event.key == '+':
274            Data['Layer'] = min(Data['Layer']+1,HKLmax[i])
275        elif event.key == '-':
276            Data['Layer'] = max(Data['Layer']-1,HKLmin[i])
277        elif event.key == '0':
278            Data['Layer'] = 0
279            Data['Scale'] = 1.0
280        elif event.key in hklfChoice and 'HKLF' in Name:
281            Data['Type'] = hklfChoice[event.key]           
282            newPlot = True
283        elif event.key in pwdrChoice and 'PWDR' in Name:
284            Data['Type'] = pwdrChoice[event.key]           
285            newPlot = True       
286        PlotSngl(G2frame,newPlot,Data,hklRef,Title)
287
288    def OnSCMotion(event):
289        xpos = event.xdata
290        if xpos:
291            xpos = round(xpos)                                        #avoid out of frame mouse position
292            ypos = round(event.ydata)
293            zpos = Data['Layer']
294            if '100' in Data['Zone']:
295                HKLtxt = '(%3d,%3d,%3d)'%(zpos,xpos,ypos)
296            elif '010' in Data['Zone']:
297                HKLtxt = '(%3d,%3d,%3d)'%(xpos,zpos,ypos)
298            elif '001' in Data['Zone']:
299                HKLtxt = '(%3d,%3d,%3d)'%(xpos,ypos,zpos)
300            Page.canvas.SetToolTipString(HKLtxt)
301            G2frame.G2plotNB.status.SetStatusText('HKL = '+HKLtxt,0)
302            G2frame.G2plotNB.status.SetStatusText('Use K-box to set plot controls',1)
303               
304    def OnSCPress(event):
305        zpos = Data['Layer']
306        xpos = event.xdata
307        if xpos:
308            pos = int(round(event.xdata)),int(round(event.ydata))
309            if '100' in Data['Zone']:
310                Page.canvas.SetToolTipString('(picked:(%3d,%3d,%3d))'%(zpos,pos[0],pos[1]))
311                hkl = np.array([zpos,pos[0],pos[1]])
312            elif '010' in Data['Zone']:
313                Page.canvas.SetToolTipString('(picked:(%3d,%3d,%3d))'%(pos[0],zpos,pos[1]))
314                hkl = np.array([pos[0],zpos,pos[1]])
315            elif '001' in Data['Zone']:
316                Page.canvas.SetToolTipString('(picked:(%3d,%3d,%3d))'%(pos[0],pos[1],zpos))
317                hkl = np.array([pos[0],pos[1],zpos])
318            h,k,l = hkl
319            hklf = HKLF[np.where(np.all(HKL-hkl == [0,0,0],axis=1))]
320            if len(hklf):
321                Fosq,sig,Fcsq = hklf[0]
322                HKLtxt = '( %.2f %.3f %.2f %.2f)'%(Fosq,sig,Fcsq,(Fosq-Fcsq)/(scale*sig))
323                G2frame.G2plotNB.status.SetStatusText('Fosq, sig, Fcsq, delFsq/sig = '+HKLtxt,1)
324                                 
325    Name = G2frame.PatternTree.GetItemText(G2frame.PatternId)
326    if not Title:
327        Title = Name
328    try:
329        plotNum = G2frame.G2plotNB.plotList.index('Structure Factors')
330        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
331        if not newPlot:
332            Plot = Page.figure.gca()          #get previous powder plot & get limits
333            xylim = Plot.get_xlim(),Plot.get_ylim()
334        Page.figure.clf()
335        Plot = Page.figure.gca()          #get a fresh plot after clf()
336    except ValueError:
337        Plot = G2frame.G2plotNB.addMpl('Structure Factors').gca()
338        plotNum = G2frame.G2plotNB.plotList.index('Structure Factors')
339        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
340        Page.canvas.mpl_connect('button_press_event', OnSCPress)
341        Page.canvas.mpl_connect('motion_notify_event', OnSCMotion)
342        Page.canvas.mpl_connect('key_press_event', OnSCKeyPress)
343        Page.keyPress = OnSCKeyPress
344        Page.Choice = (' key press','i: increase scale','d: decrease scale',
345            'h: select 100 zone','k: select 010 zone','l: select 001 zone',
346            'f: select Fo','s: select Fosq','u: select unit Fc',
347            '+: increase index','-: decrease index','0: zero layer',)
348        if 'HKLF' in Name:
349            Page.Choice += ('w: select |DFsq|/sig','1: select |DFsq|>sig','3: select |DFsq|>3sig',)
350    Page.SetFocus()
351   
352    G2frame.G2plotNB.status.SetStatusText('Use K-box to set plot controls',1)
353    Plot.set_aspect(aspect='equal')
354   
355    Type = Data['Type']           
356    scale = Data['Scale']
357    HKLmax = Data['HKLmax']
358    HKLmin = Data['HKLmin']
359    FosqMax = Data['FoMax']
360    FoMax = math.sqrt(FosqMax)
361    xlabel = ['k, h=','h, k=','h, l=']
362    ylabel = ['l','l','k']
363    zones = ['100','010','001']
364    pzone = [[1,2],[0,2],[0,1]]
365    izone = zones.index(Data['Zone'])
366    Plot.set_title(Data['Type']+' for '+Title)
367    HKL = []
368    HKLF = []
369    time0 = time.time()
370    for refl in hklRef:
371        H = np.array(refl[:3])
372        if 'HKLF' in Name:
373            Fosq,sig,Fcsq = refl[5:8]
374        else:
375            Fosq,sig,Fcsq = refl[8],1.0,refl[9]
376        HKL.append(H)
377        HKLF.append([Fosq,sig,Fcsq])
378        if H[izone] == Data['Layer']:
379            A = 0
380            B = 0
381            if Type == 'Fosq':
382                A = scale*Fosq/FosqMax
383                B = scale*Fcsq/FosqMax
384                C = abs(A-B)
385            elif Type == 'Fo':
386                A = scale*math.sqrt(max(0,Fosq))/FoMax
387                B = scale*math.sqrt(max(0,Fcsq))/FoMax
388                C = abs(A-B)
389            elif Type == 'Unit Fc':
390                A = scale/2
391                B = scale/2
392                C = 0.0
393                if Fcsq and Fosq > 0:
394                    A *= min(1.0,Fosq/Fcsq)
395                    C = abs(A-B)
396            elif Type == '|DFsq|/sig':
397                if sig > 0.:
398                    A = (Fosq-Fcsq)/(3*sig)
399                B = 0
400            elif Type == '|DFsq|>sig':
401                if sig > 0.:
402                    A = (Fosq-Fcsq)/(3*sig)
403                if abs(A) < 1.0: A = 0
404                B = 0                   
405            elif Type == '|DFsq|>3sig':
406                if sig > 0.:
407                    A = (Fosq-Fcsq)/(3*sig)
408                if abs(A) < 3.0: A = 0
409                B = 0                   
410            xy = (H[pzone[izone][0]],H[pzone[izone][1]])
411            if Type in ['|DFsq|/sig','|DFsq|>sig','|DFsq|>3sig']:
412                if A > 0.0:
413                    Plot.add_artist(Circle(xy,radius=A,ec='g',fc='w'))
414                else:
415                    Plot.add_artist(Circle(xy,radius=-A,ec='r',fc='w'))
416            else:
417                if A > 0.0 and A > B:
418                    Plot.add_artist(Circle(xy,radius=A,ec='g',fc='w'))
419                if B:
420                    Plot.add_artist(Circle(xy,radius=B,ec='b',fc='w'))
421                    if A < B:
422                        Plot.add_artist(Circle(xy,radius=A,ec='g',fc='w'))
423                    radius = C
424                    if radius > 0:
425                        if A > B:
426                            Plot.add_artist(Circle(xy,radius=radius,ec='g',fc='g'))
427                        else:                   
428                            Plot.add_artist(Circle(xy,radius=radius,ec='r',fc='r'))
429#    print 'plot time: %.3f'%(time.time()-time0)
430    HKL = np.array(HKL,dtype=np.int)
431    HKLF = np.array(HKLF)
432    Plot.set_xlabel(xlabel[izone]+str(Data['Layer']),fontsize=12)
433    Plot.set_ylabel(ylabel[izone],fontsize=12)
434    if not newPlot:
435        Page.toolbar.push_current()
436        Plot.set_xlim(xylim[0])
437        Plot.set_ylim(xylim[1])
438#        xylim = []
439        Page.toolbar.push_current()
440        Page.toolbar.draw()
441    else:
442        Plot.set_xlim((HKLmin[pzone[izone][0]],HKLmax[pzone[izone][0]]))
443        Plot.set_ylim((HKLmin[pzone[izone][1]],HKLmax[pzone[izone][1]]))
444        Page.canvas.draw()
445       
446################################################################################
447##### Plot3DSngl
448################################################################################
449
450def Plot3DSngl(G2frame,newPlot=False,Data=None,hklRef=None,Title=''):
451    '''3D Structure factor plotting package - displays reflections as rings proportional
452        to F, F**2, etc. as requested as 3D array
453    '''
454
455    def OnKeyBox(event):
456        mode = cb.GetValue()
457        if mode in ['jpeg','bmp','tiff',]:
458            try:
459                import Image as Im
460            except ImportError:
461                try:
462                    from PIL import Image as Im
463                except ImportError:
464                    print "PIL/pillow Image module not present. Cannot save images without this"
465                    raise Exception("PIL/pillow Image module not found")
466            Fname = os.path.join(Mydir,generalData['Name']+'.'+mode)
467            size = Page.canvas.GetSize()
468            glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
469            if mode in ['jpeg',]:
470                Pix = glReadPixels(0,0,size[0],size[1],GL_RGBA, GL_UNSIGNED_BYTE)
471                im = Image.new("RGBA", (size[0],size[1]))
472            else:
473                Pix = glReadPixels(0,0,size[0],size[1],GL_RGB, GL_UNSIGNED_BYTE)
474                im = Image.new("RGB", (size[0],size[1]))
475            im.fromstring(Pix)
476            im.save(Fname,mode)
477            cb.SetValue(' save as/key:')
478            G2frame.G2plotNB.status.SetStatusText('Drawing saved to: '+Fname,1)
479        else:
480            event.key = cb.GetValue()[0]
481            cb.SetValue(' save as/key:')
482            wx.CallAfter(OnKey,event)
483        Page.canvas.SetFocus() # redirect the Focus from the button back to the plot
484       
485    def OnKey(event):           #on key UP!!
486        Choice = {'F':'Fo','S':'Fosq','U':'Unit','D':'dFsq','W':'dFsq/sig'}
487        try:
488            keyCode = event.GetKeyCode()
489            if keyCode > 255:
490                keyCode = 0
491            key = chr(keyCode)
492        except AttributeError:       #if from OnKeyBox above
493            key = str(event.key).upper()
494        if key in ['C']:
495            drawingData['viewPoint'][0] = drawingData['default']
496            drawingData['viewDir'] = [0,0,1]
497            drawingData['oldxy'] = []
498            V0 = np.array([0,0,1])
499            V = np.inner(Amat,V0)
500            V /= np.sqrt(np.sum(V**2))
501            A = np.arccos(np.sum(V*V0))
502            Q = G2mth.AV2Q(A,[0,1,0])
503            drawingData['Quaternion'] = Q
504            Q = drawingData['Quaternion']
505        elif key == '+':
506            Data['Scale'] *= 1.25
507        elif key == '-':
508            Data['Scale'] /= 1.25
509        elif key == '0':
510            Data['Scale'] = 1.0
511        elif key == 'I':
512            Data['Iscale'] = not Data['Iscale']
513        elif key in Choice:
514            Data['Type'] = Choice[key]
515        Draw('key')
516           
517    Name = G2frame.PatternTree.GetItemText(G2frame.PatternId)
518    if Title != 'no phase':
519        generalData = G2frame.GetPhaseData()[Title]['General']
520        cell = generalData['Cell'][1:7]
521    else:
522        cell = [10,10,10,90,90,90]
523    drawingData = Data['Drawing']
524    defaultViewPt = copy.copy(drawingData['viewPoint'])
525    Amat,Bmat = G2lat.cell2AB(cell)         #Amat - crystal to cartesian, Bmat - inverse
526    Gmat,gmat = G2lat.cell2Gmat(cell)
527    invcell = G2lat.Gmat2cell(Gmat)
528    A4mat = np.concatenate((np.concatenate((Amat,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
529    B4mat = np.concatenate((np.concatenate((Bmat,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
530    drawingData['Quaternion'] = G2mth.AV2Q(2*np.pi,np.inner(Bmat,[0,0,1]))
531    Wt = np.array([255,255,255])
532    Rd = np.array([255,0,0])
533    Gr = np.array([0,255,0])
534    wxGreen = wx.Colour(0,255,0)
535    Bl = np.array([0,0,255])
536    Or = np.array([255,128,0])
537    wxOrange = wx.Colour(255,128,0)
538    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]])
539    uEdges = np.array([
540        [uBox[0],uBox[1]],[uBox[0],uBox[3]],[uBox[0],uBox[4]],[uBox[1],uBox[2]]])
541    uColors = [Rd,Gr,Bl]
542   
543    def FillHKLRC():
544        R = np.zeros(len(hklRef))
545        C = []
546        HKL = []
547        RC = []
548        for i,refl in enumerate(hklRef):
549            H = np.array(refl[:3])
550            if 'HKLF' in Name:
551                Fosq,sig,Fcsq = refl[5:8]
552            else:
553                Fosq,sig,Fcsq = refl[8],1.0,refl[9]
554            HKL.append(H)
555            if Data['Type'] == 'Unit':
556                R[i] = 0.1
557                C.append(Gr)
558            elif Data['Type'] == 'Fosq':
559                if Fosq > 0:
560                    R[i] = Fosq
561                    C.append(Gr)
562                else:
563                    R[i] = -Fosq
564                    C.append(Rd)
565            elif Data['Type'] == 'Fo':
566                if Fosq > 0:
567                    R[i] = np.sqrt(Fosq)
568                    C.append(Gr)
569                else:
570                    R[i] = np.sqrt(-Fosq)
571                    C.append(Rd)
572            elif Data['Type'] == 'dFsq/sig':
573                dFsig = (Fosq-Fcsq)/sig
574                if dFsig > 0:
575                    R[i] = dFsig
576                    C.append(Gr)
577                else:
578                    R[i] = -dFsig
579                    C.append(Rd)
580            elif Data['Type'] == 'dFsq':
581                dF = Fosq-Fcsq
582                if dF > 0:
583                    R[i] = dF
584                    C.append(Gr)
585                else:
586                    R[i] = -dF
587                    C.append(Rd)
588        R /= np.max(R)
589        R *= Data['Scale']
590        if Data['Iscale']:
591            R = np.where(R<=1.,R,1.)
592            C = np.array(C)
593            C = (C.T*R).T
594            R = np.ones_like(R)*0.1     
595        return HKL,zip(list(R),C)
596
597    def SetTranslation(newxy):
598#first get translation vector in screen coords.       
599        oldxy = drawingData['oldxy']
600        if not len(oldxy): oldxy = list(newxy)
601        dxy = newxy-oldxy
602        drawingData['oldxy'] = list(newxy)
603        V = np.array([-dxy[0],dxy[1],0.])
604#then transform to rotated crystal coordinates & apply to view point       
605        Q = drawingData['Quaternion']
606        V = np.inner(Bmat,G2mth.prodQVQ(G2mth.invQ(Q),V))
607        Tx,Ty,Tz = drawingData['viewPoint'][0]
608        Tx += V[0]*0.1
609        Ty += V[1]*0.1
610        Tz += V[2]*0.1
611        drawingData['viewPoint'][0] =  Tx,Ty,Tz
612       
613    def SetRotation(newxy):
614        'Perform a rotation in x-y space due to a left-mouse drag'
615    #first get rotation vector in screen coords. & angle increment       
616        oldxy = drawingData['oldxy']
617        if not len(oldxy): oldxy = list(newxy)
618        dxy = newxy-oldxy
619        drawingData['oldxy'] = list(newxy)
620        V = np.array([dxy[1],dxy[0],0.])
621        A = 0.25*np.sqrt(dxy[0]**2+dxy[1]**2)
622        if not A: return # nothing changed, nothing to do
623    # next transform vector back to xtal coordinates via inverse quaternion
624    # & make new quaternion
625        Q = drawingData['Quaternion']
626        V = G2mth.prodQVQ(G2mth.invQ(Q),np.inner(Bmat,V))
627        DQ = G2mth.AVdeg2Q(A,V)
628        Q = G2mth.prodQQ(Q,DQ)
629        drawingData['Quaternion'] = Q
630    # finally get new view vector - last row of rotation matrix
631        VD = np.inner(Bmat,G2mth.Q2Mat(Q)[2])
632        VD /= np.sqrt(np.sum(VD**2))
633        drawingData['viewDir'] = VD
634       
635    def SetRotationZ(newxy):                       
636#first get rotation vector (= view vector) in screen coords. & angle increment       
637        View = glGetIntegerv(GL_VIEWPORT)
638        cent = [View[2]/2,View[3]/2]
639        oldxy = drawingData['oldxy']
640        if not len(oldxy): oldxy = list(newxy)
641        dxy = newxy-oldxy
642        drawingData['oldxy'] = list(newxy)
643        V = drawingData['viewDir']
644        A = [0,0]
645        A[0] = dxy[1]*.25
646        A[1] = dxy[0]*.25
647        if newxy[0] > cent[0]:
648            A[0] *= -1
649        if newxy[1] < cent[1]:
650            A[1] *= -1       
651# next transform vector back to xtal coordinates & make new quaternion
652        Q = drawingData['Quaternion']
653        V = np.inner(Amat,V)
654        Qx = G2mth.AVdeg2Q(A[0],V)
655        Qy = G2mth.AVdeg2Q(A[1],V)
656        Q = G2mth.prodQQ(Q,Qx)
657        Q = G2mth.prodQQ(Q,Qy)
658        drawingData['Quaternion'] = Q
659
660    def OnMouseDown(event):
661        xy = event.GetPosition()
662        drawingData['oldxy'] = list(xy)
663       
664    def OnMouseMove(event):
665        if event.ShiftDown():           #don't want any inadvertant moves when picking
666            return
667        newxy = event.GetPosition()
668                               
669        if event.Dragging():
670            if event.LeftIsDown():
671                SetRotation(newxy)
672                Q = drawingData['Quaternion']
673            elif event.RightIsDown():
674                SetTranslation(newxy)
675                Tx,Ty,Tz = drawingData['viewPoint'][0]
676            elif event.MiddleIsDown():
677                SetRotationZ(newxy)
678                Q = drawingData['Quaternion']
679            Draw('move')
680       
681    def OnMouseWheel(event):
682        if event.ShiftDown():
683            return
684        drawingData['cameraPos'] += event.GetWheelRotation()/120.
685        drawingData['cameraPos'] = max(0.1,min(20.00,drawingData['cameraPos']))
686        Draw('wheel')
687       
688    def SetBackground():
689        R,G,B,A = Page.camera['backColor']
690        glClearColor(R,G,B,A)
691        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
692       
693    def SetLights():
694        glEnable(GL_DEPTH_TEST)
695        glShadeModel(GL_SMOOTH)
696        glEnable(GL_LIGHTING)
697        glEnable(GL_LIGHT0)
698        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,0)
699        glLightfv(GL_LIGHT0,GL_AMBIENT,[1,1,1,.8])
700        glLightfv(GL_LIGHT0,GL_DIFFUSE,[1,1,1,1])
701       
702    def RenderUnitVectors(x,y,z):
703        xyz = np.array([x,y,z])
704        glEnable(GL_COLOR_MATERIAL)
705        glLineWidth(1)
706        glPushMatrix()
707        glTranslate(x,y,z)
708        glBegin(GL_LINES)
709        for line,color in zip(uEdges,uColors):
710            glColor3ubv(color)
711            glVertex3fv(-line[1])
712            glVertex3fv(line[1])
713        glEnd()
714        glPopMatrix()
715        glColor4ubv([0,0,0,0])
716        glDisable(GL_COLOR_MATERIAL)
717               
718    def RenderDots(XYZ,RC):
719        glEnable(GL_COLOR_MATERIAL)
720        XYZ = np.array(XYZ)
721        glPushMatrix()
722        for xyz,rc in zip(XYZ,RC):
723            x,y,z = xyz
724            r,c = rc
725            glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,c)
726            glPointSize(r*50)
727            glBegin(GL_POINTS)
728            glVertex3fv(xyz)
729            glEnd()
730        glPopMatrix()
731        glColor4ubv([0,0,0,0])
732        glDisable(GL_COLOR_MATERIAL)
733       
734    def Draw(caller=''):
735#useful debug?       
736#        if caller:
737#            print caller
738# end of useful debug
739        G2frame.G2plotNB.status.SetStatusText('Plot type = %s for %s'%(Data['Type'],Name),1)
740        VS = np.array(Page.canvas.GetSize())
741        aspect = float(VS[0])/float(VS[1])
742        cPos = drawingData['cameraPos']
743        Zclip = drawingData['Zclip']*cPos/20.
744        Q = drawingData['Quaternion']
745        Tx,Ty,Tz = drawingData['viewPoint'][0]
746        G,g = G2lat.cell2Gmat(cell)
747        GS = G
748        GS[0][1] = GS[1][0] = math.sqrt(GS[0][0]*GS[1][1])
749        GS[0][2] = GS[2][0] = math.sqrt(GS[0][0]*GS[2][2])
750        GS[1][2] = GS[2][1] = math.sqrt(GS[1][1]*GS[2][2])
751       
752        HKL,RC = FillHKLRC()
753       
754        SetBackground()
755        glInitNames()
756        glPushName(0)
757       
758        glMatrixMode(GL_PROJECTION)
759        glLoadIdentity()
760        glViewport(0,0,VS[0],VS[1])
761        gluPerspective(20.,aspect,cPos-Zclip,cPos+Zclip)
762        gluLookAt(0,0,cPos,0,0,0,0,1,0)
763        SetLights()           
764           
765        glMatrixMode(GL_MODELVIEW)
766        glLoadIdentity()
767        matRot = G2mth.Q2Mat(Q)
768        matRot = np.concatenate((np.concatenate((matRot,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
769        glMultMatrixf(matRot.T)
770        glMultMatrixf(B4mat.T)
771        glTranslate(-Tx,-Ty,-Tz)
772        x,y,z = drawingData['viewPoint'][0]
773        RenderUnitVectors(x,y,z)
774        RenderUnitVectors(0,0,0)
775        RenderDots(HKL,RC)
776        time0 = time.time()
777        if Page.context: Page.canvas.SetCurrent(Page.context)    # wx 2.9 fix
778        Page.canvas.SwapBuffers()
779
780    # PlotStructure execution starts here (N.B. initialization above)
781    try:
782        plotNum = G2frame.G2plotNB.plotList.index('3D Structure Factors')
783        Page = G2frame.G2plotNB.nb.GetPage(plotNum)       
784    except ValueError:
785        Plot = G2frame.G2plotNB.addOgl('3D Structure Factors')
786        plotNum = G2frame.G2plotNB.plotList.index('3D Structure Factors')
787        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
788        Page.views = False
789        view = False
790        altDown = False
791    Font = Page.GetFont()
792    Page.SetFocus()
793    Page.Choice = None
794    choice = [' save as/key:','jpeg','tiff','bmp','c: recenter to default','+: increase scale',
795    '-: decrease scale','f: Fobs','s: Fobs**2','u: unit','d: Fo-Fc','w: DF/sig','i: toggle intensity scaling']
796    cb = wx.ComboBox(G2frame.G2plotNB.status,style=wx.CB_DROPDOWN|wx.CB_READONLY,choices=choice)
797    cb.Bind(wx.EVT_COMBOBOX, OnKeyBox)
798    cb.SetValue(' save as/key:')
799    Page.canvas.Bind(wx.EVT_MOUSEWHEEL, OnMouseWheel)
800    Page.canvas.Bind(wx.EVT_LEFT_DOWN, OnMouseDown)
801    Page.canvas.Bind(wx.EVT_RIGHT_DOWN, OnMouseDown)
802    Page.canvas.Bind(wx.EVT_MIDDLE_DOWN, OnMouseDown)
803    Page.canvas.Bind(wx.EVT_KEY_UP, OnKey)
804    Page.canvas.Bind(wx.EVT_MOTION, OnMouseMove)
805#    Page.canvas.Bind(wx.EVT_SIZE, OnSize)
806    Page.camera['position'] = drawingData['cameraPos']
807    Page.camera['viewPoint'] = np.inner(Amat,drawingData['viewPoint'][0])
808    Page.camera['backColor'] = np.array(list(drawingData['backColor'])+[0,])/255.
809    try:
810        Page.canvas.SetCurrent()
811    except:
812        pass
813    Draw('main')
814#    if firstCall: Draw('main') # draw twice the first time that graphics are displayed
815
816       
817################################################################################
818##### PlotPatterns
819################################################################################
820           
821def PlotPatterns(G2frame,newPlot=False,plotType='PWDR'):
822    '''Powder pattern plotting package - displays single or multiple powder patterns as intensity vs
823    2-theta, q or TOF. Can display multiple patterns as "waterfall plots" or contour plots. Log I
824    plotting available.
825    '''
826    global HKL
827    global exclLines
828    global DifLine
829    global Ymax
830    plottype = plotType
831   
832    def OnPlotKeyPress(event):
833        try:        #one way to check if key stroke will work on plot
834            Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters'))
835        except TypeError:
836            G2frame.G2plotNB.status.SetStatusText('Select '+plottype+' pattern first',1)
837            return
838        newPlot = False
839        if event.key == 'w':
840            G2frame.Weight = not G2frame.Weight
841            if not G2frame.Weight and 'PWDR' in plottype:
842                G2frame.SinglePlot = True
843            newPlot = True
844        elif event.key == 'e' and 'SASD' in plottype:
845            G2frame.ErrorBars = not G2frame.ErrorBars
846        elif event.key == 'b':
847            G2frame.SubBack = not G2frame.SubBack
848            if not G2frame.SubBack:
849                G2frame.SinglePlot = True               
850        elif event.key == 'n':
851            if G2frame.Contour:
852                pass
853            else:
854                G2frame.logPlot = not G2frame.logPlot
855                if not G2frame.logPlot:
856                    G2frame.Offset[0] = 0
857                newPlot = True
858        elif event.key == 's' and 'PWDR' in plottype:
859            if G2frame.Contour:
860                choice = [m for m in mpl.cm.datad.keys() if not m.endswith("_r")]
861                choice.sort()
862                dlg = wx.SingleChoiceDialog(G2frame,'Select','Color scheme',choice)
863                if dlg.ShowModal() == wx.ID_OK:
864                    sel = dlg.GetSelection()
865                    G2frame.ContourColor = choice[sel]
866                else:
867                    G2frame.ContourColor = 'Paired'
868                dlg.Destroy()
869            elif G2frame.SinglePlot:
870                G2frame.SqrtPlot = not G2frame.SqrtPlot
871                if G2frame.SqrtPlot:
872                    G2frame.delOffset = .002
873                    G2frame.refOffset = -1.0
874                    G2frame.refDelt = .001
875                else:
876                    G2frame.delOffset = .02
877                    G2frame.refOffset = -1.0
878                    G2frame.refDelt = .01
879            newPlot = True
880        elif event.key == 'u' and (G2frame.Contour or not G2frame.SinglePlot):
881            if G2frame.Contour:
882                G2frame.Cmax = min(1.0,G2frame.Cmax*1.2)
883            elif G2frame.Offset[0] < 100.:
884                G2frame.Offset[0] += 1.
885        elif event.key == 'd' and (G2frame.Contour or not G2frame.SinglePlot):
886            if G2frame.Contour:
887                G2frame.Cmax = max(0.0,G2frame.Cmax*0.8)
888            elif G2frame.Offset[0] > 0.:
889                G2frame.Offset[0] -= 1.
890        elif event.key == 'l' and not G2frame.SinglePlot:
891            G2frame.Offset[1] -= 1.
892        elif event.key == 'r' and not G2frame.SinglePlot:
893            G2frame.Offset[1] += 1.
894        elif event.key == 'o' and not G2frame.SinglePlot:
895            G2frame.Cmax = 1.0
896            G2frame.Offset = [0,0]
897        elif event.key == 'c' and 'PWDR' in plottype:
898            newPlot = True
899            if not G2frame.Contour:
900                G2frame.SinglePlot = False
901                G2frame.Offset = [0.,0.]
902            else:
903                G2frame.SinglePlot = True               
904            G2frame.Contour = not G2frame.Contour
905        elif event.key == 'q': 
906            if 'PWDR' in plottype:
907                newPlot = True
908                G2frame.qPlot = not G2frame.qPlot
909                G2frame.dPlot = False
910            elif 'SASD' in plottype:
911                newPlot = True
912                G2frame.sqPlot = not G2frame.sqPlot
913        elif event.key == 't' and 'PWDR' in plottype:
914            G2frame.dPlot = not G2frame.dPlot
915            G2frame.qPlot = False
916            newPlot = True     
917        elif event.key == 'm':
918            G2frame.SqrtPlot = False
919            G2frame.SinglePlot = not G2frame.SinglePlot               
920            newPlot = True
921        elif event.key == '+':
922            if G2frame.PickId:
923                G2frame.PickId = False
924        elif event.key == 'i' and G2frame.Contour:                  #for smoothing contour plot
925            choice = ['nearest','bilinear','bicubic','spline16','spline36','hanning',
926               'hamming','hermite','kaiser','quadric','catrom','gaussian','bessel',
927               'mitchell','sinc','lanczos']
928            dlg = wx.SingleChoiceDialog(G2frame,'Select','Interpolation',choice)
929            if dlg.ShowModal() == wx.ID_OK:
930                sel = dlg.GetSelection()
931                G2frame.Interpolate = choice[sel]
932            else:
933                G2frame.Interpolate = 'nearest'
934            dlg.Destroy()
935           
936        wx.CallAfter(PlotPatterns,G2frame,newPlot=newPlot,plotType=plottype)
937       
938    def OnMotion(event):
939        xpos = event.xdata
940        if xpos:                                        #avoid out of frame mouse position
941            ypos = event.ydata
942            Page.canvas.SetCursor(wx.CROSS_CURSOR)
943            try:
944                Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters'))
945                if G2frame.qPlot and 'PWDR' in plottype:
946                    q = xpos
947                    dsp = 2.*np.pi/q
948                    try:
949                        xpos = G2lat.Dsp2pos(Parms,2.0*np.pi/xpos)
950                    except ValueError:      #avoid bad value in asin beyond upper limit
951                        pass
952                elif 'SASD' in plottype:
953                    q = xpos
954                    dsp = 2.*np.pi/q
955                elif G2frame.dPlot:
956                    dsp = xpos
957                    q = 2.*np.pi/dsp
958                    xpos = G2lat.Dsp2pos(Parms,xpos)
959                else:
960                    dsp = G2lat.Pos2dsp(Parms,xpos)
961                    q = 2.*np.pi/dsp
962                if G2frame.Contour: #PWDR only
963                    if 'C' in Parms['Type'][0]:
964                        G2frame.G2plotNB.status.SetStatusText('2-theta =%9.3f d =%9.5f q = %9.5f pattern ID =%5d'%(xpos,dsp,q,int(ypos)),1)
965                    else:
966                        G2frame.G2plotNB.status.SetStatusText('TOF =%9.3f d =%9.5f q = %9.5f pattern ID =%5d'%(xpos,dsp,q,int(ypos)),1)
967                else:
968                    if 'C' in Parms['Type'][0]:
969                        if 'PWDR' in plottype:
970                            if G2frame.SqrtPlot:
971                                G2frame.G2plotNB.status.SetStatusText('2-theta =%9.3f d =%9.5f q = %9.5f sqrt(Intensity) =%9.2f'%(xpos,dsp,q,ypos),1)
972                            else:
973                                G2frame.G2plotNB.status.SetStatusText('2-theta =%9.3f d =%9.5f q = %9.5f Intensity =%9.2f'%(xpos,dsp,q,ypos),1)
974                        elif 'SASD' in plottype:
975                            G2frame.G2plotNB.status.SetStatusText('q =%12.5g Intensity =%12.5g d =%9.1f'%(q,ypos,dsp),1)
976                    else:
977                        if G2frame.SqrtPlot:
978                            G2frame.G2plotNB.status.SetStatusText('TOF =%9.3f d =%9.5f q =%9.5f sqrt(Intensity) =%9.2f'%(xpos,dsp,q,ypos),1)
979                        else:
980                            G2frame.G2plotNB.status.SetStatusText('TOF =%9.3f d =%9.5f q =%9.5f Intensity =%9.2f'%(xpos,dsp,q,ypos),1)
981                if G2frame.itemPicked:
982                    Page.canvas.SetToolTipString('%9.5f'%(xpos))
983                if G2frame.PickId:
984                    found = []
985                    if G2frame.PatternTree.GetItemText(G2frame.PickId) in ['Index Peak List','Unit Cells List','Reflection Lists'] or \
986                        'PWDR' in G2frame.PatternTree.GetItemText(PickId):
987                        if len(HKL):
988                            view = Page.toolbar._views.forward()[0][:2]
989                            wid = view[1]-view[0]
990                            found = HKL[np.where(np.fabs(HKL.T[5]-xpos) < 0.002*wid)]
991                        if len(found):
992                            h,k,l = found[0][:3] 
993                            Page.canvas.SetToolTipString('%d,%d,%d'%(int(h),int(k),int(l)))
994                        else:
995                            Page.canvas.SetToolTipString('')
996
997            except TypeError:
998                G2frame.G2plotNB.status.SetStatusText('Select '+plottype+' pattern first',1)
999               
1000    def OnPress(event): #ugh - this removes a matplotlib error for mouse clicks in log plots                 
1001        olderr = np.seterr(invalid='ignore')
1002                                                   
1003    def OnPick(event):
1004        if G2frame.itemPicked is not None: return
1005        PatternId = G2frame.PatternId
1006        try:
1007            Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters'))
1008        except TypeError:
1009            return
1010        PickId = G2frame.PickId
1011        pick = event.artist
1012        mouse = event.mouseevent
1013        xpos = pick.get_xdata()
1014        ypos = pick.get_ydata()
1015        ind = event.ind
1016        xy = list(zip(np.take(xpos,ind),np.take(ypos,ind))[0])
1017        if G2frame.PatternTree.GetItemText(PickId) == 'Peak List':
1018            if ind.all() != [0] and ObsLine[0].get_label() in str(pick):                                    #picked a data point
1019                data = G2frame.PatternTree.GetItemPyData(G2frame.PickId)
1020                if G2frame.qPlot:                              #qplot - convert back to 2-theta
1021                    xy[0] = G2lat.Dsp2pos(Parms,2*np.pi/xy[0])
1022                elif G2frame.dPlot:                            #dplot - convert back to 2-theta
1023                    xy[0] = G2lat.Dsp2pos(Parms,xy[0])
1024                XY = G2mth.setPeakparms(Parms,Parms2,xy[0],xy[1])
1025                data['peaks'].append(XY)
1026                data['sigDict'] = {}    #now invalid
1027                G2pdG.UpdatePeakGrid(G2frame,data)
1028                PlotPatterns(G2frame,plotType=plottype)
1029            else:                                                   #picked a peak list line
1030                G2frame.itemPicked = pick
1031        elif G2frame.PatternTree.GetItemText(PickId) == 'Limits':
1032            if ind.all() != [0]:                                    #picked a data point
1033                LimitId = G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Limits')
1034                data = G2frame.PatternTree.GetItemPyData(LimitId)
1035                if G2frame.qPlot:                              #qplot - convert back to 2-theta
1036                    xy[0] = G2lat.Dsp2pos(Parms,2*np.pi/xy[0])
1037                elif G2frame.dPlot:                            #dplot - convert back to 2-theta
1038                    xy[0] = G2lat.Dsp2pos(Parms,xy[0])
1039                if G2frame.ifGetExclude:
1040                    excl = [0,0]
1041                    excl[0] = max(data[1][0],min(xy[0],data[1][1]))
1042                    excl[1] = excl[0]+0.1
1043                    data.append(excl)
1044                    G2frame.ifGetExclude = False
1045                else:
1046                    if mouse.button==1:
1047                        data[1][0] = min(xy[0],data[1][1])
1048                    if mouse.button==3:
1049                        data[1][1] = max(xy[0],data[1][0])
1050                G2frame.PatternTree.SetItemPyData(LimitId,data)
1051                G2pdG.UpdateLimitsGrid(G2frame,data,plottype)
1052                wx.CallAfter(PlotPatterns,G2frame,plotType=plottype)
1053            else:                                                   #picked a limit line
1054                G2frame.itemPicked = pick
1055        elif G2frame.PatternTree.GetItemText(PickId) == 'Models':
1056            if ind.all() != [0]:                                    #picked a data point
1057                LimitId = G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Limits')
1058                data = G2frame.PatternTree.GetItemPyData(LimitId)
1059                if mouse.button==1:
1060                    data[1][0] = min(xy[0],data[1][1])
1061                if mouse.button==3:
1062                    data[1][1] = max(xy[0],data[1][0])
1063                G2frame.PatternTree.SetItemPyData(LimitId,data)
1064                wx.CallAfter(PlotPatterns,G2frame,plotType=plottype)
1065            else:                                                   #picked a limit line
1066                G2frame.itemPicked = pick
1067        elif G2frame.PatternTree.GetItemText(PickId) == 'Reflection Lists' or \
1068            'PWDR' in G2frame.PatternTree.GetItemText(PickId):
1069            G2frame.itemPicked = pick
1070            pick = str(pick)
1071       
1072    def OnRelease(event):
1073        if G2frame.itemPicked is None: return
1074        if str(DifLine[0]) == str(G2frame.itemPicked):
1075            ypos = event.ydata
1076            G2frame.delOffset = -ypos/Ymax
1077            G2frame.itemPicked = None
1078            PlotPatterns(G2frame,plotType=plottype)
1079            return
1080        Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Instrument Parameters'))
1081        xpos = event.xdata
1082        PickId = G2frame.PickId
1083        if G2frame.PatternTree.GetItemText(PickId) in ['Peak List','Limits'] and xpos:
1084            lines = []
1085            for line in G2frame.Lines: 
1086                lines.append(line.get_xdata()[0])
1087            try:
1088                lineNo = lines.index(G2frame.itemPicked.get_xdata()[0])
1089            except ValueError:
1090                lineNo = -1
1091            if  lineNo in [0,1] or lineNo in exclLines:
1092                LimitId = G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Limits')
1093                data = G2frame.PatternTree.GetItemPyData(LimitId)
1094                id = lineNo/2+1
1095                id2 = lineNo%2
1096                if G2frame.qPlot and 'PWDR' in plottype:
1097                    data[id][id2] = G2lat.Dsp2pos(Parms,2.*np.pi/xpos)
1098                elif G2frame.dPlot and 'PWDR' in plottype:
1099                    data[id][id2] = G2lat.Dsp2pos(Parms,xpos)
1100                else:
1101                    data[id][id2] = xpos
1102                if id > 1 and data[id][0] > data[id][1]:
1103                        data[id].reverse()
1104                data[1][0] = min(max(data[0][0],data[1][0]),data[1][1])
1105                data[1][1] = max(min(data[0][1],data[1][1]),data[1][0])
1106                G2frame.PatternTree.SetItemPyData(LimitId,data)
1107                if G2frame.PatternTree.GetItemText(G2frame.PickId) == 'Limits':
1108                    G2pdG.UpdateLimitsGrid(G2frame,data,plottype)
1109            elif lineNo > 1:
1110                PeakId = G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Peak List')
1111                data = G2frame.PatternTree.GetItemPyData(PeakId)
1112                if event.button == 3:
1113                    del data['peaks'][lineNo-2]
1114                else:
1115                    if G2frame.qPlot:
1116                        data['peaks'][lineNo-2][0] = G2lat.Dsp2pos(Parms,2.*np.pi/xpos)
1117                    elif G2frame.dPlot:
1118                        data['peaks'][lineNo-2][0] = G2lat.Dsp2pos(Parms,xpos)
1119                    else:
1120                        data['peaks'][lineNo-2][0] = xpos
1121                    data['sigDict'] = {}        #no longer valid
1122                G2frame.PatternTree.SetItemPyData(PeakId,data)
1123                G2pdG.UpdatePeakGrid(G2frame,data)
1124        elif G2frame.PatternTree.GetItemText(PickId) in ['Models',] and xpos:
1125            lines = []
1126            for line in G2frame.Lines: 
1127                lines.append(line.get_xdata()[0])
1128            try:
1129                lineNo = lines.index(G2frame.itemPicked.get_xdata()[0])
1130            except ValueError:
1131                lineNo = -1
1132            if  lineNo in [0,1]:
1133                LimitId = G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Limits')
1134                data = G2frame.PatternTree.GetItemPyData(LimitId)
1135                data[1][lineNo] = xpos
1136                data[1][0] = min(max(data[0][0],data[1][0]),data[1][1])
1137                data[1][1] = max(min(data[0][1],data[1][1]),data[1][0])
1138                G2frame.PatternTree.SetItemPyData(LimitId,data)       
1139        elif (G2frame.PatternTree.GetItemText(PickId) == 'Reflection Lists' or \
1140            'PWDR' in G2frame.PatternTree.GetItemText(PickId)) and xpos:
1141            Phases = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId,'Reflection Lists'))
1142            pick = str(G2frame.itemPicked).split('(')[1].strip(')')
1143            if 'line' not in pick:       #avoid data points, etc.
1144                num = Phases.keys().index(pick)
1145                if num:
1146                    G2frame.refDelt = -(event.ydata-G2frame.refOffset)/(num*Ymax)
1147                else:       #1st row of refl ticks
1148                    G2frame.refOffset = event.ydata
1149        PlotPatterns(G2frame,plotType=plottype)
1150        G2frame.itemPicked = None   
1151
1152    try:
1153        plotNum = G2frame.G2plotNB.plotList.index('Powder Patterns')
1154        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1155        if not newPlot:
1156            Plot = Page.figure.gca()          #get previous powder plot & get limits
1157            xylim = Plot.get_xlim(),Plot.get_ylim()
1158        Page.figure.clf()
1159        Plot = Page.figure.gca()          #get a fresh plot after clf()
1160    except ValueError:
1161        if plottype == 'SASD':
1162            G2frame.logPlot = True
1163            G2frame.ErrorBars = True
1164        newPlot = True
1165        G2frame.Cmax = 1.0
1166        Plot = G2frame.G2plotNB.addMpl('Powder Patterns').gca()
1167        plotNum = G2frame.G2plotNB.plotList.index('Powder Patterns')
1168        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1169        Page.canvas.mpl_connect('key_press_event', OnPlotKeyPress)
1170        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
1171        Page.canvas.mpl_connect('pick_event', OnPick)
1172        Page.canvas.mpl_connect('button_release_event', OnRelease)
1173        Page.canvas.mpl_connect('button_press_event',OnPress)
1174    if plottype == 'PWDR':  # avoids a very nasty clash with KILL_FOCUS in SASD TextCtrl?
1175        Page.SetFocus()
1176    G2frame.G2plotNB.status.DestroyChildren()
1177    if G2frame.Contour:
1178        Page.Choice = (' key press','d: lower contour max','u: raise contour max','o: reset contour max',
1179            'i: interpolation method','s: color scheme','c: contour off')
1180    else:
1181        if G2frame.logPlot:
1182            if 'PWDR' in plottype:
1183                if G2frame.SinglePlot:
1184                    Page.Choice = (' key press','n: log(I) off',
1185                        'c: contour on','q: toggle q plot','t: toggle d-spacing plot',
1186                            'm: toggle multidata plot','w: toggle divide by sig','+: no selection')
1187                else:
1188                    Page.Choice = (' key press','n: log(I) off',
1189                        'd: offset down','l: offset left','r: offset right','u: offset up','o: reset offset',
1190                        'c: contour on','q: toggle q plot','t: toggle d-spacing plot',
1191                        'm: toggle multidata plot','w: toggle divide by sig','+: no selection')
1192            elif 'SASD' in plottype:
1193                if G2frame.SinglePlot:
1194                    Page.Choice = (' key press','b: toggle subtract background file','n: semilog on',
1195                        'q: toggle S(q) plot','m: toggle multidata plot','w: toggle (Io-Ic)/sig plot','+: no selection')
1196                else:
1197                    Page.Choice = (' key press','b: toggle subtract background file','n: semilog on',
1198                        'd: offset down','l: offset left','r: offset right','u: offset up','o: reset offset',
1199                        'q: toggle S(q) plot','m: toggle multidata plot','w: toggle (Io-Ic)/sig plot','+: no selection')
1200        else:
1201            if 'PWDR' in plottype:
1202                if G2frame.SinglePlot:
1203                    Page.Choice = (' key press',
1204                        'b: toggle subtract background','n: log(I) on','s: toggle sqrt plot','c: contour on',
1205                        'q: toggle q plot','t: toggle d-spacing plot','m: toggle multidata plot',
1206                        'w: toggle divide by sig','+: no selection')
1207                else:
1208                    Page.Choice = (' key press','l: offset left','r: offset right','d: offset down',
1209                        'u: offset up','o: reset offset','b: toggle subtract background','n: log(I) on','c: contour on',
1210                        'q: toggle q plot','t: toggle d-spacing plot','m: toggle multidata plot',
1211                        'w: toggle divide by sig','+: no selection')
1212            elif 'SASD' in plottype:
1213                if G2frame.SinglePlot:
1214                    Page.Choice = (' key press','b: toggle subtract background file','n: loglog on','e: toggle error bars',
1215                        'q: toggle S(q) plot','m: toggle multidata plot','w: toggle (Io-Ic)/sig plot','+: no selection')
1216                else:
1217                    Page.Choice = (' key press','b: toggle subtract background file','n: loglog on','e: toggle error bars',
1218                        'd: offset down','l: offset left','r: offset right','u: offset up','o: reset offset',
1219                        'q: toggle S(q) plot','m: toggle multidata plot','w: toggle (Io-Ic)/sig plot','+: no selection')
1220
1221    Page.keyPress = OnPlotKeyPress   
1222    PickId = G2frame.PickId
1223    PatternId = G2frame.PatternId
1224    colors=['b','g','r','c','m','k']
1225    Lines = []
1226    exclLines = []
1227    if G2frame.SinglePlot:
1228        Pattern = G2frame.PatternTree.GetItemPyData(PatternId)
1229        Pattern.append(G2frame.PatternTree.GetItemText(PatternId))
1230        PlotList = [Pattern,]
1231        Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,
1232            G2frame.PatternId, 'Instrument Parameters'))
1233        Sample = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.PatternId, 'Sample Parameters'))
1234        ParmList = [Parms,]
1235        SampleList = [Sample,]
1236        Title = Pattern[-1]
1237    else:       
1238        Title = os.path.split(G2frame.GSASprojectfile)[1]
1239        PlotList = []
1240        ParmList = []
1241        SampleList = []
1242        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1243        while item:
1244            if plottype in G2frame.PatternTree.GetItemText(item):
1245                Pattern = G2frame.PatternTree.GetItemPyData(item)
1246                if len(Pattern) < 3:                    # put name on end if needed
1247                    Pattern.append(G2frame.PatternTree.GetItemText(item))
1248                PlotList.append(Pattern)
1249                ParmList.append(G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,
1250                    item,'Instrument Parameters'))[0])
1251                SampleList.append(G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,
1252                    item, 'Sample Parameters')))
1253            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)               
1254    lenX = 0
1255    if PickId:
1256        if G2frame.PatternTree.GetItemText(PickId) in ['Reflection Lists']:
1257            Phases = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId,'Reflection Lists'))
1258            HKL = []
1259            if Phases:
1260                try:
1261                    for peak in Phases[G2frame.RefList]['RefList']:
1262                        HKL.append(peak[:6])
1263                except TypeError:
1264                    for peak in Phases[G2frame.RefList]:
1265                        HKL.append(peak[:6])                   
1266                HKL = np.array(HKL)
1267        else:
1268            HKL = np.array(G2frame.HKL)
1269    Ymax = None
1270    for Pattern in PlotList:
1271        xye = Pattern[1]
1272        if xye[1] is None: continue
1273        if Ymax is None: Ymax = max(xye[1])
1274        Ymax = max(Ymax,max(xye[1]))
1275    if Ymax is None: return # nothing to plot
1276    offset = G2frame.Offset[0]*Ymax/100.0
1277    if G2frame.logPlot:
1278        Title = 'log('+Title+')'
1279    Plot.set_title(Title)
1280    if G2frame.qPlot or 'SASD' in plottype:
1281        Plot.set_xlabel(r'$Q, \AA^{-1}$',fontsize=16)
1282    elif G2frame.dPlot and 'PWDR' in plottype:
1283        Plot.set_xlabel(r'$d, \AA$',fontsize=16)
1284    else:
1285        if 'C' in ParmList[0]['Type'][0]:       
1286            Plot.set_xlabel(r'$\mathsf{2\theta}$',fontsize=16)
1287        else:
1288            Plot.set_xlabel(r'$TOF, \mathsf{\mu}$s',fontsize=16)           
1289    if G2frame.Weight:
1290        if 'PWDR' in plottype:
1291            Plot.set_ylabel(r'$\mathsf{I/\sigma(I)}$',fontsize=16)
1292        elif 'SASD' in plottype:
1293            Plot.set_ylabel(r'$\mathsf{\Delta(I)/\sigma(I)}$',fontsize=16)
1294    else:
1295        if 'C' in ParmList[0]['Type'][0]:
1296            if 'PWDR' in plottype:
1297                if G2frame.SqrtPlot:
1298                    Plot.set_ylabel(r'$\sqrt{Intensity}$',fontsize=16)
1299                else:
1300                    Plot.set_ylabel(r'$Intensity$',fontsize=16)
1301            elif 'SASD' in plottype:
1302                if G2frame.sqPlot:
1303                    Plot.set_ylabel(r'$S(Q)=I*Q^{4}$',fontsize=16)
1304                else:
1305                    Plot.set_ylabel(r'$Intensity, cm^{-1}$',fontsize=16)
1306        else:
1307            if G2frame.SqrtPlot:
1308                Plot.set_ylabel(r'$\sqrt{Normalized\ intensity}$',fontsize=16)
1309            else:
1310                Plot.set_ylabel(r'$Normalized\ intensity$',fontsize=16)
1311    if G2frame.Contour:
1312        ContourZ = []
1313        ContourY = []
1314        Nseq = 0
1315    for N,Pattern in enumerate(PlotList):
1316        Parms = ParmList[N]
1317        Sample = SampleList[N]
1318        if 'C' in Parms['Type'][0]:
1319            wave = G2mth.getWave(Parms)
1320        else:
1321            difC = Parms['difC'][1]
1322        ifpicked = False
1323        LimitId = 0
1324        if Pattern[1] is None: continue # skip over uncomputed simulations
1325        xye = ma.array(ma.getdata(Pattern[1]))
1326        Zero = Parms.get('Zero',[0.,0.])[1]
1327        if PickId:
1328            ifpicked = Pattern[2] == G2frame.PatternTree.GetItemText(PatternId)
1329            LimitId = G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Limits')
1330            limits = np.array(G2frame.PatternTree.GetItemPyData(LimitId))
1331            excls = limits[2:]
1332            for excl in excls:
1333                xye[0] = ma.masked_inside(xye[0],excl[0],excl[1])
1334        if G2frame.qPlot and 'PWDR' in plottype:
1335            Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root, Pattern[2])
1336            X = 2.*np.pi/G2lat.Pos2dsp(Parms,xye[0])
1337        elif G2frame.dPlot and 'PWDR' in plottype:
1338            Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root, Pattern[2])
1339            X = G2lat.Pos2dsp(Parms,xye[0])
1340        else:
1341            X = xye[0]
1342        if not lenX:
1343            lenX = len(X)
1344        if 'PWDR' in plottype:
1345            if G2frame.SqrtPlot:
1346                olderr = np.seterr(invalid='ignore') #get around sqrt(-ve) error
1347                Y = np.where(xye[1]>=0.,np.sqrt(xye[1]),-np.sqrt(-xye[1]))
1348                np.seterr(invalid=olderr['invalid'])
1349            else:
1350                Y = xye[1]+offset*N
1351        elif 'SASD' in plottype:
1352            B = xye[5]
1353            if G2frame.sqPlot:
1354                Y = xye[1]*Sample['Scale'][0]*(1.005)**(offset*N)*X**4
1355            else:
1356                Y = xye[1]*Sample['Scale'][0]*(1.005)**(offset*N)
1357        if LimitId and ifpicked:
1358            limits = np.array(G2frame.PatternTree.GetItemPyData(LimitId))
1359            lims = limits[1]
1360            if G2frame.qPlot and 'PWDR' in plottype:
1361                lims = 2.*np.pi/G2lat.Pos2dsp(Parms,lims)
1362            elif G2frame.dPlot and 'PWDR' in plottype:
1363                lims = G2lat.Pos2dsp(Parms,lims)
1364            Lines.append(Plot.axvline(lims[0],color='g',dashes=(5,5),picker=3.))   
1365            Lines.append(Plot.axvline(lims[1],color='r',dashes=(5,5),picker=3.))
1366            for i,item in enumerate(limits[2:]):
1367                Lines.append(Plot.axvline(item[0],color='m',dashes=(5,5),picker=3.))   
1368                Lines.append(Plot.axvline(item[1],color='m',dashes=(5,5),picker=3.))
1369                exclLines += [2*i+2,2*i+3]
1370        if G2frame.Contour:
1371           
1372            if lenX == len(X):
1373                ContourY.append(N)
1374                ContourZ.append(Y)
1375                ContourX = X
1376                Nseq += 1
1377                Plot.set_ylabel('Data sequence',fontsize=12)
1378        else:
1379            if 'SASD' in plottype and G2frame.logPlot:
1380                X *= (1.01)**(G2frame.Offset[1]*N)
1381            else:
1382                X += G2frame.Offset[1]*.005*N
1383            Xum = ma.getdata(X)
1384            DifLine = ['']
1385            if ifpicked:
1386                if G2frame.SqrtPlot:
1387                    Z = np.where(xye[3]>=0.,np.sqrt(xye[3]),-np.sqrt(-xye[3]))
1388                else:
1389                    Z = xye[3]+offset*N
1390                if 'PWDR' in plottype:
1391                    if G2frame.SqrtPlot:
1392                        W = np.where(xye[4]>=0.,np.sqrt(xye[4]),-np.sqrt(-xye[4]))
1393                        D = np.where(xye[5],(Y-Z),0.)-Ymax*G2frame.delOffset
1394                    else:
1395                        W = xye[4]+offset*N
1396                        D = xye[5]-Ymax*G2frame.delOffset  #powder background
1397                elif 'SASD' in plottype:
1398                    if G2frame.sqPlot:
1399                        W = xye[4]*X**4
1400                        Z = xye[3]*X**4
1401                        B = B*X**4
1402                    else:
1403                        W = xye[4]
1404                    if G2frame.SubBack:
1405                        YB = Y-B
1406                        ZB = Z
1407                    else:
1408                        YB = Y
1409                        ZB = Z+B
1410                    Plot.set_yscale("log",nonposy='mask')
1411                    if np.any(W>0.):
1412                        Plot.set_ylim(bottom=np.min(np.trim_zeros(W))/2.,top=np.max(Y)*2.)
1413                    else:
1414                        Plot.set_ylim(bottom=np.min(np.trim_zeros(YB))/2.,top=np.max(Y)*2.)
1415                if G2frame.logPlot:
1416                    if 'PWDR' in plottype:
1417                        Plot.set_yscale("log",nonposy='mask')
1418                        Plot.plot(X,Y,colors[N%6]+'+',picker=3.,clip_on=False)
1419                        Plot.plot(X,Z,colors[(N+1)%6],picker=False)
1420                        Plot.plot(X,W,colors[(N+2)%6],picker=False)     #background
1421                    elif 'SASD' in plottype:
1422                        Plot.set_xscale("log",nonposx='mask')
1423                        Ibeg = np.searchsorted(X,limits[1][0])
1424                        Ifin = np.searchsorted(X,limits[1][1])
1425                        if G2frame.Weight:
1426                            Plot.set_yscale("linear")
1427                            DS = (YB-ZB)*np.sqrt(xye[2])
1428                            Plot.plot(X[Ibeg:Ifin],DS[Ibeg:Ifin],colors[(N+3)%6],picker=False)
1429                            Plot.axhline(0.,color=wx.BLACK)
1430                            Plot.set_ylim(bottom=np.min(DS[Ibeg:Ifin])*1.2,top=np.max(DS[Ibeg:Ifin])*1.2)                                                   
1431                        else:
1432                            Plot.set_yscale("log",nonposy='mask')
1433                            if G2frame.ErrorBars:
1434                                if G2frame.sqPlot:
1435                                    Plot.errorbar(X,YB,yerr=X**4*Sample['Scale'][0]*np.sqrt(1./(Pattern[0]['wtFactor']*xye[2])),
1436                                        ecolor=colors[N%6],picker=3.,clip_on=False)
1437                                else:
1438                                    Plot.errorbar(X,YB,yerr=Sample['Scale'][0]*np.sqrt(1./(Pattern[0]['wtFactor']*xye[2])),
1439                                        ecolor=colors[N%6],picker=3.,clip_on=False)
1440                            else:
1441                                Plot.plot(X,YB,colors[N%6]+'+',picker=3.,clip_on=False)
1442                            Plot.plot(X,W,colors[(N+2)%6],picker=False)     #const. background
1443                            Plot.plot(X,ZB,colors[(N+1)%6],picker=False)
1444                elif G2frame.Weight and 'PWDR' in plottype:
1445                    DY = xye[1]*np.sqrt(xye[2])
1446                    Ymax = max(DY)
1447                    DZ = xye[3]*np.sqrt(xye[2])
1448                    DS = xye[5]*np.sqrt(xye[2])-Ymax*G2frame.delOffset
1449                    ObsLine = Plot.plot(X,DY,colors[N%6]+'+',picker=3.,clip_on=False)         #Io/sig(Io)
1450                    Plot.plot(X,DZ,colors[(N+1)%6],picker=False)                    #Ic/sig(Io)
1451                    DifLine = Plot.plot(X,DS,colors[(N+3)%6],picker=1.)                    #(Io-Ic)/sig(Io)
1452                    Plot.axhline(0.,color=wx.BLACK)
1453                else:
1454                    if G2frame.SubBack:
1455                        if 'PWDR' in plottype:
1456                            Plot.plot(Xum,Y-W,colors[N%6]+'+',picker=False,clip_on=False)  #Io-Ib
1457                            Plot.plot(X,Z-W,colors[(N+1)%6],picker=False)               #Ic-Ib
1458                        else:
1459                            Plot.plot(X,YB,colors[N%6]+'+',picker=3.,clip_on=False)
1460                            Plot.plot(X,ZB,colors[(N+1)%6],picker=False)
1461                    else:
1462                        if 'PWDR' in plottype:
1463                            ObsLine = Plot.plot(Xum,Y,colors[N%6]+'+',picker=3.,clip_on=False)    #Io
1464                            Plot.plot(X,Z,colors[(N+1)%6],picker=False)                 #Ic
1465                        else:
1466                            Plot.plot(X,YB,colors[N%6]+'+',picker=3.,clip_on=False)
1467                            Plot.plot(X,ZB,colors[(N+1)%6],picker=False)
1468                    if 'PWDR' in plottype:
1469                        Plot.plot(X,W,colors[(N+2)%6],picker=False)                 #Ib
1470                        DifLine = Plot.plot(X,D,colors[(N+3)%6],picker=1.)                 #Io-Ic
1471                    Plot.axhline(0.,color=wx.BLACK)
1472                Page.canvas.SetToolTipString('')
1473                if G2frame.PatternTree.GetItemText(PickId) == 'Peak List':
1474                    tip = 'On data point: Pick peak - L or R MB. On line: L-move, R-delete'
1475                    Page.canvas.SetToolTipString(tip)
1476                    data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Peak List'))
1477                    for item in data['peaks']:
1478                        if G2frame.qPlot:
1479                            Lines.append(Plot.axvline(2.*np.pi/G2lat.Pos2dsp(Parms,item[0]),color=colors[N%6],picker=2.))
1480                        elif G2frame.dPlot:
1481                            Lines.append(Plot.axvline(G2lat.Pos2dsp(Parms,item[0]),color=colors[N%6],picker=2.))
1482                        else:
1483                            Lines.append(Plot.axvline(item[0],color=colors[N%6],picker=2.))
1484                if G2frame.PatternTree.GetItemText(PickId) == 'Limits':
1485                    tip = 'On data point: Lower limit - L MB; Upper limit - R MB. On limit: MB down to move'
1486                    Page.canvas.SetToolTipString(tip)
1487                    data = G2frame.LimitsTable.GetData()
1488                   
1489            else:   #not picked
1490                if G2frame.logPlot:
1491                    if 'PWDR' in plottype:
1492                        Plot.semilogy(X,Y,colors[N%6],picker=False,nonposy='mask')
1493                    elif 'SASD' in plottype:
1494                        Plot.semilogy(X,Y,colors[N%6],picker=False,nonposy='mask')
1495                else:
1496                    if 'PWDR' in plottype:
1497                        Plot.plot(X,Y,colors[N%6],picker=False)
1498                    elif 'SASD' in plottype:
1499                        Plot.loglog(X,Y,colors[N%6],picker=False,nonposy='mask')
1500                        Plot.set_ylim(bottom=np.min(np.trim_zeros(Y))/2.,top=np.max(Y)*2.)
1501                           
1502                if G2frame.logPlot and 'PWDR' in plottype:
1503                    Plot.set_ylim(bottom=np.min(np.trim_zeros(Y))/2.,top=np.max(Y)*2.)
1504    if PickId and not G2frame.Contour:
1505        Parms,Parms2 = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Instrument Parameters'))
1506        if G2frame.PatternTree.GetItemText(PickId) in ['Index Peak List','Unit Cells List']:
1507            peaks = np.array((G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Index Peak List'))))[0]
1508            for peak in peaks:
1509                if G2frame.qPlot:
1510                    Plot.axvline(2.*np.pi/G2lat.Pos2dsp(Parms,peak[0]),color='b')
1511                if G2frame.dPlot:
1512                    Plot.axvline(G2lat.Pos2dsp(Parms,peak[0]),color='b')
1513                else:
1514                    Plot.axvline(peak[0],color='b')
1515            for hkl in G2frame.HKL:
1516                if G2frame.qPlot:
1517                    Plot.axvline(2.*np.pi/G2lat.Pos2dsp(Parms,hkl[5]),color='r',dashes=(5,5))
1518                if G2frame.dPlot:
1519                    Plot.axvline(G2lat.Pos2dsp(Parms,hkl[5]),color='r',dashes=(5,5))
1520                else:
1521                    Plot.axvline(hkl[5],color='r',dashes=(5,5))
1522        elif G2frame.PatternTree.GetItemText(PickId) in ['Reflection Lists'] or \
1523            'PWDR' in G2frame.PatternTree.GetItemText(PickId):
1524            refColors=['b','r','c','g','m','k']
1525            Phases = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId,'Reflection Lists'))
1526            for pId,phase in enumerate(Phases):
1527                try:    #patch for old style reflection lists
1528                    peaks = Phases[phase]['RefList']
1529                except TypeError:
1530                    peaks = Phases[phase]
1531                if not len(peaks):
1532                    continue
1533                peak = np.array([[peak[4],peak[5]] for peak in peaks])
1534                pos = G2frame.refOffset-pId*Ymax*G2frame.refDelt*np.ones_like(peak)
1535                if G2frame.qPlot:
1536                    Plot.plot(2*np.pi/peak.T[0],pos,refColors[pId%6]+'|',mew=1,ms=8,picker=3.,label=phase)
1537                elif G2frame.dPlot:
1538                    Plot.plot(peak.T[0],pos,refColors[pId%6]+'|',mew=1,ms=8,picker=3.,label=phase)
1539                else:
1540                    Plot.plot(peak.T[1],pos,refColors[pId%6]+'|',mew=1,ms=8,picker=3.,label=phase)
1541            if len(Phases):
1542                handles,legends = Plot.get_legend_handles_labels()  #got double entries in the legends for some reason
1543                if handles:
1544                    Plot.legend(handles[::2],legends[::2],title='Phases',loc='best')    #skip every other one
1545           
1546    if G2frame.Contour:
1547        acolor = mpl.cm.get_cmap(G2frame.ContourColor)
1548        Img = Plot.imshow(ContourZ,cmap=acolor,vmin=0,vmax=Ymax*G2frame.Cmax,interpolation=G2frame.Interpolate, 
1549            extent=[ContourX[0],ContourX[-1],ContourY[0],ContourY[-1]],aspect='auto',origin='lower')
1550        Page.figure.colorbar(Img)
1551    else:
1552        G2frame.Lines = Lines
1553    if not newPlot:
1554        Page.toolbar.push_current()
1555        Plot.set_xlim(xylim[0])
1556        Plot.set_ylim(xylim[1])
1557#        xylim = []
1558        Page.toolbar.push_current()
1559        Page.toolbar.draw()
1560    else:
1561        Page.canvas.draw()
1562    olderr = np.seterr(invalid='ignore') #ugh - this removes a matplotlib error for mouse clicks in log plots
1563    # and sqrt(-ve) in np.where usage               
1564#    G2frame.Pwdr = True
1565   
1566################################################################################
1567##### PlotDeltSig
1568################################################################################
1569           
1570def PlotDeltSig(G2frame,kind):
1571    'needs a doc string'
1572    try:
1573        plotNum = G2frame.G2plotNB.plotList.index('Error analysis')
1574        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1575        Page.figure.clf()
1576        Plot = Page.figure.gca()          #get a fresh plot after clf()
1577    except ValueError:
1578        newPlot = True
1579        G2frame.Cmax = 1.0
1580        Plot = G2frame.G2plotNB.addMpl('Error analysis').gca()
1581        plotNum = G2frame.G2plotNB.plotList.index('Error analysis')
1582        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1583    Page.Choice = None
1584    PatternId = G2frame.PatternId
1585    Pattern = G2frame.PatternTree.GetItemPyData(PatternId)
1586    Pattern.append(G2frame.PatternTree.GetItemText(PatternId))
1587    wtFactor = Pattern[0]['wtFactor']
1588    if kind == 'PWDR':
1589        limits = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Limits'))[1]
1590        xye = np.array(Pattern[1])
1591        xmin = np.searchsorted(xye[0],limits[0])
1592        xmax = np.searchsorted(xye[0],limits[1])
1593        DS = xye[5][xmin:xmax]*np.sqrt(wtFactor*xye[2][xmin:xmax])
1594    elif kind == 'HKLF':
1595        refl = Pattern[1]
1596        DS = []
1597        for ref in refl:
1598            if ref[6] > 0.:
1599                DS.append((ref[5]-ref[7])/ref[6])
1600    Page.SetFocus()
1601    G2frame.G2plotNB.status.DestroyChildren()
1602    DS.sort()
1603    EDS = np.zeros_like(DS)
1604    DX = np.linspace(0.,1.,num=len(DS),endpoint=True)
1605    oldErr = np.seterr(invalid='ignore')    #avoid problem at DX==0
1606    T = np.sqrt(np.log(1.0/DX**2))
1607    top = 2.515517+0.802853*T+0.010328*T**2
1608    bot = 1.0+1.432788*T+0.189269*T**2+0.001308*T**3
1609    EDS = np.where(DX>0,-(T-top/bot),(T-top/bot))
1610    low1 = np.searchsorted(EDS,-1.)
1611    hi1 = np.searchsorted(EDS,1.)
1612    slp,intcp = np.polyfit(EDS[low1:hi1],DS[low1:hi1],deg=1)
1613    frac = 100.*(hi1-low1)/len(DS)
1614    G2frame.G2plotNB.status.SetStatusText(  \
1615        'Over range -1. to 1. :'+' slope = %.3f, intercept = %.3f for %.2f%% of the fitted data'%(slp,intcp,frac),1)
1616    Plot.set_title('Normal probability for '+Pattern[-1])
1617    Plot.set_xlabel(r'expected $\mathsf{\Delta/\sigma}$',fontsize=14)
1618    Plot.set_ylabel(r'observed $\mathsf{\Delta/\sigma}$',fontsize=14)
1619    Plot.plot(EDS,DS,'r+',label='result')
1620    Plot.plot([-2,2],[-2,2],'k',dashes=(5,5),label='ideal')
1621    Plot.legend(loc='upper left')
1622    np.seterr(invalid='warn')
1623    Page.canvas.draw()
1624       
1625################################################################################
1626##### PlotISFG
1627################################################################################
1628           
1629def PlotISFG(G2frame,newPlot=False,type=''):
1630    ''' Plotting package for PDF analysis; displays I(q), S(q), F(q) and G(r) as single
1631    or multiple plots with waterfall and contour plots as options
1632    '''
1633    if not type:
1634        type = G2frame.G2plotNB.plotList[G2frame.G2plotNB.nb.GetSelection()]
1635    if type not in ['I(Q)','S(Q)','F(Q)','G(R)']:
1636        return
1637    superMinusOne = unichr(0xaf)+unichr(0xb9)
1638   
1639    def OnPlotKeyPress(event):
1640        newPlot = False
1641        if event.key == 'u':
1642            if G2frame.Contour:
1643                G2frame.Cmax = min(1.0,G2frame.Cmax*1.2)
1644            elif G2frame.Offset[0] < 100.:
1645                G2frame.Offset[0] += 1.
1646        elif event.key == 'd':
1647            if G2frame.Contour:
1648                G2frame.Cmax = max(0.0,G2frame.Cmax*0.8)
1649            elif G2frame.Offset[0] > 0.:
1650                G2frame.Offset[0] -= 1.
1651        elif event.key == 'l':
1652            G2frame.Offset[1] -= 1.
1653        elif event.key == 'r':
1654            G2frame.Offset[1] += 1.
1655        elif event.key == 'o':
1656            G2frame.Offset = [0,0]
1657        elif event.key == 'c':
1658            newPlot = True
1659            G2frame.Contour = not G2frame.Contour
1660            if not G2frame.Contour:
1661                G2frame.SinglePlot = False
1662                G2frame.Offset = [0.,0.]
1663        elif event.key == 's':
1664            if G2frame.Contour:
1665                choice = [m for m in mpl.cm.datad.keys() if not m.endswith("_r")]
1666                choice.sort()
1667                dlg = wx.SingleChoiceDialog(G2frame,'Select','Color scheme',choice)
1668                if dlg.ShowModal() == wx.ID_OK:
1669                    sel = dlg.GetSelection()
1670                    G2frame.ContourColor = choice[sel]
1671                else:
1672                    G2frame.ContourColor = 'Paired'
1673                dlg.Destroy()
1674            else:
1675                G2frame.SinglePlot = not G2frame.SinglePlot               
1676        elif event.key == 'i':                  #for smoothing contour plot
1677            choice = ['nearest','bilinear','bicubic','spline16','spline36','hanning',
1678               'hamming','hermite','kaiser','quadric','catrom','gaussian','bessel',
1679               'mitchell','sinc','lanczos']
1680            dlg = wx.SingleChoiceDialog(G2frame,'Select','Interpolation',choice)
1681            if dlg.ShowModal() == wx.ID_OK:
1682                sel = dlg.GetSelection()
1683                G2frame.Interpolate = choice[sel]
1684            else:
1685                G2frame.Interpolate = 'nearest'
1686            dlg.Destroy()
1687        elif event.key == 't' and not G2frame.Contour:
1688            G2frame.Legend = not G2frame.Legend
1689        PlotISFG(G2frame,newPlot=newPlot,type=type)
1690       
1691    def OnKeyBox(event):
1692        if G2frame.G2plotNB.nb.GetSelection() == G2frame.G2plotNB.plotList.index(type):
1693            event.key = cb.GetValue()[0]
1694            cb.SetValue(' key press')
1695            wx.CallAfter(OnPlotKeyPress,event)
1696        Page.canvas.SetFocus() # redirect the Focus from the button back to the plot
1697                       
1698    def OnMotion(event):
1699        xpos = event.xdata
1700        if xpos:                                        #avoid out of frame mouse position
1701            ypos = event.ydata
1702            Page.canvas.SetCursor(wx.CROSS_CURSOR)
1703            try:
1704                if G2frame.Contour:
1705                    G2frame.G2plotNB.status.SetStatusText('R =%.3fA pattern ID =%5d'%(xpos,int(ypos)),1)
1706                else:
1707                    G2frame.G2plotNB.status.SetStatusText('R =%.3fA %s =%.2f'%(xpos,type,ypos),1)                   
1708            except TypeError:
1709                G2frame.G2plotNB.status.SetStatusText('Select '+type+' pattern first',1)
1710   
1711    xylim = []
1712    try:
1713        plotNum = G2frame.G2plotNB.plotList.index(type)
1714        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1715        if not newPlot:
1716            Plot = Page.figure.gca()          #get previous plot & get limits
1717            xylim = Plot.get_xlim(),Plot.get_ylim()
1718        Page.figure.clf()
1719        Plot = Page.figure.gca()
1720    except ValueError:
1721        newPlot = True
1722        G2frame.Cmax = 1.0
1723        Plot = G2frame.G2plotNB.addMpl(type).gca()
1724        plotNum = G2frame.G2plotNB.plotList.index(type)
1725        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1726        Page.canvas.mpl_connect('key_press_event', OnPlotKeyPress)
1727        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
1728   
1729    Page.SetFocus()
1730    G2frame.G2plotNB.status.DestroyChildren()
1731    if G2frame.Contour:
1732        Page.Choice = (' key press','d: lower contour max','u: raise contour max',
1733            'i: interpolation method','s: color scheme','c: contour off')
1734    else:
1735        Page.Choice = (' key press','l: offset left','r: offset right','d: offset down','u: offset up',
1736            'o: reset offset','t: toggle legend','c: contour on','s: toggle single plot')
1737    Page.keyPress = OnPlotKeyPress
1738    PatternId = G2frame.PatternId
1739    PickId = G2frame.PickId
1740    Plot.set_title(type)
1741    if type == 'G(R)':
1742        Plot.set_xlabel(r'$R,\AA$',fontsize=14)
1743    else:
1744        Plot.set_xlabel(r'$Q,\AA$'+superMinusOne,fontsize=14)
1745    Plot.set_ylabel(r''+type,fontsize=14)
1746    colors=['b','g','r','c','m','k']
1747    name = G2frame.PatternTree.GetItemText(PatternId)[4:]
1748    Pattern = []   
1749    if G2frame.SinglePlot:
1750        name = G2frame.PatternTree.GetItemText(PatternId)
1751        name = type+name[4:]
1752        Id = G2gd.GetPatternTreeItemId(G2frame,PatternId,name)
1753        Pattern = G2frame.PatternTree.GetItemPyData(Id)
1754        if Pattern:
1755            Pattern.append(name)
1756        PlotList = [Pattern,]
1757    else:
1758        PlotList = []
1759        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1760        while item:
1761            if 'PDF' in G2frame.PatternTree.GetItemText(item):
1762                name = type+G2frame.PatternTree.GetItemText(item)[4:]
1763                Id = G2gd.GetPatternTreeItemId(G2frame,item,name)
1764                Pattern = G2frame.PatternTree.GetItemPyData(Id)
1765                if Pattern:
1766                    Pattern.append(name)
1767                    PlotList.append(Pattern)
1768            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1769    PDFdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'PDF Controls'))
1770    numbDen = G2pwd.GetNumDensity(PDFdata['ElList'],PDFdata['Form Vol'])
1771    Xb = [0.,10.]
1772    Yb = [0.,-40.*np.pi*numbDen]
1773    Ymax = 1.0
1774    lenX = 0
1775    for Pattern in PlotList:
1776        xye = Pattern[1]
1777        Ymax = max(Ymax,max(xye[1]))
1778    offset = G2frame.Offset[0]*Ymax/100.0
1779    if G2frame.Contour:
1780        ContourZ = []
1781        ContourY = []
1782        Nseq = 0
1783    for N,Pattern in enumerate(PlotList):
1784        xye = Pattern[1]
1785        if PickId:
1786            ifpicked = Pattern[2] == G2frame.PatternTree.GetItemText(PatternId)
1787        X = xye[0]
1788        if not lenX:
1789            lenX = len(X)           
1790        Y = xye[1]+offset*N
1791        if G2frame.Contour:
1792            if lenX == len(X):
1793                ContourY.append(N)
1794                ContourZ.append(Y)
1795                ContourX = X
1796                Nseq += 1
1797                Plot.set_ylabel('Data sequence',fontsize=12)
1798        else:
1799            X = xye[0]+G2frame.Offset[1]*.005*N
1800            if ifpicked:
1801                Plot.plot(X,Y,colors[N%6]+'+',picker=3.,clip_on=False)
1802                Page.canvas.SetToolTipString('')
1803            else:
1804                if G2frame.Legend:
1805                    Plot.plot(X,Y,colors[N%6],picker=False,label='Azm:'+Pattern[2].split('=')[1])
1806                else:
1807                    Plot.plot(X,Y,colors[N%6],picker=False)
1808            if type == 'G(R)':
1809                Plot.plot(Xb,Yb,color='k',dashes=(5,5))
1810            elif type == 'F(Q)':
1811                Plot.axhline(0.,color=wx.BLACK)
1812            elif type == 'S(Q)':
1813                Plot.axhline(1.,color=wx.BLACK)
1814    if G2frame.Contour:
1815        acolor = mpl.cm.get_cmap(G2frame.ContourColor)
1816        Img = Plot.imshow(ContourZ,cmap=acolor,vmin=0,vmax=Ymax*G2frame.Cmax,interpolation=G2frame.Interpolate, 
1817            extent=[ContourX[0],ContourX[-1],ContourY[0],ContourY[-1]],aspect='auto',origin='lower')
1818        Page.figure.colorbar(Img)
1819    elif G2frame.Legend:
1820        Plot.legend(loc='best')
1821    if not newPlot:
1822        Page.toolbar.push_current()
1823        Plot.set_xlim(xylim[0])
1824        Plot.set_ylim(xylim[1])
1825        xylim = []
1826        Page.toolbar.push_current()
1827        Page.toolbar.draw()
1828    else:
1829        Page.canvas.draw()
1830       
1831################################################################################
1832##### PlotCalib
1833################################################################################
1834           
1835def PlotCalib(G2frame,Inst,XY,Sigs,newPlot=False):
1836    '''plot of CW or TOF peak calibration
1837    '''
1838    def OnMotion(event):
1839        xpos = event.xdata
1840        if xpos:                                        #avoid out of frame mouse position
1841            ypos = event.ydata
1842            Page.canvas.SetCursor(wx.CROSS_CURSOR)
1843            try:
1844                G2frame.G2plotNB.status.SetStatusText('X =%9.3f %s =%9.3f'%(xpos,Title,ypos),1)                   
1845            except TypeError:
1846                G2frame.G2plotNB.status.SetStatusText('Select '+Title+' pattern first',1)
1847            found = []
1848            wid = 1
1849            view = Page.toolbar._views.forward()
1850            if view:
1851                view = view[0][:2]
1852                wid = view[1]-view[0]
1853            found = XY[np.where(np.fabs(XY.T[0]-xpos) < 0.005*wid)]
1854            if len(found):
1855                pos = found[0][1]
1856                if 'C' in Inst['Type'][0]: 
1857                    Page.canvas.SetToolTipString('position=%.4f'%(pos))
1858                else:
1859                    Page.canvas.SetToolTipString('position=%.2f'%(pos))
1860            else:
1861                Page.canvas.SetToolTipString('')
1862
1863    Title = 'Position calibration'
1864    try:
1865        plotNum = G2frame.G2plotNB.plotList.index(Title)
1866        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1867        if not newPlot:
1868            Plot = Page.figure.gca()
1869            xylim = Plot.get_xlim(),Plot.get_ylim()
1870        Page.figure.clf()
1871        Plot = Page.figure.gca()
1872    except ValueError:
1873        newPlot = True
1874        Plot = G2frame.G2plotNB.addMpl(Title).gca()
1875        plotNum = G2frame.G2plotNB.plotList.index(Title)
1876        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1877        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
1878   
1879    Page.Choice = None
1880    Page.SetFocus()
1881    G2frame.G2plotNB.status.DestroyChildren()
1882    Plot.set_title(Title)
1883    Plot.set_xlabel(r'd-spacing',fontsize=14)
1884    if 'C' in Inst['Type'][0]:
1885        Plot.set_ylabel(r'$\mathsf{\Delta(2\theta)}$',fontsize=14)
1886    else:
1887        Plot.set_ylabel(r'$\mathsf{\Delta}T/T$',fontsize=14)
1888    for ixy,xy in enumerate(XY):
1889        X,Y = xy
1890        Yc = G2lat.Dsp2pos(Inst,X)
1891        if 'C' in Inst['Type'][0]:
1892            Y = Y-Yc
1893            E = Sigs[ixy]
1894        else:
1895            Y = (Y-Yc)/Yc
1896            E = Sigs[ixy]/Yc
1897        if E:
1898            Plot.errorbar(X,Y,ecolor='k',yerr=E)
1899        Plot.plot(X,Y,'kx',picker=3)
1900        Plot.axhline(0.,color='r',linestyle='--')
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
1911################################################################################
1912##### PlotXY
1913################################################################################
1914           
1915def PlotXY(G2frame,XY,XY2=None,labelX=None,labelY=None,newPlot=False,Title=''):
1916    '''simple plot of xy data, used for diagnostic purposes
1917    '''
1918    def OnMotion(event):
1919        xpos = event.xdata
1920        if xpos:                                        #avoid out of frame mouse position
1921            ypos = event.ydata
1922            Page.canvas.SetCursor(wx.CROSS_CURSOR)
1923            try:
1924                G2frame.G2plotNB.status.SetStatusText('X =%9.3f %s =%9.3f'%(xpos,Title,ypos),1)                   
1925            except TypeError:
1926                G2frame.G2plotNB.status.SetStatusText('Select '+Title+' pattern first',1)
1927
1928    try:
1929        plotNum = G2frame.G2plotNB.plotList.index(Title)
1930        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1931        if not newPlot:
1932            Plot = Page.figure.gca()
1933            xylim = Plot.get_xlim(),Plot.get_ylim()
1934        Page.figure.clf()
1935        Plot = Page.figure.gca()
1936    except ValueError:
1937        newPlot = True
1938        Plot = G2frame.G2plotNB.addMpl(Title).gca()
1939        plotNum = G2frame.G2plotNB.plotList.index(Title)
1940        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1941        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
1942   
1943    Page.Choice = None
1944    Page.SetFocus()
1945    G2frame.G2plotNB.status.DestroyChildren()
1946    Plot.set_title(Title)
1947    if labelX:
1948        Plot.set_xlabel(r''+labelX,fontsize=14)
1949    else:
1950        Plot.set_xlabel(r'X',fontsize=14)
1951    if labelY:
1952        Plot.set_ylabel(r''+labelY,fontsize=14)
1953    else:
1954        Plot.set_ylabel(r'Y',fontsize=14)
1955    colors=['b','g','r','c','m','k']
1956    for ixy,xy in enumerate(XY):
1957        X,Y = xy
1958        Plot.plot(X,Y,colors[ixy%6]+'+',picker=False)
1959    if len(XY2):
1960        for ixy,xy in enumerate(XY2):
1961            X,Y = xy
1962            Plot.plot(X,Y,colors[ixy%6],picker=False)
1963    if not newPlot:
1964        Page.toolbar.push_current()
1965        Plot.set_xlim(xylim[0])
1966        Plot.set_ylim(xylim[1])
1967        xylim = []
1968        Page.toolbar.push_current()
1969        Page.toolbar.draw()
1970    else:
1971        Page.canvas.draw()
1972
1973################################################################################
1974##### PlotStrain
1975################################################################################
1976           
1977def PlotStrain(G2frame,data,newPlot=False):
1978    '''plot of strain data, used for diagnostic purposes
1979    '''
1980    def OnMotion(event):
1981        xpos = event.xdata
1982        if xpos:                                        #avoid out of frame mouse position
1983            ypos = event.ydata
1984            Page.canvas.SetCursor(wx.CROSS_CURSOR)
1985            try:
1986                G2frame.G2plotNB.status.SetStatusText('d-spacing =%9.5f Azimuth =%9.3f'%(ypos,xpos),1)                   
1987            except TypeError:
1988                G2frame.G2plotNB.status.SetStatusText('Select Strain pattern first',1)
1989
1990    try:
1991        plotNum = G2frame.G2plotNB.plotList.index('Strain')
1992        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
1993        if not newPlot:
1994            Plot = Page.figure.gca()
1995            xylim = Plot.get_xlim(),Plot.get_ylim()
1996        Page.figure.clf()
1997        Plot = Page.figure.gca()
1998    except ValueError:
1999        newPlot = True
2000        Plot = G2frame.G2plotNB.addMpl('Strain').gca()
2001        plotNum = G2frame.G2plotNB.plotList.index('Strain')
2002        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2003        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2004   
2005    Page.Choice = None
2006    Page.SetFocus()
2007    G2frame.G2plotNB.status.DestroyChildren()
2008    Plot.set_title('Strain')
2009    Plot.set_ylabel(r'd-spacing',fontsize=14)
2010    Plot.set_xlabel(r'Azimuth',fontsize=14)
2011    colors=['b','g','r','c','m','k']
2012    for N,item in enumerate(data['d-zero']):
2013        Y,X = np.array(item['ImtaObs'])         #plot azimuth as X & d-spacing as Y
2014        Plot.plot(X,Y,colors[N%6]+'+',picker=False)
2015        Y,X = np.array(item['ImtaCalc'])
2016        Plot.plot(X,Y,colors[N%6],picker=False)
2017    if not newPlot:
2018        Page.toolbar.push_current()
2019        Plot.set_xlim(xylim[0])
2020        Plot.set_ylim(xylim[1])
2021        xylim = []
2022        Page.toolbar.push_current()
2023        Page.toolbar.draw()
2024    else:
2025        Page.canvas.draw()
2026       
2027################################################################################
2028##### PlotSASDSizeDist
2029################################################################################
2030           
2031def PlotSASDSizeDist(G2frame):
2032   
2033    def OnPageChanged(event):
2034        PlotText = G2frame.G2plotNB.nb.GetPageText(G2frame.G2plotNB.nb.GetSelection())
2035        if 'Powder' in PlotText:
2036            PlotPatterns(G2frame,plotType='SASD',newPlot=True)
2037        elif 'Size' in PlotText:
2038            PlotSASDSizeDist(G2frame)
2039   
2040    def OnMotion(event):
2041        xpos = event.xdata
2042        if xpos:                                        #avoid out of frame mouse position
2043            ypos = event.ydata
2044            Page.canvas.SetCursor(wx.CROSS_CURSOR)
2045            try:
2046                G2frame.G2plotNB.status.SetStatusText('diameter =%9.3f f(D) =%9.3g'%(xpos,ypos),1)                   
2047            except TypeError:
2048                G2frame.G2plotNB.status.SetStatusText('Select Strain pattern first',1)
2049
2050    try:
2051        plotNum = G2frame.G2plotNB.plotList.index('Size Distribution')
2052        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2053        Page.figure.clf()
2054        Plot = Page.figure.gca()          #get a fresh plot after clf()
2055    except ValueError:
2056        newPlot = True
2057        Plot = G2frame.G2plotNB.addMpl('Size Distribution').gca()
2058        G2frame.G2plotNB.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED,OnPageChanged)
2059        plotNum = G2frame.G2plotNB.plotList.index('Size Distribution')
2060        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2061        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2062    Page.Choice = None
2063    Page.SetFocus()
2064    PatternId = G2frame.PatternId
2065    data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Models'))
2066    Bins,Dbins,BinMag = data['Size']['Distribution']
2067    Plot.set_title('Size Distribution')
2068    Plot.set_xlabel(r'$D, \AA$',fontsize=14)
2069    Plot.set_ylabel(r'$Volume distribution f(D)$',fontsize=14)
2070    if data['Size']['logBins']:
2071        Plot.set_xscale("log",nonposy='mask')
2072        Plot.set_xlim([np.min(2.*Bins)/2.,np.max(2.*Bins)*2.])
2073    Plot.bar(2.*Bins-Dbins,BinMag,2.*Dbins,facecolor='white')       #plot diameters
2074    if 'Size Calc' in data:
2075        Rbins,Dist = data['Size Calc']
2076        for i in range(len(Rbins)):
2077            if len(Rbins[i]):
2078                Plot.plot(2.*Rbins[i],Dist[i])       #plot diameters
2079    Page.canvas.draw()
2080
2081################################################################################
2082##### PlotPowderLines
2083################################################################################
2084           
2085def PlotPowderLines(G2frame):
2086    ''' plotting of powder lines (i.e. no powder pattern) as sticks
2087    '''
2088    global HKL
2089
2090    def OnMotion(event):
2091        xpos = event.xdata
2092        if xpos:                                        #avoid out of frame mouse position
2093            Page.canvas.SetCursor(wx.CROSS_CURSOR)
2094            G2frame.G2plotNB.status.SetFields(['','2-theta =%9.3f '%(xpos,)])
2095            if G2frame.PickId and G2frame.PatternTree.GetItemText(G2frame.PickId) in ['Index Peak List','Unit Cells List']:
2096                found = []
2097                if len(HKL):
2098                    view = Page.toolbar._views.forward()[0][:2]
2099                    wid = view[1]-view[0]
2100                    found = HKL[np.where(np.fabs(HKL.T[5]-xpos) < 0.002*wid)]
2101                if len(found):
2102                    h,k,l = found[0][:3] 
2103                    Page.canvas.SetToolTipString('%d,%d,%d'%(int(h),int(k),int(l)))
2104                else:
2105                    Page.canvas.SetToolTipString('')
2106
2107    try:
2108        plotNum = G2frame.G2plotNB.plotList.index('Powder Lines')
2109        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2110        Page.figure.clf()
2111        Plot = Page.figure.gca()
2112    except ValueError:
2113        Plot = G2frame.G2plotNB.addMpl('Powder Lines').gca()
2114        plotNum = G2frame.G2plotNB.plotList.index('Powder Lines')
2115        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2116        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2117       
2118    Page.Choice = None
2119    Page.SetFocus()
2120    Plot.set_title('Powder Pattern Lines')
2121    Plot.set_xlabel(r'$\mathsf{2\theta}$',fontsize=14)
2122    PickId = G2frame.PickId
2123    PatternId = G2frame.PatternId
2124    peaks = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Index Peak List'))[0]
2125    for peak in peaks:
2126        Plot.axvline(peak[0],color='b')
2127    HKL = np.array(G2frame.HKL)
2128    for hkl in G2frame.HKL:
2129        Plot.axvline(hkl[5],color='r',dashes=(5,5))
2130    xmin = peaks[0][0]
2131    xmax = peaks[-1][0]
2132    delt = xmax-xmin
2133    xlim = [max(0,xmin-delt/20.),min(180.,xmax+delt/20.)]
2134    Plot.set_xlim(xlim)
2135    Page.canvas.draw()
2136    Page.toolbar.push_current()
2137
2138################################################################################
2139##### PlotPeakWidths
2140################################################################################
2141           
2142def PlotPeakWidths(G2frame):
2143    ''' Plotting of instrument broadening terms as function of 2-theta
2144    Seen when "Instrument Parameters" chosen from powder pattern data tree
2145    '''
2146#    sig = lambda Th,U,V,W: 1.17741*math.sqrt(U*tand(Th)**2+V*tand(Th)+W)*math.pi/18000.
2147#    gam = lambda Th,X,Y: (X/cosd(Th)+Y*tand(Th))*math.pi/18000.
2148    gamFW = lambda s,g: np.exp(np.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.)
2149#    gamFW2 = lambda s,g: math.sqrt(s**2+(0.4654996*g)**2)+.5345004*g  #Ubaldo Bafile - private communication
2150    PatternId = G2frame.PatternId
2151    limitID = G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Limits')
2152    if limitID:
2153        limits = G2frame.PatternTree.GetItemPyData(limitID)[:2]
2154    else:
2155        return
2156    Parms,Parms2 = G2frame.PatternTree.GetItemPyData( \
2157        G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Instrument Parameters'))
2158    if 'C' in Parms['Type'][0]:
2159        lam = G2mth.getWave(Parms)
2160    else:
2161        difC = Parms['difC'][0]
2162    try:  # PATCH: deal with older peak lists, before changed to dict to implement TOF
2163        peaks = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Peak List'))['peaks']
2164    except TypeError:
2165        print "Your peak list needs reformatting...",
2166        item = G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Peak List')
2167        G2frame.PatternTree.SelectItem(item) 
2168        item = G2gd.GetPatternTreeItemId(G2frame,PatternId, 'Instrument Parameters')
2169        G2frame.PatternTree.SelectItem(item)
2170        print "done"
2171        return
2172    try:
2173        plotNum = G2frame.G2plotNB.plotList.index('Peak Widths')
2174        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2175        Page.figure.clf()
2176        Plot = Page.figure.gca()
2177    except ValueError:
2178        Plot = G2frame.G2plotNB.addMpl('Peak Widths').gca()
2179        plotNum = G2frame.G2plotNB.plotList.index('Peak Widths')
2180        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2181    Page.Choice = None
2182    Page.SetFocus()
2183   
2184    Page.canvas.SetToolTipString('')
2185    colors=['b','g','r','c','m','k']
2186    X = []
2187    Y = []
2188    Z = []
2189    W = []
2190    if 'C' in Parms['Type'][0]:
2191        Plot.set_title('Instrument and sample peak widths')
2192        Plot.set_xlabel(r'$Q, \AA^{-1}$',fontsize=14)
2193        Plot.set_ylabel(r'$\Delta Q/Q, \Delta d/d$',fontsize=14)
2194        try:
2195            Xmin,Xmax = limits[1]
2196            X = np.linspace(Xmin,Xmax,num=101,endpoint=True)
2197            Q = 4.*np.pi*npsind(X/2.)/lam
2198            Z = np.ones_like(X)
2199            data = G2mth.setPeakparms(Parms,Parms2,X,Z)
2200            s = 1.17741*np.sqrt(data[4])*np.pi/18000.
2201            g = data[6]*np.pi/18000.
2202            G = G2pwd.getgamFW(g,s)
2203            Y = s/nptand(X/2.)
2204            Z = g/nptand(X/2.)
2205            W = G/nptand(X/2.)
2206            Plot.plot(Q,Y,color='r',label='Gaussian')
2207            Plot.plot(Q,Z,color='g',label='Lorentzian')
2208            Plot.plot(Q,W,color='b',label='G+L')
2209           
2210            fit = G2mth.setPeakparms(Parms,Parms2,X,Z,useFit=True)
2211            sf = 1.17741*np.sqrt(fit[4])*np.pi/18000.
2212            gf = fit[6]*np.pi/18000.
2213            Gf = G2pwd.getgamFW(gf,sf)
2214            Yf = sf/nptand(X/2.)
2215            Zf = gf/nptand(X/2.)
2216            Wf = Gf/nptand(X/2.)
2217            Plot.plot(Q,Yf,color='r',dashes=(5,5),label='Gaussian fit')
2218            Plot.plot(Q,Zf,color='g',dashes=(5,5),label='Lorentzian fit')
2219            Plot.plot(Q,Wf,color='b',dashes=(5,5),label='G+L fit')
2220           
2221            X = []
2222            Y = []
2223            Z = []
2224            W = []
2225            V = []
2226            for peak in peaks:
2227                X.append(4.0*math.pi*sind(peak[0]/2.0)/lam)
2228                try:
2229                    s = 1.17741*math.sqrt(peak[4])*math.pi/18000.
2230                except ValueError:
2231                    s = 0.01
2232                g = peak[6]*math.pi/18000.
2233                G = G2pwd.getgamFW(g,s)
2234                Y.append(s/tand(peak[0]/2.))
2235                Z.append(g/tand(peak[0]/2.))
2236                W.append(G/tand(peak[0]/2.))
2237            if len(peaks):
2238                Plot.plot(X,Y,'+',color='r',label='G peak')
2239                Plot.plot(X,Z,'+',color='g',label='L peak')
2240                Plot.plot(X,W,'+',color='b',label='G+L peak')
2241            Plot.legend(loc='best')
2242            Page.canvas.draw()
2243        except ValueError:
2244            print '**** ERROR - default U,V,W profile coefficients yield sqrt of negative value at 2theta =', \
2245                '%.3f'%(2*theta)
2246            G2frame.G2plotNB.Delete('Peak Widths')
2247    else:   #'T'OF
2248        Plot.set_title('Instrument and sample peak coefficients')
2249        Plot.set_xlabel(r'$Q, \AA^{-1}$',fontsize=14)
2250        Plot.set_ylabel(r'$\alpha, \beta, \Delta Q/Q, \Delta d/d$',fontsize=14)
2251        Xmin,Xmax = limits[1]
2252        T = np.linspace(Xmin,Xmax,num=101,endpoint=True)
2253        Z = np.ones_like(T)
2254        data = G2mth.setPeakparms(Parms,Parms2,T,Z)
2255        ds = T/difC
2256        Q = 2.*np.pi/ds
2257        A = data[4]
2258        B = data[6]
2259        S = 1.17741*np.sqrt(data[8])/T
2260        G = data[10]/T
2261        Plot.plot(Q,A,color='r',label='Alpha')
2262        Plot.plot(Q,B,color='g',label='Beta')
2263        Plot.plot(Q,S,color='b',label='Gaussian')
2264        Plot.plot(Q,G,color='m',label='Lorentzian')
2265
2266        fit = G2mth.setPeakparms(Parms,Parms2,T,Z)
2267        ds = T/difC
2268        Q = 2.*np.pi/ds
2269        Af = fit[4]
2270        Bf = fit[6]
2271        Sf = 1.17741*np.sqrt(fit[8])/T
2272        Gf = fit[10]/T
2273        Plot.plot(Q,Af,color='r',dashes=(5,5),label='Alpha fit')
2274        Plot.plot(Q,Bf,color='g',dashes=(5,5),label='Beta fit')
2275        Plot.plot(Q,Sf,color='b',dashes=(5,5),label='Gaussian fit')
2276        Plot.plot(Q,Gf,color='m',dashes=(5,5),label='Lorentzian fit')
2277       
2278        T = []
2279        A = []
2280        B = []
2281        S = []
2282        G = []
2283        W = []
2284        Q = []
2285        V = []
2286        for peak in peaks:
2287            T.append(peak[0])
2288            A.append(peak[4])
2289            B.append(peak[6])
2290            Q.append(2.*np.pi*difC/peak[0])
2291            S.append(1.17741*np.sqrt(peak[8])/peak[0])
2292            G.append(peak[10]/peak[0])
2293           
2294       
2295        Plot.plot(Q,A,'+',color='r',label='Alpha peak')
2296        Plot.plot(Q,B,'+',color='g',label='Beta peak')
2297        Plot.plot(Q,S,'+',color='b',label='Gaussian peak')
2298        Plot.plot(Q,G,'+',color='m',label='Lorentzian peak')
2299        Plot.legend(loc='best')
2300        Page.canvas.draw()
2301
2302   
2303################################################################################
2304##### PlotSizeStrainPO
2305################################################################################
2306           
2307def PlotSizeStrainPO(G2frame,data,Start=False):
2308    '''Plot 3D mustrain/size/preferred orientation figure. In this instance data is for a phase
2309    '''
2310   
2311    PatternId = G2frame.PatternId
2312    generalData = data['General']
2313    SGData = generalData['SGData']
2314    SGLaue = SGData['SGLaue']
2315    if Start:                   #initialize the spherical harmonics qlmn arrays
2316        ptx.pyqlmninit()
2317        Start = False
2318#    MuStrKeys = G2spc.MustrainNames(SGData)
2319    cell = generalData['Cell'][1:]
2320    A,B = G2lat.cell2AB(cell[:6])
2321    Vol = cell[6]
2322    useList = data['Histograms']
2323    phase = generalData['Name']
2324    plotType = generalData['Data plot type']
2325    plotDict = {'Mustrain':'Mustrain','Size':'Size','Preferred orientation':'Pref.Ori.'}
2326    for ptype in plotDict:
2327        G2frame.G2plotNB.Delete(ptype)
2328    if plotType in ['None']:
2329        return       
2330
2331    for item in useList:
2332        if useList[item]['Show']:
2333            break
2334    else:
2335        return            #nothing to show!!
2336   
2337    numPlots = len(useList)
2338
2339    if plotType in ['Mustrain','Size']:
2340        Plot = mp3d.Axes3D(G2frame.G2plotNB.add3D(plotType))
2341    else:
2342        Plot = G2frame.G2plotNB.addMpl(plotType).gca()       
2343    plotNum = G2frame.G2plotNB.plotList.index(plotType)
2344    Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2345    Page.Choice = None
2346    Page.SetFocus()
2347    G2frame.G2plotNB.status.SetStatusText('',1)
2348    if not Page.IsShown():
2349        Page.Show()
2350   
2351    for item in useList:
2352        if useList[item]['Show']:
2353            PHI = np.linspace(0.,360.,30,True)
2354            PSI = np.linspace(0.,180.,30,True)
2355            X = np.outer(npsind(PHI),npsind(PSI))
2356            Y = np.outer(npcosd(PHI),npsind(PSI))
2357            Z = np.outer(np.ones(np.size(PHI)),npcosd(PSI))
2358            try:        #temp patch instead of 'mustrain' for old files with 'microstrain'
2359                coeff = useList[item][plotDict[plotType]]
2360            except KeyError:
2361                break
2362            if plotType in ['Mustrain','Size']:
2363                if coeff[0] == 'isotropic':
2364                    X *= coeff[1][0]
2365                    Y *= coeff[1][0]
2366                    Z *= coeff[1][0]                               
2367                elif coeff[0] == 'uniaxial':
2368                   
2369                    def uniaxCalc(xyz,iso,aniso,axes):
2370                        Z = np.array(axes)
2371                        cp = abs(np.dot(xyz,Z))
2372                        sp = np.sqrt(1.-cp**2)
2373                        R = iso*aniso/np.sqrt((iso*cp)**2+(aniso*sp)**2)
2374                        return R*xyz
2375                       
2376                    iso,aniso = coeff[1][:2]
2377                    axes = np.inner(A,np.array(coeff[3]))
2378                    axes /= nl.norm(axes)
2379                    Shkl = np.array(coeff[1])
2380                    XYZ = np.dstack((X,Y,Z))
2381                    XYZ = np.nan_to_num(np.apply_along_axis(uniaxCalc,2,XYZ,iso,aniso,axes))
2382                    X,Y,Z = np.dsplit(XYZ,3)
2383                    X = X[:,:,0]
2384                    Y = Y[:,:,0]
2385                    Z = Z[:,:,0]
2386               
2387                elif coeff[0] == 'ellipsoidal':
2388                   
2389                    def ellipseCalc(xyz,E,R):
2390                        XYZ = xyz*E.T
2391                        return np.inner(XYZ.T,R)
2392                       
2393                    S6 = coeff[4]
2394                    Sij = G2lat.U6toUij(S6)
2395                    E,R = nl.eigh(Sij)
2396                    XYZ = np.dstack((X,Y,Z))
2397                    XYZ = np.nan_to_num(np.apply_along_axis(ellipseCalc,2,XYZ,E,R))
2398                    X,Y,Z = np.dsplit(XYZ,3)
2399                    X = X[:,:,0]
2400                    Y = Y[:,:,0]
2401                    Z = Z[:,:,0]
2402                   
2403                elif coeff[0] == 'generalized':
2404                   
2405                    def genMustrain(xyz,SGData,A,Shkl):
2406                        uvw = np.inner(A.T,xyz)
2407                        Strm = np.array(G2spc.MustrainCoeff(uvw,SGData))
2408                        sum = np.sum(np.multiply(Shkl,Strm))
2409                        sum = np.where(sum > 0.01,sum,0.01)
2410                        sum = np.sqrt(sum)*math.pi/.18      #centidegrees to radians!
2411                        return sum*xyz
2412                       
2413                    Shkl = np.array(coeff[4])
2414                    if np.any(Shkl):
2415                        XYZ = np.dstack((X,Y,Z))
2416                        XYZ = np.nan_to_num(np.apply_along_axis(genMustrain,2,XYZ,SGData,A,Shkl))
2417                        X,Y,Z = np.dsplit(XYZ,3)
2418                        X = X[:,:,0]
2419                        Y = Y[:,:,0]
2420                        Z = Z[:,:,0]
2421                           
2422                if np.any(X) and np.any(Y) and np.any(Z):
2423                    errFlags = np.seterr(all='ignore')
2424                    Plot.plot_surface(X,Y,Z,rstride=1,cstride=1,color='g',linewidth=1)
2425                    np.seterr(all='ignore')
2426                    xyzlim = np.array([Plot.get_xlim3d(),Plot.get_ylim3d(),Plot.get_zlim3d()]).T
2427                    XYZlim = [min(xyzlim[0]),max(xyzlim[1])]
2428                    Plot.set_xlim3d(XYZlim)
2429                    Plot.set_ylim3d(XYZlim)
2430                    Plot.set_zlim3d(XYZlim)
2431                    Plot.set_aspect('equal')
2432                if plotType == 'Size':
2433                    Plot.set_title('Crystallite size for '+phase+'\n'+coeff[0]+' model')
2434                    Plot.set_xlabel(r'X, $\mu$m')
2435                    Plot.set_ylabel(r'Y, $\mu$m')
2436                    Plot.set_zlabel(r'Z, $\mu$m')
2437                else:   
2438                    Plot.set_title(r'$\mu$strain for '+phase+'\n'+coeff[0]+' model')
2439                    Plot.set_xlabel(r'X, $\mu$strain')
2440                    Plot.set_ylabel(r'Y, $\mu$strain')
2441                    Plot.set_zlabel(r'Z, $\mu$strain')
2442            else:
2443                h,k,l = generalData['POhkl']
2444                if coeff[0] == 'MD':
2445                    print 'March-Dollase preferred orientation plot'
2446               
2447                else:
2448                    PH = np.array(generalData['POhkl'])
2449                    phi,beta = G2lat.CrsAng(PH,cell[:6],SGData)
2450                    SHCoef = {}
2451                    for item in coeff[5]:
2452                        L,N = eval(item.strip('C'))
2453                        SHCoef['C%d,0,%d'%(L,N)] = coeff[5][item]                       
2454                    ODFln = G2lat.Flnh(Start,SHCoef,phi,beta,SGData)
2455                    X = np.linspace(0,90.0,26)
2456                    Y = G2lat.polfcal(ODFln,'0',X,0.0)
2457                    Plot.plot(X,Y,color='k',label=str(PH))
2458                    Plot.legend(loc='best')
2459                    Plot.set_title('Axial distribution for HKL='+str(PH)+' in '+phase)
2460                    Plot.set_xlabel(r'$\psi$',fontsize=16)
2461                    Plot.set_ylabel('MRD',fontsize=14)
2462    Page.canvas.draw()
2463   
2464################################################################################
2465##### PlotTexture
2466################################################################################
2467           
2468def PlotTexture(G2frame,data,Start=False):
2469    '''Pole figure, inverse pole figure, 3D pole distribution and 3D inverse pole distribution
2470    plotting.
2471    dict generalData contains all phase info needed which is in data
2472    '''
2473
2474    shModels = ['cylindrical','none','shear - 2/m','rolling - mmm']
2475    SamSym = dict(zip(shModels,['0','-1','2/m','mmm']))
2476    PatternId = G2frame.PatternId
2477    generalData = data['General']
2478    SGData = generalData['SGData']
2479    textureData = generalData['SH Texture']
2480    G2frame.G2plotNB.Delete('Texture')
2481    if not textureData['Order']:
2482        return                  #no plot!!
2483    SHData = generalData['SH Texture']
2484    SHCoef = SHData['SH Coeff'][1]
2485    cell = generalData['Cell'][1:7]
2486    Amat,Bmat = G2lat.cell2AB(cell)
2487    sq2 = 1.0/math.sqrt(2.0)
2488   
2489    def rp2xyz(r,p):
2490        z = npcosd(r)
2491        xy = np.sqrt(1.-z**2)
2492        return xy*npsind(p),xy*npcosd(p),z
2493           
2494    def OnMotion(event):
2495        SHData = data['General']['SH Texture']
2496        if event.xdata and event.ydata:                 #avoid out of frame errors
2497            xpos = event.xdata
2498            ypos = event.ydata
2499            if 'Inverse' in SHData['PlotType']:
2500                r = xpos**2+ypos**2
2501                if r <= 1.0:
2502                    if 'equal' in G2frame.Projection: 
2503                        r,p = 2.*npasind(np.sqrt(r)*sq2),npatan2d(ypos,xpos)
2504                    else:
2505                        r,p = 2.*npatand(np.sqrt(r)),npatan2d(ypos,xpos)
2506                    ipf = G2lat.invpolfcal(IODFln,SGData,np.array([r,]),np.array([p,]))
2507                    xyz = np.inner(Amat.T,np.array([rp2xyz(r,p)]))
2508                    y,x,z = list(xyz/np.max(np.abs(xyz)))
2509                   
2510                    G2frame.G2plotNB.status.SetFields(['',
2511                        'psi =%9.3f, beta =%9.3f, MRD =%9.3f hkl=%5.2f,%5.2f,%5.2f'%(r,p,ipf,x,y,z)])
2512                                   
2513            elif 'Axial' in SHData['PlotType']:
2514                pass
2515               
2516            else:                       #ordinary pole figure
2517                z = xpos**2+ypos**2
2518                if z <= 1.0:
2519                    z = np.sqrt(z)
2520                    if 'equal' in G2frame.Projection: 
2521                        r,p = 2.*npasind(z*sq2),npatan2d(ypos,xpos)
2522                    else:
2523                        r,p = 2.*npatand(z),npatan2d(ypos,xpos)
2524                    pf = G2lat.polfcal(ODFln,SamSym[textureData['Model']],np.array([r,]),np.array([p,]))
2525                    G2frame.G2plotNB.status.SetFields(['','phi =%9.3f, gam =%9.3f, MRD =%9.3f'%(r,p,pf)])
2526   
2527    try:
2528        plotNum = G2frame.G2plotNB.plotList.index('Texture')
2529        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2530        Page.figure.clf()
2531        Plot = Page.figure.gca()
2532        if not Page.IsShown():
2533            Page.Show()
2534    except ValueError:
2535        Plot = G2frame.G2plotNB.addMpl('Texture').gca()
2536        plotNum = G2frame.G2plotNB.plotList.index('Texture')
2537        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2538        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2539
2540    Page.Choice = None
2541    Page.SetFocus()
2542    G2frame.G2plotNB.status.SetFields(['',''])   
2543    PH = np.array(SHData['PFhkl'])
2544    phi,beta = G2lat.CrsAng(PH,cell,SGData)
2545    ODFln = G2lat.Flnh(Start,SHCoef,phi,beta,SGData)
2546    PX = np.array(SHData['PFxyz'])
2547    gam = atan2d(PX[0],PX[1])
2548    xy = math.sqrt(PX[0]**2+PX[1]**2)
2549    xyz = math.sqrt(PX[0]**2+PX[1]**2+PX[2]**2)
2550    psi = asind(xy/xyz)
2551    IODFln = G2lat.Glnh(Start,SHCoef,psi,gam,SamSym[textureData['Model']])
2552    if 'Axial' in SHData['PlotType']:
2553        X = np.linspace(0,90.0,26)
2554        Y = G2lat.polfcal(ODFln,SamSym[textureData['Model']],X,0.0)
2555        Plot.plot(X,Y,color='k',label=str(SHData['PFhkl']))
2556        Plot.legend(loc='best')
2557        Plot.set_title('Axial distribution for HKL='+str(SHData['PFhkl']))
2558        Plot.set_xlabel(r'$\psi$',fontsize=16)
2559        Plot.set_ylabel('MRD',fontsize=14)
2560       
2561    else:       
2562        npts = 201
2563        if 'Inverse' in SHData['PlotType']:
2564            X,Y = np.meshgrid(np.linspace(1.,-1.,npts),np.linspace(-1.,1.,npts))
2565            R,P = np.sqrt(X**2+Y**2).flatten(),npatan2d(X,Y).flatten()
2566            if 'equal' in G2frame.Projection:
2567                R = np.where(R <= 1.,2.*npasind(R*sq2),0.0)
2568            else:
2569                R = np.where(R <= 1.,2.*npatand(R),0.0)
2570            Z = np.zeros_like(R)
2571            Z = G2lat.invpolfcal(IODFln,SGData,R,P)
2572            Z = np.reshape(Z,(npts,npts))
2573            CS = Plot.contour(Y,X,Z,aspect='equal')
2574            Plot.clabel(CS,fontsize=9,inline=1)
2575            try:
2576                Img = Plot.imshow(Z.T,aspect='equal',cmap=G2frame.ContourColor,extent=[-1,1,-1,1])
2577            except ValueError:
2578                pass
2579            Page.figure.colorbar(Img)
2580            Plot.set_title('Inverse pole figure for XYZ='+str(SHData['PFxyz']))
2581            Plot.set_xlabel(G2frame.Projection.capitalize()+' projection')
2582                       
2583        else:
2584            X,Y = np.meshgrid(np.linspace(1.,-1.,npts),np.linspace(-1.,1.,npts))
2585            R,P = np.sqrt(X**2+Y**2).flatten(),npatan2d(X,Y).flatten()
2586            if 'equal' in G2frame.Projection:
2587                R = np.where(R <= 1.,2.*npasind(R*sq2),0.0)
2588            else:
2589                R = np.where(R <= 1.,2.*npatand(R),0.0)
2590            Z = np.zeros_like(R)
2591            Z = G2lat.polfcal(ODFln,SamSym[textureData['Model']],R,P)
2592            Z = np.reshape(Z,(npts,npts))
2593            try:
2594                CS = Plot.contour(Y,X,Z,aspect='equal')
2595                Plot.clabel(CS,fontsize=9,inline=1)
2596            except ValueError:
2597                pass
2598            Img = Plot.imshow(Z.T,aspect='equal',cmap=G2frame.ContourColor,extent=[-1,1,-1,1])
2599            Page.figure.colorbar(Img)
2600            Plot.set_title('Pole figure for HKL='+str(SHData['PFhkl']))
2601            Plot.set_xlabel(G2frame.Projection.capitalize()+' projection')
2602    Page.canvas.draw()
2603
2604################################################################################
2605##### PlotCovariance
2606################################################################################
2607           
2608def PlotCovariance(G2frame,Data):
2609    'needs a doc string'
2610    if not Data:
2611        print 'No covariance matrix available'
2612        return
2613    varyList = Data['varyList']
2614    values = Data['variables']
2615    Xmax = len(varyList)
2616    covMatrix = Data['covMatrix']
2617    sig = np.sqrt(np.diag(covMatrix))
2618    xvar = np.outer(sig,np.ones_like(sig))
2619    covArray = np.divide(np.divide(covMatrix,xvar),xvar.T)
2620    title = ' for\n'+Data['title']
2621    newAtomDict = Data.get('newAtomDict',{})
2622   
2623
2624    def OnPlotKeyPress(event):
2625        newPlot = False
2626        if event.key == 's':
2627            choice = [m for m in mpl.cm.datad.keys() if not m.endswith("_r")]
2628            choice.sort()
2629            dlg = wx.SingleChoiceDialog(G2frame,'Select','Color scheme',choice)
2630            if dlg.ShowModal() == wx.ID_OK:
2631                sel = dlg.GetSelection()
2632                G2frame.VcovColor = choice[sel]
2633            else:
2634                G2frame.VcovColor = 'RdYlGn'
2635            dlg.Destroy()
2636        PlotCovariance(G2frame,Data)
2637
2638    def OnMotion(event):
2639        #there is a problem here - reports wrong values
2640        if event.button:
2641            ytics = imgAx.get_yticks()
2642            ytics = np.where(ytics<len(varyList),ytics,-1)
2643            ylabs = [np.where(0<=i ,varyList[int(i)],' ') for i in ytics]
2644            imgAx.set_yticklabels(ylabs)           
2645        if event.xdata and event.ydata:                 #avoid out of frame errors
2646            xpos = int(event.xdata+.5)
2647            ypos = int(event.ydata+.5)
2648            if -1 < xpos < len(varyList) and -1 < ypos < len(varyList):
2649                if xpos == ypos:
2650                    value = values[xpos]
2651                    name = varyList[xpos]
2652                    if varyList[xpos] in newAtomDict:
2653                        name,value = newAtomDict[name]                       
2654                    msg = '%s value = %.4g, esd = %.4g'%(name,value,sig[xpos])
2655                else:
2656                    msg = '%s - %s: %5.3f'%(varyList[xpos],varyList[ypos],covArray[xpos][ypos])
2657                Page.canvas.SetToolTipString(msg)
2658                G2frame.G2plotNB.status.SetFields(['',msg])
2659               
2660    try:
2661        plotNum = G2frame.G2plotNB.plotList.index('Covariance')
2662        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2663        Page.figure.clf()
2664        Plot = Page.figure.gca()
2665        if not Page.IsShown():
2666            Page.Show()
2667    except ValueError:
2668        Plot = G2frame.G2plotNB.addMpl('Covariance').gca()
2669        plotNum = G2frame.G2plotNB.plotList.index('Covariance')
2670        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2671        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2672        Page.canvas.mpl_connect('key_press_event', OnPlotKeyPress)
2673    Page.Choice = ['s: to change colors']
2674    Page.keyPress = OnPlotKeyPress
2675    Page.SetFocus()
2676    G2frame.G2plotNB.status.SetFields(['',''])   
2677    acolor = mpl.cm.get_cmap(G2frame.VcovColor)
2678    Img = Plot.imshow(covArray,aspect='equal',cmap=acolor,interpolation='nearest',origin='lower',
2679        vmin=-1.,vmax=1.)
2680    imgAx = Img.get_axes()
2681    ytics = imgAx.get_yticks()
2682    ylabs = [varyList[int(i)] for i in ytics[:-1]]
2683    imgAx.set_yticklabels(ylabs)
2684    colorBar = Page.figure.colorbar(Img)
2685    Plot.set_title('V-Cov matrix'+title)
2686    Plot.set_xlabel('Variable number')
2687    Plot.set_ylabel('Variable name')
2688    Page.canvas.draw()
2689   
2690################################################################################
2691##### PlotTorsion
2692################################################################################
2693
2694def PlotTorsion(G2frame,phaseName,Torsion,TorName,Names=[],Angles=[],Coeff=[]):
2695    'needs a doc string'
2696   
2697    global names
2698    names = Names
2699    sum = np.sum(Torsion)
2700    torsion = np.log(2*Torsion+1.)/sum
2701    tMin = np.min(torsion)
2702    tMax = np.max(torsion)
2703    torsion = 3.*(torsion-tMin)/(tMax-tMin)
2704    X = np.linspace(0.,360.,num=45)
2705   
2706    def OnPick(event):
2707        ind = event.ind[0]
2708        msg = 'atoms:'+names[ind]
2709        Page.canvas.SetToolTipString(msg)
2710        try:
2711            page = G2frame.dataDisplay.GetSelection()
2712        except:
2713            return
2714        if G2frame.dataDisplay.GetPageText(page) == 'Torsion restraints':
2715            torGrid = G2frame.dataDisplay.GetPage(page).Torsions
2716            torGrid.ClearSelection()
2717            for row in range(torGrid.GetNumberRows()):
2718                if names[ind] in torGrid.GetCellValue(row,0):
2719                    torGrid.SelectRow(row)
2720            torGrid.ForceRefresh()
2721               
2722    def OnMotion(event):
2723        if event.xdata and event.ydata:                 #avoid out of frame errors
2724            xpos = event.xdata
2725            ypos = event.ydata
2726            msg = 'torsion,energy: %5.3f %5.3f'%(xpos,ypos)
2727            Page.canvas.SetToolTipString(msg)
2728
2729    try:
2730        plotNum = G2frame.G2plotNB.plotList.index('Torsion')
2731        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2732        Page.figure.clf()
2733        Plot = Page.figure.gca()
2734        if not Page.IsShown():
2735            Page.Show()
2736    except ValueError:
2737        Plot = G2frame.G2plotNB.addMpl('Torsion').gca()
2738        plotNum = G2frame.G2plotNB.plotList.index('Torsion')
2739        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2740        Page.canvas.mpl_connect('pick_event', OnPick)
2741        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2742   
2743    Page.SetFocus()
2744    G2frame.G2plotNB.status.SetFields(['','Use mouse LB to identify torsion atoms'])
2745    Plot.plot(X,torsion,'b+')
2746    if len(Coeff):
2747        X2 = np.linspace(0.,360.,45)
2748        Y2 = np.array([-G2mth.calcTorsionEnergy(x,Coeff)[1] for x in X2])
2749        Plot.plot(X2,Y2,'r')
2750    if len(Angles):
2751        Eval = np.array([-G2mth.calcTorsionEnergy(x,Coeff)[1] for x in Angles])
2752        Plot.plot(Angles,Eval,'ro',picker=5)
2753    Plot.set_xlim((0.,360.))
2754    Plot.set_title('Torsion angles for '+TorName+' in '+phaseName)
2755    Plot.set_xlabel('angle',fontsize=16)
2756    Plot.set_ylabel('Energy',fontsize=16)
2757    Page.canvas.draw()
2758   
2759################################################################################
2760##### PlotRama
2761################################################################################
2762
2763def PlotRama(G2frame,phaseName,Rama,RamaName,Names=[],PhiPsi=[],Coeff=[]):
2764    'needs a doc string'
2765
2766    global names
2767    names = Names
2768    rama = np.log(2*Rama+1.)
2769    ramaMax = np.max(rama)
2770    rama = np.reshape(rama,(45,45))
2771    global Phi,Psi
2772    Phi = []
2773    Psi = []
2774
2775    def OnPlotKeyPress(event):
2776        newPlot = False
2777        if event.key == 's':
2778            choice = [m for m in mpl.cm.datad.keys() if not m.endswith("_r")]
2779            choice.sort()
2780            dlg = wx.SingleChoiceDialog(G2frame,'Select','Color scheme',choice)
2781            if dlg.ShowModal() == wx.ID_OK:
2782                sel = dlg.GetSelection()
2783                G2frame.RamaColor = choice[sel]
2784            else:
2785                G2frame.RamaColor = 'RdYlGn'
2786            dlg.Destroy()
2787        PlotRama(G2frame,phaseName,Rama)
2788
2789    def OnPick(event):
2790        ind = event.ind[0]
2791        msg = 'atoms:'+names[ind]
2792        Page.canvas.SetToolTipString(msg)
2793        try:
2794            page = G2frame.dataDisplay.GetSelection()
2795        except:
2796            return
2797        if G2frame.dataDisplay.GetPageText(page) == 'Ramachandran restraints':
2798            ramaGrid = G2frame.dataDisplay.GetPage(page).Ramas
2799            ramaGrid.ClearSelection()
2800            for row in range(ramaGrid.GetNumberRows()):
2801                if names[ind] in ramaGrid.GetCellValue(row,0):
2802                    ramaGrid.SelectRow(row)
2803            ramaGrid.ForceRefresh()
2804
2805    def OnMotion(event):
2806        if event.xdata and event.ydata:                 #avoid out of frame errors
2807            xpos = event.xdata
2808            ypos = event.ydata
2809            msg = 'phi/psi: %5.3f %5.3f'%(xpos,ypos)
2810            Page.canvas.SetToolTipString(msg)
2811           
2812    try:
2813        plotNum = G2frame.G2plotNB.plotList.index('Ramachandran')
2814        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2815        Page.figure.clf()
2816        Plot = Page.figure.gca()
2817        if not Page.IsShown():
2818            Page.Show()
2819    except ValueError:
2820        Plot = G2frame.G2plotNB.addMpl('Ramachandran').gca()
2821        plotNum = G2frame.G2plotNB.plotList.index('Ramachandran')
2822        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2823        Page.canvas.mpl_connect('pick_event', OnPick)
2824        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2825        Page.canvas.mpl_connect('key_press_event', OnPlotKeyPress)
2826
2827    Page.Choice = ['s: to change colors']
2828    Page.keyPress = OnPlotKeyPress
2829    Page.SetFocus()
2830    G2frame.G2plotNB.status.SetFields(['','Use mouse LB to identify phi/psi atoms'])
2831    acolor = mpl.cm.get_cmap(G2frame.RamaColor)
2832    if RamaName == 'All' or '-1' in RamaName:
2833        if len(Coeff): 
2834            X,Y = np.meshgrid(np.linspace(-180.,180.,45),np.linspace(-180.,180.,45))
2835            Z = np.array([-G2mth.calcRamaEnergy(x,y,Coeff)[1] for x,y in zip(X.flatten(),Y.flatten())])
2836            Plot.contour(X,Y,np.reshape(Z,(45,45)))
2837        Img = Plot.imshow(rama,aspect='equal',cmap=acolor,interpolation='nearest',
2838            extent=[-180,180,-180,180],origin='lower')
2839        if len(PhiPsi):
2840            Phi,Psi = PhiPsi.T
2841            Phi = np.where(Phi>180.,Phi-360.,Phi)
2842            Psi = np.where(Psi>180.,Psi-360.,Psi)
2843            Plot.plot(Phi,Psi,'ro',picker=5)
2844        Plot.set_xlim((-180.,180.))
2845        Plot.set_ylim((-180.,180.))
2846    else:
2847        if len(Coeff): 
2848            X,Y = np.meshgrid(np.linspace(0.,360.,45),np.linspace(0.,360.,45))
2849            Z = np.array([-G2mth.calcRamaEnergy(x,y,Coeff)[1] for x,y in zip(X.flatten(),Y.flatten())])
2850            Plot.contour(X,Y,np.reshape(Z,(45,45)))
2851        Img = Plot.imshow(rama,aspect='equal',cmap=acolor,interpolation='nearest',
2852            extent=[0,360,0,360],origin='lower')
2853        if len(PhiPsi):
2854            Phi,Psi = PhiPsi.T
2855            Plot.plot(Phi,Psi,'ro',picker=5)
2856        Plot.set_xlim((0.,360.))
2857        Plot.set_ylim((0.,360.))
2858    Plot.set_title('Ramachandran for '+RamaName+' in '+phaseName)
2859    Plot.set_xlabel(r'$\phi$',fontsize=16)
2860    Plot.set_ylabel(r'$\psi$',fontsize=16)
2861    colorBar = Page.figure.colorbar(Img)
2862    Page.canvas.draw()
2863
2864
2865################################################################################
2866##### PlotSeq
2867################################################################################
2868def PlotSelectedSequence(G2frame,ColumnList,TableGet,SelectX,fitnum=None,fitvals=None):
2869    '''Plot a result from a sequential refinement
2870
2871    :param wx.Frame G2frame: The main GSAS-II tree "window"
2872    :param list ColumnList: list of int values corresponding to columns
2873      selected as y values
2874    :param function TableGet: a function that takes a column number
2875      as argument and returns the column label, the values and there ESDs (or None)
2876    :param function SelectX: a function that returns a selected column
2877      number (or None) as the X-axis selection
2878    '''
2879    global Title,xLabel,yLabel
2880    xLabel = yLabel = Title = ''
2881    def OnMotion(event):
2882        if event.xdata and event.ydata:                 #avoid out of frame errors
2883            xpos = event.xdata
2884            ypos = event.ydata
2885            msg = '%5.3f %.6g'%(xpos,ypos)
2886            Page.canvas.SetToolTipString(msg)
2887
2888    def OnKeyPress(event):
2889        global Title,xLabel,yLabel
2890        if event.key == 's':
2891            G2frame.seqXaxis = G2frame.seqXselect()
2892            Draw()
2893        elif event.key == 't':
2894            dlg = G2gd.MultiStringDialog(G2frame,'Set titles & labels',[' Title ',' x-Label ',' y-Label '],
2895                [Title,xLabel,yLabel])
2896            if dlg.Show():
2897                Title,xLabel,yLabel = dlg.GetValues()
2898            dlg.Destroy()
2899            Draw()
2900           
2901    def Draw():
2902        global Title,xLabel,yLabel
2903        Page.SetFocus()
2904        G2frame.G2plotNB.status.SetStatusText('press s to select X axis, t to change titles',1)
2905        Plot.clear()
2906        if G2frame.seqXaxis is not None:   
2907            xName,X,Xsig = Page.seqTableGet(G2frame.seqXaxis)
2908        else:
2909            X = np.arange(0,G2frame.SeqTable.GetNumberRows(),1)
2910            xName = 'Data sequence number'
2911        for col in Page.seqYaxisList:
2912            name,Y,sig = Page.seqTableGet(col)
2913            if sig:
2914                if G2frame.seqReverse and not G2frame.seqXaxis:
2915                    Y = Y[::-1]
2916                    sig = sig[::-1]
2917                Plot.errorbar(X,Y,yerr=sig,label=name)
2918            else:
2919                if G2frame.seqReverse and not G2frame.seqXaxis:
2920                    Y = Y[::-1]
2921                Plot.plot(X,Y)
2922                Plot.plot(X,Y,'o',label=name)
2923        if Page.fitvals:
2924            if G2frame.seqReverse and not G2frame.seqXaxis:
2925                Page.fitvals = Page.fitvals[::-1]
2926            Plot.plot(X,Page.fitvals,label='Fit')
2927           
2928        Plot.legend(loc='best')
2929        print Title,xLabel,yLabel
2930        if Title:
2931            Plot.set_title(Title)
2932        else:
2933            Plot.set_title('')
2934        if xLabel:
2935            Plot.set_xlabel(xLabel)
2936        else:
2937            Plot.set_xlabel(xName)
2938        if yLabel:
2939            Plot.set_ylabel(yLabel)
2940        else:
2941            Plot.set_ylabel('Parameter values')
2942        Page.canvas.draw()           
2943           
2944    G2frame.seqXselect = SelectX
2945    try:
2946        G2frame.seqXaxis
2947    except:
2948        G2frame.seqXaxis = None
2949
2950    if fitnum is None:
2951        label = 'Sequential refinement'
2952    else:
2953        label = 'Parametric fit #'+str(fitnum+1)
2954    try:
2955        plotNum = G2frame.G2plotNB.plotList.index(label)
2956        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2957        Page.figure.clf()
2958        Plot = Page.figure.gca()
2959        if not Page.IsShown():
2960            Page.Show()
2961    except ValueError:
2962        Plot = G2frame.G2plotNB.addMpl(label).gca()
2963        plotNum = G2frame.G2plotNB.plotList.index(label)
2964        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
2965        Page.canvas.mpl_connect('key_press_event', OnKeyPress)
2966        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
2967    Page.Choice = ['s - select x-axis','t - change titles',]
2968    Page.keyPress = OnKeyPress
2969    Page.seqYaxisList = ColumnList
2970    Page.seqTableGet = TableGet
2971    Page.fitvals = fitvals
2972       
2973    Draw()
2974    G2frame.G2plotNB.nb.SetSelection(plotNum) # raises plot tab
2975               
2976################################################################################
2977##### PlotExposedImage & PlotImage
2978################################################################################
2979           
2980def PlotExposedImage(G2frame,newPlot=False,event=None):
2981    '''General access module for 2D image plotting
2982    '''
2983    plotNo = G2frame.G2plotNB.nb.GetSelection()
2984    if G2frame.G2plotNB.nb.GetPageText(plotNo) == '2D Powder Image':
2985        PlotImage(G2frame,newPlot,event,newImage=True)
2986    elif G2frame.G2plotNB.nb.GetPageText(plotNo) == '2D Integration':
2987        PlotIntegration(G2frame,newPlot,event)
2988
2989def OnStartMask(G2frame):
2990    '''Initiate the start of a Frame or Polygon map
2991
2992    :param wx.Frame G2frame: The main GSAS-II tree "window"
2993    :param str eventkey: a single letter ('f' or 'p') that
2994      determines what type of mask is created.   
2995    '''
2996    Masks = G2frame.PatternTree.GetItemPyData(
2997        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
2998    if G2frame.MaskKey == 'f':
2999        Masks['Frames'] = []
3000    elif G2frame.MaskKey == 'p':
3001        Masks['Polygons'].append([])
3002    elif G2frame.MaskKey == 's':
3003        Masks['Points'].append([])
3004    elif G2frame.MaskKey == 'a':
3005        Masks['Arcs'].append([])
3006    elif G2frame.MaskKey == 'r':
3007        Masks['Rings'].append([])
3008    G2imG.UpdateMasks(G2frame,Masks)
3009    PlotImage(G2frame,newImage=True)
3010   
3011def OnStartNewDzero(G2frame):
3012    '''Initiate the start of adding a new d-zero to a strain data set
3013
3014    :param wx.Frame G2frame: The main GSAS-II tree "window"
3015    :param str eventkey: a single letter ('a') that
3016      triggers the addition of a d-zero.   
3017    '''
3018    G2frame.dataFrame.GetStatusBar().SetStatusText('Add strain ring active - LB pick d-zero value',0)
3019    G2frame.PickId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Stress/Strain')
3020    data = G2frame.PatternTree.GetItemPyData(G2frame.PickId)
3021    return data
3022
3023def PlotImage(G2frame,newPlot=False,event=None,newImage=True):
3024    '''Plot of 2D detector images as contoured plot. Also plot calibration ellipses,
3025    masks, etc.
3026    '''
3027    from matplotlib.patches import Ellipse,Arc,Circle,Polygon
3028    import numpy.ma as ma
3029    Dsp = lambda tth,wave: wave/(2.*npsind(tth/2.))
3030    global Data,Masks,StrSta
3031    colors=['b','g','r','c','m','k']
3032    Data = G2frame.PatternTree.GetItemPyData(
3033        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
3034# patch
3035    if 'invert_x' not in Data:
3036        Data['invert_x'] = False
3037        Data['invert_y'] = True
3038# end patch
3039    Masks = G2frame.PatternTree.GetItemPyData(
3040        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
3041    try:    #may be absent
3042        StrSta = G2frame.PatternTree.GetItemPyData(
3043            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Stress/Strain'))
3044    except TypeError:   #is missing
3045        StrSta = {}
3046
3047    def OnImMotion(event):
3048        Page.canvas.SetToolTipString('')
3049        sizexy = Data['size']
3050        if event.xdata and event.ydata and len(G2frame.ImageZ):                 #avoid out of frame errors
3051            Page.canvas.SetToolTipString('%8.2f %8.2fmm'%(event.xdata,event.ydata))
3052            Page.canvas.SetCursor(wx.CROSS_CURSOR)
3053            item = G2frame.itemPicked
3054            pixelSize = Data['pixelSize']
3055            scalex = 1000./pixelSize[0]
3056            scaley = 1000./pixelSize[1]
3057            if item and G2frame.PatternTree.GetItemText(G2frame.PickId) == 'Image Controls':
3058                if 'Text' in str(item):
3059                    Page.canvas.SetToolTipString('%8.3f %8.3fmm'%(event.xdata,event.ydata))
3060                else:
3061                    xcent,ycent = Data['center']
3062                    xpos = event.xdata-xcent
3063                    ypos = event.ydata-ycent
3064                    tth,azm = G2img.GetTthAzm(event.xdata,event.ydata,Data)
3065                    if 'line3' in  str(item) or 'line4' in str(item) and not Data['fullIntegrate']:
3066                        Page.canvas.SetToolTipString('%6d deg'%(azm))
3067                    elif 'line1' in  str(item) or 'line2' in str(item):
3068                        Page.canvas.SetToolTipString('%8.3f deg'%(tth))                           
3069            else:
3070                xpos = event.xdata
3071                ypos = event.ydata
3072                xpix = xpos*scalex
3073                ypix = ypos*scaley
3074                Int = 0
3075                if (0 <= xpix <= sizexy[0]) and (0 <= ypix <= sizexy[1]):
3076                    Int = G2frame.ImageZ[ypix][xpix]
3077                tth,azm,D,dsp = G2img.GetTthAzmDsp(xpos,ypos,Data)
3078                Q = 2.*math.pi/dsp
3079                fields = ['','Detector 2-th =%9.3fdeg, dsp =%9.3fA, Q = %6.5fA-1, azm = %7.2fdeg, I = %6d'%(tth,dsp,Q,azm,Int)]
3080                if G2frame.MaskKey in ['p','f']:
3081                    fields[1] = 'Polygon/frame mask pick - LB next point, RB close polygon'
3082                elif G2frame.StrainKey:
3083                    fields[0] = 'd-zero pick active'
3084                G2frame.G2plotNB.status.SetFields(fields)
3085
3086    def OnImPlotKeyPress(event):
3087        try:
3088            PickName = G2frame.PatternTree.GetItemText(G2frame.PickId)
3089        except TypeError:
3090            return
3091        if PickName == 'Masks':
3092            if event.key in ['l','p','f','s','a','r']:
3093                G2frame.MaskKey = event.key
3094                OnStartMask(G2frame)
3095                PlotImage(G2frame,newPlot=False)
3096               
3097        elif PickName == 'Stress/Strain':
3098            if event.key in ['a',]:
3099                G2frame.StrainKey = event.key
3100                StrSta = OnStartNewDzero(G2frame)
3101                PlotImage(G2frame,newPlot=False)
3102               
3103        elif PickName == 'Image Controls':
3104            if event.key in ['c',]:
3105                Xpos = event.xdata
3106                if not Xpos:            #got point out of frame
3107                    return
3108                Ypos = event.ydata
3109                dlg = wx.MessageDialog(G2frame,'Are you sure you want to change the center?',
3110                    'Center change',style=wx.OK|wx.CANCEL)
3111                try:
3112                    if dlg.ShowModal() == wx.ID_OK:
3113                        print 'move center to: ',Xpos,Ypos
3114                        Data['center'] = [Xpos,Ypos]
3115                        G2imG.UpdateImageControls(G2frame,Data,Masks)
3116                        PlotImage(G2frame,newPlot=False)
3117                finally:
3118                    dlg.Destroy()
3119                return
3120            elif event.key == 'l':
3121                G2frame.logPlot = not G2frame.logPlot
3122            elif event.key in ['x',]:
3123                Data['invert_x'] = not Data['invert_x']
3124            elif event.key in ['y',]:
3125                Data['invert_y'] = not Data['invert_y']
3126            PlotImage(G2frame,newPlot=True)
3127           
3128    def OnKeyBox(event):
3129        if G2frame.G2plotNB.nb.GetSelection() == G2frame.G2plotNB.plotList.index('2D Powder Image'):
3130            event.key = cb.GetValue()[0]
3131            cb.SetValue(' key press')
3132            if event.key in ['l','s','a','r','p','x','y']:
3133                wx.CallAfter(OnImPlotKeyPress,event)
3134        Page.canvas.SetFocus() # redirect the Focus from the button back to the plot
3135                       
3136    def OnImPick(event):
3137        if G2frame.PatternTree.GetItemText(G2frame.PickId) not in ['Image Controls','Masks']:
3138            return
3139        if G2frame.itemPicked is not None: return
3140        G2frame.itemPicked = event.artist
3141        G2frame.mousePicked = event.mouseevent
3142       
3143    def OnImRelease(event):
3144        try:
3145            PickName = G2frame.PatternTree.GetItemText(G2frame.PickId)
3146        except TypeError:
3147            return
3148        if PickName not in ['Image Controls','Masks','Stress/Strain']:
3149            return
3150        pixelSize = Data['pixelSize']
3151        scalex = 1000./pixelSize[0]
3152        scaley = 1000./pixelSize[1]
3153#        pixLimit = Data['pixLimit']    #can be too tight
3154        pixLimit = 20       #this makes the search box 40x40 pixels
3155        if G2frame.itemPicked is None and PickName == 'Image Controls' and len(G2frame.ImageZ):
3156            Xpos = event.xdata
3157            if not (Xpos and G2frame.ifGetRing):                   #got point out of frame
3158                return
3159            Ypos = event.ydata
3160            if Ypos and not Page.toolbar._active:         #make sure zoom/pan not selected
3161                if event.button == 1:
3162                    Xpix = Xpos*scalex
3163                    Ypix = Ypos*scaley
3164                    xpos,ypos,I,J = G2img.ImageLocalMax(G2frame.ImageZ,pixLimit,Xpix,Ypix)
3165                    if I and J:
3166                        xpos += .5                              #shift to pixel center
3167                        ypos += .5
3168                        xpos /= scalex                          #convert to mm
3169                        ypos /= scaley
3170                        Data['ring'].append([xpos,ypos])
3171                elif event.button == 3:
3172                    G2frame.dataFrame.GetStatusBar().SetStatusText('Calibrating...',0)
3173                    if G2img.ImageCalibrate(G2frame,Data):
3174                        G2frame.dataFrame.GetStatusBar().SetStatusText('Calibration successful - Show ring picks to check',0)
3175                        print 'Calibration successful'
3176                    else:
3177                        G2frame.dataFrame.GetStatusBar().SetStatusText('Calibration failed - Show ring picks to diagnose',0)
3178                        print 'Calibration failed'
3179                    G2frame.ifGetRing = False
3180                    G2imG.UpdateImageControls(G2frame,Data,Masks)
3181                    return
3182                PlotImage(G2frame,newImage=False)
3183            return
3184        elif G2frame.MaskKey and PickName == 'Masks':
3185            Xpos,Ypos = [event.xdata,event.ydata]
3186            if not Xpos or not Ypos or Page.toolbar._active:  #got point out of frame or zoom/pan selected
3187                return
3188            if G2frame.MaskKey == 's' and event.button == 1:
3189                Masks['Points'][-1] = [Xpos,Ypos,1.]
3190                G2frame.MaskKey = ''               
3191            elif G2frame.MaskKey == 'r' and event.button == 1:
3192                tth = G2img.GetTth(Xpos,Ypos,Data)
3193                Masks['Rings'][-1] = [tth,0.1]
3194                G2frame.MaskKey = ''               
3195            elif G2frame.MaskKey == 'a' and event.button == 1:
3196                tth,azm = G2img.GetTthAzm(Xpos,Ypos,Data)
3197                azm = int(azm)               
3198                Masks['Arcs'][-1] = [tth,[azm-5,azm+5],0.1]
3199                G2frame.MaskKey = ''               
3200            elif G2frame.MaskKey =='p':
3201                polygon = Masks['Polygons'][-1]
3202                if len(polygon) > 2 and event.button == 3:
3203                    x0,y0 = polygon[0]
3204                    polygon.append([x0,y0])
3205                    G2frame.MaskKey = ''
3206                    G2frame.G2plotNB.status.SetFields(['','Polygon closed'])
3207                else:
3208                    G2frame.G2plotNB.status.SetFields(['','New polygon point: %.1f,%.1f'%(Xpos,Ypos)])
3209                    polygon.append([Xpos,Ypos])
3210            elif G2frame.MaskKey =='f':
3211                frame = Masks['Frames']
3212                if len(frame) > 2 and event.button == 3:
3213                    x0,y0 = frame[0]
3214                    frame.append([x0,y0])
3215                    G2frame.MaskKey = ''
3216                    G2frame.G2plotNB.status.SetFields(['','Frame closed'])
3217                else:
3218                    G2frame.G2plotNB.status.SetFields(['','New frame point: %.1f,%.1f'%(Xpos,Ypos)])
3219                    frame.append([Xpos,Ypos])
3220            G2imG.UpdateMasks(G2frame,Masks)
3221            PlotImage(G2frame,newImage=False)
3222        elif PickName == 'Stress/Strain' and G2frame.StrainKey:
3223            Xpos,Ypos = [event.xdata,event.ydata]
3224            if not Xpos or not Ypos or Page.toolbar._active:  #got point out of frame or zoom/pan selected
3225                return
3226            dsp = G2img.GetDsp(Xpos,Ypos,Data)
3227            StrSta['d-zero'].append({'Dset':dsp,'Dcalc':0.0,'pixLimit':10,'cutoff':1.0,
3228                'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]],'Emat':[1.0,1.0,1.0]})
3229            R,r = G2img.MakeStrStaRing(StrSta['d-zero'][-1],G2frame.ImageZ,Data)
3230            if not len(R):
3231                del StrSta['d-zero'][-1]
3232                G2frame.ErrorDialog('Strain peak selection','WARNING - No points found for this ring selection')
3233            StrSta['d-zero'] = G2mth.sortArray(StrSta['d-zero'],'Dset',reverse=True)
3234            G2frame.StrainKey = ''
3235            G2imG.UpdateStressStrain(G2frame,StrSta)
3236            PlotStrain(G2frame,StrSta)
3237            PlotImage(G2frame,newPlot=False)           
3238        else:
3239            Xpos,Ypos = [event.xdata,event.ydata]
3240            if not Xpos or not Ypos or Page.toolbar._active:  #got point out of frame or zoom/pan selected
3241                return
3242            if G2frame.ifGetRing:                          #delete a calibration ring pick
3243                xypos = [Xpos,Ypos]
3244                rings = Data['ring']
3245                for ring in rings:
3246                    if np.allclose(ring,xypos,.01,0):
3247                        rings.remove(ring)
3248            else:
3249                tth,azm,dsp = G2img.GetTthAzmDsp(Xpos,Ypos,Data)[:3]
3250                itemPicked = str(G2frame.itemPicked)
3251                if 'Line2D' in itemPicked and PickName == 'Image Controls':
3252                    if 'line1' in itemPicked:
3253                        Data['IOtth'][0] = max(tth,0.001)
3254                    elif 'line2' in itemPicked:
3255                        Data['IOtth'][1] = tth
3256                    elif 'line3' in itemPicked:
3257                        Data['LRazimuth'][0] = int(azm)
3258                    elif 'line4' in itemPicked and not Data['fullIntegrate']:
3259                        Data['LRazimuth'][1] = int(azm)
3260                   
3261                    Data['LRazimuth'][0] %= 360
3262                    Data['LRazimuth'][1] %= 360
3263                    if Data['LRazimuth'][0] > Data['LRazimuth'][1]:
3264                        Data['LRazimuth'][1] += 360                       
3265                    if Data['fullIntegrate']:
3266                        Data['LRazimuth'][1] = Data['LRazimuth'][0]+360
3267                       
3268                    if  Data['IOtth'][0] > Data['IOtth'][1]:
3269                        Data['IOtth'][0],Data['IOtth'][1] = Data['IOtth'][1],Data['IOtth'][0]
3270                       
3271                    G2frame.InnerTth.SetValue("%8.2f" % (Data['IOtth'][0]))
3272                    G2frame.OuterTth.SetValue("%8.2f" % (Data['IOtth'][1]))
3273                    G2frame.Lazim.SetValue("%6d" % (Data['LRazimuth'][0]))
3274                    G2frame.Razim.SetValue("%6d" % (Data['LRazimuth'][1]))
3275                elif 'Circle' in itemPicked and PickName == 'Masks':
3276                    spots = Masks['Points']
3277                    newPos = itemPicked.split(')')[0].split('(')[2].split(',')
3278                    newPos = np.array([float(newPos[0]),float(newPos[1])])
3279                    for spot in spots:
3280                        if np.allclose(np.array([spot[:2]]),newPos):
3281                            spot[:2] = Xpos,Ypos
3282                    G2imG.UpdateMasks(G2frame,Masks)
3283                elif 'Line2D' in itemPicked and PickName == 'Masks':
3284                    Obj = G2frame.itemPicked.findobj()
3285                    rings = Masks['Rings']
3286                    arcs = Masks['Arcs']
3287                    polygons = Masks['Polygons']
3288                    frame = Masks['Frames']
3289                    for ring in G2frame.ringList:
3290                        if Obj == ring[0]:
3291                            rN = ring[1]
3292                            if ring[2] == 'o':
3293                                rings[rN][0] = G2img.GetTth(Xpos,Ypos,Data)-rings[rN][1]/2.
3294                            else:
3295                                rings[rN][0] = G2img.GetTth(Xpos,Ypos,Data)+rings[rN][1]/2.
3296                    for arc in G2frame.arcList:
3297                        if Obj == arc[0]:
3298                            aN = arc[1]
3299                            if arc[2] == 'o':
3300                                arcs[aN][0] = G2img.GetTth(Xpos,Ypos,Data)-arcs[aN][2]/2
3301                            elif arc[2] == 'i':
3302                                arcs[aN][0] = G2img.GetTth(Xpos,Ypos,Data)+arcs[aN][2]/2
3303                            elif arc[2] == 'l':
3304                                arcs[aN][1][0] = int(G2img.GetAzm(Xpos,Ypos,Data))
3305                            else:
3306                                arcs[aN][1][1] = int(G2img.GetAzm(Xpos,Ypos,Data))
3307                    for poly in G2frame.polyList:   #merging points problem here?
3308                        if Obj == poly[0]:
3309                            ind = G2frame.itemPicked.contains(G2frame.mousePicked)[1]['ind'][0]
3310                            oldPos = np.array([G2frame.mousePicked.xdata,G2frame.mousePicked.ydata])
3311                            pN = poly[1]
3312                            for i,xy in enumerate(polygons[pN]):
3313                                if np.allclose(np.array([xy]),oldPos,atol=1.0):
3314                                    if event.button == 1:
3315                                        polygons[pN][i] = Xpos,Ypos
3316                                    elif event.button == 3:
3317                                        polygons[pN].insert(i,[Xpos,Ypos])
3318                                        break
3319                    if frame:
3320                        oldPos = np.array([G2frame.mousePicked.xdata,G2frame.mousePicked.ydata])
3321                        for i,xy in enumerate(frame):
3322                            if np.allclose(np.array([xy]),oldPos,atol=1.0):
3323                                if event.button == 1:
3324                                    frame[i] = Xpos,Ypos
3325                                elif event.button == 3:
3326                                    frame.insert(i,[Xpos,Ypos])
3327                                    break
3328                    G2imG.UpdateMasks(G2frame,Masks)
3329#                else:                  #keep for future debugging
3330#                    print str(G2frame.itemPicked),event.xdata,event.ydata,event.button
3331            PlotImage(G2frame,newImage=True)
3332            G2frame.itemPicked = None
3333           
3334    try:
3335        plotNum = G2frame.G2plotNB.plotList.index('2D Powder Image')
3336        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3337        if not newPlot:
3338            Plot = Page.figure.gca()          #get previous powder plot & get limits
3339            xylim = Plot.get_xlim(),Plot.get_ylim()
3340        if newImage:
3341            Page.figure.clf()
3342            Plot = Page.figure.gca()          #get a fresh plot after clf()
3343    except ValueError:
3344        Plot = G2frame.G2plotNB.addMpl('2D Powder Image').gca()
3345        plotNum = G2frame.G2plotNB.plotList.index('2D Powder Image')
3346        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3347        Page.canvas.mpl_connect('key_press_event', OnImPlotKeyPress)
3348        Page.canvas.mpl_connect('motion_notify_event', OnImMotion)
3349        Page.canvas.mpl_connect('pick_event', OnImPick)
3350        Page.canvas.mpl_connect('button_release_event', OnImRelease)
3351        xylim = []
3352    Page.Choice = None
3353    if not event:                       #event from GUI TextCtrl - don't want focus to change to plot!!!
3354        Page.SetFocus()
3355    Title = G2frame.PatternTree.GetItemText(G2frame.Image)[4:]
3356    G2frame.G2plotNB.status.DestroyChildren()
3357    if G2frame.logPlot:
3358        Title = 'log('+Title+')'
3359    Plot.set_title(Title)
3360    try:
3361        if G2frame.PatternTree.GetItemText(G2frame.PickId) in ['Image Controls',]:
3362            Page.Choice = (' key press','l: log(I) on','x: flip x','y: flip y',)
3363            if G2frame.logPlot:
3364                Page.Choice[1] = 'l: log(I) off'
3365            Page.keyPress = OnImPlotKeyPress
3366        elif G2frame.PatternTree.GetItemText(G2frame.PickId) in ['Masks',]:
3367            Page.Choice = (' key press','l: log(I) on','s: spot mask','a: arc mask','r: ring mask',
3368                'p: polygon mask','f: frame mask',)
3369            if G2frame.logPlot:
3370                Page.Choice[1] = 'l: log(I) off'
3371            Page.keyPress = OnImPlotKeyPress
3372        elif G2frame.PatternTree.GetItemText(G2frame.PickId) in ['Stress/Strain',]:
3373            Page.Choice = (' key press','a: add new ring',)
3374            Page.keyPress = OnImPlotKeyPress
3375    except TypeError:
3376        pass
3377    size,imagefile = G2frame.PatternTree.GetItemPyData(G2frame.Image)
3378    dark = Data.get('dark image',[0,''])
3379    if dark[0]:
3380        darkfile = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame, 
3381            G2frame.root,dark[0]))[1]
3382    if imagefile != G2frame.oldImagefile:
3383        imagefile = G2IO.CheckImageFile(G2frame,imagefile)
3384        if not imagefile:
3385            G2frame.G2plotNB.Delete('2D Powder Image')
3386            return
3387        G2frame.PatternTree.SetItemPyData(G2frame.Image,[size,imagefile])
3388        G2frame.ImageZ = G2IO.GetImageData(G2frame,imagefile,imageOnly=True)
3389        if dark[0]:
3390            darkImg = G2IO.GetImageData(G2frame,darkfile,imageOnly=True)
3391            G2frame.ImageZ += dark[1]*darkImg
3392        G2frame.oldImagefile = imagefile
3393
3394    imScale = 1
3395    if len(G2frame.ImageZ) > 1024:
3396        imScale = len(G2frame.ImageZ)/1024
3397    sizexy = Data['size']
3398    pixelSize = Data['pixelSize']
3399    scalex = 1000./pixelSize[0]
3400    scaley = 1000./pixelSize[1]
3401    Xmax = sizexy[0]*pixelSize[0]/1000.
3402    Ymax = sizexy[1]*pixelSize[1]/1000.
3403    xlim = (0,Xmax)
3404    ylim = (Ymax,0)
3405    Imin,Imax = Data['range'][1]
3406    acolor = mpl.cm.get_cmap(Data['color'])
3407    xcent,ycent = Data['center']
3408    Plot.set_xlabel('Image x-axis, mm',fontsize=12)
3409    Plot.set_ylabel('Image y-axis, mm',fontsize=12)
3410    #do threshold mask - "real" mask - others are just bondaries
3411    Zlim = Masks['Thresholds'][1]
3412    wx.BeginBusyCursor()
3413    try:
3414           
3415        if newImage:                   
3416            Imin,Imax = Data['range'][1]
3417            MA = ma.masked_greater(ma.masked_less(G2frame.ImageZ,Zlim[0]),Zlim[1])
3418            MaskA = ma.getmaskarray(MA)
3419            A = G2img.ImageCompress(MA,imScale)
3420            AM = G2img.ImageCompress(MaskA,imScale)
3421            if G2frame.logPlot:
3422                A = np.where(A>Imin,np.where(A<Imax,A,0),0)
3423                A = np.where(A>0,np.log(A),0)
3424                AM = np.where(AM>0,np.log(AM),0)
3425                Imin,Imax = [np.amin(A),np.amax(A)]
3426            ImgM = Plot.imshow(AM,aspect='equal',cmap='Reds',
3427                interpolation='nearest',vmin=0,vmax=2,extent=[0,Xmax,Ymax,0])
3428            Img = Plot.imshow(A,aspect='equal',cmap=acolor,
3429                interpolation='nearest',vmin=Imin,vmax=Imax,extent=[0,Xmax,Ymax,0])
3430   
3431        Plot.plot(xcent,ycent,'x')
3432        #G2frame.PatternTree.GetItemText(item)
3433        if Data['showLines']:
3434            LRAzim = Data['LRazimuth']                  #NB: integers
3435            Nazm = Data['outAzimuths']
3436            delAzm = float(LRAzim[1]-LRAzim[0])/Nazm
3437            AzmthOff = Data['azmthOff']
3438            IOtth = Data['IOtth']
3439            wave = Data['wavelength']
3440            dspI = wave/(2.0*sind(IOtth[0]/2.0))
3441            ellI = G2img.GetEllipse(dspI,Data)           #=False if dsp didn't yield an ellipse (ugh! a parabola or a hyperbola)
3442            dspO = wave/(2.0*sind(IOtth[1]/2.0))
3443            ellO = G2img.GetEllipse(dspO,Data)           #Ditto & more likely for outer ellipse
3444            Azm = np.array(range(LRAzim[0],LRAzim[1]+1))-AzmthOff
3445            if ellI:
3446                xyI = []
3447                for azm in Azm:
3448                    xy = G2img.GetDetectorXY(dspI,azm,Data)
3449                    if np.any(xy):
3450                        xyI.append(xy)
3451                if len(xyI):
3452                    xyI = np.array(xyI)
3453                    arcxI,arcyI = xyI.T
3454                    Plot.plot(arcxI,arcyI,picker=3)
3455            if ellO:
3456                xyO = []
3457                for azm in Azm:
3458                    xy = G2img.GetDetectorXY(dspO,azm,Data)
3459                    if np.any(xy):
3460                        xyO.append(xy)
3461                if len(xyO):
3462                    xyO = np.array(xyO)
3463                    arcxO,arcyO = xyO.T               
3464                    Plot.plot(arcxO,arcyO,picker=3)
3465            if ellO and ellI:
3466                Plot.plot([arcxI[0],arcxO[0]],[arcyI[0],arcyO[0]],picker=3)
3467                Plot.plot([arcxI[-1],arcxO[-1]],[arcyI[-1],arcyO[-1]],picker=3)
3468            for i in range(Nazm):
3469                cake = LRAzim[0]+i*delAzm-AzmthOff
3470                if Data['centerAzm']:
3471                    cake += delAzm/2.
3472                ind = np.searchsorted(Azm,cake)
3473                Plot.plot([arcxI[ind],arcxO[ind]],[arcyI[ind],arcyO[ind]],color='k',dashes=(5,5))
3474                   
3475        if G2frame.PatternTree.GetItemText(G2frame.PickId) in 'Image Controls':
3476            for xring,yring in Data['ring']:
3477                Plot.plot(xring,yring,'r+',picker=3)
3478            if Data['setRings']:
3479                N = 0
3480                for ring in Data['rings']:
3481                    xring,yring = np.array(ring).T[:2]
3482                    Plot.plot(xring,yring,'.',color=colors[N%6])
3483                    N += 1           
3484            for ellipse in Data['ellipses']:      #what about hyperbola?
3485                cent,phi,[width,height],col = ellipse
3486                if width > 0:       #ellipses
3487                    Plot.add_artist(Ellipse([cent[0],cent[1]],2*width,2*height,phi,ec=col,fc='none'))
3488                    Plot.text(cent[0],cent[1],'+',color=col,ha='center',va='center')
3489        if G2frame.PatternTree.GetItemText(G2frame.PickId) in 'Stress/Strain':
3490            for N,ring in enumerate(StrSta['d-zero']):
3491                xring,yring = ring['ImxyObs']
3492                Plot.plot(xring,yring,colors[N%6]+'.')
3493        #masks - mask lines numbered after integration limit lines
3494        spots = Masks['Points']
3495        rings = Masks['Rings']
3496        arcs = Masks['Arcs']
3497        polygons = Masks['Polygons']
3498        if 'Frames' not in Masks:
3499            Masks['Frames'] = []
3500        frame = Masks['Frames']
3501        for spot in spots:
3502            if spot:
3503                x,y,d = spot
3504                Plot.add_artist(Circle((x,y),radius=d/2,fc='none',ec='r',picker=3))
3505        G2frame.ringList = []
3506        for iring,ring in enumerate(rings):
3507            if ring:
3508                tth,thick = ring
3509                wave = Data['wavelength']
3510                xy1 = []
3511                xy2 = []
3512                Azm = np.linspace(0,362,181)
3513                for azm in Azm:
3514                    xy1.append(G2img.GetDetectorXY(Dsp(tth+thick/2.,wave),azm,Data))      #what about hyperbola
3515                    xy2.append(G2img.GetDetectorXY(Dsp(tth-thick/2.,wave),azm,Data))      #what about hyperbola
3516                x1,y1 = np.array(xy1).T
3517                x2,y2 = np.array(xy2).T
3518                G2frame.ringList.append([Plot.plot(x1,y1,'r',picker=3),iring,'o'])           
3519                G2frame.ringList.append([Plot.plot(x2,y2,'r',picker=3),iring,'i'])
3520        G2frame.arcList = []
3521        for iarc,arc in enumerate(arcs):
3522            if arc:
3523                tth,azm,thick = arc           
3524                wave = Data['wavelength']
3525                xy1 = []
3526                xy2 = []
3527                aR = azm[0],azm[1],azm[1]-azm[0]
3528                if azm[1]-azm[0] > 180:
3529                    aR[2] /= 2
3530                Azm = np.linspace(aR[0],aR[1],aR[2])
3531                for azm in Azm:
3532                    xy1.append(G2img.GetDetectorXY(Dsp(tth+thick/2.,wave),azm,Data))      #what about hyperbola
3533                    xy2.append(G2img.GetDetectorXY(Dsp(tth-thick/2.,wave),azm,Data))      #what about hyperbola
3534                x1,y1 = np.array(xy1).T
3535                x2,y2 = np.array(xy2).T
3536                G2frame.arcList.append([Plot.plot(x1,y1,'r',picker=3),iarc,'o'])           
3537                G2frame.arcList.append([Plot.plot(x2,y2,'r',picker=3),iarc,'i'])
3538                G2frame.arcList.append([Plot.plot([x1[0],x2[0]],[y1[0],y2[0]],'r',picker=3),iarc,'l'])
3539                G2frame.arcList.append([Plot.plot([x1[-1],x2[-1]],[y1[-1],y2[-1]],'r',picker=3),iarc,'u'])
3540        G2frame.polyList = []
3541        for ipoly,polygon in enumerate(polygons):
3542            if polygon:
3543                x,y = np.hsplit(np.array(polygon),2)
3544                G2frame.polyList.append([Plot.plot(x,y,'r+',picker=10),ipoly])
3545                Plot.plot(x,y,'r')           
3546        G2frame.frameList = []
3547        if frame:
3548            x,y = np.hsplit(np.array(frame),2)
3549            G2frame.frameList.append([Plot.plot(x,y,'g+',picker=10),0])
3550            Plot.plot(x,y,'g')           
3551        if newImage:
3552            colorBar = Page.figure.colorbar(Img)
3553        Plot.set_xlim(xlim)
3554        Plot.set_ylim(ylim)
3555        if Data['invert_x']:
3556            Plot.invert_xaxis()
3557        if Data['invert_y']:
3558            Plot.invert_yaxis()
3559        if not newPlot and xylim:
3560            Page.toolbar.push_current()
3561            Plot.set_xlim(xylim[0])
3562            Plot.set_ylim(xylim[1])
3563            xylim = []
3564            Page.toolbar.push_current()
3565            Page.toolbar.draw()
3566            # patch for wx 2.9 on Mac, to force a redraw
3567            i,j= wx.__version__.split('.')[0:2]
3568            if int(i)+int(j)/10. > 2.8 and 'wxOSX' in wx.PlatformInfo:
3569                Page.canvas.draw()
3570        else:
3571            Page.canvas.draw()
3572    finally:
3573        wx.EndBusyCursor()
3574       
3575################################################################################
3576##### PlotIntegration
3577################################################################################
3578           
3579def PlotIntegration(G2frame,newPlot=False,event=None):
3580    '''Plot of 2D image after image integration with 2-theta and azimuth as coordinates
3581    '''
3582           
3583    def OnMotion(event):
3584        Page.canvas.SetToolTipString('')
3585        Page.canvas.SetCursor(wx.CROSS_CURSOR)
3586        azm = event.ydata
3587        tth = event.xdata
3588        if azm and tth:
3589            G2frame.G2plotNB.status.SetFields(\
3590                ['','Detector 2-th =%9.3fdeg, azm = %7.2fdeg'%(tth,azm)])
3591                               
3592    try:
3593        plotNum = G2frame.G2plotNB.plotList.index('2D Integration')
3594        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3595        if not newPlot:
3596            Plot = Page.figure.gca()          #get previous plot & get limits
3597            xylim = Plot.get_xlim(),Plot.get_ylim()
3598        Page.figure.clf()
3599        Plot = Page.figure.gca()          #get a fresh plot after clf()
3600       
3601    except ValueError:
3602        Plot = G2frame.G2plotNB.addMpl('2D Integration').gca()
3603        plotNum = G2frame.G2plotNB.plotList.index('2D Integration')
3604        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3605        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
3606        Page.views = False
3607        view = False
3608    Page.Choice = None
3609    if not event:
3610        Page.SetFocus()
3611       
3612    Data = G2frame.PatternTree.GetItemPyData(
3613        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
3614    image = G2frame.Integrate[0]
3615    xsc = G2frame.Integrate[1]
3616    ysc = G2frame.Integrate[2]
3617    Imin,Imax = Data['range'][1]
3618    acolor = mpl.cm.get_cmap(Data['color'])
3619    Plot.set_title(G2frame.PatternTree.GetItemText(G2frame.Image)[4:])
3620    Plot.set_ylabel('azimuth',fontsize=12)
3621    Plot.set_xlabel('2-theta',fontsize=12)
3622    Img = Plot.imshow(image,cmap=acolor,vmin=Imin,vmax=Imax,interpolation='nearest', \
3623        extent=[ysc[0],ysc[-1],xsc[-1],xsc[0]],aspect='auto')
3624    colorBar = Page.figure.colorbar(Img)
3625#    if Data['ellipses']:           
3626#        for ellipse in Data['ellipses']:
3627#            x,y = np.array(G2img.makeIdealRing(ellipse[:3])) #skip color
3628#            tth,azm = G2img.GetTthAzm(x,y,Data)
3629##            azm = np.where(azm < 0.,azm+360,azm)
3630#            Plot.plot(tth,azm,'b,')
3631    if not newPlot:
3632        Page.toolbar.push_current()
3633        Plot.set_xlim(xylim[0])
3634        Plot.set_ylim(xylim[1])
3635        xylim = []
3636        Page.toolbar.push_current()
3637        Page.toolbar.draw()
3638    else:
3639        Page.canvas.draw()
3640               
3641################################################################################
3642##### PlotTRImage
3643################################################################################
3644           
3645def PlotTRImage(G2frame,tax,tay,taz,newPlot=False):
3646    '''a test plot routine - not normally used
3647    ''' 
3648           
3649    def OnMotion(event):
3650        Page.canvas.SetToolTipString('')
3651        Page.canvas.SetCursor(wx.CROSS_CURSOR)
3652        azm = event.xdata
3653        tth = event.ydata
3654        if azm and tth:
3655            G2frame.G2plotNB.status.SetFields(\
3656                ['','Detector 2-th =%9.3fdeg, azm = %7.2fdeg'%(tth,azm)])
3657                               
3658    try:
3659        plotNum = G2frame.G2plotNB.plotList.index('2D Transformed Powder Image')
3660        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3661        if not newPlot:
3662            Plot = Page.figure.gca()          #get previous plot & get limits
3663            xylim = Plot.get_xlim(),Plot.get_ylim()
3664        Page.figure.clf()
3665        Plot = Page.figure.gca()          #get a fresh plot after clf()
3666       
3667    except ValueError:
3668        Plot = G2frame.G2plotNB.addMpl('2D Transformed Powder Image').gca()
3669        plotNum = G2frame.G2plotNB.plotList.index('2D Transformed Powder Image')
3670        Page = G2frame.G2plotNB.nb.GetPage(plotNum)
3671        Page.canvas.mpl_connect('motion_notify_event', OnMotion)
3672        Page.views = False
3673        view = False
3674    Page.Choice = None
3675    Page.SetFocus()
3676       
3677    Data = G2frame.PatternTree.GetItemPyData(
3678        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
3679    Imin,Imax = Data['range'][1]
3680    step = (Imax-Imin)/5.
3681    V = np.arange(Imin,Imax,step)
3682    acolor = mpl.cm.get_cmap(Data['color'])
3683    Plot.set_title(G2frame.PatternTree.GetItemText(G2frame.Image)[4:])
3684    Plot.set_xlabel('azimuth',fontsize=12)
3685    Plot.set_ylabel('2-theta',fontsize=12)
3686    Plot.contour(tax,tay,taz,V,cmap=acolor)
3687    if Data['showLines']:
3688        IOtth = Data['IOtth']
3689        if Data['fullIntegrate']:
3690            LRAzim = [-180,180]
3691        else:
3692            LRAzim = Data['LRazimuth']                  #NB: integers
3693        Plot.plot([LRAzim[0],LRAzim[1]],[IOtth[0],IOtth[0]],picker=True)
3694        Plot.plot([LRAzim[0],LRAzim[1]],[IOtth[1],IOtth[1]],picker=True)
3695        if not Data['fullIntegrate']:
3696            Plot.plot([LRAzim[0],LRAzim[0]],[IOtth[0],IOtth[1]],picker=True)
3697            Plot.plot([LRAzim[1],LRAzim[1]],[IOtth[0],IOtth[1]],picker=True)
3698    if Data['setRings']:
3699        rings = np.concatenate((Data['rings']),axis=0)
3700        for xring,yring,dsp in rings:
3701            x,y = G2img.GetTthAzm(xring,yring,Data)
3702            Plot.plot(y,x,'r+')           
3703    if Data['ellipses']:           
3704        for ellipse in Data['ellipses']:
3705            ring = np.array(G2img.makeIdealRing(ellipse[:3])) #skip color
3706            x,y = np.hsplit(ring,2)
3707            tth,azm = G2img.GetTthAzm(x,y,Data)
3708            Plot.plot(azm,tth,'b,')
3709    if not newPlot:
3710        Page.toolbar.push_current()
3711        Plot.set_xlim(xylim[0])
3712        Plot.set_ylim(xylim[1])
3713        xylim = []
3714        Page.toolbar.push_current()
3715        Page.toolbar.draw()
3716    else:
3717        Page.canvas.draw()
3718       
3719################################################################################
3720##### PlotStructure
3721################################################################################
3722           
3723def PlotStructure(G2frame,data,firstCall=False):
3724    '''Crystal structure plotting package. Can show structures as balls, sticks, lines,
3725    thermal motion ellipsoids and polyhedra
3726    '''
3727
3728    def FindPeaksBonds(XYZ):
3729        rFact = data['Drawing'].get('radiusFactor',0.85)    #data['Drawing'] could be empty!
3730        Bonds = [[] for x in XYZ]
3731        for i,xyz in enumerate(XYZ):
3732            Dx = XYZ-xyz
3733            dist = np.sqrt(np.sum(np.inner(Dx,Amat)**2,axis=1))
3734            IndB = ma.nonzero(ma.masked_greater(dist,rFact*2.2))
3735            for j in IndB[0]:
3736                Bonds[i].append(Dx[j]/2.)
3737                Bonds[j].append(-Dx[j]/2.)
3738        return Bonds
3739
3740    # PlotStructure initialization here
3741    ForthirdPI = 4.0*math.pi/3.0
3742    generalData = data['General']
3743    cell = generalData['Cell'][1:7]
3744    Vol = generalData['Cell'][7:8][0]
3745    Amat,Bmat = G2lat.cell2AB(cell)         #Amat - crystal to cartesian, Bmat - inverse
3746    Gmat,gmat = G2lat.cell2Gmat(cell)
3747    A4mat = np.concatenate((np.concatenate((Amat,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
3748    B4mat = np.concatenate((np.concatenate((Bmat,[[0],[0],[0]]),axis=1),[[0,0,0,1],]),axis=0)
3749    SGData = generalData['SGData']
3750    Mydir = generalData['Mydir']
3751    atomData = data['Atoms']
3752    mapPeaks = []
3753    drawingData = data['Drawing']
3754    if not drawingData:
3755        return          #nothing setup, nothing to draw   
3756    if 'Map Peaks' in data:
3757        mapPeaks = np.array(data['Map Peaks'])
3758        peakMax = 100.
3759        if len(mapPeaks):
3760            peakMax = np.max(mapPeaks.T[0])
3761    resRBData = data['RBModels'].get('Residue',[])
3762    vecRBData = data['RBModels'].get('Vector',[])
3763    rbAtmDict = {}
3764    for rbObj in resRBData+vecRBData:
3765        exclList = ['X' for i in range(len(rbObj['Ids']))]
3766        rbAtmDict.update(dict(zip(rbObj['Ids'],exclList)))
3767    testRBObj = data.get('testRBObj',{})
3768    rbObj = testRBObj.get('rbObj',{})
3769    MCSA = data.get('MCSA',{})
3770    mcsaModels = MCSA.get('Models',[])
3771    if len(mcsaModels) > 1:
3772        XYZs,Types = G2mth.UpdateMCSAxyz(Bmat,MCSA)
3773        mcsaXYZ = []
3774        mcsaTypes = []
3775        neqv = 0
3776        for xyz,atyp in zip(XYZs,Types):
3777            equiv = G2spc.GenAtom(xyz,SGData,All=True,Move=False)
3778            neqv = max(neqv,len(equiv))
3779            for item in equiv:
3780                mcsaXYZ.append(item[0]) 
3781                mcsaTypes.append(atyp)
3782        mcsaXYZ = np.array(mcsaXYZ)
3783        mcsaTypes = np.array(mcsaTypes)
3784        nuniq = mcsaXYZ.shape[0]/neqv
3785        mcsaXYZ = np.reshape(mcsaXYZ,(nuniq,neqv,3))
3786        mcsaTypes = np.reshape(mcsaTypes,(nuniq,neqv))
3787        cent = np.fix(np.sum(mcsaXYZ+2.,axis=0)/nuniq)-2
3788        cent[0] = [0,0,0]   #make sure 1st one isn't moved
3789        mcsaXYZ = np.swapaxes(mcsaXYZ,0,1)-cent[:,np.newaxis,:]
3790        mcsaTypes = np.swapaxes(mcsaTypes,0,1)
3791        mcsaXYZ = np.reshape(mcsaXYZ,(nuniq*neqv,3))
3792        mcsaTypes = np.reshape(mcsaTypes,(nuniq*neqv))                       
3793        mcsaBonds = FindPeaksBonds(mcsaXYZ)       
3794    drawAtoms = drawingData.get('Atoms',[])
3795    mapData = {}
3796    flipData = {}
3797    rhoXYZ = []
3798    showBonds = False
3799    if 'Map' in generalData:
3800        mapData = generalData['Map']
3801        showBonds = mapData.get('Show bonds',False)
3802    if 'Flip' in generalData:
3803        flipData = generalData['Flip']                       
3804        flipData['mapRoll'] = [0,0,0]
3805    Wt = np.array([255,255,255])
3806    Rd = np.array([255,0,0])
3807    Gr = np.array([0,255,0])
3808    wxGreen = wx.Colour(0,255,0)
3809    Bl = np.array([0,0,255])
3810    Or = np.array([255,128,0])
3811    wxOrange = wx.Colour(255,128,0)
3812    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]])
3813    uEdges = np.array([
3814        [uBox[0],uBox[1]],[uBox[0],uBox[3]],[uBox[0],uBox[4]],[uBox[1],uBox[2]], 
3815        [uBox[2],uBox[3]],[uBox[1],uBox[5]],[uBox[2],uBox[6]],[uBox[3],uBox[7]], 
3816        [uBox[4],uBox[5]],[uBox[5],uBox[6]],[uBox[6],uBox[7]],[uBox[7],uBox[4]]])
3817    mD = 0.1
3818    mV = np.array([[[-mD,0,0],[mD,0,0]],[[0,-mD,0],[0,mD,0]],[[0,0,-mD],[0,0,mD]]])
3819    mapPeakVecs = np.inner(mV,Bmat)
3820
3821    uColors = [Rd,Gr,Bl,Wt, Wt,Wt,Wt,Wt, Wt,Wt,Wt,Wt]
3822    altDown = False
3823    shiftDown = False
3824    ctrlDown = False
3825   
3826    def OnKeyBox(event):
3827#        Draw()                          #make sure plot is fresh!!
3828        mode = cb.GetValue()
3829        if mode in ['jpeg','bmp','tiff',]:
3830            try:
3831                import Image as Im
3832            except ImportError:
3833                try:
3834                    from PIL import Image as Im
3835                except ImportError:
3836                    print "PIL/pillow Image module not present. Cannot save images without this"
3837                    raise Exception("PIL/pillow Image module not found")
3838            Fname = os.path.join(Mydir,generalData['Name']+'.'+mode)
3839            size = Page.canvas.GetSize()
3840            glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
3841            if mode in ['jpeg',]:
3842                Pix = glReadPixels(0,0,size[0],size[1],GL_RGBA, GL_UNSIGNED_BYTE)
3843                im = Image.new("RGBA", (size[0],size[1]))
3844            else:
3845                Pix = glReadPixels(0,0,size[0],size[1],GL_RGB, GL_UNSIGNED_BYTE)
3846                im = Image.new("RGB", (size[0],size[1]))
3847            im.fromstring(Pix)
3848            im.save(Fname,mode)
3849            cb.SetValue(' save as/key:')
3850            G2frame.G2plotNB.status.SetStatusText('Drawing saved to: '+Fname,1)
3851        else:
3852            event.key = cb.GetValue()[0]
3853            cb.SetValue(' save as/key:')
3854            wx.CallAfter(OnKey,event)
3855        Page.canvas.SetFocus() # redirect the Focus from the button back to the plot
3856
3857    def OnKey(event):           #on key UP!!
3858#        Draw()                          #make sure plot is fresh!!
3859        try:
3860            keyCode = event.GetKeyCode()
3861            if keyCode > 255:
3862                keyCode = 0
3863            key = chr(keyCode)
3864        except AttributeError:       #if from OnKeyBox above
3865            key = str(event.key).upper()
3866        indx = drawingData['selectedAtoms']
3867        cx,ct = drawingData['atomPtrs'][:2]
3868        if key in ['C']:
3869            drawingData['viewPoint'] = [[.5,.5,.5],[0,0]]
3870            drawingData['viewDir'] = [0,0,1]
3871            drawingData['oldxy'] = []
3872            V0 = np.array([0,0,1])
3873            V = np.inner(Amat,V0)
3874            V /= np.sqrt(np.sum(V**2))
3875            A = np.arccos(np.sum(V*V0))
3876            Q = G2mth.AV2Q(A,[0,1,0])
3877            drawingData['Quaternion'] = Q
3878            SetViewPointText(drawingData['viewPoint'][0])
3879            SetViewDirText(drawingData['viewDir'])
3880            Q = drawingData['Quaternion']
3881            G2frame.G2plotNB.status.SetStatusText('New quaternion: %.2f+, %.2fi+ ,%.2fj+, %.2fk'%(Q[0],Q[1],Q[2],Q[3]),1)
3882        elif key in ['N']:
3883            drawAtoms = drawingData['Atoms']
3884            if not len(drawAtoms):      #no atoms
3885                return
3886            pI = drawingData['viewPoint'][1]
3887            if not len(pI):
3888                pI = [0,0]
3889            if indx:
3890                pI[0] = indx[pI[1]]
3891                Tx,Ty,Tz = drawAtoms[pI[0]][cx:cx+3]
3892                pI[1] += 1
3893                if pI[1] >= len(indx):
3894                    pI[1] = 0
3895            else:
3896                Tx,Ty,Tz = drawAtoms[pI[0]][cx:cx+3]               
3897                pI[0] += 1
3898                if pI[0] >= len(drawAtoms):
3899                    pI[0] = 0
3900            drawingData['viewPoint'] = [[Tx,Ty,Tz],pI]
3901            SetViewPointText(drawingData['viewPoint'][0])
3902            G2frame.G2plotNB.status.SetStatusText('View point at atom '+drawAtoms[pI[0]][ct-1]+str(pI),1)
3903               
3904        elif key in ['P']:
3905            drawAtoms = drawingData['Atoms']
3906            if not len(drawAtoms):      #no atoms
3907                return
3908            pI = drawingData['viewPoint'][1]
3909            if not len(pI):
3910                pI = [0,0]
3911            if indx:
3912                pI[0] = indx[pI[1]]
3913                Tx,Ty,Tz = drawAtoms[pI[0]][cx:cx+3]
3914                pI[1] -= 1
3915                if pI[1] < 0:
3916                    pI[1] = len(indx)-1
3917            else:
3918                Tx,Ty,Tz = drawAtoms[pI[0]][cx:cx+3]               
3919                pI[0] -= 1
3920                if pI[0] < 0:
3921                    pI[0] = len(drawAtoms)-1
3922            drawingData['viewPoint'] = [[Tx,Ty,Tz],pI]
3923            SetViewPointText(drawingData['viewPoint'][0])           
3924            G2frame.G2plotNB.status.SetStatusText('View point at atom '+drawAtoms[pI[0]][ct-1]+str(pI),1)
3925        elif key in ['U','D','L','R'] and mapData['Flip'] == True:
3926            dirDict = {'U':[0,1],'D':[0,-1],'L':[-1,0],'R':[1,0]}
3927            SetMapRoll(dirDict[key])
3928            SetPeakRoll(dirDict[key])
3929            SetMapPeaksText(mapPeaks)
3930        Draw('key')
3931           
3932    def GetTruePosition(xy,Add=False):
3933        View = glGetIntegerv(GL_VIEWPORT)
3934        Proj = glGetDoublev(GL_PROJECTION_MATRIX)
3935        Model = glGetDoublev(GL_MODELVIEW_MATRIX)
3936        Zmax = 1.
3937        if Add:
3938            Indx = GetSelectedAtoms()
3939        if G2frame.dataDisplay.GetPageText(getSelection()) == 'Map peaks':
3940            for i,peak in enumerate(mapPeaks):
3941                x,y,z = peak[1:4]
3942                X,Y,Z = gluProject(x,y,z,Model,Proj,View)
3943                XY = [int(X),int(View[3]-Y)]
3944                if np.allclose(xy,XY,atol=10) and Z < Zmax:
3945                    Zmax = Z
3946                    try:
3947                        Indx.remove(i)
3948                        ClearSelectedAtoms()
3949                        for id in Indx:
3950                            SetSelectedAtoms(id,Add)
3951                    except:
3952                        SetSelectedAtoms(i,Add)
3953        else:
3954            cx = drawingData['atomPtrs'][0]
3955            for i,atom in enumerate(drawAtoms):
3956                x,y,z = atom[cx:cx+3]
3957                X,Y,Z = gluProject(x,y,z,Model,Proj,View)
3958                XY = [int(X),int(View[3]-Y)]
3959                if np.allclose(xy,XY,atol=10) and Z < Zmax:
3960                    Zmax = Z
3961                    try:
3962                        Indx.remove(i)
3963                        ClearSelectedAtoms()
3964                        for id in Indx:
3965                            SetSelectedAtoms(id,Add)
3966                    except:
3967                        SetSelectedAtoms(i,Add)
3968                                       
3969    def OnMouseDown(event):
3970        xy = event.GetPosition()
3971        if event.ShiftDown():
3972            if event.LeftIsDown():
3973                GetTruePosition(xy)
3974            elif event.RightIsDown():
3975                GetTruePosition(xy,True)
3976        else:
3977            drawingData['oldxy'] = list(xy)
3978       
3979    def OnMouseMove(event):
3980        if event.ShiftDown():           #don't want any inadvertant moves when picking
3981            return
3982        newxy = event.GetPosition()
3983                               
3984        if event.Dragging():
3985            if event.AltDown() and rbObj:
3986                if event.LeftIsDown():
3987                    SetRBRotation(newxy)
3988                    Q = rbObj['Orient'][0]
3989                    G2frame.G2plotNB.status.SetStatusText('New quaternion: %.2f+, %.2fi+ ,%.2fj+, %.2fk'%(Q[0],Q[1],Q[2],Q[3]),1)
3990                elif event.RightIsDown():
3991                    SetRBTranslation(newxy)
3992                    Tx,Ty,Tz = rbObj['Orig'][0]
3993                    G2frame.G2plotNB.status.SetStatusText('New view point: %.4f, %.4f, %.4f'%(Tx,Ty,Tz),1)
3994                elif event.MiddleIsDown():
3995                    SetRBRotationZ(newxy)
3996                    Q = rbObj['Orient'][0]
3997                    G2frame.G2plotNB.status.SetStatusText('New quaternion: %.2f+, %.2fi+ ,%.2fj+, %.2fk'%(Q[0],Q[1],Q[2],Q[3]),1)
3998                Draw('move')
3999            elif not event.ControlDown():
4000                if event.LeftIsDown():
4001                    SetRotation(newxy)
4002                    Q = drawingData['Quaternion']
4003                    G2frame.G2plotNB.status.SetStatusText('New quaternion: %.2f+, %.2fi+ ,%.2fj+, %.2fk'%(Q[0],Q[1],Q[2],Q[3]),1)
4004                elif event.RightIsDown():
4005                    SetTranslation(newxy)
4006                    Tx,Ty,Tz = drawingData['viewPoint'][0]
4007                    G2frame.G2plotNB.status.SetStatusText('New view point: %.4f, %.4f, %.4f'%(Tx,Ty,Tz),1)
4008                elif event.MiddleIsDown():
4009                    SetRotationZ(newxy)
4010                    Q = drawingData['Quaternion']
4011                    G2frame.G2plotNB.status.SetStatusText('New quaternion: %.2f+, %.2fi+ ,%.2fj+, %.2fk'%(Q[0],Q[1],Q[2],Q[3]),1)
4012                Draw('move')
4013           
4014       
4015    def OnMouseWheel(event):
4016        if event.ShiftDown():
4017            return
4018        drawingData['cameraPos'] += event.GetWheelRotation()/24.
4019        drawingData['cameraPos'] = max(10,min(500,drawingData['cameraPos']))
4020        G2frame.G2plotNB.status.SetStatusText('New camera distance: %.2f'%(drawingData['cameraPos']),1)
4021        page = getSelection()
4022        if page:
4023            if G2frame.dataDisplay.GetPageText(page) == 'Draw Options':
4024                G2frame.dataDisplay.cameraPosTxt.SetLabel('Camera Position: '+'%.2f'%(drawingData['cameraPos']))
4025                G2frame.dataDisplay.cameraSlider.SetValue(drawingData['cameraPos'])
4026            Draw('wheel')
4027       
4028    def getSelection():
4029        try:
4030            return G2frame.dataDisplay.GetSelection()
4031        except AttributeError:
4032            G2frame.G2plotNB.status.SetStatusText('Select this from Phase data window!',1)
4033            return 0
4034           
4035    def SetViewPointText(VP):
4036        page = getSelection()
4037        if page:
4038            if G2frame.dataDisplay.GetPageText(page) == 'Draw Options':
4039                G2frame.dataDisplay.viewPoint.SetValue('%.3f %.3f %.3f'%(VP[0],VP[1],VP[2]))
4040               
4041    def SetRBOrigText():
4042        page = getSelection()
4043        if page:
4044            if G2frame.dataDisplay.GetPageText(page) == 'RB Models':
4045                for i,sizer in enumerate(testRBObj['Sizers']['Xsizers']):
4046                    sizer.SetValue('%8.5f'%(testRBObj['rbObj']['Orig'][0][i]))
4047                   
4048    def SetRBOrienText():
4049        page = getSelection()
4050        if page:
4051            if G2frame.dataDisplay.GetPageText(page) == 'RB Models':
4052                for i,sizer in enumerate(testRBObj['Sizers']['Osizers']):
4053                    sizer.SetValue('%8.5f'%(testRBObj['rbObj']['Orient'][0][i]))
4054               
4055    def SetViewDirText(VD):
4056        page = getSelection()
4057        if page:
4058            if G2frame.dataDisplay.GetPageText(page) == 'Draw Options':
4059                G2frame.dataDisplay.viewDir.SetValue('%.3f %.3f %.3f'%(VD[0],VD[1],VD[2]))
4060               
4061    def SetMapPeaksText(mapPeaks):
4062        page = getSelection()
4063        if page:
4064            if G2frame.dataDisplay.GetPageText(page) == 'Map peaks':
4065                G2frame.MapPeaksTable.SetData(mapPeaks)
4066                panel = G2frame.dataDisplay.GetPage(page).GetChildren()
4067                names = [child.GetName() for child in panel]
4068                panel[names.index('grid window')].Refresh()
4069           
4070    def ClearSelectedAtoms():
4071        page = getSelection()
4072        if page:
4073            if G2frame.dataDisplay.GetPageText(page) == 'Draw Atoms':
4074                G2frame.dataDisplay.GetPage(page).ClearSelection()      #this is the Atoms grid in Draw Atoms
4075            elif G2frame.dataDisplay.GetPageText(page) == 'Map peaks':
4076                G2frame.dataDisplay.GetPage(page).ClearSelection()      #this is the Atoms grid in Atoms
4077            elif G2frame.dataDisplay.GetPageText(page) == 'Atoms':
4078                G2frame.dataDisplay.GetPage(page).ClearSelection()      #this is the Atoms grid in Atoms
4079               
4080                   
4081    def SetSelectedAtoms(ind,Add=False):
4082        page = getSelection()
4083        if page:
4084            if G2frame.dataDisplay.GetPageText(page) == 'Draw Atoms':
4085                G2frame.dataDisplay.GetPage(page).SelectRow(ind,Add)      #this is the Atoms grid in Draw Atoms
4086            elif G2frame.dataDisplay.GetPageText(page) == 'Map peaks':
4087                G2frame.dataDisplay.GetPage(page).SelectRow(ind,Add)                 
4088            elif G2frame.dataDisplay.GetPageText(page) == 'Atoms':
4089                Id = drawAtoms[ind][-3]
4090                for i,atom in enumerate(atomData):
4091                    if atom[-1] == Id:
4092                        G2frame.dataDisplay.GetPage(page).SelectRow(i)      #this is the Atoms grid in Atoms
4093                 
4094    def GetSelectedAtoms():
4095        page = getSelection()
4096        Ind = []
4097        if page:
4098            if G2frame.dataDisplay.GetPageText(page) == 'Draw Atoms':
4099                Ind = G2frame.dataDisplay.GetPage(page).GetSelectedRows()      #this is the Atoms grid in Draw Atoms
4100            elif G2frame.dataDisplay.GetPageText(page) == 'Map peaks':
4101                Ind = G2frame.dataDisplay.GetPage(page).GetSelectedRows()
4102            elif G2frame.dataDisplay.GetPageText(page) == 'Atoms':
4103                Ind = G2frame.dataDisplay.GetPage(page).GetSelectedRows()      #this is the Atoms grid in Atoms
4104        return Ind
4105                                       
4106    def SetBackground():
4107        R,G,B,A = Page.camera['backColor']
4108        glClearColor(R,G,B,A)
4109        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
4110       
4111    def SetLights():
4112        glEnable(GL_DEPTH_TEST)
4113        glShadeModel(GL_SMOOTH)
4114        glEnable(GL_LIGHTING)
4115        glEnable(GL_LIGHT0)
4116        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,0)
4117        glLightfv(GL_LIGHT0,GL_AMBIENT,[1,1,1,.8])
4118        glLightfv(GL_LIGHT0,GL_DIFFUSE,[1,1,1,1])
4119       
4120    def GetRoll(newxy,rho):
4121        Q = drawingData['Quaternion']
4122        dxy = G2mth.prodQVQ(G2mth.invQ(Q),np.inner(Bmat,newxy+[0,]))
4123        dxy = np.array(dxy*rho.shape)       
4124        roll = np.where(dxy>0.5,1,np.where(dxy<-.5,-1,0))
4125        return roll
4126               
4127    def SetMapRoll(newxy):
4128        rho = mapData['rho']
4129        roll = GetRoll(newxy,rho)
4130        mapData['rho'] = np.roll(np.roll(np.roll(rho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
4131        drawingData['oldxy'] = list(newxy)
4132       
4133    def SetPeakRoll(newxy):
4134        rho = mapData['rho']
4135        roll = GetRoll(newxy,rho)
4136        steps = 1./np.array(rho.shape)
4137        dxy = roll*steps
4138        for peak in mapPeaks:
4139            peak[1:4] += dxy
4140            peak[1:4] %= 1.
4141            peak[4] = np.sqrt(np.sum(np.inner(Amat,peak[1:4])**2))
4142               
4143    def SetTranslation(newxy):
4144#first get translation vector in screen coords.       
4145        oldxy = drawingData['oldxy']
4146        if not len(oldxy): oldxy = list(newxy)
4147        dxy = newxy-oldxy
4148        drawingData['oldxy'] = list(newxy)
4149        V = np.array([-dxy[0],dxy[1],0.])
4150#then transform to rotated crystal coordinates & apply to view point       
4151        Q = drawingData['Quaternion']
4152        V = np.inner(Bmat,G2mth.prodQVQ(G2mth.invQ(Q),V))
4153        Tx,Ty,Tz = drawingData['viewPoint'][0]
4154        Tx += V[0]*0.01
4155        Ty += V[1]*0.01
4156        Tz += V[2]*0.01
4157        drawingData['viewPoint'][0] =  Tx,Ty,Tz
4158        SetViewPointText([Tx,Ty,Tz])
4159       
4160    def SetRBTranslation(newxy):
4161#first get translation vector in screen coords.       
4162        oldxy = drawingData['oldxy']
4163        if not len(oldxy): oldxy = list(newxy)
4164        dxy = newxy-oldxy
4165        drawingData['oldxy'] = list(newxy)
4166        V = np.array([-dxy[0],dxy[1],0.])
4167#then transform to rotated crystal coordinates & apply to RB origin       
4168        Q = drawingData['Quaternion']
4169        V = np.inner(Bmat,G2mth.prodQVQ(G2mth.invQ(Q),V))
4170        Tx,Ty,Tz = rbObj['Orig'][0]
4171        Tx -= V[0]*0.01
4172        Ty -= V[1]*0.01
4173        Tz -= V[2]*0.01
4174        rbObj['Orig'][0] =  Tx,Ty,Tz
4175        SetRBOrigText()
4176       
4177    def SetRotation(newxy):
4178        'Perform a rotation in x-y space due to a left-mouse drag'
4179    #first get rotation vector in screen coords. & angle increment       
4180        oldxy = drawingData['oldxy']
4181        if not len(oldxy): oldxy = list(newxy)
4182        dxy = newxy-oldxy
4183        drawingData['oldxy'] = list(newxy)
4184        V = np.array([dxy[1],dxy[0],0.])
4185        A = 0.25*np.sqrt(dxy[0]**2+dxy[1]**2)
4186        if not A: return # nothing changed, nothing to do
4187    # next transform vector back to xtal coordinates via inverse quaternion
4188    # & make new quaternion
4189        Q = drawingData['Quaternion']
4190        V = G2mth.prodQVQ(G2mth.invQ(Q),np.inner(Bmat,V))
4191        DQ = G2mth.AVdeg2Q(A,V)
4192        Q = G2mth.prodQQ(Q,DQ)
4193        drawingData['Quaternion'] = Q
4194    # finally get new view vector - last row of rotation matrix
4195        VD = np.inner(Bmat,G2mth.Q2Mat(Q)[2])
4196        VD /= np.sqrt(np.sum(VD**2))
4197        drawingData['viewDir'] = VD
4198        SetViewDirText(VD)
4199       
4200    def SetRotationZ(newxy):                       
4201#first get rotation vector (= view vector) in screen coords. & angle increment       
4202        View = glGetIntegerv(GL_VIEWPORT)
4203        cent = [View[2]/2,View[3]/2]
4204        oldxy = drawingData['oldxy']
4205        if not len(oldxy): oldxy = list(newxy)
4206        dxy = newxy-oldxy
4207        drawingData['oldxy'] = list(newxy)
4208        V = drawingData['viewDir']
4209        A = [0,0]
4210        A[0] = dxy[1]*.25
4211        A[1] = dxy[0]*.25
4212        if newxy[0] > cent[0]:
4213            A[0] *= -1
4214        if newxy[1] < cent[1]:
4215            A[1] *= -1       
4216# next transform vector back to xtal coordinates & make new quaternion
4217        Q = drawingData['Quaternion']
4218        V = np.inner(Amat,V)
4219        Qx = G2mth.AVdeg2Q(A[0],V)
4220        Qy = G2mth.AVdeg2Q(A[1],V)
4221        Q = G2mth.prodQQ(Q,Qx)
4222        Q = G2mth.prodQQ(Q,Qy)
4223        drawingData['Quaternion'] = Q
4224
4225    def SetRBRotation(newxy):
4226#first get rotation vector in screen coords. & angle increment       
4227        oldxy = drawingData['oldxy']
4228        if not len(oldxy): oldxy = list(newxy)
4229        dxy = newxy-oldxy
4230        drawingData['oldxy'] = list(newxy)
4231        V = np.array([dxy[1],dxy[0],0.])
4232        A = 0.25*np.sqrt(dxy[0]**2+dxy[1]**2)
4233# next transform vector back to xtal coordinates via inverse quaternion
4234# & make new quaternion
4235        Q = rbObj['Orient'][0]              #rotate RB to Cart
4236        QC = drawingData['Quaternion']      #rotate Cart to drawing
4237        V = G2mth.prodQVQ(G2mth.invQ(QC),V)
4238        V = G2mth.prodQVQ(G2mth.invQ(Q),V)
4239        DQ = G2mth.AVdeg2Q(A,V)
4240        Q = G2mth.prodQQ(Q,DQ)
4241        rbObj['Orient'][0] = Q
4242        SetRBOrienText()
4243       
4244    def SetRBRotationZ(newxy):                       
4245#first get rotation vector (= view vector) in screen coords. & angle increment       
4246        View = glGetIntegerv(GL_VIEWPORT)
4247        cent = [View[2]/2,View[3]/2]
4248        oldxy = drawingData['oldxy']
4249        if not len(oldxy): oldxy = list(newxy)
4250        dxy = newxy-oldxy
4251        drawingData['oldxy'] = list(newxy)
4252        V = drawingData['viewDir']
4253        A = [0,0]
4254        A[0] = dxy[1]*.25
4255        A[1] = dxy[0]*.25
4256        if newxy[0] < cent[0]:
4257            A[0] *= -1
4258        if newxy[1] > cent[1]:
4259            A[1] *= -1       
4260# next transform vector back to RB coordinates & make new quaternion
4261        Q = rbObj['Orient'][0]              #rotate RB to cart
4262        V = np.inner(Amat,V)
4263        V = -G2mth.prodQVQ(G2mth.invQ(Q),V)
4264        Qx = G2mth.AVdeg2Q(A[0],V)
4265        Qy = G2mth.AVdeg2Q(A[1],V)
4266        Q = G2mth.prodQQ(Q,Qx)
4267        Q = G2mth.prodQQ(Q,Qy)
4268        rbObj['Orient'][0] = Q
4269        SetRBOrienText()
4270
4271    def RenderBox():
4272        glEnable(GL_COLOR_MATERIAL)
4273        glLineWidth(2)
4274        glEnable(GL_BLEND)
4275        glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)
4276        glEnable(GL_LINE_SMOOTH)
4277        glBegin(GL_LINES)
4278        for line,color in zip(uEdges,uColors):
4279            glColor3ubv(color)
4280            glVertex3fv(line[0])
4281            glVertex3fv(line[1])
4282        glEnd()
4283        glColor4ubv([0,0,0,0])
4284        glDisable(GL_LINE_SMOOTH)
4285        glDisable(GL_BLEND)
4286        glDisable(GL_COLOR_MATERIAL)
4287       
4288    def RenderUnitVectors(x,y,z):
4289        xyz = np.array([x,y,z])
4290        glEnable(GL_COLOR_MATERIAL)
4291        glLineWidth(1)
4292        glPushMatrix()
4293        glTranslate(x,y,z)
4294        glScalef(1/cell[0],1/cell[1],1/cell[2])
4295        glBegin(GL_LINES)
4296        for line,color in zip(uEdges,uColors)[:3]:
4297            glColor3ubv(color)
4298            glVertex3fv(-line[1]/2.)
4299            glVertex3fv(line[1]/2.)
4300        glEnd()
4301        glPopMatrix()
4302        glColor4ubv([0,0,0,0])
4303        glDisable(GL_COLOR_MATERIAL)
4304               
4305    def RenderSphere(x,y,z,radius,color):
4306        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color)
4307        glPushMatrix()
4308        glTranslate(x,y,z)
4309        glMultMatrixf(B4mat.T)
4310        q = gluNewQuadric()
4311        gluSphere(q,radius,20,10)
4312        glPopMatrix()
4313       
4314    def RenderDots(XYZ,RC):
4315        glEnable(GL_COLOR_MATERIAL)
4316        XYZ = np.array(XYZ)
4317        glPushMatrix()
4318        for xyz,rc in zip(XYZ,RC):
4319            x,y,z = xyz
4320            r,c = rc
4321            glColor3ubv(c)
4322            glPointSize(r*50)
4323            glBegin(GL_POINTS)
4324            glVertex3fv(xyz)
4325            glEnd()
4326        glPopMatrix()
4327        glColor4ubv([0,0,0,0])
4328        glDisable(GL_COLOR_MATERIAL)
4329       
4330    def RenderSmallSphere(x,y,z,radius,color):
4331        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color)
4332        glPushMatrix()
4333        glTranslate(x,y,z)
4334        glMultMatrixf(B4mat.T)
4335        q = gluNewQuadric()
4336        gluSphere(q,radius,4,2)
4337        glPopMatrix()
4338               
4339    def RenderEllipsoid(x,y,z,ellipseProb,E,R4,color):
4340        s1,s2,s3 = E
4341        glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,color)
4342        glPushMatrix()
4343        glTranslate(x,y,z)
4344        glMultMatrixf(B4mat.T)
4345        glMultMatrixf(R4.T)
4346        glEnable(GL_NORMALIZE)
4347        glScale(s1,s2,s3)
4348        q