#pragma rtGlobals=3 #pragma version=3.02 #pragma IgorVersion=7 #pragma moduleName=baselines // 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. // Version history // 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 Menu "Analysis" Submenu "Packages" "Baselines",/Q, BL_init() end end Menu "Macros" "Baselines", /Q, BL_init() end Menu "GraphMarquee", dynamic BL_MarqueeMenu("-") BL_MarqueeMenu("Add Region to Fit"), /Q, BL_setMarquee(1) BL_MarqueeMenu("Remove Region From Fit"), /Q, BL_setMarquee(0) BL_MarqueeMenu("Clear All Fit Regions"), /Q, BL_SetRegion(-inf, inf, 0) End function BL_setMarquee(add) variable add GetWindow /Z BL_panel activeSW string s_graph = parseFilepath(0, s_value, "#", 0, 0) controlinfo /W=BL_panel popTrace string xAxisName= StringByKey("XAXIS", traceinfo(s_graph,s_value,0)) getmarquee /W=$s_graph /Z $xAxisName if (V_flag) BL_SetRegion(V_left, V_right, add) 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 Function/S BL_MarqueeMenu(str) string str GetWindow /Z BL_panel activeSW if (v_flag) return "" endif string s_graph=parseFilepath(0, s_value, "#", 0, 0) if (stringmatch(WinName(0,1),s_graph)==0) return "" endif DFREF dfr=GetDFREF() wave /SDFR=dfr w_display, w_mask if (waveexists(w_display)==0) return "" endif checkdisplayed /W=$s_graph 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 function BL_init() if(BL_MakePanel()) BL_ResetPackage() // create fit waves and add to plot BL_doFit() endif end 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 s_graph=WinName(0,1) dowindow /F $s_graph // clear any package detritus from the last used baseline graph BL_clear() DFREF dfr=GetDFREF() // make sure there's space to the left of the graph for the panel getwindow /Z $s_graph wsize if (V_left<200) movewindow /W=$s_graph 200, V_top, -1, -1 endif // make panel NewPanel /K=1/N=BL_panel/W=(200,0,0,320)/HOST=$s_graph/EXT=1 as "Baseline Controls" ModifyPanel /W=$s_graph#BL_panel, noEdit=1 variable i=0, deltaY=30, v_L=30, v_T=10 GroupBox group0,pos={v_L-20,v_T+deltaY*i},size={180.00,60.00},title="Data wave" GroupBox group0,fSize=14 i++ // store values internally in these controls PopupMenu popTrace, mode=1, Value=BL_traces(), title="",pos={v_L,v_T+deltaY*i},size={130,20} PopupMenu popTrace, help={"select data wave" }, proc=BL_popup i+=1.5 GroupBox group1,pos={v_L-20,v_T+deltaY*i},size={180.00,60.00},title="Baseline type" GroupBox group1,fSize=14 i++ string fNames="\"line;poly 3;poly 4;gauss;gauss3;lor;exp;dblexp;sin;hillequation;sigmoid;power;lognormal;" fNames+="spline;line between cursors;tangent\"" PopupMenu popBL, mode=1, title="",pos={v_L,v_T+deltaY*i},size={130,20}, Value=#fNames PopupMenu popBL, help={"select baseline type" }, proc=BL_popup SetVariable setvarSmooth,pos={v_L+30,v_T+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=14, proc=BL_SetVar SetVariable setvarSmooth,disable=1 i+=1.5 GroupBox group2,pos={v_L-20,v_T+deltaY*i},size={180.00,70.00},title="Fit regions" GroupBox group2,fSize=14 i+=1 Button BL_reset,pos={v_L-5,v_T+deltaY*i+5},size={60,20},title="Clear all", proc=BL_button Button BL_reset,help={"Clear all fit regions"} Button BL_add,pos={v_L+70,v_T+deltaY*i},size={30,30},title="+", proc=BL_button Button BL_add,help={"Remove marquee to fit region"} Button BL_remove,pos={v_L+115,v_T+deltaY*i},size={30,30},title="-", proc=BL_button Button BL_remove,help={"Remove marquee from fit region"} i+=2 Button BL_sub,pos={60,v_T+deltaY*i},size={70,20},title="Subtract", proc=BL_button Button BL_sub,help={"Subtract baseline"} i++ Button BL_all,pos={60,v_T+deltaY*i},size={70,20},title="All in one", proc=BL_button Button BL_all,help={"Subtract baseline from all traces"} return 1 end // create fit waves and add to plot static function BL_ResetPackage() GetWindow /Z BL_panel activeSW string s_graph = parseFilepath(0, s_value, "#", 0, 0) removefromgraph /W=$s_graph /Z w_display, w_base, w_sub, tangent0, tangent1 controlinfo /W=BL_panel popTrace string s_trace=s_value wave w=TraceNameToWaveRef(s_graph, s_trace) if (waveexists(w)==0) return 0 endif wave w_x=XWaveRefFromTrace(s_graph, s_trace) 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 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=$s_graph w_display vs w_x else appendtograph /W=$s_graph w_display endif ModifyGraph /W=$s_graph mode(w_display)=7,hbFill(w_display)=4 ModifyGraph /W=$s_graph rgb(w_display)=(49151,53155,65535,32768) string s_firsttrace=stringfromlist(0, tracenamelist(s_graph,";",5)) ReorderTraces /W=$s_graph $s_firsttrace, {w_display} // preserve offsets in (vertical only) variable DataOffsetY=BL_getYoffset(s_graph, s_trace) ModifyGraph /W=$s_graph offset(w_display)={0,-1e9} duplicate /O w dfr:w_base /WAVE=w_base w_base=nan if (waveexists(w_x)) appendtograph /W=$s_graph w_base vs w_x else appendtograph /W=$s_graph w_base endif // figure a contrasting colour for baseline and sub waves make /free w_c={0,10000,65535} BL_chooseColour(w_c, s_graph, s_trace) ModifyGraph /W=$s_graph rgb(w_base)=(w_c[0],w_c[1],w_c[2]) ModifyGraph /W=$s_graph offset(w_base)={0,DataOffsetY} duplicate /O w dfr:w_sub /WAVE=w_sub w_sub=nan if (waveexists(w_x)) appendtograph /W=$s_graph w_sub vs w_x else appendtograph /W=$s_graph w_sub endif ModifyGraph /W=$s_graph rgb(w_sub)=(w_c[0],w_c[1],w_c[2]) string yAxisName= StringByKey("YAXIS", traceinfo(s_graph,s_trace,0)) getaxis /W=$s_graph /Q $yAxisName setaxis /W=$s_graph /Z $yAxisName, v_min, v_max end // ------------ control action procedures -------------------- Function BL_popup(s) STRUCT WMPopupAction &s if (s.eventCode==-1) return 0 endif string s_graph=parseFilepath(0, s.win, "#", 0, 0) controlinfo /W=BL_panel popTrace string s_trace=s_value if(stringmatch(s.ctrlName,"popTrace")) BL_ResetPackage() controlinfo /W=BL_panel popBL // force an update of baseline controlinfo /W=BL_panel popBL s.ctrlName="popBL" s.popStr=s_value endif if(stringmatch(s.ctrlName,"popBL")) 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 w=TraceNameToWaveRef(s_graph, s_trace) if (waveexists(w)==0) return 0 endif wave w_x=XWaveRefFromTrace(s_graph, s_trace) // put cursors on graph variable Xval, Yval string xAxisName= StringByKey("XAXIS", traceinfo(s_graph,s_trace,0)) string yAxisName= StringByKey("YAXIS", traceinfo(s_graph,s_trace,0)) getaxis /W=$s_graph /Q $yAxisName setaxis $yAxisName, v_min, v_max Yval=V_min+(V_max-V_min)/2 getaxis /W=$s_graph /Q $xAxisName Xval=V_min+(V_max-V_min)*.1 if (waveexists(w_x)==0) Yval=WaveMin(w, V_min, Xval)+BL_getYoffset(s_graph, s_trace) endif cursor /F /W=$s_graph /N=1 I $s_trace Xval, Yval Xval=V_min+(V_max-V_min)*.9 if (waveexists(w_x)==0) Yval=WaveMin(w, Xval, V_max)+BL_getYoffset(s_graph, s_trace) endif // set the hook before placing second cursor on graph SetWindow $s_graph hook(BL_CsrHook)=BL_CsrLineHook cursor /F /W=$s_graph /N=1 J $s_trace Xval, Yval else Cursor /K /W=$s_graph I Cursor /K /W=$s_graph J SetWindow $s_graph hook(BL_CsrHook)=$"" endif if(stringmatch(s.popStr,"tangent")) DFREF dfr=GetDFREF() make /o/n=0 dfr:tangent0 /WAVE=tangent0, dfr:tangent1 /WAVE=tangent1 checkdisplayed /W=$s_graph tangent0, tangent1 if (!(v_flag&1)) appendtograph /W=$s_graph tangent0 endif if(!(v_flag&2)) appendtograph /W=$s_graph tangent1 endif // preserve Y offsets // X offsets will lead only to trouble variable DataOffsetY=BL_getYoffset(s_graph, s_trace) ModifyGraph /W=$s_graph offset(tangent0)={0,DataOffsetY} ModifyGraph /W=$s_graph offset(tangent1)={0,DataOffsetY} else removefromgraph /Z /W=$s_graph tangent0, tangent1 endif endif BL_DoFit() end function BL_getYoffset(s_graph, s_trace) string s_graph, s_trace variable OffsetX=0,OffsetY=0 string infostr=TraceInfo(s_graph, s_trace, 0 ) infostr = GrepList(infostr, "offset", 0,";") infostr=StringFromList(0, infostr,";") sscanf infostr, "offset(x)={%g,%g}", OffsetX,OffsetY return OffsetY end Function BL_SetVar(s) : SetVariableControl STRUCT WMSetVariableAction &s if (s.eventCode==-1) string s_graph=parseFilepath(0, s.win, "#", 0, 0) BL_RemoveWaves(s_graph) 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 Function BL_button(s) STRUCT WMButtonAction &s if(s.eventCode!=2) return 0 endif string s_graph=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=BL_panel popTrace variable savMode=v_value string s_list=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