#pragma TextEncoding = "UTF-8" #pragma rtGlobals=3 #pragma version=3.08 #pragma IgorVersion=6 #pragma moduleName=baselines // --------------------- Project Updater header ---------------------- // If you're using Igor Pro 7 or later and have Project Updater // installed, this package can check periodically for new releases. // http: www.igorexchange.com/project/Updater static constant kCHECK_FOR_UPDATES=1 // set to 1 to check for updates on init static function CheckForUpdates() if(kCHECK_FOR_UPDATES) #if (exists("Updater#UpdateCheck")==6) return Updater#UpdateCheck("fileloc:"+FunctionPath("")) #endif endif return 0 end // -------------------------------------------------------------------- // by tony.withers@uwo.ca. Please tell me when you find bugs! // How to use: // Must have a spectrum plotted in the top graph window. When you select // Baselines from the macros menu a baseline control panel is attached to // the graph. Select spectrum and baseline type from popup menus in the // control panel. Use the graph marquee tool (click and drag) to select a // region of the spectrum, then either click within the marquee and // select "add region to fit", or click the + button in the control panel // to add a fitting region. Subtract will make copies of the current // baseline and baseline-subtracted waves in the current data folder. The // output waves have a _Sub or _BL suffix. Close the panel to clean up // after fitting. // The tangent baseline will attempt to find a common tangent to two // cubic functions. You must have two regions selected for fitting. The // fit will be successful only if the selected regions can each be fit // nicely with a cubic function, and if the two cubics have a common // tangent. // The line between cursors baseline allows you to position the cursors // labelled I and J manually. // The gauss3 baseline fits a Gauss1D function with the first coefficient // set to 0. // Execute BL_SetRegion(x1, x2, value) with value = 0/1 to remove/add a // fit region from the command line. // Note that waves with _sub and _BL suffixes cannot be chosen as data // waves for baseline fitting. // A user-defined fit function can be added by editing the last three // functions in this file. // Version history // 3.08 restores printing to history of commands for setting mask regions // 3.07 Record fit coefficients in wave note of output waves, so that // user has access to these for further processing // 3.06 Added headers for Project Updater. // 3.05 Jan 5 2018 Adds an easy-to-edit user defined fit function - see // the last three functions in this file. // 3.04 11/11/17 Should be backward compatible for Igor 6. // 3.03 Panel size decreased for compatibility with smaller or lower // resolution screens. Some bugs fixed. // 3.02 bug fix: spline fit was damaged by 'cosmetic' changes in version 3.01. // 3.01 cosmetic changes // 3.00 Sep 11 2017. Switch to panel interface. Closing panel should // clear baseline waves from graph. Baseline updates as user interacts // with controls on panel or changes mask wave. This was a major rewrite, // so let me know when you find the bugs. // 2.20 code cleanup to use waverefs instead of global string variables // 2.10 Aug 22 2017 cleaned up the 'tangent' baseline for a beta release // 2.00 1/2/15 made it work for X-Y data; added wave notes to output waves // 1.50 finally fixed offsets // 1.22 fixed offset calculation // 1.21 12/10/09 // 1.20 6/24/08 added line between cursors // 1.12 6/9/08 // 1.11 9/12/07 // 1.10 7/23/07 added smoothed spline baseline // 1.00 7/3/07 static constant kSILENT=0 // set kSILENT=1 to prevent some non-error output to history Menu "Analysis" Submenu "Packages" "Baselines", /Q, baselines#BL_init() end end Menu "Macros" "Baselines", /Q, baselines#BL_init() end Menu "GraphMarquee", dynamic baselines#BL_MarqueeMenu("-") baselines#BL_MarqueeMenu("Add Region to Fit"), /Q, baselines#BL_setMarquee(1) baselines#BL_MarqueeMenu("Remove Region From Fit"), /Q, baselines#BL_setMarquee(0) baselines#BL_MarqueeMenu("Clear All Fit Regions"), /Q, baselines#BL_SetRegion(-inf, inf, 0);SetMarquee 0, 0, 0, 0 End static function BL_setMarquee(add) variable add string graphStr=BL_getGraph() controlinfo /W=$graphStr+"#BL_panel" popTrace string xAxisName=StringByKey("XAXIS", traceinfo(graphStr,s_value,0)) getmarquee /W=$graphStr /K/Z $xAxisName if (V_flag) BL_SetRegion(V_left, V_right, add) if(kSILENT==0) printf "BL_SetRegion(%g, %g, %d)\r", V_left, V_right, add endif return 1 endif return 0 end static Function/DF GetDFREF() DFREF dfr = root:Packages:Baselines if (DataFolderRefStatus(dfr) != 1) DFREF dfr = CreatePackageFolder() endif return dfr end static function /DF CreatePackageFolder() NewDataFolder /O root:Packages NewDataFolder /O root:Packages:Baselines DFREF dfr = root:Packages:Baselines return dfr end static function/S BL_MarqueeMenu(str) string str string graphStr=BL_getGraph() if (strlen(graphStr)==0) return "" endif if (stringmatch(WinName(0,1),graphStr)==0) return "" endif DFREF dfr=GetDFREF() wave /Z /SDFR=dfr w_display, w_mask if (waveexists(w_display)==0) return "" endif checkdisplayed /W=$graphStr w_display if(V_flag==0) // not initialized return "" endif strswitch(str) case "Remove Region From Fit": if(wavemax(w_mask)==0) return "" endif case "Clear All Fit Regions": if(wavemax(w_mask)==0) return "" endif endswitch return str end static function BL_init() if (CheckForUpdates()) return 0 // this file has been updated; quit to allow user to reload endif if(BL_MakePanel()) BL_ResetPackage() // create fit waves and add to plot BL_doFit() endif end static function BL_MakePanel() if (strlen(WinList("*",";","WIN:1"))==0) // no graphs doalert 0, "Baseline requires a trace plotted in a graph window." return 0 endif // make sure the top graph is visible string graphStr=WinName(0,1) dowindow /F $graphStr // clear any package detritus from the last used baseline graph BL_clear(1) // make sure there's space to the left of the graph for the panel getwindow /Z $graphStr wsize if (V_left<200) #if IgorVersion() >= 7 movewindow /W=$graphStr 200, V_top, -1, -1 #else movewindow /W=$graphStr 200, V_top, 200+(V_right-V_left), v_bottom #endif endif // make panel NewPanel /K=1/N=BL_panel/W=(200,0,0,270)/HOST=$graphStr/EXT=1 as "Baseline Controls" ModifyPanel /W=$graphStr#BL_panel, noEdit=1 if(strlen(baselines#BL_traces())==0) doalert 0, "no eligible traces for baseline fitting plotted in top graph - check wave names" KillWindow $graphStr+"#BL_panel" return 0 endif variable i=0, deltaY=25, vL=30, vT=5, font=12, groupw=180 GroupBox group0,pos={vL-20,vT+deltaY*i},size={groupw,deltaY*2},title="Data wave" GroupBox group0,fSize=font i+=1 // store values internally in these controls PopupMenu popTrace, mode=1, Value=baselines#BL_traces(), title="",pos={vL,vT+deltaY*i},size={130,20} PopupMenu popTrace, help={"select data wave" }, proc=baselines#BL_popup i+=1.5 GroupBox group1,pos={vL-20,vT+deltaY*i},size={groupw,deltaY*2},title="Baseline type" GroupBox group1,fSize=font i+=1 string fNames="\"line;poly 3;poly 4;gauss;gauss3;lor;exp;dblexp;sin;hillequation;sigmoid;power;lognormal;" fNames+="spline;line between cursors;tangent;" fNames+=UserFitName()+"\"" PopupMenu popBL, mode=1, title="",pos={vL,vT+deltaY*i},size={130,20}, Value=#fNames PopupMenu popBL, help={"select baseline type" }, proc=baselines#BL_popup, fSize=font SetVariable setvarSmooth,pos={vL+30,vT+deltaY*i},size={100,16},title="" SetVariable setvarSmooth,limits={1e-5,100,0.01},value=_NUM:0.1, bodyWidth=60 SetVariable setvarSmooth,help={"spline smoothing factor"}, fSize=font, proc=baselines#BL_SetVar SetVariable setvarSmooth,disable=1 i+=1.5 GroupBox group2,pos={vL-20,vT+deltaY*i},size={groupw,deltaY*2.5},title="Fit regions" GroupBox group2, fSize=font i+=1 Button BL_reset,pos={vL-5,vT+deltaY*i+5},size={60,20},title="Clear all", proc=baselines#BL_button Button BL_reset,help={"Clear all fit regions"}, fSize=font Button BL_add,pos={vL+70,vT+deltaY*i},size={30,30},title="+", proc=baselines#BL_button Button BL_add,help={"Remove marquee to fit region"}, fSize=font Button BL_remove,pos={vL+115,vT+deltaY*i},size={30,30},title="-", proc=baselines#BL_button Button BL_remove,help={"Remove marquee from fit region"}, fSize=font i+=2 Button BL_sub,pos={60,vT+deltaY*i},size={70,20},title="Subtract", proc=baselines#BL_button Button BL_sub,help={"Subtract baseline"}, fSize=font i+=1 Button BL_all,pos={60,vT+deltaY*i},size={70,20},title="All in one", proc=baselines#BL_button Button BL_all,help={"Subtract baseline from all traces"}, fSize=font return 1 end // create fit waves and add to plot static function BL_ResetPackage() string graphStr=BL_getGraph() removefromgraph /W=$graphStr /Z w_display, w_base, w_sub, tangent0, tangent1 controlinfo /W=$graphStr+"#BL_panel" popTrace string traceStr=s_value wave /Z w=TraceNameToWaveRef(graphStr, traceStr) if (waveexists(w)==0) return 0 endif wave /Z w_x=XWaveRefFromTrace(graphStr, traceStr) DFREF dfr=GetDFREF() duplicate /O w dfr:w_Display /WAVE=w_display // don't reset the mask wave if new data wave has same length as previous one // in case we want to apply the same fit to many spectra wave /Z w_mask=dfr:w_mask if (waveexists(w_mask)) if (numpnts(w_mask)!=numpnts(w_display)) duplicate /O w dfr:w_mask w_mask=0 w_display=nan endif else duplicate /O w dfr:w_mask wave w_mask=dfr:w_mask w_mask=0 endif w_display = w_mask[p] ? inf : NaN if (waveexists(w_x)) appendtograph /W=$graphStr w_display vs w_x else appendtograph /W=$graphStr w_display endif ModifyGraph /W=$graphStr mode(w_display)=7,hbFill(w_display)=4 ModifyGraph /W=$graphStr rgb(w_display)=(54693,56967,65535) ModifyGraph /W=$graphStr axisOnTop=1 string firstTraceStr=stringfromlist(0, tracenamelist(graphStr,";",5)) ReorderTraces /W=$graphStr $firstTraceStr, {w_display} // preserve offsets (vertical only) variable DataOffsetY=BL_getYoffset(graphStr, traceStr) ModifyGraph /W=$graphStr offset(w_display)={0,-1e9} duplicate /O w dfr:w_base /WAVE=w_base w_base=nan if (waveexists(w_x)) appendtograph /W=$graphStr w_base vs w_x else appendtograph /W=$graphStr w_base endif // figure out a contrasting colour for baseline and sub waves make /free w_RGB={0,10000,65535} BL_chooseColour(w_RGB, graphStr, traceStr) ModifyGraph /W=$graphStr rgb(w_base)=(w_RGB[0],w_RGB[1],w_RGB[2]) ModifyGraph /W=$graphStr offset(w_base)={0,DataOffsetY} duplicate /O w dfr:w_sub /WAVE=w_sub w_sub=nan if (waveexists(w_x)) appendtograph /W=$graphStr w_sub vs w_x else appendtograph /W=$graphStr w_sub endif ModifyGraph /W=$graphStr rgb(w_sub)=(w_RGB[0],w_RGB[1],w_RGB[2]) string yAxisName= StringByKey("YAXIS", traceinfo(graphStr,traceStr,0)) getaxis /W=$graphStr /Q $yAxisName setaxis /W=$graphStr /Z $yAxisName, V_min, V_max end // ------------ control action procedures -------------------- static function BL_popup(s) STRUCT WMPopupAction &s if (s.eventCode==-1) return 0 endif string graphStr=parseFilepath(0, s.win, "#", 0, 0) //string panelStr=graphStr+"#BL_panel" controlinfo /W=$s.win popTrace string traceStr=s_value if(stringmatch(s.ctrlName,"popTrace")) BL_ResetPackage() controlinfo /W=$s.win popBL // force an update of baseline controlinfo /W=$s.win popBL s.ctrlName="popBL" s.popStr=s_value endif if(stringmatch(s.ctrlName,"popBL")) if(strlen(traceStr)==0 || stringmatch(traceStr, "_none_")) return 0 endif if(stringmatch(s.popStr,"spline")) SetVariable setvarSmooth, Win=$s.win, disable=0 else SetVariable setvarSmooth, Win=$s.win, disable=1 endif if(stringmatch(s.popStr,"line between cursors")) wave /Z w=TraceNameToWaveRef(graphStr, traceStr) if (waveexists(w)==0) return 0 endif wave /Z w_x=XWaveRefFromTrace(graphStr, traceStr) // put cursors on graph variable Xval, Yval string xAxisName= StringByKey("XAXIS", traceinfo(graphStr,traceStr,0)) string yAxisName= StringByKey("YAXIS", traceinfo(graphStr,traceStr,0)) getaxis /W=$graphStr /Q $yAxisName setaxis $yAxisName, V_min, V_max Yval=V_min+(V_max-V_min)/2 getaxis /W=$graphStr /Q $xAxisName Xval=V_min+(V_max-V_min)*.1 if (waveexists(w_x)==0) Yval=WaveMin(w, V_min, Xval)+BL_getYoffset(graphStr, traceStr) endif cursor /F /W=$graphStr /N=1 I $traceStr Xval, Yval Xval=V_min+(V_max-V_min)*.9 if (waveexists(w_x)==0) Yval=WaveMin(w, Xval, V_max)+BL_getYoffset(graphStr, traceStr) endif // set the hook before placing second cursor on graph SetWindow $graphStr hook(BL_CsrHook)=baselines#BL_CsrLineHook cursor /F /W=$graphStr /N=1 J $traceStr Xval, Yval else Cursor /K /W=$graphStr I Cursor /K /W=$graphStr J SetWindow $graphStr hook(BL_CsrHook)=$"" endif if(stringmatch(s.popStr,"tangent")) DFREF dfr=GetDFREF() make /o/n=0 dfr:tangent0 /WAVE=tangent0, dfr:tangent1 /WAVE=tangent1 // make sure the zero-point waves won't plot outside x-axis // range before appending to graph getaxis /W=$graphStr /Q $(StringByKey("XAXIS", traceinfo(graphStr,traceStr,0))) setscale /P x, V_min, 1, tangent0, tangent1 checkdisplayed /W=$graphStr tangent0, tangent1 if (!(v_flag&1)) appendtograph /W=$graphStr tangent0 endif if(!(v_flag&2)) appendtograph /W=$graphStr tangent1 endif // preserve Y offsets // X offsets will lead only to trouble variable DataOffsetY=BL_getYoffset(graphStr, traceStr) ModifyGraph /W=$graphStr offset(tangent0)={0,DataOffsetY} ModifyGraph /W=$graphStr offset(tangent1)={0,DataOffsetY} else removefromgraph /Z /W=$graphStr tangent0, tangent1 endif endif BL_DoFit() end static function BL_SetVar(s) : SetVariableControl STRUCT WMSetVariableAction &s if (s.eventCode==-1) BL_clear(0) return 0 endif if(stringmatch(s.ctrlName,"setvarSmooth")) // reset increment value to 10% of current value SetVariable setvarSmooth win=$s.win,limits={1e-5,inf,abs(s.dval/10)} endif BL_doFit() return 0 End static function BL_button(s) STRUCT WMButtonAction &s if(s.eventCode!=2) return 0 endif string graphStr=parseFilepath(0, s.win, "#", 0, 0) strswitch(s.ctrlName) case "BL_sub": BL_subtract(0) break case "BL_all": string msg="subtract baseline from all traces using current settings?\r" msg+="existing baselines will be overwritten!" doalert 1, msg if(v_flag==2) return 0 endif controlinfo /W=$graphStr+"#BL_panel" popTrace variable savMode=v_value string listStr=baselines#BL_traces() variable i for(i=0;i0 && w_mask[pnt]!=w_mask[pnt-1]) w_limits[j]=pnt-w_mask[pnt-1] j+=1 endif pnt+=1 while(j<4 && pnt= 7 GetWindow /Z BL_panel activeSW return parseFilepath(0, s_value, "#", 0, 0) #endif string listStr=WinList("*", ";","WIN:1") string panelStr, graphStr variable i do graphStr=stringfromlist(i, listStr) panelStr=graphStr+"#BL_panel" if (wintype(panelStr)) return graphStr endif i+=1 while (i