#pragma TextEncoding="UTF-8" #pragma rtGlobals=3 #pragma version=1.10 #pragma IgorVersion=7.0 #include // https://www.wavemetrics.com/user/tony // This code replicates a small subset of the functionality of the Global Analysis package // (Analysis > Global Fit). The purpose is to add some user interface tweaks to make it // easer to set up a fit, at the expense of hardwiring to a specific kind of fit. // For fitting n pseudovoigt peaks to each of N waves. // Amplitude is unique to each peak, but each set of // N peaks has constant position, width and shape parameters. // USAGE // Run "New Multi-Wave Fit" from macros menu. // Select multiple waves to fit. // Waves must have equal numbers of points and x scaling // (may be relaxed in future version, ask me if you need this) // Expand graph x axis to desired fitting range. // Use peak editor panel to add and delete peaks. // The width, shape and amplitude parameters are related to width, // shape and amplitude as explained in help for VoigtFunc. // Command (or control) click to add a peak at x position of mouse cursor. // Mouse-over peaks and click to select; use mouse to move peak position, // mousewheel up-down to adjust amplitude, mousewheel left-right to adjust shape, // shift + mousewheel up-down to adjust width parameter // By default new peaks are constrained to have positive amplitude. Adjust as required. // Fit parameters can be saved and loaded. // Data waves are not saved with fit, so data waves must exist when reloading a fit. // MultiWaveFit will OVERWRITE waves starting with MF_ in the same data folder as your data waves! // // version 1.10 // shift-click to hold ALL coefficents of the selected peak in all spectra Menu "Macros" "New Multi-Wave Fit...", /Q, MF_StartNewMultiWaveFit() end // start from scratch function MF_StartNewMultiWaveFit() NewDataFolder/O root:Packages NewDataFolder/O root:Packages:MultiFit DFREF dfr = root:Packages:MultiFit wave /Z/SDFR=dfr /WAVE w_MFlist wave /Z/SDFR=dfr w_settings, w_coeff, w_hold wave /Z/SDFR=dfr /T t_min, t_max if(waveexists(w_MFlist)) if (numpnts(w_MFlist)&&numpnts(w_coeff)) // save previous parameter set DFREF savDFR=GetDataFolderDFR() SetDataFolder dfr string newDF=uniquename("MFsave", 11, 0) newdatafolder $newDF DFREF newDFR=$newDF duplicate w_Settings newDFR:w_Settings duplicate w_MFlist newDFR:w_MFlist duplicate w_coeff newDFR:w_coeff duplicate w_hold newDFR:w_hold duplicate t_min newDFR:t_min duplicate t_max newDFR:t_max SetDataFolder savDFR endif endif make /o/n=0/WAVE dfr:w_MFlist make /o/n=0 dfr:w_toFit, dfr:w_theFit make /o/n=0/D dfr:w_coeff make /o/n=0 dfr:w_hold make /o/n=0/T dfr:t_max, dfr:t_min make /o /n=5 dfr:w_mouse=0 make /o /n=20 dfr:w_Settings /wave=w_settings w_Settings=0 setdimlabel 0, 0, null, w_Settings setdimlabel 0, 1, xmin, w_Settings setdimlabel 0, 2, xmax, w_Settings setdimlabel 0, 3, specPnts, w_Settings setdimlabel 0, 4, numWaves, w_Settings setdimlabel 0, 5, numPeaks, w_Settings // for future use setdimlabel 0, 6, oneWidth, w_Settings setdimlabel 0, 7, oneShape, w_Settings w_Settings[%null]=nan dowindow /k MFGraph // kill peak waves variable index=0 do wave /z w=WaveRefIndexedDFR(dfr, index) if (!WaveExists(w)) break endif if (stringmatch (nameofwave(w), "peak*")) killwaves /z w if (waveexists(w)) // failed to kill index+=1 endif else index+=1 endif while (1) MF_makeWaveSelectorPanel() end // run this once a list of fit waves has been prepared function MF_setupMultiWaveFit() // set up fit waves MF_createFitWaves() MF_updateFitWaves() // plot graph MF_makeMFGraph() MF_makePeakEditor() end // this function OVERWRITES waves starting with MF_ function MF_createFitWaves() DFREF dfr = root:Packages:MultiFit wave /SDFR=dfr /WAVE w_MFlist wave /SDFR=dfr w_coeff variable numWaves, waveNum, numPeaks, peakNum string s_peakname numWaves=numpnts(w_MFlist) numPeaks=dimsize(w_coeff, 0)/(numWaves+3) for (waveNum=0;waveNum0) setaxis /W=MFgraph bottom, w_settings[%xmin], w_settings[%xmax] endif SetWindow MFGraph hook(pkdrag)=MF_graphHook end function MF_doFit() // set up the fit DFREF dfr = root:Packages:MultiFit wave /SDFR=dfr /WAVE w_MFlist wave /SDFR=dfr w_settings, w_coeff, w_toFit, w_theFit, w_hold wave /T /SDFR=dfr t_max, t_min variable numWaves, numPeaks, waveNum, peakNum getaxis /W=MFGraph/Q bottom // these aren't totally accurate - based on graph axis, not wave points w_settings[%xmin]=v_min w_settings[%xmax]=v_max numWaves=numpnts(w_MFlist) numPeaks=dimsize(w_coeff, 0)/(numWaves+3) redimension /N=0 w_toFit for (wavenum=0;wavenum"+t_min[i] endif if(strlen(t_max[i])) insertpoints numpnts(t_c), 1, t_c t_c[numpnts(t_c)-1]="K"+num2str(i)+"<"+t_max[i] endif endif endfor // should be ready to do fit... FuncFit/NTHR=0 /H=s_hold MF_fitNPeaks w_coeff w_toFit /D /C=t_c MF_updateFitWaves() // just for checking: w_theFit=MF_fitNPeaks(w_coeff, p) variable p0, x0, width, shape, amp, wl, wg for(peakNum=0;peakNum min(v_max, v_min) && newx < max(v_max, v_min) ) delx=newx-oldx if (numtype(delx)==0) w_coeff[p0]+=delx w_mouse[3]=newx endif endif MF_updateFitWaves() hookresult=0 // won't be able to drag to offset! else foo=TraceFromPixel(s.mouseloc.h,s.mouseloc.v, "WINDOW:MFgraph;DELTAX:4;DELTAY:4;") if (strlen(foo)) tname=StringByKey("TRACE", foo) if(stringMatch(tname, "peak*") && waveexists(dfr:$tname)) sscanf tname, "peak%d_%d", peakNum, waveNum MF_setControls(peakNum, waveNum) endif endif endif break case 5: // mouseup if (GetKeyState(0)^2^0) MF_addPeak(pos=AxisValFromPixel("MFgraph", "bottom", s.mouseloc.h)) break endif if(w_mouse[0]==1) w_mouse[0]=0 ModifyGraph /W=MFgraph rgb($"peak"+num2str(w_mouse[1])+"_"+num2str(w_mouse[2]))=(2,39321,1) else //choosing a peak to edit foo=TraceFromPixel(s.mouseloc.h,s.mouseloc.v, "WINDOW:MFgraph;DELTAX:4;DELTAY:4;") if (strlen(foo)) tname=StringByKey("TRACE", foo) if(stringMatch(tname, "peak*") && waveexists(dfr:$tname)) sscanf tname, "peak%d_%d", peakNum, waveNum MF_setControls(peakNum, waveNum) newx=AxisValFromPixel("MFgraph", "bottom", s.mouseloc.h) newy=AxisValFromPixel("MFgraph", "axis"+num2str(peakNum), s.mouseloc.v) w_mouse={1, peakNum, waveNum, newx, newy} ModifyGraph /W=MFgraph rgb($"peak"+num2str(peakNum)+"_"+num2str(waveNum))=(65535,0,52428) getaxis /Q/W=MFgraph bottom setaxis /W=MFgraph bottom, v_min, v_max getaxis /Q/W=MFgraph $"axis"+num2str(waveNum) setaxis /W=MFgraph $"axis"+num2str(waveNum), v_min, v_max hookResult = 1 endif endif endif break case 22: // mousewheel if (w_mouse[0]) // we've already grabbed a peak peakNum=w_mouse[1] p0=(peakNum-1)*(w_settings[%numWaves]+3) // use scroll left/right to adjust shape if(!(GetKeyState(0)&2^2)) if (s.wheelDx>0) w_coeff[p0+2]*=1.05 elseif(s.wheelDx<0) w_coeff[p0+2]*=0.95 endif endif // use scroll up/down to adjust amplitude // shift+scroll to adjust width // shift key turns Dy into Dx, so take account of this! if(GetKeyState(0)&2^2) if (s.wheelDx>0) w_coeff[p0+1]*=1.05 elseif(s.wheelDx<0) w_coeff[p0+1]*=0.95 endif else if (s.wheelDy>0) w_coeff[p0+2+w_mouse[2]]*=1.05 elseif (s.wheelDy<0) w_coeff[p0+2+w_mouse[2]]*=0.95 endif endif MF_updateFitWaves() hookresult=1 endif break case 1: // deactivate if (w_mouse[0]) w_mouse[0]=0 ModifyGraph /W=MFgraph rgb($"peak"+num2str(w_mouse[1])+"_"+num2str(w_mouse[2]))=(2,39321,1) endif break endswitch return hookResult // If non-zero, we handled event and Igor will ignore it. End // you can use this from the command line to redraw vertical axes // scaled by their ranges function MF_setAxes() variable i=0 make /free/n=0 w_range do getaxis /W=MFGraph/Q $"axis"+num2str(i+1) if (v_flag) break endif w_range[i]={abs(V_max-V_min)} i+=1 while (1) variable ax_count=numpnts(w_range), RangeTotal=sum(w_range) variable ax_min=0, ax_max=0 variable j make /free w={3,1,2,5,6,4} for(j=0;j