#if (IgorVersion() < 6.2) #pragma rtGlobals=1 #else #pragma rtGlobals=3 // Use modern global access method. #endif #pragma moduleName= WMMultiPeakFit2 #pragma version=2.06 #pragma IgorVersion=6.10 #include version>=2.00 #include version>=1.10 #include version>=5.05 //*************************************** // JW 090708 version 2.01 // Added background information to results report list and notebook // JW 080104 first beta release of version 2.00 // JW 080109: beta version 2 // Fixed bug: If Do Fit is clicked before any peaks have been added to the list, an error results. // Fixed bug: If a graph is selected in the Start Multi-peak Fit panel, the graph is killed, then Continue is clicked, Bad Things happen. // Fixed bug: If there are no peaks in the list and you try to use the Add or Edit Peaks item from the marquee, a bad peak is created. // Fixed bug: Deleting the last peak left the last peak in the fit curve on the graph. // JW 080110: beta version 3 // Fixed bug: If you open the Baseline row before adding any peaks, you get a NULL wave reference for the auto-locate wave. // Fixed bug: If you close the control panel while the Edit or Add Peaks windows is open, an error results. Now, closing // the control panel or the graph also closes the Edit or Add Peaks window. // Fixed bug: If you click the Peak Results button when the Do Fit button is not enabled, an error results. // Added contextual menu to the peak list with Delete Peak when you click on a peak container row. // Added help message when the Do Fit button is not enabled telling how to add peaks. // JW 080121: beta version 4 // Changed default baseline type from "None" to "Constant" // Changed default panel position to "Right" // Added ability to right-click a peak in the Add or Edit graph and select Remove. // Right-click on peak list offers either "Delete Selected Peaks" or "Delete Peak N" where N is the number of the peak clicked on. // Changed lblPosMode for residuals and peaks to 1 (absolute mode). // Drawing the gray lines on the graph now restores the existing draw layer selection. This depends on a feature added to Igor 6.1 on 1/25/2008. // Changed delete key handling to use new listbox event 12. // JW 080130: beta version 5 // Added Set Waves from Trace menu to the Start Multi-peak Fit panel. Shown only when From Target is checked and the target window is a graph. // Fixed bug: when From Target was checked, it was impossible to select any waves that weren't in the first trace in the target graph. // JW 080131: beta version 5 // Fixed bug: Bringing up the Add or Edit Peaks graph failed to restore the current data folder properly. // JW 080403: beta version 7 // Fixed bug: New peaks created in the Add or Edit Peaks graph had NaNs or zeroes where they shouldn't have if you chose either ExpModGauss or // ExpConfExp peak functions. // Fixed problem: If graph had log X axis, the fit curve went kooky after the fit was finished. // JW 080501: patch for beta version 7 // Fixed bug: Selecting baseline "None", then clicking Auto-locate Peaks Now would fill the fit curve and residuals with NaNs because it was setting // the baseline back to "Constant" when it shouldn't have, and without properly initializing the coefficient wave. // JW 080508: beta version 8 // Added LogNormal peak shape. That involved changing only PeakFunctions2.ipf // JW 080509: beta version 9 // Added background curve to graph. // JW 080513: // The Add or Edit graph now alters only peaks that are modified or added during the Add or Edit session. // JW 080515: // Improved FuncFit error control. // JW 080520: // Results graph button on Results control panel. // Added Help button to the Start Multi-peak Fit panel. // JW 080522: beta version 10 // Fixed a bug in reported errors in results table and delimited text file. // JW 080527: beta version 10 // Added Options disclosure and support for mask and weight waves. // JW 090127: beta vaersion 11 // Needed /Z flag on Wave statements for weighting and masking waves. // // JW 091012: Release 2.01 // Removed two vestigial lines in MPF2_DoMPFit() that caused a "WAVE Reference to failed" error message. // JW 091229: Release 2.02 // Added abs() to calculation of npnts in MPF2_AddFitCurveToGraph() to handled reversed X values. But the Add/Edit window still doesn't handle this well (at all!). // JW 100211: Release 2.03 // Added Fit Curve Points control to the Options section of the control panel. // Changed tab-delimited output notebook tab stops to 108 points. // JW 100329: Release 2.04 // Added handling for user renaming the graph window. // Added an additional sanity check to MPF2_GraphMarqueeDef for cases where no graph window is open. // JW 100419: Release 2.05 // Added Total Area printout on results window // Fixed bug: Include Background Info checkbox in the Results window didn't move when the window was re-sized. // The mask wave was specified using /W instead of /M! // JW 100426: Still release 2.05, since we haven't released a new beta yet // Fixed out-of-bounds access to HoldStrings wave in MPF2_HoldStringForPeakListItem() // JP 100622: Still 2.05, more out-of-bound HoldString access fixes in PeakListClosingNotify() and PeakListOpenNotify(). // JW 100713: Release 2.06 // Fixed bug: If you re-used a previous set, it would wipe out all the baseline coefficients except the first. // Fixed another index-out-of-range error. // Fixed bug: Closing and re-opening the panel (using a previously saved set) failed to restore holds. //*************************************** //*************************************** // POSSIBLE ENHANCEMENTS // // 1) Preferences // Residuals Yes or No // fit curve Yes or No // Updates during fits Yes or No // Add baseline curve Yes or No // 2) Some sort of support for metadata: // A) Date // B) File name // C) Any kind of user data... // 3) Support for database... // 4) Function that can get Amplitude, Area and FWHM from a peak that doesn't have the param function // 5) Bootstrap estimation of the errors for the peak parameters. // 6) Constraints // DONE 7) Weighting? // DONE 8) Separate baseline curve added to graph. // 9) Support for additional peak types: // Weibull http://www.systat.com/products/tablecurve2d/help/?sec=1247 // Doniac-Sunjic // Logistic Dose Response Peak? http://www.systat.com/products/tablecurve2d/help/?sec=1186 // 10) Add auto-pick from the residuals to add peaks missed the first time around? // 12) Ability to create a baseline-subtracted data set. // 13) Export a fit curve data set. // 14) A GUI for MPF2_AutoMPFit() // 15) A new Manual Peak Adjust that allows for assymetric peaks. Ability to drag left and right sides separately- but how to *not* screw up symmetric peaks? //*************************************** #include #include #include #include #include #include static strconstant MPF2_VERSIONSTRING="2.05" strconstant PEAK_INFO_SUFFIX="_PeakFuncInfo" strconstant BL_INFO_SUFFIX="_BLFuncInfo" strconstant MENU_ARROW_STRING="\JR\W523" Menu "Analysis" Submenu "Multi-peak Fit" "Start New Multi-peak Fit", /Q, fStartMultipeakFit2() "Unload Multi-peak Fit Package", /Q, MPF2_UnloadMultiPeakFit() "Help for Multi-peak Fitting", /Q, DisplayHelpTopic "Multi-peak Fitting" "Multi-peak Fitting Guided Tour", /Q, MPF2_LoadMPF2Demo() end end Menu "GraphMarquee", dynamic MPF2_GraphMarqueeDef(),/Q, MPF2_MarqueeHandler() end Function MPF2_LoadMPF2Demo() DoAlert 1, "Continuing will close the current experiment and open the Multi-peak Fitting Demo experiment.\r\rContinue?" if (V_flag == 1) Execute/P/Q/Z "LOADFILE :Examples:Curve Fitting:Multi-peak fit 2 Demo.pxp" endif end Function MPF2_UnloadMultiPeakFit() if (WinType("MultiPeak2StarterPanel") != 0) DoWindow/K MultiPeak2StarterPanel endif Execute/P/Q/Z "DELETEINCLUDE " Execute/P/Q/Z "COMPILEPROCEDURES " end Structure MPFitInfoStruct Variable NPeaks Wave yWave Wave xWave Wave maskWave Wave weightWave Variable XPointRangeBegin Variable XPointRangeEnd Variable FitCurvePoints Variable fitOptions // this value will be used to set V_fitOptions String ListOfFunctions // first item in the list is the baseline type, which might be "None"; the rest will be one peak type per peak to fit. String ListOfCWaveNames // first is name of coefficient wave for baseline. If baseline function is "None", the first item is ignored (but must be present) String ListOfHoldStrings // semicolon-separated string containing one hold string for each fit function in ListOfFunctions. If it is "" then no holds. Any one entry can be ""; and that will make no holds for that particular fit function. // outputs String FuncListString // the string used with FuncFit {String=...} Variable fitError Variable fitQuitReason String fitErrorMsg Variable chisq Variable fitPnts Variable dateTimeOfFit EndStructure Constant MPF2_Err_NoError = 0 Constant MPF2_Err_NoSuchBLType = -1 Constant MPF2_Err_BLCoefWaveNotFound = -2 Constant MPF2_Err_NoSuchPeakType = -3 Constant MPF2_Err_PeakCoefWaveNotFound = -4 Constant MPF2_Err_BadNumberOfFunctions = -5 Constant MPF2_Err_BadNumberOfCWaves = -6 Constant MPF2_Err_XYListLengthMismatch = -7 Constant MPF2_Err_NoDataSets = -8 Constant MPF2_Err_MissingDataSet = -9 Constant MPF2_Err_NoDataFolder = -10 Constant MPF2_Err_BLCoefWrongNPnts = -11 Constant MPF2_Err_PeakCoefWrongNPnts = -12 Constant MPF2_Err_UserCancelledBatchRun = -13 Constant MPF2_Err_SingularMatrixError = -14 Constant MPF2_Err_NaNorInf = -15 Constant MPF2_Err_IterationLimit = -16 Constant MPF2_Err_OutOfMemory = -17 Constant MPF2_ErrorFromDoMPFit = -10000 // resultDFBase String containing a base name for data folders to be created to hold results. That would include the fit coefficient waves and // error estimate waves for a given data set. The data folder names will be generated by adding "_n" to resultDFBase, where n is the sequence // number of the dataset within the list yWaveList. // peakType string expression giving the name of a peak type. The peak type string will have "_PeakFuncInfo" appended; if there // is no function of that name, it returns MPF2_Err_NoSuchPeakType. // PeakCoefWaveFormat a string containing "%d" somewhere. It will be used as the format string for a call to sprintf for generating coefficient wave names for the peaks. // BLType string expression giving the name of a baseline type. If BLType is "None", no baseline is added. Otherwise, the peak type // string will have "_BLFuncInfo" appended and if there is no function of that name, it returns MPF2_Err_NoSuchBLType. // BLCoefWaveName a string containg the name to use for the baseline coefficient wave. // yWaveList list of the Y waves with peak data to be fit. // xWaveList list of the X waves with peak data to be fit. Any entry in the list that doesn't name an actual wave causes the fit to use "_calculated_". // You can use "" to indicate that there all data sets should use "_calculated_". // InitialGuessOptions 0: Do not run AutoPeakFind. Initial guesses will be pre-loaded in coefficient waves in the data folder resultDFBase+"_0". // Use those values for every data set. // 1: Do not run AutoPeakFind. Initial guesses will be pre-loaded in coefficient waves in the data folder resultDFBase+"_0". // Use those values for the first data set, then use the previous result as initial guess for the next. // 2: Do not run AutoPeakFind. Initial guesses will be pre-loaded for every data set in a series of data folders resultDFBase+"_n", n = 0,1,... // 3: Run AutoPeakFind once on the first data set and use the result as the initial guess for every data set. // 4: Run AutoPeakFind once on the first data set and use the result as the initial guess for the first data set. For every other data set, // use the result of the previous fit as the initial guess for the next. // 5: Run AutoPeakFind on every data set to generate initial guesses for each fit. // noiseEst, smFact If InitialGuessOptions > 2, and you provide values for both these parameters, then EstPeakNoiseAndSmfact() will not be called. // If InitialGuessOptions > 2, and you provide values for one of these parameters, then EstPeakNoiseAndSmfact() will be called to get // a value for the missing one. // If InitialGuessOptions > 2, and you don't provide values for either of these parameters, then EstPeakNoiseAndSmfact() will be called // to get estimated values. // If InitialGuessOptions <= 2, these are ignored. // noiseEstMult, smFactMult // Used only if InitialGuessOptions > 2. // When EstPeakNoiseAndSmfact is called to estimate noiseEst and smFact, the resulting estimates are multiplied by these factors. // These factors are used only if you don't provide your own value of noiseEst or smFact. That is, if you don't provide either noiseEst or smFact, // then EstPeakNoiseAndSmfact() will be called, and these multipliers will be applied to the results. // If you provide one of noiseEst or smFact, EstPeakNoiseAndSmfact will be called to get the missing value, and the multiplier will be applied // to the estimate of the missing value. If you provide both noiseEst and smFact, EstPeakNoiseAndSmfact() will not be called, and these factors // will be ignored. Default is 1 for both. // minAutoFindFraction Fraction of most intense peak that will be accepted as the least intense peak. Default is 0.05. Function MPF2_AutoMPFit(resultDFBase, peakType, PeakCoefWaveFormat, BLType, BLCoefWaveName, yWaveList, xWaveList, InitialGuessOptions [, noiseEst, smFact, noiseEstMult, smFactMult, minAutoFindFraction]) String resultDFBase String peakType, PeakCoefWaveFormat String BLType, BLCoefWaveName String yWaveList, xWaveList Variable InitialGuessOptions Variable noiseEst, smFact Variable noiseEstMult, smFactMult Variable minAutoFindFraction Variable nDataSets = ItemsInList(yWaveList) if (nDataSets == 0) return MPF2_Err_NoDataSets endif if ( (strlen(xWaveList) > 0) && (ItemsInList(xWaveList) != nDataSets) ) return MPF2_Err_XYListLengthMismatch // ******** EXIT *********** endif Variable i, j Variable nPeakParams Variable nBLParams String PeakFitFuncName String BLFuncName Variable nPeaks Variable/C peakFindParams string cwavename // check y waves for existence. Can't check the X waves- non-existence simply means "_calculated_" for (i = 0; i < nDataSets; i += 1) string wname = StringFromList(i, yWaveList) Wave/Z w = $wname if (!WaveExists(w)) return MPF2_Err_MissingDataSet // ******** EXIT *********** endif endfor // Find out about the peak type. String PeakInfoFuncName = peakType+PEAK_INFO_SUFFIX if (strlen(FunctionInfo(PeakInfoFuncName)) == 0) return MPF2_Err_NoSuchPeakType // ******** EXIT *********** endif FUNCREF MPF2_FuncInfoTemplate peakInfoFunc=$PeakInfoFuncName nPeakParams = ItemsInList(peakInfoFunc(PeakFuncInfo_ParamNames)) PeakFitFuncName = peakInfoFunc(PeakFuncInfo_PeakFName) // Find out about the baseline type. String BLInfoFuncName = BLType+BL_INFO_SUFFIX if (strlen(FunctionInfo(BLInfoFuncName)) == 0) return MPF2_Err_NoSuchBLType // ******** EXIT *********** endif FUNCREF MPF2_FuncInfoTemplate BLInfoFunc=$BLInfoFuncName nBLParams = ItemsInList(BLInfoFunc(BLFuncInfo_ParamNames)) // the "None" baseline type has zero parameters, but an info func exists for it BLFuncName = BLInfoFunc(BLFuncInfo_BaselineFName) Variable runEstPeakNoiseAndSmfact = 1 Variable neednoiseEst = 1 Variable needsmFact = 1 if (InitialGuessOptions < 3) runEstPeakNoiseAndSmfact = 0 endif if (runEstPeakNoiseAndSmfact) if (!ParamIsDefault(noiseEst)) neednoiseEst = 0 endif if (!ParamIsDefault(smFact)) needsmFact = 0 endif if (!(neednoiseEst || needsmFact)) runEstPeakNoiseAndSmfact = 0 endif endif if (ParamIsDefault(noiseEstMult)) noiseEstMult = 1 endif if (ParamIsDefault(smFactMult)) smFactMult = 1 endif if (ParamIsDefault(minAutoFindFraction)) minAutoFindFraction = 0.05 endif String dataFolderPath = GetDataFolder(1) String resultDF String sourceDF // Do data folder for data set 0 if (InitialGuessOptions < 3) // Sanity check for user-supplied results data folder for data set number 0 resultDF = resultDFBase+"_0" if (!DataFolderExists(resultDF)) return MPF2_Err_NoDataFolder // ******** EXIT *********** endif SetDataFolder resultDF if (nBLParams > 0) Wave w = $BLCoefWaveName if (!WaveExists(w)) SetDataFolder dataFolderPath return MPF2_Err_BLCoefWaveNotFound // ******** EXIT *********** endif if (numpnts(w) != nBLParams) SetDataFolder dataFolderPath return MPF2_Err_BLCoefWrongNPnts // ******** EXIT *********** endif endif // This loop counts the peaks as it checks the length of the coefficient waves nPeaks = 0 do sprintf cwavename, PeakCoefWaveFormat, nPeaks Wave w = $cwavename if (!WaveExists(w)) break; endif if (numpnts(w) != nPeakParams) SetDataFolder dataFolderPath return MPF2_Err_PeakCoefWrongNPnts // ******** EXIT *********** endif nPeaks += 1 while(1) if (nPeaks == 0) SetDataFolder dataFolderPath return MPF2_Err_PeakCoefWaveNotFound // ******** EXIT *********** endif Variable/G gNumPeaks = nPeaks else // Create results data folder for data set 0 Wave yw = $StringFromList(0, yWaveList) Wave/Z xw = $StringFromList(0, xWaveList) resultDF = resultDFBase+"_0" KillDataFolder/Z $resultDF NewDataFolder/O/S $resultDF if (runEstPeakNoiseAndSmfact) peakFindParams = EstPeakNoiseAndSmfact(yw, 0, numpnts(yw)-1) if (neednoiseEst) noiseEst = real(peakFindParams)*noiseEstMult endif if (needsmFact) smFact = imag(peakFindParams)*smFactMult endif endif nPeaks = AutoFindPeaks(yw, 0, numpnts(yw)-1, noiseEst, smFact, Inf) Wave wpi = W_AutoPeakInfo // may or may not exist AdjustAutoPeakInfoForX(wpi, yw, xw) nPeaks = TrimAmpAutoPeakInfo(wpi, minAutoFindFraction) MPF2_SortAutoPeakWave(wpi) CreateCWavesInCDFFromAutoPkInfo(wpi, peakType, PeakCoefWaveFormat) Variable/G gNumPeaks = nPeaks if (nBLParams > 0) Make/D/O/N=(nBLParams) $BLCoefWaveName endif endif SetDataFolder dataFolderPath // InitialGuessOptions 0: Do not run AutoPeakFind. Initial guesses will be pre-loaded in coefficient waves in the data folder resultDFBase+"_0". // Use those values for every data set. // 1: Do not run AutoPeakFind. Initial guesses will be pre-loaded in coefficient waves in the data folder resultDFBase+"_0". // Use those values for the first data set, then use the previous result as initial guess for the next. // 2: Do not run AutoPeakFind. Initial guesses will be pre-loaded for every data set in a series of data folders resultDFBase+"_n", n = 0,1,... // 3: Run AutoPeakFind once on the first data set and use the result as the initial guess for every data set. // 4: Run AutoPeakFind once on the first data set and use the result as the initial guess for the first data set. For every other data set, // use the result of the previous fit as the initial guess for the next. // 5: Run AutoPeakFind on every data set to generate initial guesses for each fit. switch (InitialGuessOptions) case 0: case 3: // these options use the data set 0 initial guesses for all data sets, so we need to duplicate the data folder for data set 0 nDataSets times sourceDF = resultDFBase+"_0" for (i = 1; i < nDataSets; i += 1) resultDF = resultDFBase+"_"+num2str(i) if (DataFolderExists(resultDF)) KillDataFolder/Z $resultDF endif DuplicateDataFolder $sourceDF, $resultDF endfor break; case 5: for (i = 1; i < nDataSets; i += 1) Wave yw = $StringFromList(i, yWaveList) Wave/Z xw = $StringFromList(i, xWaveList) resultDF = resultDFBase+"_"+num2str(i) KillDataFolder/Z $resultDF NewDataFolder/O/S $resultDF if (runEstPeakNoiseAndSmfact) peakFindParams = EstPeakNoiseAndSmfact(yw, 0, numpnts(yw)-1) if (neednoiseEst) noiseEst = real(peakFindParams)*noiseEstMult endif if (needsmFact) smFact = imag(peakFindParams)*smFactMult endif endif nPeaks = AutoFindPeaks(yw, 0, numpnts(yw)-1, noiseEst, smFact, Inf) Wave wpi = W_AutoPeakInfo // may or may not exist AdjustAutoPeakInfoForX(wpi, yw, xw) nPeaks = TrimAmpAutoPeakInfo(wpi, minAutoFindFraction) MPF2_SortAutoPeakWave(wpi) CreateCWavesInCDFFromAutoPkInfo(wpi, peakType, PeakCoefWaveFormat) Variable/G gNumPeaks = nPeaks if (nBLParams > 0) Make/D/O/N=(nBLParams) $BLCoefWaveName endif SetDataFolder dataFolderPath endfor break; case 2: // do a sanity check of each of the data folders for (i = 1; i < nDataSets; i += 1) resultDF = resultDFBase+"_"+num2str(i) if (!DataFolderExists(resultDF)) SetDataFolder dataFolderPath return MPF2_Err_NoDataFolder // ******** EXIT *********** endif SetDataFolder resultDF if (nBLParams > 0) Wave w = $BLCoefWaveName if (!WaveExists(w)) SetDataFolder dataFolderPath return MPF2_Err_BLCoefWaveNotFound // ******** EXIT *********** endif if (numpnts(w) != nBLParams) SetDataFolder dataFolderPath return MPF2_Err_BLCoefWrongNPnts // ******** EXIT *********** endif endif // This loop counts the peaks as it checks the length of the coefficient waves nPeaks = 0 do sprintf cwavename, PeakCoefWaveFormat, nPeaks Wave w = $cwavename if (!WaveExists(w)) break; endif if (numpnts(w) != nPeakParams) SetDataFolder dataFolderPath return MPF2_Err_PeakCoefWrongNPnts // ******** EXIT *********** endif nPeaks += 1 while(1) if (nPeaks == 0) SetDataFolder dataFolderPath return MPF2_Err_PeakCoefWaveNotFound // ******** EXIT *********** endif Variable/G gNumPeaks = nPeaks if (nBLParams > 0) Make/D/O/N=(nBLParams) $BLCoefWaveName endif SetDataFolder dataFolderPath endfor break; endswitch Variable nonfatalError = 0 STRUCT MPFitInfoStruct MPFs i = 0 do // for (i = 0; i < nDataSets; i += 1) Wave MPFs.yWave = $StringFromList(i, yWaveList) Wave/Z MPFs.xWave = $StringFromList(i, xWaveList) resultDF = resultDFBase+"_"+num2str(i) SetDataFolder resultDF NVAR gNumPeaks MPFs.NPeaks = gNumPeaks MPFs.XPointRangeBegin = 0 MPFs.XPointRangeEnd = numpnts(MPFs.yWave)-1 MPFs.FitCurvePoints = 100 MPFs.fitOptions = 4 MPFs.ListOfFunctions = BLType+";" MPFs.ListOfCWaveNames = BLCoefWaveName+";" MPFs.ListOfHoldStrings = "" for (j = 0; j < gNumPeaks; j += 1) sprintf cwavename, PeakCoefWaveFormat, j MPFs.ListOfFunctions += peakType+";" MPFs.ListOfCWaveNames += cwavename+";" endfor Variable err = MPF2_DoMPFit(MPFs, GetDataFolder(1), doUpdates=0, doAutoDest=0, doAutoResid=0) if (err) err += MPF2_ErrorFromDoMPFit Variable/G MPFError = err SetDataFolder dataFolderPath return err // ******** EXIT *********** endif if (MPFs.fitError || ((MPFs.fitQuitReason > 0) && (MPFs.fitQuitReason < 3)) ) Variable doRestore = 1 if (MPFs.fitQuitReason == 2) Variable/G MPFError = MPF2_Err_UserCancelledBatchRun SetDataFolder dataFolderPath return MPF2_Err_UserCancelledBatchRun // ******** EXIT *********** endif if (MPFs.fitError & 2) Variable/G MPFError = MPF2_Err_SingularMatrixError endif if (MPFs.fitError & 4) Variable/G MPFError = MPF2_Err_OutOfMemory endif if (MPFs.fitError & 8) Variable/G MPFError = MPF2_Err_NaNorInf endif if (MPFs.fitQuitReason == 1) Variable/G MPFError = MPF2_Err_NaNorInf doRestore = 0 // allow the fit to continue from where it left off if Do Fit is clicked again endif if (doRestore) MPF2_RestoreCoefWavesFromBackup(MPFs.ListOfCWaveNames, GetDataFolder(1)) endif else Variable/G MPFError = MPF2_Err_NoError endif if (MPFError) nonfatalError = MPFError endif SetDataFolder dataFolderPath i += 1 if (i == nDataSets) break; endif if ( (InitialGuessOptions == 1) || (InitialGuessOptions == 4) ) sourceDF = resultDFBase+"_"+num2str(i-1) resultDF = resultDFBase+"_"+num2str(i) if (DataFolderExists(resultDF)) //KillDataFolder/Z $resultDF KillDataFolder $resultDF endif DuplicateDataFolder $sourceDF, $resultDF endif // endfor while(1) return nonfatalError end // Function to do a multi-peak fit from client code. // Input: a structure with information about the fit, plus the name of a data folder containing coefficient waves, // one for each peak, plus a coefficient wave for the baseline (unless the baseline function is "none"). // The contents of DataFolderName is a full path to the data folder, with final ":" // Returns the function list string used in FuncFit sum-of-functions list. Function MPF2_DoMPFit(MPstruct, DataFolderName [, doUpdates, doAutoDest, doAutoResid]) STRUCT MPFitInfoStruct &MPstruct String DataFolderName Variable doUpdates, doAutoDest, doAutoResid if (ParamIsDefault(doUpdates)) doUpdates = 1 endif if (ParamIsDefault(doAutoDest)) doAutoDest = 1 endif if (ParamIsDefault(doAutoResid)) doAutoResid = 1 endif Variable npeaks = MPstruct.NPeaks Wave yw = MPstruct.yWave Wave/Z xw = MPstruct.xWave if (ItemsInList(MPstruct.ListOfFunctions) != npeaks+1) // +1 for the baseline function return MPF2_Err_BadNumberOfFunctions endif if (ItemsInList(MPstruct.ListOfCWaveNames) != npeaks+1) return MPF2_Err_BadNumberOfCWaves endif MPstruct.FuncListString = "" String holdString String BL_TypeName = StringFromList(0, MPstruct.ListOfFunctions) Variable doBaseLine = CmpStr(BL_TypeName, "None") != 0 if (doBaseLine) String BL_FuncName Variable nBLParams FUNCREF MPF2_FuncInfoTemplate blinfo = $(BL_typename + BL_INFO_SUFFIX) BL_FuncName = blinfo(BLFuncInfo_BaselineFName) nBLParams = ItemsInList(blinfo(BLFuncInfo_ParamNames)) if (nBLParams == 0) return MPF2_Err_NoSuchBLType endif STRUCT MPF2_BLFitStruct BLStruct if (WaveExists(xw)) BLStruct.xStart = xw[MPstruct.XPointRangeBegin] BLStruct.xEnd = xw[MPstruct.XPointRangeEnd] else BLStruct.xStart = pnt2x(yw, MPstruct.XPointRangeBegin) BLStruct.xEnd = pnt2x(yw, MPstruct.XPointRangeEnd) endif String blcoefwname = StringFromList(0, MPstruct.ListOfCWaveNames) Wave/Z blcoefwave = $(DataFolderName+PossiblyQuoteName(blcoefwname)) if (!WaveExists(blcoefwave)) return MPF2_Err_BLCoefWaveNotFound endif MPstruct.FuncListString += "{"+BL_FuncName+", "+GetWavesDataFolder(blcoefwave,2) Duplicate/O blcoefwave, blepswave blepswave = 1e-6 MPstruct.FuncListString += ", EPSW="+GetWavesDataFolder(blepswave,2) holdString = StringFromList(0, MPstruct.ListOfHoldStrings) if (strlen(holdString) > 0) MPstruct.FuncListString += ", HOLD=\""+holdString+"\"" endif MPstruct.FuncListString += ", STRC=BLStruct}" endif Variable i for (i = 0; i < nPeaks; i += 1) String PeakTypeName = StringFromList(i+1, MPstruct.ListOfFunctions) FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) String PeakFuncName = infoFunc(PeakFuncInfo_PeakFName) if (strlen(PeakFuncName) == 0) return MPF2_Err_NoSuchPeakType endif String pwname = StringFromList(i+1, MPstruct.ListOfCWaveNames) pwname = PossiblyQuoteName(pwname) pwname = DataFolderName + pwname Wave/Z coefw = $pwname if (!WaveExists(coefw)) return MPF2_Err_PeakCoefWaveNotFound endif MPstruct.FuncListString += "{"+PeakFuncName+","+pwname Duplicate/O coefw, $(NameOfWave(coefw)+"eps") Wave epsw = $(NameOfWave(coefw)+"eps") epsw = 1e-6 MPstruct.FuncListString += ", EPSW="+GetWavesDataFolder(epsw,2) holdString = StringFromList(i+1, MPstruct.ListOfHoldStrings) // i+1 to account for the fact that the first hold string goes with the baseline if (strlen(holdString) > 0) MPstruct.FuncListString += ", HOLD=\""+holdString+"\"" endif MPstruct.FuncListString += "}" endfor Variable V_FitQuitReason = 0 Variable V_FitMaxIters=500 Variable V_FitOptions=MPStruct.fitOptions //print MPstruct.FuncListString MPstruct.fitErrorMsg = "" MPF2_BackupCoefWaves(MPstruct.ListOfCWaveNames, DataFolderName) Variable errorCode=0 DebuggerOptions Variable doDebugOnError = V_debugOnError DebuggerOptions debugOnError=0 try FuncFit/Q=1/N=(doUpdates == 0 ? 1 : 0)/M=2 {string=MPstruct.FuncListString} yw[MPstruct.XPointRangeBegin, MPstruct.XPointRangeEnd] /X=xw[MPstruct.XPointRangeBegin, MPstruct.XPointRangeEnd]/W=MPstruct.weightWave[MPstruct.XPointRangeBegin, MPstruct.XPointRangeEnd]/I=1/M=MPstruct.maskWave[MPstruct.XPointRangeBegin, MPstruct.XPointRangeEnd] /AD=(doAutoDest)/AR=(doAutoResid)/A=1/NWOK;AbortOnRTE catch MPstruct.fitErrorMsg = GetRTErrMessage() Variable semiPos = strsearch(MPstruct.fitErrorMsg, ";", 0) if (semiPos >= 0) MPstruct.fitErrorMsg = MPstruct.fitErrorMsg[semiPos+1, inf] endif errorCode = GetRTError(1) endtry DebuggerOptions debugOnError=doDebugOnError MPstruct.dateTimeOfFit = DateTime MPstruct.fitPnts = V_npnts MPstruct.chisq = V_chisq MPstruct.fitError = errorCode MPstruct.fitQuitReason = V_FitQuitReason return MPF2_Err_NoError end Function MPF2_RestoreCoefWavesFromBackup(listofWaveNames, DataFolderName) String listofWaveNames, DataFolderName String saveDF = GetDataFolder(1) SetDataFolder DataFolderName Variable nWaves = ItemsInList(listofWaveNames) Variable i for (i = 0; i < nWaves; i += 1) Wave backupWave = $("MPF2_CoefsBackup_"+num2istr(i)) Duplicate/O backupWave, $StringFromList(i, listofWaveNames) endfor end Function fStartMultipeakFit2() if (WinType("MultiPeak2StarterPanel") == 7) DoWindow/F MultiPeak2StarterPanel return 0 endif MultiPeakFit2_Initialize() end Function MultiPeakFit2_Initialize() if (!DataFolderExists("root:Packages:MultiPeakFit2")) String SaveDF = GetDataFolder(2) SetDataFolder root: NewDataFolder/O/S Packages NewDataFolder/O/S MultiPeakFit2 Variable/G currentSetNumber = 0 String/G MPF2_DoFitHelpBoxText MPF2_DoFitHelpBoxText = "To get started, add peaks to the list." MPF2_DoFitHelpBoxText += "\rEither click the \f01Auto-locate Peaks\f]0 button, above," MPF2_DoFitHelpBoxText += "\ror drag a marquee on the graph and select" MPF2_DoFitHelpBoxText += "\r\f01Add or Edit Peaks\f]0 from the marquee menu." Variable/G MPF2_DontShowHelpMessage=0 SetDataFolder saveDF endif fBuildMultiPeak2StarterPanel() end Function fBuildMultiPeak2StarterPanel() NewPanel /W=(508,354,772,533)/K=1 as "Start Multi-peak Fit" RenameWindow $S_name, MultiPeak2StarterPanel TitleBox MPF2_YWaveButtonTitle,pos={11,10},size={35,12},title="Y Wave:",frame=0 //\K(0,0,65535)xxx\K(52428,1,1)yyy\K(0,0,0)zzz TitleBox MPF2_XWaveButtonTitle,pos={11,34},size={35,12},title="X Wave:",frame=0 Button MPF2_SelectYWaveButton,pos={52,6},size={200,20},title="",fSize=10 MakeButtonIntoWSPopupButton("MultiPeak2StarterPanel", "MPF2_SelectYWaveButton", "MPF2_WaveSelectNotify") Button MPF2_SelectXWaveButton,pos={52,30},size={200,20},title="",fSize=10 MakeButtonIntoWSPopupButton("MultiPeak2StarterPanel", "MPF2_SelectXWaveButton", "MPF2_WaveSelectNotify") PopupWS_AddSelectableString("MultiPeak2StarterPanel", "MPF2_SelectXWaveButton", "_calculated_") PopupWS_SetSelectionFullPath("MultiPeak2StarterPanel", "MPF2_SelectXWaveButton", "_calculated_") CheckBox MPF2_StartPanel_FromTarget,pos={17,62},size={72,14},title="From Target" CheckBox MPF2_StartPanel_FromTarget,value= 0,proc=MPF2_Starter_FromTitleCheckProc PopupMenu MPF2_StartPanel_TraceMenu,pos={94,59},size={152,20},proc=MPF2_StarterChooseTraceMenu,title="Set Waves from Trace" PopupMenu MPF2_StartPanel_TraceMenu,mode=0,value= #"TraceNameList(WinName(0,1), \";\", 1)", disable=1 PopupMenu MPF2_ChooseGraph,pos={34,100},size={190,20},title="Use Graph:" PopupMenu MPF2_ChooseGraph,mode=1,bodyWidth= 140,value= #"\"New Graph;\"+MPF2_ListGraphsWSelectedWaves()" PopupMenu MPF2_PanelPositionMenu,pos={50,127},size={123,20},title="Panel Position" PopupMenu MPF2_PanelPositionMenu,mode=2,value= #"\"Below;Right;Left;Above\"" Button MPF2_DataSelectedContinueButton,pos={11,153},size={100,20},proc=MPF2_WaveSelectContinueBtnProc,title="Continue",fSize=10 Button MPF2_StartPanelHelp,pos={187,153},size={60,20},proc=MPF2_DoHelpButtonProc,title="Help",fSize=10 SetWindow MultiPeak2StarterPanel,hook(MPF2_StarterHook)=MPF2_StarterHook // center the panel in the monitor or the Windows MDI window Variable scrnLeft, scrnTop, scrnRight, scrnBottom, scrnWidth, scrnHeight if (CmpStr(IgorInfo(2), "Macintosh") == 0) String scrnInfo = StringByKey("SCREEN1", IgorInfo(0)) Variable rectPos = strsearch(scrnInfo, "RECT=", 0) scrnInfo = scrnInfo[rectPos+5, strlen(scrnInfo)-1] sscanf scrnInfo, "%d,%d,%d,%d", scrnLeft, scrnTop, scrnRight, scrnBottom scrnWidth = scrnRight-scrnLeft scrnHeight = scrnBottom-scrnTop elseif (CmpStr(IgorInfo(2), "Windows") == 0) GetWindow kwFrameInner, wsize scrnWidth = V_right-V_left scrnHeight = V_bottom-V_top endif Variable halfWidth = 132*72/screenResolution Variable Height = 179*72/screenResolution MoveWindow/W=MultiPeak2StarterPanel scrnWidth/2 - halfWidth, scrnHeight/2 - Height, scrnWidth/2 + halfWidth, scrnHeight/2 end Function MPF2_StarterHook(s) STRUCT WMWinHookStruct &s strswitch (s.eventName) case "activate": String lastTarget = GetUserData(s.winName, "", "lastTarget") if (CmpStr(lastTarget, WinName(0, 1+2, 1)) != 0) ControlInfo/W=MultiPeak2StarterPanel MPF2_StartPanel_FromTarget if (V_value) // from target is checked MPF2_Starter_FromTitleCheckProc("MPF2_StartPanel_FromTarget", 1) //print WinName(0, 0xFFFF), WinName(1, 0xFFFF) endif SetWindow $s.winName, userData(lastTarget)=WinName(0, 1+2, 1) endif break; // case "deactivate": // SetWindow $s.winName, userData(lastTarget)=WinName(0, 1+2, 1) // break; endswitch if (CmpStr(s.eventName, "activate") == 0) endif end Function BuildMultiPeak2Panel(hostgraph, setNumber, position) String hostgraph Variable setNumber Variable position DoWindow/F $hostgraph Variable left, top, right, bottom switch(position) case 0: // right left=0; top=346; right=261; bottom=346; break; case 1: // left left=261; top=346; right=0; bottom=346; break; case 2: // below left=261; top=0; right=261; bottom=346; break; case 3: // above left=261; top=346; right=261; bottom=0; break; endswitch NewPanel /W=(left,top,right,bottom)/K=1/HOST=$hostgraph/EXT=(position) as "Multi-peak Fit Set "+num2str(setNumber) String panelName = hostgraph+"#"+S_name RenameWindow $panelName, MultiPeak2Panel panelName = hostgraph+"#MultiPeak2Panel" ModifyPanel/W=$panelName fixedSize=0 DefineGuide UGH0={FT,25} DefineGuide UGH1={UGH0,68} DefineGuide UGH3={FB,-23} DefineGuide UGH2={UGH3,-89} String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Variable/G $(DFPath+":negativePeaks") = NumVarOrDefault(DFPath+":negativePeaks", 0) NVAR negativePeaks = $(DFPath+":negativePeaks") Variable/G $(DFPath+":panelPosition") = position Variable/G $(DFPath+":MPF2_UserCursors") = NumVarOrDefault(DFPath+":MPF2_UserCursors", 0) NVAR useCursors = $(DFPath+":MPF2_UserCursors") CheckBox MPF2_UserCursorsCheckbox,win=$panelName,pos={10,4},size={102,14},title="Use Graph Cursors" CheckBox MPF2_UserCursorsCheckbox,win=$panelName,value=useCursors Button MPF2_HelpButton,pos={203,1},size={50,20},title="Help", proc=MPF2_DoHelpButtonProc NewPanel/N=P0/W=(0,86,261,149)/FG=(,UGH0,,UGH1)/HOST=$panelName ModifyPanel frameStyle=0, frameInset=0 GroupBox MPF2_LocatePeaksGroupBox,pos={8,2},size={244,61},title="Locate Peaks" GroupBox MPF2_LocatePeaksGroupBox,fStyle=1 Button MPF2_AutoLocatePeaksButton,pos={44,21},size={158,20},proc=MPF2_AutoLocatePeaksButtonProc,title="Auto-locate Peaks Now" Button MPF2_AutoLocatePeaksButton,fSize=10 CheckBox MPF2_NegativePeaksCheck,pos={74,44},size={85,14},title="Negative Peaks" CheckBox MPF2_NegativePeaksCheck,variable=negativePeaks CheckBox MPF2_DiscloseAutoPickParams,pos={14,45},size={16,14},proc=MPF2_DiscloseAutoPickCheckProc,title="" CheckBox MPF2_DiscloseAutoPickParams,value=0,mode=2 SetActiveSubwindow ## NewPanel/N=P1/FG=(FL,UGH1,FR,UGH2)/HOST=$panelName ModifyPanel frameStyle=0, frameInset=0 String listPanel = panelName+"#P1" ListBox MPF2_PeakList,win=$listPanel,pos={6,4},size={241,134},clickEventModifiers=2+4 // don't allow checkbox toggle or selection with option or context click ListBox MPF2_PeakList win=$listPanel,userdata(MPF2_DataSetNumber)=num2str(setnumber) MakeListIntoHierarchicalList(listPanel, "MPF2_PeakList", "PeakListOpenNotify", selectionMode=WMHL_SelectionNonContinguous, userListProc="MPF2_PeakListProc") WMHL_AddColumns(listPanel, "MPF2_PeakList", 2 ) WMHL_SetNotificationProc(listPanel, "MPF2_PeakList", "PeakListClosingNotify", WMHL_SetClosingNotificationProc) WMHL_AddObject(listPanel, "MPF2_PeakList", "", "Baseline", 1) WMHL_ExtraColumnData(listPanel, "MPF2_PeakList", 0, 0, "Constant"+MENU_ARROW_STRING, 0) ListBox MPF2_PeakList win=$listPanel,widths={4,15,16,10} SetActiveSubwindow ## NewPanel/N=P2/W=(65,86,261,260)/FG=(FL,UGH2,FR,UGH3)/HOST=$panelName ModifyPanel frameStyle=0, frameInset=0 String buttonPanel = panelName+"#P2" PopupMenu MPF2_SetAllPeakTypesMenu,win=$buttonPanel,pos={65,3},size={185,20},proc=MPF2_SetAllPeakTypesMenuProc,title="Set Peak Type for All Peaks" PopupMenu MPF2_SetAllPeakTypesMenu,win=$buttonPanel,mode=0,value= #"MPF2_ListPeakTypeNames()" Button MPF2_DoFitButton,win=$buttonPanel,pos={19,35},size={50,20},proc=MPF2_DoFitButtonProc,title="Do Fit",fSize=10 Button MPF2_DoFitButton,win=$buttonPanel,fStyle=1,fColor=(32768,32770,65535), disable=2 Button MPF2_PeakResultsButton,win=$buttonPanel,pos={141,35},size={109,20},proc=MPF2_PeakResultsButtonProc,title="Peak Results...",fSize=10 Button MPF2_RevertToGuessesButton,win=$buttonPanel,pos={19,65},size={109,20},proc=MPF2_RevertToPreviousButtonProc,title="Revert to Guesses",fSize=10 SetActiveSubwindow ## NVAR MPF2_DontShowHelpMessage = root:Packages:MultiPeakFit2:MPF2_DontShowHelpMessage NewPanel/FG=(FL,UGH2,FR,FB)/HOST=# TitleBox MPF2_DoFitHelpBox,pos={14,35},size={193,60},frame=0 TitleBox MPF2_DoFitHelpBox,variable= root:Packages:MultiPeakFit2:MPF2_DoFitHelpBoxText Button MPF2_HelpPanelCloseButton,pos={113,12},size={30,20},title="OK",proc=MPF2_DoFitHelpBoxButtonProc CheckBox MPF2_HelpPanelDontShow,pos={16,88},size={169,14},title="Don't show me this message again" CheckBox MPF2_HelpPanelDontShow,variable=MPF2_DontShowHelpMessage RenameWindow #,HelpPanel SetActiveSubwindow ## NewPanel/W=(65,86,196,346)/FG=(FL,UGH3,FR,FB)/HOST=# ModifyPanel frameStyle=0, frameInset=0 NVAR/Z MPF2OptionsShowing = $(DFPath+":MPF2OptionsShowing") if (!NVAR_Exists(MPF2OptionsShowing)) Variable/G $(DFPath+":MPF2OptionsShowing") NVAR MPF2OptionsShowing = $(DFPath+":MPF2OptionsShowing") MPF2OptionsShowing = 0 String/G $(DFPath+":MPF2WeightWaveName") SVAR MPF2WeightWaveName = $(DFPath+":MPF2WeightWaveName") MPF2WeightWaveName = "" String weightPath = "_none_" String/G $(DFPath+":MPF2MaskWaveName") SVAR MPF2MaskWaveName = $(DFPath+":MPF2MaskWaveName") MPF2MaskWaveName = "" String maskPath = "_none_" else SVAR MPF2WeightWaveName = $(DFPath+":MPF2WeightWaveName") SVAR MPF2MaskWaveName = $(DFPath+":MPF2MaskWaveName") Wave/Z w = $MPF2WeightWaveName if (WaveExists(w)) weightPath = GetWavesDataFolder(w, 2) else weightPath = "_none_" endif Wave/Z w = $MPF2MaskWaveName if (WaveExists(w)) maskPath = GetWavesDataFolder(w, 2) else maskPath = "_none_" endif endif CheckBox MPF2_DiscloseOptions,pos={10,5},size={51,14},title="Options",value= 0,mode=2,proc=MPF2_DiscloseOptions,fsize=10 TitleBox MPF2_MaskWaveTitle,pos={25,33},size={57,13},title="Mask Wave:",fSize=10,frame=0 Button MPF2_SelectMaskWave,pos={91,30},size={159,20},title="" MakeButtonIntoWSPopupButton(panelName+"#P3", "MPF2_SelectMaskWave", "MPF2_MaskWaveSelectNotify") PopupWS_AddSelectableString(panelName+"#P3", "MPF2_SelectMaskWave", "_none_") PopupWS_SetSelectionFullPath(panelName+"#P3", "MPF2_SelectMaskWave", maskPath) TitleBox MPF2_WeightWaveTitle,pos={15,59},size={67,13},title="Weight Wave:",fSize=10,frame=0 Button MPF2_SelectWeightWave,pos={91,55},size={159,20},title="" MakeButtonIntoWSPopupButton(panelName+"#P3", "MPF2_SelectWeightWave", "MPF2_WeightWaveSelectNotify") PopupWS_AddSelectableString(panelName+"#P3", "MPF2_SelectWeightWave", "_none_") PopupWS_SetSelectionFullPath(panelName+"#P3", "MPF2_SelectWeightWave", weightPath) SetVariable MPF2_SetFitCurvePoints,pos={50,87},size={143,16},bodyWidth=60,title="Fit Curve Points:" SetVariable MPF2_SetFitCurvePoints,fSize=10,value= _STR:"Auto",proc=MPF2_SetFitCurvePointsProc RenameWindow #,P3 SetActiveSubwindow ## if (MPF2_DontShowHelpMessage) SetWindow $(panelName+"#HelpPanel"),hide=1 else SetWindow $buttonPanel,hide=1 SetWindow $(panelName+"#P3"),hide=1 endif SetWindow $panelName userdata(MPF2_hostgraph) = hostgraph SetWindow $panelName userdata(MPF2_DataSetNumber) = num2str(setNumber) SetWindow $hostgraph, hook(MPF2_DataGraphHook)=MPF2_DataGraphHook SetWindow $panelName, hook(MPF2_PanelKillHook)=MPF2_PanelKillHook SetWindow $panelName, hook(MPF2_PanelResizeHook)=MPF2_PanelResizeHook if (MPF2OptionsShowing) STRUCT WMCheckboxAction s s.win = panelName+"#P3" s.checked = 1 s.eventCode = 2 CheckBox MPF2_DiscloseOptions,win=$(panelName+"#P3"),value=1 MPF2_DiscloseOptions(s) endif EndMacro Function MPF2_SetFitCurvePointsProc(s) : SetVariableControl struct WMSetVariableAction &s switch (s.eventCode) case 2: // enter key case 3: // live update String panelName = ParseFilePath(1, s.win, "#", 1, 0) panelName = panelName[0, strlen(panelName)-2] // ParseFilePath leaves the separator string on the end Variable setNumber = GetSetNumberFromWinName(s.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") SVAR YWvName = $(DFpath+":YWvName") Wave YData = $YWvName SVAR XWvName = $(DFpath+":XWvName") Wave/Z XData = $XWvName MPF2_AddFitCurveToGraph(setNumber, wpi, YData, XData, 1, overridePoints=MPF2_getFitCurvePoints(panelName)) break; endswitch end static Function MPF2_EnableDisableDoFitButton(setNumber) Variable setNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") SVAR gname = $(DFpath+":GraphName") String panelName = gname+"#MultiPeak2Panel" // the hook function is attached to the host graph if (!WaveExists(wpi) || (DimSize(wpi, 0) == 0)) Button MPF2_DoFitButton,win=$(panelName+"#P2"),disable=2 TitleBox MPF2_DoFitHelpBox,win=$(panelName+"#P2"),disable=0 else Button MPF2_DoFitButton,win=$(panelName+"#P2"),disable=0 TitleBox MPF2_DoFitHelpBox,win=$(panelName+"#P2"),disable=1 endif end static Function/S MPF2_FindPeaksOutOfRange(setNumber, pl, pr) Variable setNumber, pl, pr // String saveDF = GetDataFolder(1) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) // SetDataFolder DFpath Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") SVAR YWvName = $(DFpath+":YWvName") Wave yw = $YWvName SVAR XWvName = $(DFpath+":XWvName") Wave/Z xw = $XWvName Variable XStart, XEnd if (WaveExists(xw)) XStart = xw[pl] XEnd = xw[pr] else XStart = pnt2x(yw, pl) XEnd = pnt2x(yw, pr) endif String peakList = "" if (WaveExists(wpi)) Variable npeaks = DimSize(wpi, 0) Variable i for (i = 0; i < npeaks; i += 1) if ( (wpi[i][0] < XStart) || (wpi[i][0] > XEnd) ) peakList += num2str(i)+";" endif endfor endif return peakList // SetDataFolder saveDF end static Function MPF2_getFitCurvePoints(panelName) String panelName Variable points = 0 ControlInfo/W=$(panelName+"#P3") MPF2_SetFitCurvePoints String pointsStr = S_value if (CmpStr(S_value, "Auto") != 0) points = str2num(pointsStr) if (Numtype(points) != 0) points = 0 endif endif return points end // returns 1 to indicate the peak was deleted, or 0 if it was not. It might not be deleted if the peakItemString is not a peak parent row. static Function MPF2_DeleteAPeak(gname, peakItemString) String gname, peakItemString Variable returnValue = 0 String panelName = gname+"#MultiPeak2Panel" // the hook function is attached to the host graph Variable setNumber = GetSetNumberFromWinName(gname) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") SVAR YWvName = $(DFpath+":YWvName") Wave YData = $YWvName SVAR XWvName = $(DFpath+":XWvName") Wave/Z XData = $XWvName if (WaveExists(wpi)) Variable selectedRow = WMHL_GetRowNumberForItem(panelName+"#P1", "MPF2_PeakList", peakItemString) Variable BaselineRow = WMHL_GetRowNumberForItem(panelName+"#P1", "MPF2_PeakList", "Baseline") if (selectedRow > BaselineRow) // don't delete the baseline row // save the baseline type String baselineStr = WMHL_GetExtraColumnData(panelName+"#P1", "MPF2_PeakList", 0, BaselineRow) String BL_TypeName = MPF2_PeakOrBLTypeFromListString(baselineStr) Variable peakNumber if (WMHL_RowIsContainer(panelName+"#P1", "MPF2_PeakList", selectedRow)) sscanf peakItemString, "Peak %d", peakNumber MPF2_RemoveAllPeaksFromGraph(gname) DoUpdate MPF2_DeletePeakInfo(setNumber, peakNumber) NVAR negativePeaks = $(DFpath+":negativePeaks") MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1) MPF2_EnableDisableDoFitButton(setNumber) // DoUpdate MPF2_AddFitCurveToGraph(setNumber, wpi, YData, XData, 1, overridePoints=MPF2_getFitCurvePoints(panelName)) // DoUpdate returnValue = 1 endif MPF2_InfoForBaseline(setnumber, BL_TypeName) endif endif return returnValue end Function MPF2_DataGraphHook(s) STRUCT WMWinHookStruct &s Variable i, nItems Variable poundPos = strsearch(s.winName, "#", 0) String gname = s.winName String panelName = s.winName+"#MultiPeak2Panel" // the hook function is attached to the host graph Variable setNumber = GetSetNumberFromWinName(gname) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Variable returnValue = 0 Variable peakNumber strswitch(s.eventName) case "cursormoved": ControlInfo/W=$panelName MPF2_UserCursorsCheckbox if (!V_value) return 0 endif NVAR XPointRangeBegin = $(DFPath+":XPointRangeBegin") NVAR XPointRangeEnd = $(DFPath+":XPointRangeEnd") Variable pl = pcsr(A) Variable pr = pcsr(B) Variable oldpl = XPointRangeBegin Variable oldpr = XPointRangeEnd XPointRangeEnd = pr XPointRangeBegin = pl if (pl > pr) Variable temp = pl pl = pr pr = temp endif if ( (pl > oldpl) || (pr < oldpr) ) String peakList = MPF2_FindPeaksOutOfRange(setNumber, pl, pr) if (strlen(peakList) > 0) Variable peaksToDelete = ItemsInList(peakList) MPF2_RemoveAllPeaksFromGraph(gname) for (i = peaksToDelete-1; i >= 0 ; i -= 1) peakNumber = str2num(StringFromList(i, peakList)) MPF2_DeletePeakInfo(setNumber, peakNumber) endfor NVAR negativePeaks = $(DFpath+":negativePeaks") Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1) MPF2_EnableDisableDoFitButton(setNumber) //DoUpdate SVAR YWvName = $(DFpath+":YWvName") Wave YData = $YWvName SVAR XWvName = $(DFpath+":XWvName") Wave/Z XData = $XWvName MPF2_AddFitCurveToGraph(setNumber, wpi, YData, XData, 1, overridePoints=MPF2_getFitCurvePoints(panelName)) //DoUpdate endif endif break; case "renamed": SVAR savedGName = $(DFpath+":GraphName") savedGName = s.winName break; endswitch return returnValue end Function MPF2_PanelKillHook(s) STRUCT WMWinHookStruct &s // the panel is #MultiPeak2Panel, but the subwindows will get kill events also, and they have names like #MultiPeak2Panel#P0 Variable firstPoundPos = strsearch(s.winName, "#", 0, 0) if (firstPoundPos > 0) string hostname = s.winName hostname = hostname[firstPoundPos+1, strlen(s.winName)-1] if (CmpStr(hostname, "MultiPeak2Panel") != 0) return 0 endif endif // if ( (s.eventCode == 2) || (s.eventCode == 14) ) // kill or subwindowKill. This hook function will receive kill if the panel close box is clicked, or subwindowkill if the host graph is killed strswitch (s.eventName) case "kill": case "subwindowKill": // save a list of the peak functions and baseline function for use in re-constituting the peak list in the future MPF2_RefreshHoldStrings(s.winName) MPF2_SaveFunctionTypes(s.winName) Variable setNumber = GetSetNumberFromWinName(s.winName) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Variable/G $(DFPath+":MPF2_UserCursors") NVAR useCursors = $(DFPath+":MPF2_UserCursors") ControlInfo/W=$(s.winName) MPF2_UserCursorsCheckbox useCursors = V_value if (WinType("EditOrAddPeaksGraph") == 1) Variable editSetNumber = str2num(GetUserData("EditOrAddPeaksGraph", "", "MPF2_DataSetNumber")) if (editSetNumber == setNumber) DoWindow/K EditOrAddPeaksGraph endif endif break; endswitch end static constant minWidth=266 static constant minHeight=346 Function MPF2_PanelResizeHook(s) STRUCT WMWinHookStruct &s if ( s.eventCode == 6) // resize // enforce minimum size Variable factor = ScreenResolution/72 GetWindow $s.winName wsize Variable left = V_left*factor Variable right = V_right*factor Variable top = V_top*factor Variable bottom = V_bottom*factor Variable height = minHeight ControlInfo/W=$(s.winName+"#P0") MPF2_DiscloseAutoPickParams if (V_value) height = minHeight+65 endif height = max(height, bottom-top) Variable width = max(minWidth, right-left) Variable setNumber = GetSetNumberFromWinName(s.winName) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) NVAR position = $(DFPath+":panelPosition") // height /= factor // width /= factor switch(position) case 0: // right left=0; top=height; right=width; bottom=height; break; case 1: // left left=width; top=height; right=0; bottom=height; break; case 2: // below left=width; top=0; right=width; bottom=height; break; case 3: // above left=width; top=height; right=width; bottom=0; break; endswitch MoveSubWindow/W=$s.winName fnum=(left, top, right, bottom) // re-size the listbox width = NumberByKey("POSITION", GuideInfo(s.winName, "FR")) top = NumberByKey("POSITION", GuideInfo(s.winName, "UGH1")) bottom = NumberByKey("POSITION", GuideInfo(s.winName, "UGH2")) height = bottom-top ListBox MPF2_PeakList,win=$(s.winName+"#P1"),size={width-20,height-7} endif end Function MPF2_SaveFunctionTypes(panelName) String panelName // full subwindow path String saveDF = GetDataFolder(1) Variable setNumber = GetSetNumberFromWinName(panelName) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SetDataFolder DFpath String/G SavedFunctionTypes Wave/Z wpi = W_AutoPeakInfo SetDataFolder saveDF Variable BaselineRow = WMHL_GetRowNumberForItem(panelName+"#P1", "MPF2_PeakList", "Baseline") String baselineStr = WMHL_GetExtraColumnData(panelName+"#P1", "MPF2_PeakList", 0, BaselineRow) SavedFunctionTypes = MPF2_PeakOrBLTypeFromListString(baselineStr)+";" Variable nPeaks = 0 if (WaveExists(wpi)) nPeaks = DimSize(wpi, 0) endif Variable i for (i = 0; i < nPeaks; i += 1) Variable theRow = WMHL_GetRowNumberForItem(panelName+"#P1", "MPF2_PeakList", "Peak "+num2istr(i)) SavedFunctionTypes += MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(panelName+"#P1", "MPF2_PeakList", 0, theRow) ) + ";" endfor end Function MPF2_WaveSelectNotify(event, wavepath, windowName, ctrlName) Variable event String wavepath String windowName String ctrlName ControlInfo/W=MultiPeak2StarterPanel MPF2_ChooseGraph String selection = S_value String graphItems = MPF2_ListGraphsWSelectedWaves() Variable selectedItem if (strlen(graphItems) == 0) selectedItem = 1 else selectedItem = WhichListItem(selection, graphItems) selectedItem += 2 // 1 for zero-based list index, 1 for the New Graph item at the start of the menu. If nothing found, returns -1, so this results in selecting item 1, the New Graph item. endif PopupMenu MPF2_ChooseGraph,win=MultiPeak2StarterPanel,mode=(selectedItem) ControlUpdate/W=MultiPeak2StarterPanel MPF2_ChooseGraph end Function MPF2_TraceMenuProc(ctrlName,popNum,popStr) : PopupMenuControl String ctrlName Variable popNum String popStr Wave yw = TraceNameToWaveRef("", popStr) Wave/Z xw = XWaveRefFromTrace("", popStr) String waveTitle = "Y Wave: "+NameOfWave(yw)+"\rX Wave: " if (WaveExists(xw)) waveTitle += NameOfWave(xw) else waveTitle += "_calculated_" endif TitleBox MPF2_InfoTitleBox, title=waveTitle // Do auto-find End Function MPF2_StarterChooseTraceMenu(ctrlName,popNum,popStr) : PopupMenuControl String ctrlName Variable popNum String popStr String gname = WinName(0,1) Wave yw = TraceNameToWaveRef(gname, popStr) Wave/Z xw = XWaveRefFromTrace(gname, popStr) PopupWS_SetSelectionFullPath("MultiPeak2StarterPanel", "MPF2_SelectYWaveButton", GetWavesDataFolder(yw, 2)) if (WaveExists(xw)) PopupWS_SetSelectionFullPath("MultiPeak2StarterPanel", "MPF2_SelectXWaveButton", GetWavesDataFolder(xw, 2)) else PopupWS_SetSelectionFullPath("MultiPeak2StarterPanel", "MPF2_SelectXWaveButton", "_calculated_") endif End Function/S MPF2_ListGraphsWSelectedWaves() String yWName = PopupWS_GetSelectionFullPath("MultiPeak2StarterPanel", "MPF2_SelectYWaveButton") String xWName = PopupWS_GetSelectionFullPath("MultiPeak2StarterPanel", "MPF2_SelectXWaveButton") Wave/Z yw = $yWName Wave/Z xw = $xWName if (!WaveExists(yw)) return "" endif return MPF2_ListGraphsWithWaves(yw, xw) end Function/S MPF2_ListGraphsWithWaves(yw, xw) Wave yw Wave/Z xw String theList="" Variable i=0 do String gname = WinName(i, 1) if (strlen(gname) == 0) break; endif Variable gotit = 0 CheckDisplayed/W=$gname yw if (V_flag) String tlist = TraceNameList(gname, ";", 1) Variable nTraces = ItemsInList(tlist) Variable j=0 for (j = 0; j < nTraces; j += 1) String tname = StringFromList(j, tlist) Wave dyw = TraceNameToWaveRef(gname, tname) if (CmpStr(GetWavesDataFolder(dyw, 2), GetWavesDataFolder(yw, 2)) == 0) if (WaveExists(xw)) Wave/Z dxw = XWaveRefFromTrace(gname, tname) if (WaveExists(dxw) && (CmpStr(GetWavesDataFolder(dxw, 2), GetWavesDataFolder(xw, 2)) == 0) ) gotit = 1 break; endif else if (!WaveExists(XWaveRefFromTrace(gname, tname))) gotit = 1 break; endif endif endif endfor endif if (gotit) theList += gname+";" endif i += 1 while(1) return theList end //Function MPF2_CopyGraphButtonProc(ctrlName) : ButtonControl // String ctrlName // //// GetUserData(winName, objID, userdataName ) // String winRecreationStr = WinRecreation(WinName(0, 1), 0) // Execute/Z winRecreationStr // DoWindow/F MultiPeak2Panel //End Function MPF2_Starter_FromTitleCheckProc(ctrlName,checked) : CheckBoxControl String ctrlName Variable checked // 1 if selelcted, 0 if not String targetWin = WinName(0, 1+2, 1) String optionsStr = "" if (checked) optionsStr = "WIN:"+targetWin+",DIMS:1" endif PopupWS_MatchOptions("MultiPeak2StarterPanel", "MPF2_SelectYWaveButton", listoptions = optionsStr) PopupWS_MatchOptions("MultiPeak2StarterPanel", "MPF2_SelectXWaveButton", listoptions = optionsStr) if (checked) if (WinType(targetWin) == 1) // target is a graph: pick the first trace and pre-set the popups to the Y and X waves from the first trace; pre-select the target graph as the graph to use String tracename = StringFromList(0, TraceNameList(targetWin, ";", 1)) Wave yw = TraceNameToWaveRef(targetWin, tracename) Wave/Z xw = XWaveRefFromTrace(targetWin, tracename) PopupWS_SetSelectionFullPath("MultiPeak2StarterPanel", "MPF2_SelectYWaveButton", GetWavesDataFolder(yw, 2)) if (WaveExists(xw)) PopupWS_SetSelectionFullPath("MultiPeak2StarterPanel", "MPF2_SelectXWaveButton", GetWavesDataFolder(xw, 2)) else PopupWS_SetSelectionFullPath("MultiPeak2StarterPanel", "MPF2_SelectXWaveButton", "_calculated_") endif // String graphs = MPF2_ListGraphsWSelectedWaves() // Variable itemNumber = WhichListItem(targetWin, graphs)+2 // +1 for one-based menu items, +1 for "New Graph" being item 1 // PopupMenu MPF2_ChooseGraph,win=MultiPeak2StarterPanel,mode=itemNumber PopupMenu MPF2_ChooseGraph,win=MultiPeak2StarterPanel,mode=1 PopupMenu MPF2_StartPanel_TraceMenu,win=MultiPeak2StarterPanel, disable=0 elseif(WinType(targetWin) == 2) // target is a table. Pre-select "New Graph". Pre-select _calculated_ as the X wave (?); Pre-select first wave in table as the Y wave. Variable i=0 do Wave/Z w = WaveRefIndexed(targetWin, i, 1) if (WaveExists(w)) if (WaveDims(w) == 1) break; endif else break; endif i += 1 while(1) PopupMenu MPF2_ChooseGraph,win=MultiPeak2StarterPanel,mode=1 PopupWS_SetSelectionFullPath("MultiPeak2StarterPanel", "MPF2_SelectYWaveButton", GetWavesDataFolder(w, 2)) PopupWS_SetSelectionFullPath("MultiPeak2StarterPanel", "MPF2_SelectXWaveButton", "_calculated_") PopupMenu MPF2_StartPanel_TraceMenu,win=MultiPeak2StarterPanel, disable=1 endif else PopupMenu MPF2_StartPanel_TraceMenu,win=MultiPeak2StarterPanel, disable=1 endif end Function MPF2_WaveSelectContinueBtnProc(ctrlName) : ButtonControl String ctrlName Variable setnumber Variable buildingFromOldData = 0 NVAR currentSetNumber = root:Packages:MultiPeakFit2:currentSetNumber String yWName = PopupWS_GetSelectionFullPath("MultiPeak2StarterPanel", "MPF2_SelectYWaveButton") String xWName = PopupWS_GetSelectionFullPath("MultiPeak2StarterPanel", "MPF2_SelectXWaveButton") Wave/Z yw = $yWName Wave/Z xw = $xWName if (!WaveExists(yw)) DoAlert 0, "It appears you have not selected data waves yet." return -1 endif String SaveDF = GetDataFolder(2) Variable Panelposition = 0 ControlInfo/W=MultiPeak2StarterPanel MPF2_PanelPositionMenu strswitch(S_value) case "Below": Panelposition = 2 break; case "Left": Panelposition = 1 break; case "Right": Panelposition = 0 break; case "Above": Panelposition = 3 break; endswitch ControlInfo/W=MultiPeak2StarterPanel MPF2_ChooseGraph String gname = S_value if ((CmpStr(gname, "New Graph") != 0) && WinType(gname) != 1) DoAlert 1, "The chosen graph does not exist. Do you want to make a new graph?" if (V_flag == 1) gname = "New Graph" else return -1 endif endif if (CmpStr(gname, "New Graph") == 0) currentSetNumber += 1 setnumber = currentSetNumber if (WaveExists(xw)) Display yw vs xw else Display yw endif String newGName = "MultipeakFit_Set"+num2str(currentSetNumber) RenameWindow $S_name, $newGName SetWindow $newGName userData(MPF2_DataSetNumber)=num2str(currentSetNumber) // SetWindow $newGName hook(MPF2_GraphHook)=MPF2_GraphHook gname = newGName else if (WinType(gname+"#MultiPeak2Panel")) DoWindow/F $gname return 0 else String graphSetNumberStr = GetUserData(gname, "", "MPF2_DataSetNumber" ) if (strlen(graphSetNumberStr) > 0) DoAlert 1, "The graph "+gname+" is already associated with Multipeak Fit set number "+graphSetNumberStr+"\r\rDo you want to re-use that set?" if (V_flag == 2) // No- start over from scratch currentSetNumber += 1 setnumber = currentSetNumber SetWindow $gname userData(MPF2_DataSetNumber)=num2str(currentSetNumber) else // Yes- build the panel using the old data setnumber = str2num(graphSetNumberStr) String fName = "root:Packages:MultiPeakFit2:"+MPF2_FolderNameFromSetNumber(setnumber) SetDataFolder $fName SVAR YWvName SVAR XWvName if (CmpStr(yWName, YWvName) != 0) DoAlert 0, "The Y wave you selected ("+yWName+") is not the same as the one previously used with this graph ("+YWvName+"). Maybe it was re-named?" SetDataFolder saveDF return 0 endif if (WaveExists(xw)) if (CmpStr(xWName, XWvName) != 0) DoAlert 0, "The X wave you selected ("+xWName+") is not the same as the one previously used with this graph ("+XWvName+"). Maybe it was re-named?" SetDataFolder saveDF return 0 endif else Wave/Z xxw = $XWvName if (WaveExists(xxw)) DoAlert 0, "Previously, this graph was used with an XY pair, but you have selected _calculated_ for the X wave." SetDataFolder saveDF return 0 endif endif buildingFromOldData = 1 endif else currentSetNumber += 1 setnumber = currentSetNumber SetWindow $gname userData(MPF2_DataSetNumber)=num2str(currentSetNumber) endif endif endif if (!buildingFromOldData) SetDataFolder root:Packages:MultiPeakFit2 NewDataFolder/S/O $MPF2_FolderNameFromSetNumber(setnumber) String/G YWvName = GetWavesDataFolder(yw, 2) String/G XWvName = "" if (WaveExists(xw)) XWvName = GetWavesDataFolder(xw, 2) endif endif String/G GraphName = gname BuildMultiPeak2Panel(gname, setNumber, Panelposition) SetWindow $gname userdata(MPF2_DataSetNumber)=num2str(setNumber) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) if (buildingFromOldData) NVAR position = $(DFPath+":panelPosition") NVAR negativePeaks = $(DFPath+":negativePeaks") Wave wpi = $(DFPath+":W_AutoPeakInfo") SVAR SavedFunctionTypes = $(DFPath+":SavedFunctionTypes") ControlInfo/W=$gname#MultiPeak2Panel#P1 MPF2_PeakList ListBox MPF2_PeakList win=$gname#MultiPeak2Panel#P1,userdata(MPF2_DataSetNumber)=num2str(setnumber) MakeListIntoHierarchicalList(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "PeakListOpenNotify", selectionMode=WMHL_SelectionContinguous, userListProc="MPF2_PeakListProc") WMHL_AddColumns(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 2 ) WMHL_SetNotificationProc(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "PeakListClosingNotify", WMHL_SetClosingNotificationProc) Wave/Z cwave = $(DFPath+":'Baseline Coefs'") if (!WaveExists(cwave)) Make/O/D/N=1 $(DFPath+":'Baseline Coefs'") endif WMHL_AddObject(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "", "Baseline", 1) WMHL_ExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, 0, StringFromList(0, SavedFunctionTypes)+MENU_ARROW_STRING, 0) Variable npeaks = DimSize(wpi, 0) Variable i for (i = 0; i < npeaks; i += 1) WMHL_AddObject(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "", "Peak "+num2str(i), 1) WMHL_ExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, i+1, StringFromList(i+1, SavedFunctionTypes)+MENU_ARROW_STRING, 0) endfor ListBox MPF2_PeakList win=$gname#MultiPeak2Panel#P1,widths={4,15,16,10} MPF2_AddFitCurveToGraph(setNumber, wpi, yw, xw, 1) MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1) Wave/Z/T HoldStrings if (!WaveExists(HoldStrings)) Make/T/N=(npeaks+1)/O $(DFPath+":HoldStrings")="" endif MPF2_HideHelpPanel(gname+"#MultiPeak2Panel#") else Make/O/D/N=1 $(DFPath+":'Baseline Coefs'") Variable RangeBegin, RangeEnd, RangeReversed MPF2_SetDataPointRange(gname, yw, xw, RangeBegin, RangeEnd, RangeReversed) Variable/G XPointRangeBegin = RangeBegin Variable/G XPointRangeEnd = RangeEnd Variable/G XPointRangeReversed = RangeReversed Variable userAborted = 0 try Variable/C ctmp = EstPeakNoiseAndSmfact(yw, RangeBegin, RangeEnd);AbortOnRTE // from PeakAutoFind.ipf catch if (V_AbortCode == -4) if (GetRTError(1) == 57) userAborted = 1 endif endif endtry if (userAborted) Variable/G AutoFindNoiseLevel = 0 Variable/G AutoFindSmoothFactor = 1 else Variable/G AutoFindNoiseLevel = real(ctmp) Variable/G AutoFindSmoothFactor = imag(ctmp) endif Variable/G AutoFindTrimFraction = 0.05 Make/T/N=1/O $(DFPath+":HoldStrings")="" // Initially a row for just the baseline endif ControlInfo/W=$gname#MultiPeak2Panel#P0 MPF2_DiscloseAutoPickParams if (V_value==1) SetVariable MPF2_SetAutoFindNoiseLevel,win=$gname#MultiPeak2Panel,value=AutoFindNoiseLevel SetVariable MPF2_SetAutoPeakSmoothFactor,win=$gname#MultiPeak2Panel,value=AutoFindSmoothFactor SetVariable MPF2_SetAutoPeakMinFraction,win=$gname#MultiPeak2Panel,value=AutoFindTrimFraction endif NVAR negativePeaks CheckBox MPF2_NegativePeaksCheck,win=$gname#MultiPeak2Panel#P0,variable=negativePeaks MPF2_EnableDisableDoFitButton(setNumber) SetDataFolder saveDF End static Function/S MPF2_FolderNameFromSetNumber(setnumber) Variable setnumber return "MPF_SetFolder_"+num2str(setnumber) end static Function/S MPF2_FolderPathFromSetNumber(setnumber) Variable setnumber return "root:Packages:MultiPeakFit2:"+MPF2_FolderNameFromSetNumber(setnumber) end Function MPF2_DiscloseAutoPickCheckProc(s) : CheckBoxControl STRUCT WMCheckboxAction &s if (s.eventCode != 2) return 0 endif String panelName = ParseFilePath(1, s.win, "#", 1, 0) panelName = panelName[0, strlen(panelName)-2] // ParseFilePath leaves the separator string on the end String gname = GetUserData(panelName, "", "MPF2_hostgraph") ControlInfo/W=$s.win MPF2_LocatePeaksGroupBox Variable width = V_Width Variable height = V_Height Variable gbright = V_left+width Variable gbtop = V_top+V_height Variable setNumber = GetSetNumberFromWinName(s.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) NVAR position = $(DFPath+":panelPosition") Variable left, top, right, bottom Variable panelHeight = s.checked ? 346+65 : 346 switch(position) case 0: // right left=0; top=panelHeight; right=261; bottom=panelHeight; break; case 1: // left left=261; top=panelHeight; right=0; bottom=panelHeight; break; case 2: // below left=261; top=0; right=261; bottom=panelHeight; break; case 3: // above left=261; top=panelHeight; right=261; bottom=0; break; endswitch if (s.checked) DefineGuide/W=$panelName UGH1={UGH0,130} MoveSubwindow/W=$panelName fnum=(left, top, right, bottom) height += 65 else DefineGuide/W=$panelName UGH1={UGH0,65} MoveSubwindow/W=$panelName fnum=(left, top, right, bottom) height -= 65 endif ModifyControl MPF2_LocatePeaksGroupBox, win=$s.win, size={width, height} ControlUpdate/W=$s.win MPF2_LocatePeaksGroupBox String saveDF = GetDataFolder(1) // NVAR currentSetNumber = root:Packages:MultiPeakFit2:currentSetNumber // Variable setNumber = GetSetNumberFromWinName(gname) // String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SetDataFolder DFpath NVAR AutoFindNoiseLevel NVAR AutoFindSmoothFactor NVAR AutoFindTrimFraction SetDataFolder saveDF DoUpdate if (s.checked) SetVariable MPF2_SetAutoFindNoiseLevel,win=$s.win,pos={15,gbtop+2},size={132,15},title="Noise level:" SetVariable MPF2_SetAutoFindNoiseLevel,win=$s.win,value=AutoFindNoiseLevel,bodyWidth= 60 SetVariable MPF2_SetAutoPeakSmoothFactor,win=$s.win,pos={15,gbtop+23},size={132,15},title="Smooth Factor:" SetVariable MPF2_SetAutoPeakSmoothFactor,win=$s.win,value=AutoFindSmoothFactor,bodyWidth= 60 SetVariable MPF2_SetAutoPeakMinFraction,win=$s.win,pos={15,gbtop+44},size={132,15},title="Min Fraction:" SetVariable MPF2_SetAutoPeakMinFraction,win=$s.win,value=AutoFindTrimFraction,bodyWidth= 60 Button MPF2_AutoPickEstimate,win=$s.win,pos={157,83},size={85,20},title="Estimate Now" Button MPF2_AutoPickEstimate,win=$s.win,fSize=10,proc=MPF2_EstimateAutoPickPButton else KillControl/W=$s.win MPF2_SetAutoFindNoiseLevel KillControl/W=$s.win MPF2_SetAutoPeakSmoothFactor KillControl/W=$s.win MPF2_SetAutoPeakMinFraction KillControl/W=$s.win MPF2_AutoPickEstimate endif End MPF2_OpenOptionsPanel(setNumber) Variable setNumber Variable setNumber = GetSetNumberFromWinName(s.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Function MPF2_DiscloseOptions(s) : CheckBoxControl STRUCT WMCheckboxAction &s if (s.eventCode != 2) return 0 endif String panelName = ParseFilePath(1, s.win, "#", 1, 0) panelName = panelName[0, strlen(panelName)-2] // ParseFilePath leaves the separator string on the end String gname = GetUserData(panelName, "", "MPF2_hostgraph") Variable setNumber = GetSetNumberFromWinName(s.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) NVAR position = $(DFPath+":panelPosition") NVAR MPF2OptionsShowing = $(DFPath+":MPF2OptionsShowing") MPF2OptionsShowing = s.checked Variable factor = ScreenResolution/72 GetWindow $panelName wsize Variable left = V_left*factor Variable right = V_right*factor Variable top = V_top*factor Variable bottom = V_bottom*factor Variable panelHeight = bottom-top Variable panelWidth = right-left panelHeight = s.checked ? panelHeight+100 : panelHeight-100 switch(position) case 0: // right left=0; top=panelHeight; right=panelWidth; bottom=panelHeight; break; case 1: // left left=panelWidth; top=panelHeight; right=0; bottom=panelHeight; break; case 2: // below left=panelWidth; top=0; right=panelWidth; bottom=panelHeight; break; case 3: // above left=panelWidth; top=panelHeight; right=panelWidth; bottom=0; break; endswitch if (s.checked) DefineGuide/W=$panelName UGH3={FB,-123} MoveSubwindow/W=$panelName fnum=(left, top, right, bottom) else DefineGuide/W=$panelName UGH3={FB,-23} MoveSubwindow/W=$panelName fnum=(left, top, right, bottom) endif end Function MPF2_MaskWaveSelectNotify(event, wavepath, windowName, ctrlName) Variable event String wavepath String windowName String ctrlName if (event == WMWS_SelectionChanged) Variable setNumber = GetSetNumberFromWinName(windowName) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR MPF2WeightWaveName = $(DFPath+":MPF2WeightWaveName") Wave/Z w = $wavepath if (WaveExists(w)) MPF2WeightWaveName = wavepath else MPF2WeightWaveName = "" endif endif end Function MPF2_WeightWaveSelectNotify(event, wavepath, windowName, ctrlName) Variable event String wavepath String windowName String ctrlName if (event == WMWS_SelectionChanged) Variable setNumber = GetSetNumberFromWinName(windowName) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR MPF2WeightWaveName = $(DFPath+":MPF2MaskWaveName") Wave/Z w = $wavepath if (WaveExists(w)) MPF2WeightWaveName = wavepath else MPF2WeightWaveName = "" endif endif end Function MPF2_DoHelpButtonProc(ctrlName) : ButtonControl String ctrlName DisplayHelpTopic "Multi-peak Fitting" end Function MPF2_EstimateAutoPickPButton(ba) : ButtonControl STRUCT WMButtonAction &ba switch( ba.eventCode ) case 2: // mouse up Variable setNumber = GetSetNumberFromWinName(ba.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFpath SVAR YWvName Wave YData = $YWvName NVAR XPointRangeBegin NVAR XPointRangeEnd Variable/C ctmp = EstPeakNoiseAndSmfact(YData, XPointRangeBegin, XPointRangeEnd) // from PeakAutoFind.ipf Variable/G AutoFindNoiseLevel = real(ctmp) Variable/G AutoFindSmoothFactor = imag(ctmp) Variable/G AutoFindTrimFraction = 0.05 SetDataFolder saveDF break endswitch return 0 end static Function MPF2_SetDataPointRange(gname, YData, XData, RangeBegin, RangeEnd, RangeReversed) string gname Wave YData Wave/Z XData Variable &RangeBegin Variable &RangeEnd Variable &RangeReversed RangeBegin= 0; RangeEnd= numpnts(YData)-1; Variable te= RangeEnd Variable V_Flag= 0 CheckDisplayed/W=$gname YData Variable isGraphed= V_Flag Variable checkAxis = 0 if( isGraphed ) ControlInfo/W=$(gname+"#MultiPeak2Panel") MPF2_UserCursorsCheckbox if (V_value) if (strlen(CsrInfo(A, gname)) == 0) DoAlert 0, "The Use Graph Cursors checkbox is checked, but the A cursor is not on the graph." checkAxis = 1 endif if (strlen(CsrInfo(B, gname)) == 0) DoAlert 0, "The Use Graph Cursors checkbox is checked, but the A cursor is not on the graph." checkAxis = 1 endif RangeBegin= pcsr(A, gname) RangeEnd= pcsr(B, gname) else checkAxis = 1 endif if (checkAxis) GetAxis /Q bottom if(!WaveExists(XData)) RangeBegin= x2pnt(YData,V_min) RangeEnd= x2pnt(YData,V_max) else RangeBegin=BinarySearchClipped(XData,V_min) RangeEnd=BinarySearchClipped(XData,V_max) endif endif endif RangeReversed= RangeBegin>RangeEnd if( RangeReversed ) variable tmp= RangeBegin RangeBegin= RangeEnd RangeEnd= tmp endif return 0 End // eliminates peaks smaller than minPeakFraction*(max peak height) Function MPF2_TrimAmpAutoPeakInfo(wpi, minPeakFraction) Wave wpi Variable minPeakFraction Variable numRows = DimSize(wpi,0) Variable maxHeight = 0 Variable minHeight Variable i for (i = 0; i < numRows; i += 1) maxHeight = max(maxHeight, wpi[i][2]) endfor minHeight= maxHeight*minPeakFraction // user want peaks to be bigger than this for (i = numRows-1; i >= 0; i -= 1) // go backwards so we can delete rows without screwing up the index if( wpi[i][2] < minHeight ) DeletePoints i,1,wpi endif i -= 1 endfor return DimSize(wpi,0) end static Function MPF2_ClearOutOldWaves(setNumber) Variable setNumber String saveDF = GetDataFolder(1) // NVAR currentSetNumber = root:Packages:MultiPeakFit2:currentSetNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SetDataFolder DFpath SVAR gname = $(DFpath+":GraphName") Variable index Variable i=0 String tlist = TraceNameList(gname, ";", 1 ) Variable nItems = ItemsInList(tlist) for (i = 0; i < nItems; i += 1) String tname = StringFromList(i, tlist) if (CmpStr(tname[0,5], "'Peak ", 1) == 0) // looking for traces with names beginning with "Peak ". I control the trace names, so I know that case-sensitive is OK. Reduces the chances of getting the wrong trace from a user's graph. RemoveFromGraph/W=$gname/Z $tname endif endfor DoUpdate String wname i = 0 do wname = "Peak "+num2str(i) Wave/Z w = $wname if (!WaveExists(w)) break endif KillWaves/Z w i += 1 while(1) i = 0 do wname = "Peak "+num2str(i)+" Coefs" Wave/Z w = $wname if (!WaveExists(w)) break endif KillWaves/Z w i += 1 while(1) i = 0 do wname = "Peak "+num2str(i)+" CoefsBackup" Wave/Z w = $wname if (!WaveExists(w)) break endif KillWaves/Z w i += 1 while(1) i = 0 do wname = "W_sigma_"+num2str(i) Wave/Z w = $wname if (!WaveExists(w)) break endif KillWaves/Z w i += 1 while(1) SetDataFolder saveDF end Function MPF2_AutoLocatePeaksButtonProc(s) : ButtonControl STRUCT WMButtonAction &s if (s.eventCode != 2) // mouse-up in the control return 0 endif Variable RangeBegin Variable RangeEnd Variable RangeReversed Variable noiseFactor String saveDF = GetDataFolder(1) Variable setNumber = GetSetNumberFromWinName(s.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SetDataFolder DFpath SVAR gname = GraphName SVAR YWvName SVAR XWvName Wave YData = $YWvName Wave/Z XData = $XWvName MPF2_ClearOutOldWaves(setNumber) NVAR AutoFindNoiseLevel NVAR AutoFindSmoothFactor NVAR AutoFindTrimFraction MPF2_SetDataPointRange(gname, YData, XData, RangeBegin, RangeEnd, RangeReversed) NVAR XPointRangeBegin NVAR XPointRangeEnd XPointRangeBegin = RangeBegin XPointRangeEnd = RangeEnd Wave/Z wpi = W_AutoPeakInfo // If it exists, an autofind was done previously, and the list should be in a finished state. In case the user has selected a baseline function, try to get it so it can be restored. Variable doingBaseline = 0 String BaselineFunc Variable baselineIsOpen = 0 Variable BaselineRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Baseline") if (BaselineRow == 0) // another test for the list being in a finished state String baselineStr = WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, BaselineRow) if (CmpStr(baselineStr, "None"+MENU_ARROW_STRING) != 0) doingBaseline = 1 BaselineFunc = MPF2_PeakOrBLTypeFromListString(baselineStr) baselineIsOpen = WMHL_RowIsOpen(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", BaselineRow) Wave/Z 'Baseline Coefs' if (!WaveExists('Baseline Coefs')) FUNCREF MPF2_FuncInfoTemplate BLinfoFunc=$(BaselineFunc+BL_INFO_SUFFIX) String ParamNameList = BLinfoFunc(BLFuncInfo_ParamNames) Variable nparams = ItemsInList(ParamNameList) Wave/Z w = $(DFPath+":"+"'Baseline Coefs'") if (!WaveExists(w)) Make/D/N=(nparams) $(DFPath+":"+"'Baseline Coefs'") endif DoingBaseline = 1 endif endif endif ControlInfo/W=$s.win MPF2_NegativePeaksCheck NVAR negativePeaks negativePeaks = V_value if (negativePeaks) Duplicate/O YData, TempYDataForNegativePeaks Wave w = TempYDataForNegativePeaks w = -w else Wave w = YData endif Variable npks = AutoFindPeaks(w, XPointRangeBegin, XPointRangeEnd, AutoFindNoiseLevel, AutoFindSmoothFactor, Inf) // from PeakAutoFind.ipf Wave wpi = W_AutoPeakInfo // may or may not exist if (negativePeaks) KillWaves TempYDataForNegativePeaks endif if( npks>0 ) AdjustAutoPeakInfoForX(wpi, YData, XData) npks = TrimAmpAutoPeakInfo(wpi, AutoFindTrimFraction) MPF2_SortAutoPeakWave(wpi) if (negativePeaks) wpi[][2] = -wpi[p][2] endif CreateCoefWavesFromAutoPeakInfo(setnumber, wpi, "Gauss") MPF2_PutAutoPeakResultIntoList(setNumber, wpi, 1) if (doingBaseline) if (CmpStr(BaselineFunc, "None") != 0) WMHL_ExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, BaselineRow, BaselineFunc+MENU_ARROW_STRING, 0) WMHL_OpenAContainer(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Baseline") if (!baselineIsOpen) WMHL_CloseAContainer(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Baseline") endif endif endif MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1) MPF2_AddFitCurveToGraph(setNumber, wpi, YData, XData, 1, overridePoints=MPF2_getFitCurvePoints(gname+"#MultiPeak2Panel")) endif MPF2_EnableDisableDoFitButton(setNumber) SetDataFolder saveDF End // will get the setnumber from a window name of the form "graphname" or "GraphName#MultiPeak2Panel" or any sub-panel of GraphName#MultiPeak2Panel. // Depends on UserData named "MPF2_DataSetNumber" stored in both the graph window and the main exterior panel window. Static Function GetSetNumberFromWinName(windowName) String windowName String windowWithData Variable poundPos = strsearch(windowName, "#", 0) if (poundPos < 0) windowWithData = windowName else poundPos = strsearch(windowName, "#", poundPos+1) if (poundPos < 0) windowWithData = windowName else windowWithData = windowName[0,poundPos-1] endif endif return str2num(GetUserData(windowWithData, "", "MPF2_DataSetNumber")) end Function MPF2_SetAllPeakTypesMenuProc(s) : PopupMenuControl STRUCT WMPopupAction &s if (s.eventCode != 2) return 0 endif Variable setNumber = GetSetNumberFromWinName(s.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR YWvName = $(DFpath+":YWvName") Wave yw = $YWvName SVAR XWvName = $(DFpath+":XWvName") Wave/Z xw = $XWvName Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") SVAR gname = $(DFpath+":GraphName") String listPanelName = gname+"#MultiPeak2Panel#P1" Variable i=1 // don't do anything to the baseline row do if (strlen(WMHL_GetItemForRowNumber(listPanelName, "MPF2_PeakList", i)) == 0) break; endif if (WMHL_RowIsContainer(listPanelName, "MPF2_PeakList", i)) WMHL_ExtraColumnData(listPanelName, "MPF2_PeakList", 0, i, s.popStr+MENU_ARROW_STRING, 0) MPF2_CoefWaveForListRow(setNumber, i, s.popStr) if (WMHL_RowIsOpen(listPanelName, "MPF2_PeakList", i)) string theItem = WMHL_GetItemForRowNumber(listPanelName, "MPF2_PeakList", i) WMHL_CloseAContainer(listPanelName, "MPF2_PeakList", theItem) // info for this row has changed. Opening the row re-evaluates the info WMHL_OpenAContainer(listPanelName, "MPF2_PeakList", theItem) endif endif i += 1 while (1) MPF2_AddFitCurveToGraph(setNumber, wpi, yw, xw, 1, overridePoints=MPF2_getFitCurvePoints(gname+"#MultiPeak2Panel")) NVAR negativePeaks = $(DFpath+":negativePeaks") MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1) MPF2_EnableDisableDoFitButton(setNumber) End // Sorts the master coefficient estimate wave (usually from the AutoFindPeaks function). Sorts by X location value. // If the caller includes a listOfTypes, the list is sorted along with the coefficients estimage wave. Static Function/S MPF2_SortAutoPeakWave(wpi [, listOfTypes, holdwave, killIndexWave]) Wave wpi String &listOfTypes Wave/Z/T holdwave Variable killIndexWave if (ParamIsDefault(killIndexWave)) killIndexWave = 1 endif String indexWaveName = MPF2_MakeIndexAutoPeakWave(wpi) Wave MPF2_indexwave = $indexWaveName Duplicate/O wpi, MPF2_TempWave wpi = MPF2_TempWave[MPF2_indexwave[p]][q] //DoUpdate if (!ParamIsDefault(holdwave)) Duplicate/O/T holdwave, MPF2_TempHoldWave holdwave = "" holdwave[1,] = MPF2_TempHoldWave[MPF2_indexwave[p-1]+1] endif //DoUpdate if (!ParamIsDefault(listOfTypes)) Variable i Variable nPeaks = DimSize(wpi, 0) String newList = StringFromList(0, listOfTypes)+";" // copy the baseline function type for (i = 0; i < nPeaks; i += 1) newList += StringFromList(MPF2_indexwave[i]+1, listOfTypes)+";" endfor listOfTypes = newList endif if (killIndexWave) KillWaves MPF2_indexwave indexWaveName = "" endif KillWaves MPF2_TempWave return indexWaveName end // Sorts the master coefficient estimate wave (usually from the AutoFindPeaks function). Sorts by X location value. // If the caller includes a listOfTypes, the list is sorted along with the coefficients estimage wave. Static Function/S MPF2_MakeIndexAutoPeakWave(wpi) Wave wpi Make/D/N=(DimSize(wpi, 0))/O MPF2_sortwave, MPF2_indexwave MPF2_sortwave = wpi[p][0] MakeIndex MPF2_sortwave, MPF2_indexwave KillWaves MPF2_sortwave return GetWavesDataFolder(MPF2_indexwave, 2) end Static Function CreateCoefWavesFromAutoPeakInfo(setNumber, AutoPeakInfo, peakTypeName) Variable setNumber Wave AutoPeakInfo String peakTypeName // NVAR currentSetNumber = root:Packages:MultiPeakFit2:currentSetNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFPath CreateCWavesInCDFFromAutoPkInfo(AutoPeakInfo, peakTypeName, "Peak %d Coefs") SetDataFolder saveDF end Static Function CreateCWavesInCDFFromAutoPkInfo(AutoPeakInfo, peakTypeName, coefWaveNameFormat) Wave AutoPeakInfo String peakTypeName String coefWaveNameFormat Variable npeaks = DimSize(AutoPeakInfo, 0) FUNCREF MPF2_FuncInfoTemplate infoFunc=$(peakTypeName+PEAK_INFO_SUFFIX) Variable nparams String GaussGuessConversionFuncName = infoFunc(PeakFuncInfo_GaussConvFName) if (strlen(GaussGuessConversionFuncName) == 0) else FUNCREF MPF2_GaussGuessConvTemplate gconvFunc=$GaussGuessConversionFuncName endif String newWName Variable i for (i = 0; i < npeaks; i += 1) sprintf newWName, coefWaveNameFormat, i Make/D/O/N=(DimSize(AutoPeakInfo, 1)) $newWName Wave w = $newWName w = AutoPeakInfo[i][p] gconvFunc(w) endfor end Static Function MPF2_CoefWaveForPeak(setNumber, AutoPeakInfo, peakNumber, peakTypeName) Variable setNumber Wave AutoPeakInfo Variable peakNumber String peakTypeName String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFPath FUNCREF MPF2_FuncInfoTemplate infoFunc=$(peakTypeName+PEAK_INFO_SUFFIX) Variable nparams String GaussGuessConversionFuncName = infoFunc(PeakFuncInfo_GaussConvFName) if (strlen(GaussGuessConversionFuncName) == 0) else FUNCREF MPF2_GaussGuessConvTemplate gconvFunc=$GaussGuessConversionFuncName endif String newWName = "Peak "+num2str(peakNumber)+" Coefs" Make/D/O/N=(DimSize(AutoPeakInfo, 1)) $newWName Wave w = $newWName w = AutoPeakInfo[peakNumber][p] // Manually editing or dragging out a peak doesn't have a provision for assymetry; the left and right widths are zero. That // screws up the ExpModGauss function guess generator, so here we will simply copy them from the width in column 1 w[3] = w[1]/2 w[4] = w[1]/2 gconvFunc(w) SetDataFolder saveDF end Static Function BinarySearchClipped(w,x) WAVE w Variable x Variable p= BinarySearch(w,x) if( p == -2 ) p= numpnts(w)-1 elseif( p == -1 ) p= 0 endif return p End Static Function MPF2_PutAutoPeakResultIntoList(setNumber, autoPeakInfo, initializeBaseline [, listOfPeakTypes]) Variable setNumber Wave autoPeakInfo Variable initializeBaseline String listOfPeakTypes if (!WaveExists(autoPeakInfo) || (DimSize(autoPeakInfo, 0) == 0)) return 0 endif String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR gname = $(DFpath+":GraphName") SVAR YWvName = $(DFpath+":YWvName") SVAR XWvName = $(DFpath+":XWvName") Wave YData = $YWvName Wave/Z XData = $XWvName Variable i Variable nPeaks = DimSize( autoPeakInfo, 0) Variable currentRow = 0 Variable reOpenBaseline = 0 if (initializeBaseline) // Start from scratch ControlInfo/W=$gname#MultiPeak2Panel#P1 MPF2_PeakList Variable listHeight = V_height Variable listWidth = V_width Variable listTop = V_top Variable listLeft = V_left Variable BaselineRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Baseline") String baselineStr if (BaselineRow == 0) baselineStr = WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, BaselineRow) else baselineStr = "Constant"+MENU_ARROW_STRING endif // now re-build it ListBox MPF2_PeakList win=$gname#MultiPeak2Panel#P1,userdata(MPF2_DataSetNumber)=num2str(setnumber) MakeListIntoHierarchicalList(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "PeakListOpenNotify", selectionMode=WMHL_SelectionContinguous, userListProc="MPF2_PeakListProc") WMHL_SetNotificationProc(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "PeakListClosingNotify", WMHL_SetClosingNotificationProc) WMHL_AddColumns(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 2 ) WMHL_AddObject(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "", "Baseline", 1) WMHL_ExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, currentRow, baselineStr, 0) currentRow += 1 else // preserve the baseline info. Just delete all the peaks and add them back again. reOpenBaseline = WMHL_RowIsOpen(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0) WMHL_CloseAContainer(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Baseline") i = 0 do Variable theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2str(i)) if (theRow < 0) break; endif WMHL_DeleteRowAndChildren(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", theRow) i += 1 while (1) currentRow = 1 endif String peakTypeName = "Gauss" for (i = 0; i < npeaks; i += 1) WMHL_AddObject(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "", "Peak "+num2str(i), 1) if (!ParamIsDefault(listOfPeakTypes)) peakTypeName = StringFromList(i+1, listOfPeakTypes) // i+1 because the first is the baseline type endif WMHL_ExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, currentRow, peakTypeName+MENU_ARROW_STRING, 0) currentRow += 1 endfor ControlInfo/W=gname#MultiPeak2Panel#P1 MPF2_PeakList ListBox MPF2_PeakList win=$gname#MultiPeak2Panel#P1,widths={4,15,16,10} if (reOpenBaseline) WMHL_OpenAContainer(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Baseline") endif end Function/S MPF2_FuncInfoTemplate(InfoDesired) Variable InfoDesired return "" // so we can tell when the template ran by mistake end Function MPF2_GaussGuessConvTemplate(w) Wave w return -1 end Function MPF2_PeakFunctionTemplate(w, yw, xw) Wave w Wave yw, xw end Function MPF2_BaselineFunctionTemplate(s) STRUCT MPF2_BLFitStruct &s return -1 end Function/S MPF2_PeakOrBLTypeFromListString(listString) String listString Variable pos = strsearch(listString, MENU_ARROW_STRING, 0) return listString[0,pos-1] end Function PeakListOpenNotify(HostWindow, ListControlName, ContainerPath) String HostWindow, ListControlName, ContainerPath Variable setNumber = str2num(GetUserData(HostWindow, ListControlName, "MPF2_DataSetNumber")) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Variable theRow = WMHL_GetRowNumberForItem(HostWindow, ListControlName, ContainerPath) String PeakType = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(HostWindow, ListControlName, 0, theRow) ) String ParamNameList, GaussGuessConversionFuncName, PeakFuncName Variable nparams Variable DoingBaseline = 0 if (CmpStr(ContainerPath, "Baseline") == 0) FUNCREF MPF2_FuncInfoTemplate BLinfoFunc=$(PeakType+BL_INFO_SUFFIX) ParamNameList = BLinfoFunc(BLFuncInfo_ParamNames) nparams = ItemsInList(ParamNameList) Wave/Z w = $(DFPath+":"+"'Baseline Coefs'") if (!WaveExists(w)) Make/D/N=(nparams) $(DFPath+":"+"'Baseline Coefs'") endif DoingBaseline = 1 else FUNCREF MPF2_FuncInfoTemplate PKinfoFunc=$(PeakType+PEAK_INFO_SUFFIX) ParamNameList = PKinfoFunc(PeakFuncInfo_ParamNames) nparams = ItemsInList(ParamNameList) endif String wavePath = DFPath+":"+PossiblyQuoteName(ParseFilePath(0, ContainerPath, ":", 1, 0)+" Coefs") Wave w = $wavePath Variable i Variable nrows = DimSize(w, 0) Wave/Z/T HoldStrings Variable HoldStringsRow Variable PeakNumber sscanf ContainerPath, "Peak %d", PeakNumber HoldStringsRow = DoingBaseline ? 0 : PeakNumber+1 for (i = 0; i < nrows; i += 1) WMHL_AddObject(HostWindow, ListControlName, ContainerPath, StringFromList(i, ParamNameList), 0) WMHL_ExtraColumnData(HostWindow, ListControlName, 0, theRow+i+1, num2str(w[i]), 1) Variable selwaveValue = 0x20 // if (WaveExists(HoldStrings) && (StrLen(HoldStrings[HoldStringsRow]) > 0)) Variable haveHoldString= WaveExists(HoldStrings) && (HoldStringsRow < DimSize(HoldStrings,0)) && (StrLen(HoldStrings[HoldStringsRow]) > 0) if (haveHoldString) // JP100622 String holdString = HoldStrings[HoldStringsRow] if (char2num(holdString[i]) == char2num("1")) selwaveValue = 0x10+0x20 endif endif WMHL_ExtraColumnData(HostWindow, ListControlName, 1, theRow+i+1, "Hold", 0, setSelWaveValue=selwaveValue) endfor end // When a peak container row closes we have to save the info from Hold checkboxes. Function PeakListClosingNotify(HostWindow, ListControlName, ContainerPath, ContainerParentPath, FirstChildRow, LastChildRow) String HostWindow, ListControlName, ContainerPath, ContainerParentPath Variable FirstChildRow, LastChildRow Variable setNumber = str2num(GetUserData(HostWindow, ListControlName, "MPF2_DataSetNumber")) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFPath Wave/Z/T HoldStrings Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") Variable npeaks=0 if (WaveExists(wpi)) npeaks = DimSize(wpi, 0) endif Variable DoingBaseline = (CmpStr(ContainerPath, "Baseline") == 0) Variable i Variable peakNumber sscanf ContainerPath, "Peak %d", peakNumber Variable HoldStringsRow = DoingBaseline ? 0 : peakNumber+1 if (NumType(HoldStringsRow)) return 0 endif String theHolds = "" for (i = FirstChildRow; i <= LastChildRow; i += 1) Variable isChecked = (WMHL_GetExtraColumnSelValue(HostWindow, ListControlName, 1, i) & 0x10) != 0 if (isChecked) theHolds += "1" else theHolds += "0" endif endfor Variable rowsNeeded= max(npeaks+1,HoldStringsRow+1) // JP100622 if (!WaveExists(HoldStrings) ) Make/T/N=(rowsNeeded) HoldStrings elseif (DimSize(HoldStrings,0) < rowsNeeded) Redimension/N=(rowsNeeded,-1,-1,-1) HoldStrings endif HoldStrings[HoldStringsRow] = theHolds // +1 to account for the baseline hold row return 0 end Function MPF2_RemoveAllPeaksFromGraph(gname) String gname // NVAR currentSetNumber = root:Packages:MultiPeakFit2:currentSetNumber Variable setNumber = GetSetNumberFromWinName(gname) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) // SVAR gname = $(DFpath+":GraphName") Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") String saveDF = GetDataFolder(1) SetDataFolder DFPath Variable nPeaks = DimSize(wpi, 0) Variable i for (i = 0; i < nPeaks; i += 1) String PeakWaveName = "Peak "+num2istr(i) Wave w = $PeakWaveName Wave coefs = $("Peak "+num2istr(i)+" Coefs") CheckDisplayed/W=$gname w if (V_flag != 0) RemoveFromGraph/W=$gname $NameOfWave(w) endif endfor SetDataFolder saveDF end Function MPF2_DeletePeakInfo(setNumber, peakNumber) Variable setNumber, peakNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFPath Wave wpi = W_AutoPeakInfo Variable npeaks = DimSize(wpi, 0) SVAR gname = GraphName if (peakNumber >= npeaks) return -1 endif DeletePoints peakNumber, 1, wpi Wave/T HoldStrings DeletePoints peakNumber+1, 1, HoldStrings Variable theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(peakNumber)) WMHL_DeleteRowAndChildren(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", theRow) Wave coefs = $("Peak "+num2istr(peakNumber)+" Coefs") KillWaves coefs npeaks -= 1 Variable i for (i = peakNumber; i < nPeaks; i += 1) Wave coefs = $("Peak "+num2istr(i+1)+" Coefs") Rename coefs, $("Peak "+num2istr(i)+" Coefs") theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(i+1)) WMHL_ChangeItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(i+1), "Peak "+num2istr(i)) endfor SetDataFolder saveDF end Function MPF2_AddPeaksToGraph(setNumber, wfi, doTags, doGrayLines) Variable setNumber Wave/Z wfi Variable doTags Variable doGrayLines if (!WaveExists(wfi) || (DimSize(wfi, 0) == 0)) return 0 endif String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR gname = $(DFpath+":GraphName") Variable i String saveDF = GetDataFolder(1) SetDataFolder DFPath NVAR XPointRangeBegin NVAR XPointRangeEnd SVAR YWvName = $"YWvName" SVAR XWvName = $"XWvName" Wave yw = $YWvName Wave/Z xw = $XWvName Variable XStart, XEnd if (WaveExists(xw)) XStart = xw[XPointRangeBegin] XEnd = xw[XPointRangeEnd] else XStart = pnt2x(yw, XPointRangeBegin) XEnd = pnt2x(yw, XPointRangeEnd) endif String taglist = AnnotationList(gname) Variable nItems = ItemsInList(taglist) String aTagName for (i = 0; i < nItems; i += 1) aTagName = StringFromList(i, taglist) if (stringmatch(aTagName, "PeakTag*")) Tag/W=$gname/K/N=$aTagName endif endfor SetDrawLayer/W=$gname/K ProgBack String oldDrawLayer = S_name Variable nPeaks = DimSize(wfi, 0) Make/N=200/O/D MPF2_TempXWave for (i = 0; i < nPeaks; i += 1) String PeakWaveName = "Peak "+num2istr(i) Make/D/N=200/O $PeakWaveName Wave w = $PeakWaveName Wave coefs = $("Peak "+num2istr(i)+" Coefs") Variable theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(i)) String PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, theRow) ) FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) String PeakFuncName = infoFunc(PeakFuncInfo_PeakFName) FUNCREF MPF2_PeakFunctionTemplate peakFunc = $PeakFuncName Variable width = wfi[i][1] Variable center = coefs[0] SetScale/I x max(center-4*width, XStart), min(center+4*width, XEnd), "", w MPF2_TempXWave = pnt2x(w, p) peakFunc(coefs, w, MPF2_TempXWave) CheckDisplayed/W=$gname w if (V_flag == 0) AppendToGraph/L=Peaks_Left/W=$gname w endif if (doTags) String anchorCode = "MB" if (w(center) < 0) anchorCode = "MT" endif Tag/W=$gname/N=$("PeakTag"+num2str(i))/A=$anchorCode/F=0/L=0/P=1/Y=1/X=0/B=1 $NameOfWave(w), center, "\\Zr080"+num2istr(i) endif if (doGrayLines) SetDrawEnv/W=$gname linefgc=(56797,56797,56797), xcoord=bottom, ycoord=prel DrawLine/W=$gname center, 0, center, 1 endif endfor KillWaves MPF2_TempXWave MPF2_AdjustAxes(gname) SetDrawLayer/W=$gname $oldDrawLayer SetDataFolder saveDF end Function MPF2_UpdateBaselineOnGraph(setNumber, FitWaveName, OldCoefs, OldBLType, DoResidual, ResidWaveName, XDataWave, YDataWave) Variable setNumber String FitWaveName Wave OldCoefs String OldBLType Variable DoResidual String ResidWaveName Wave/Z XDataWave Wave YDataWave Variable nParams Variable i String BL_FuncName String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR gname = $(DFpath+":GraphName") String saveDF = GetDataFolder(1) SetDataFolder DFPath NVAR XPointRangeBegin NVAR XPointRangeEnd Wave/Z w = $FitWaveName if (!WaveExists(w)) Wave/Z wpi = W_AutoPeakInfo MPF2_AddFitCurveToGraph(setNumber, wpi, yDataWave, xDataWave, doResidual, overridePoints=MPF2_getFitCurvePoints(gname+"#MultiPeak2Panel")) endif if (doResidual) Wave rw = $ResidWaveName endif STRUCT MPF2_BLFitStruct BLStruct if (WaveExists(xDataWave)) BLStruct.xStart = xDataWave[XPointRangeBegin] BLStruct.xEnd = xDataWave[XPointRangeEnd] else BLStruct.xStart = pnt2x(yDataWave, XPointRangeBegin) BLStruct.xEnd = pnt2x(yDataWave, XPointRangeEnd) endif if (CmpStr(OldBLType, "None") != 0) FUNCREF MPF2_FuncInfoTemplate blinfo = $(OldBLType + BL_INFO_SUFFIX) BL_FuncName = blinfo(BLFuncInfo_BaselineFName) FUNCREF MPF2_BaselineFunctionTemplate blFunc = $BL_FuncName Wave BLStruct.cWave = OldCoefs for (i = 0; i < numpnts(w); i += 1) BLStruct.x = pnt2x(w, i) w[i] -= blFunc(BLStruct) endfor if (doResidual) if (WaveExists(xDataWave)) for (i = 0; i < numpnts(rw); i += 1) BLStruct.x = xDataWave[i] rw[i] += blFunc(BLStruct) endfor else for (i = 0; i < numpnts(rw); i += 1) BLStruct.x = pnt2x(rw, i) rw[i] += blFunc(BLStruct) endfor endif endif endif String baselineStr = WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, 0) if (CmpStr(baselineStr, "None"+MENU_ARROW_STRING) != 0) String BL_TypeName = MPF2_PeakOrBLTypeFromListString(baselineStr) FUNCREF MPF2_FuncInfoTemplate blinfo = $(BL_typename + BL_INFO_SUFFIX) BL_FuncName = blinfo(BLFuncInfo_BaselineFName) FUNCREF MPF2_BaselineFunctionTemplate blFunc = $BL_FuncName Wave BLStruct.cWave = 'Baseline Coefs' for (i = 0; i < numpnts(w); i += 1) BLStruct.x = pnt2x(w, i) w[i] += blFunc(BLStruct) endfor if (doResidual) if (WaveExists(xDataWave)) for (i = 0; i < numpnts(rw); i += 1) BLStruct.x = xDataWave[i] rw[i] -= blFunc(BLStruct) endfor else for (i = 0; i < numpnts(rw); i += 1) BLStruct.x = pnt2x(rw, i) rw[i] -= blFunc(BLStruct) endfor endif endif endif SetDataFolder saveDF end Function MPF2_UpdateOnePeakOnGraph(setNumber, PeakNumber, FitWaveName, OldCoefs, OldPeakType, DoResidual, ResidWaveName, XDataWave) Variable setNumber Variable PeakNumber String FitWaveName Wave OldCoefs String OldPeakType Variable DoResidual String ResidWaveName Wave/Z XDataWave String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR gname = $(DFpath+":GraphName") String saveDF = GetDataFolder(1) SetDataFolder DFPath FUNCREF MPF2_FuncInfoTemplate infoFunc=$(OldPeakType+PEAK_INFO_SUFFIX) String PeakFuncName = infoFunc(PeakFuncInfo_PeakFName) FUNCREF MPF2_PeakFunctionTemplate peakFunc = $PeakFuncName Wave w = $FitWaveName Make/N=(numpnts(w))/D/O MPF2_TempXWave,MPF2_TempYWave MPF2_TempXWave = pnt2x(w, p) peakFunc(OldCoefs, MPF2_TempYWave, MPF2_TempXWave) w -= MPF2_TempYWave if (DoResidual) Wave rw = $ResidWaveName Make/N=(numpnts(rw))/D/O MPF2_TempXRWave,MPF2_TempYRWave if (WaveExists(XDataWave)) peakFunc(OldCoefs, MPF2_TempYRWave, XDataWave) else MPF2_TempXRWave = pnt2x(rw, p) peakFunc(OldCoefs, MPF2_TempYRWave, MPF2_TempXRWave) endif rw += MPF2_TempYRWave endif Variable theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(PeakNumber)) String PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, theRow) ) FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) PeakFuncName = infoFunc(PeakFuncInfo_PeakFName) FUNCREF MPF2_PeakFunctionTemplate peakFunc = $PeakFuncName Wave coefs = $("Peak "+num2istr(PeakNumber)+" Coefs") peakFunc(coefs, MPF2_TempYWave, MPF2_TempXWave) w += MPF2_TempYWave String PeakWaveName = "Peak "+num2istr(PeakNumber) Wave w = $PeakWaveName Make/D/N=(numpnts(w))/O MPF2_TempPeakXWave MPF2_TempPeakXWave = pnt2x(w, p) peakFunc(coefs, w, MPF2_TempPeakXWave) if (DoResidual) if (WaveExists(XDataWave)) peakFunc(coefs, MPF2_TempYRWave, XDataWave) else peakFunc(coefs, MPF2_TempYRWave, MPF2_TempXRWave) endif rw -= MPF2_TempYRWave endif KillWaves/Z MPF2_TempYWave, MPF2_TempXWave, MPF2_TempYRWave, MPF2_TempXRWave, MPF2_TempPeakXWave SetDataFolder saveDF end Function MPF2_MinPeakWidth(wpi) Wave/Z wpi if (!WaveExists(wpi) || (DimSize(wpi, 0) == 0)) return 0 endif Variable nPeaks = DimSize(wpi, 0) Variable i Variable minWidth = inf for (i = 0; i < nPeaks; i += 1) minWidth = min(wpi[i][1], minWidth) endfor return minWidth end Function MPF2_AdjustAxes(gname) String gname Variable axisBits = 0 GetAxis/W=$gname/Q Left if (V_flag == 0) axisBits += 1 endif GetAxis/W=$gname/Q Res_Left if (V_flag == 0) axisBits += 2 endif GetAxis/W=$gname/Q Peaks_Left if (V_flag == 0) axisBits += 4 endif switch (axisBits) case 1: ModifyGraph/W=$gname axisEnab(left)={0,1} break; case 2: // highly unlikely ModifyGraph/W=$gname axisEnab(Res_Left)={0, 1}, lblPosMode(Res_Left)=1 // lblPosMode set to Absolute mode break; case 3: ModifyGraph/W=$gname axisEnab(left)={0,.75} ModifyGraph/W=$gname axisEnab(Res_Left)={.8, 1}, lblPosMode(Res_Left)=1 // lblPosMode set to Absolute mode break; case 4: // highly unlikely ModifyGraph/W=$gname axisEnab(Peaks_Left)={0, 1}, lblPosMode(Peaks_Left)=1 // lblPosMode set to Absolute mode ModifyGraph/W=$gname freePos(Peaks_Left)={0,kwFraction} break; case 5: ModifyGraph/W=$gname axisEnab(left)={.25, 1} ModifyGraph/W=$gname axisEnab(Peaks_Left)={0, .2}, lblPosMode(Peaks_Left)=1 // lblPosMode set to Absolute mode ModifyGraph/W=$gname freePos(Peaks_Left)={0,kwFraction} break; case 6: // highly unlikely ModifyGraph/W=$gname axisEnab(Res_Left)={.52, 1}, lblPosMode(Res_Left)=1 // lblPosMode set to Absolute mode ModifyGraph/W=$gname axisEnab(Peaks_Left)={0, .48}, lblPosMode(Peaks_Left)=1 // lblPosMode set to Absolute mode ModifyGraph/W=$gname freePos(Peaks_Left)={0,kwFraction} break; case 7: ModifyGraph/W=$gname axisEnab(left)={.25,.75} ModifyGraph/W=$gname axisEnab(Res_Left)={.8, 1}, lblPosMode(Res_Left)=1 // lblPosMode set to Absolute mode ModifyGraph/W=$gname axisEnab(Peaks_Left)={0, .2}, lblPosMode(Peaks_Left)=1 // lblPosMode set to Absolute mode ModifyGraph/W=$gname freePos(Peaks_Left)={0,kwFraction} break; endswitch end Function MPF2_AddFitCurveToGraph(setNumber, wfi, yDataWave, xDataWave, doResidual [, overridePoints]) Variable setNumber Wave/Z wfi Wave yDataWave Wave/Z xDataWave Variable doResidual Variable overridePoints String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR gname = $(DFpath+":GraphName") String saveDF = GetDataFolder(1) SetDataFolder DFPath NVAR XPointRangeBegin NVAR XPointRangeEnd NVAR XPointRangeReversed Variable xleft Variable xright if (WaveExists(xDataWave)) xleft = xDataWave[XPointRangeBegin] xright = xDataWave[XPointRangeEnd] else xleft = pnt2x(yDataWave, XPointRangeBegin) xright = pnt2x(yDataWave, XPointRangeEnd) endif Variable dx = MPF2_MinPeakWidth(wfi)/10 Variable npnts = 200 if (overridePoints > 0) npnts = overridePoints else if (dx > 0) npnts = abs(xright-xleft)/dx endif endif Variable/G MPF2_FitCurvePoints = npnts String fitName = "fit_"+NameOfWave(yDataWave) Make/O/N=(npnts) $fitName Wave yw = $fitName setscale/I x min(xleft, xright), max(xleft, xright), yw yw = 0 CheckDisplayed/W=$gname yw if (V_flag == 0) AppendToGraph/W=$gname yw ModifyGraph/W=$gname rgb($NameOfWave(yw))=(1,4,52428) else Wave/Z txw = XWaveRefFromTrace(gname, NameOfWave(yw) ) if (WaveExists(txw)) ReplaceWave/W=$gname/X trace=$NameOfWave(yw), $"" endif endif if (doResidual) Duplicate/O yDataWave, $("Res_"+NameOfWave(yDataWave)) Wave rw = $("Res_"+NameOfWave(yDataWave)) if (WaveType(rw) != 4) Redimension/D rw endif if (XPointRangeBegin > 0) rw[0,XPointRangeBegin-1] = 0 endif if (XPointRangeEnd < numpnts(rw)-2) rw[XPointRangeEnd+1,]=0 endif CheckDisplayed/W=$gname rw if (V_flag == 0) if (WaveExists(xDataWave)) AppendToGraph/W=$gname/L=Res_left rw vs xDataWave else AppendToGraph/W=$gname/L=Res_left rw endif ModifyGraph/W=$gname rgb($NameOfWave(yw))=(1,4,52428) ModifyGraph freePos(Res_left)={0,kwFraction} endif endif Variable nParams Variable i String bkgName Variable BaselineRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Baseline") String baselineStr = WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, BaselineRow) if (CmpStr(baselineStr, "None"+MENU_ARROW_STRING) != 0) String BL_FuncName String BL_TypeName = MPF2_PeakOrBLTypeFromListString(baselineStr) FUNCREF MPF2_FuncInfoTemplate blinfo = $(BL_typename + BL_INFO_SUFFIX) BL_FuncName = blinfo(BLFuncInfo_BaselineFName) FUNCREF MPF2_BaselineFunctionTemplate blFunc = $BL_FuncName STRUCT MPF2_BLFitStruct BLStruct if (WaveExists(xDataWave)) BLStruct.xStart = xDataWave[XPointRangeBegin] BLStruct.xEnd = xDataWave[XPointRangeEnd] else BLStruct.xStart = pnt2x(yDataWave, XPointRangeBegin) BLStruct.xEnd = pnt2x(yDataWave, XPointRangeEnd) endif Wave BLStruct.cWave = 'Baseline Coefs' for (i = 0; i < numpnts(yw); i += 1) BLStruct.x = pnt2x(yw, i) yw[i] += blFunc(BLStruct) endfor if (doResidual) for (i = XPointRangeBegin; i <= XPointRangeEnd; i += 1) if (WaveExists(xDataWave)) BLStruct.x = xDataWave[i] else BLStruct.x = pnt2x(rw, i) endif rw[i] -= blFunc(BLStruct) endfor endif bkgName = "Bkg_"+NameOfWave(yDataWave) Make/O/N=500 $bkgName Wave bkgw = $bkgName SetScale x leftx(yw), rightx(yw), bkgw bkgw = yw(x) CheckDisplayed/W=$gname bkgw if (V_flag == 0) AppendToGraph/W=$gname bkgw ModifyGraph/W=$gname rgb($NameOfWave(bkgw))=(2,39321,1) endif else bkgName = "Bkg_"+NameOfWave(yDataWave) RemoveFromGraph/W=$gname/Z $bkgName endif Variable nPeaks = 0 if (WaveExists(wfi)) nPeaks = DimSize(wfi, 0) endif Make/N=(numpnts(yw))/O/D MPF2_TempXWave, MPF2_TempYWave MPF2_TempXWave = pnt2x(yw, p) MPF2_TempYWave = yw if (doResidual) Duplicate/O rw, MPF2_TempYRWave Make/N=(numpnts(rw))/O/D MPF2_TempXRWave if (WaveExists(xDataWave)) MPF2_TempXRWave = XDataWave else MPF2_TempXRWave = pnt2x(rw, p) endif endif for (i = 0; i < nPeaks; i += 1) Wave coefs = $("Peak "+num2istr(i)+" Coefs") Variable theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(i)) String PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, theRow) ) FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) String PeakFuncName = infoFunc(PeakFuncInfo_PeakFName) FUNCREF MPF2_PeakFunctionTemplate peakFunc = $PeakFuncName peakFunc(coefs, MPF2_TempYWave, MPF2_TempXWave) yw += MPF2_TempYWave if (doResidual) peakFunc(coefs, MPF2_TempYRWave, MPF2_TempXRWave) rw[XPointRangeBegin, XPointRangeEnd] -= MPF2_TempYRWave[p] endif //doupdate endfor KillWaves/Z MPF2_TempXWave, MPF2_TempYWave, MPF2_TempXRWave, MPF2_TempYRWave SetDataFolder saveDF MPF2_AdjustAxes(gname) //doupdate end Function/S MPF2_ListPeakTypeNames() String funcList = FunctionList("*"+PEAK_INFO_SUFFIX, ";", "NPARAMS:1,VALTYPE:4") String theList="" Variable nItems = ItemsInList(funcList) Variable i for (i = 0; i < nItems; i += 1) String oneFunc = StringFromList(i, funcList) theList += oneFunc[0,strlen(oneFunc)-strlen(PEAK_INFO_SUFFIX)-1] + ";" endfor return theList end Function/S MPF2_ListBaseLineTypeNames() String funcList = FunctionList("*"+BL_INFO_SUFFIX, ";", "NPARAMS:1") String theList="" Variable nItems = ItemsInList(funcList) Variable i for (i = 0; i < nItems; i += 1) String oneFunc = StringFromList(i, funcList) theList += oneFunc[0,strlen(oneFunc)-strlen(BL_INFO_SUFFIX)-1] + ";" endfor return theList end Function MPF2_GetSavedPeakOrBLInfo(setNumber, ListRow, SavedCoefWave, SavedPeakorBLType, PeakNumber) Variable setNumber Variable ListRow Wave SavedCoefWave String &SavedPeakorBLType Variable &PeakNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFPath SVAR gname = GraphName string parentitem = WMHL_GetParentItemForRow(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", ListRow) if (strlen(parentItem) > 0) ListRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", parentitem) endif if (ListRow == 0) // it's the container row for the baseline String baselineStr = WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, 0) SavedPeakorBLType = MPF2_PeakOrBLTypeFromListString(baselineStr) if (CmpStr(baselineStr, "None"+MENU_ARROW_STRING) != 0) Wave cwave = 'Baseline Coefs' Redimension/N=(numpnts(cwave)) SavedCoefWave SavedCoefWave = cwave endif else SavedPeakorBLType = MPF2_PeakOrBLTypeFromListString(WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, ListRow)) String peakName = ParseFilePath(0, WMHL_GetItemForRowNumber(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", ListRow), ":", 1, 0) String wavePath = peakName+" Coefs" Wave cwave = $wavePath Redimension/N=(numpnts(cwave)) SavedCoefWave SavedCoefWave = cwave sscanf peakName, "Peak %d", peakNumber endif SetDataFolder saveDF end Function SetHoldCheckboxesFromWave(setNumber) Variable setNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Wave/T HoldStrings = $(DFPath+":HoldStrings") SVAR gname = $(DFPath+":GraphName") Variable i, j String hs Variable nchildren Variable selWaveValue // set checkboxes for baseline row if (WMHL_RowIsOpen(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0)) hs = HoldStrings[0] nchildren = ItemsInList(WMHL_ListChildRows(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0)) for (j = 0; j < nchildren; j += 1) selWaveValue = 0x20 // a checkbox with the Checked attribute turned off if ( (strlen(hs) > 0) && (char2num(hs[j]) == char2num("1")) ) selwaveValue = 0x10+0x20 endif WMHL_ExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 1, j+1, "Hold", 0, setSelWaveValue=selwaveValue) endfor endif i = 0 do Variable parentRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2str(i)) if (parentRow < 0) break; endif if (WMHL_RowIsOpen(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", parentRow)) hs = HoldStrings[i+1] nchildren = ItemsInList(WMHL_ListChildRows(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", parentRow)) for (j = 0; j < nchildren; j += 1) selWaveValue = 0x20 // a checkbox with the Checked attribute turned off if ( (strlen(hs) > 0) && (char2num(hs[j]) == char2num("1")) ) selwaveValue = 0x10+0x20 endif WMHL_ExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 1, parentRow+j+1, "Hold", 0, setSelWaveValue=selwaveValue) endfor endif i += 1 while (1) end Static Function/S RemoveNonPeaksFromList(inputList) String inputList Variable nItems = itemsInList(inputList) Variable i String newList = "" for (i = nItems-1; i >= 0; i -= 1) String oneItem = StringFromList(i, inputList) if (CmpStr(oneItem[0,4], "Peak ") == 0) newList += oneItem+";" endif endfor return newList end // Action procedure Function MPF2_PeakListProc(s) STRUCT WMListboxAction &s if (s.eventCode == -1) // control being killed, presumably because the window is being closed return 0 endif Variable setNumber = GetSetNumberFromWinName(s.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Variable retValue = 0 Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") Variable npeaks = 0 if (WaveExists(wpi)) npeaks = DimSize(wpi, 0) endif SVAR YWvName = $(DFpath+":YWvName") Wave yw = $YWvName SVAR XWvName = $(DFpath+":XWvName") Wave/Z xw = $XWvName SVAR gname=$(DFPath+":"+"GraphName") Variable i String peakorBLtype="" Variable peakNumber string parentitem string rowItem Variable itemRow String peakType Variable rowIsOpen switch(s.eventCode) case 1: // mouse down if ( (s.row >= 0) && (s.row < DimSize(s.listWave, 0)) ) if (s.col >= 1) Make/D/O/N=0 SavedCoefWave MPF2_GetSavedPeakOrBLInfo(setNumber, s.row, SavedCoefWave, peakorBLtype, peakNumber) parentitem = WMHL_GetParentItemForRow(s.win, s.ctrlName, s.row) Variable parentRow = WMHL_GetRowNumberForItem(s.win, s.ctrlName, parentItem) if (strlen(parentitem) > 0) // > 0 means "not the baseline row" // has a parent, must be one of the coefficient value rows. Possibly put up a menu with "Set all to this value" or "Hold all of this" if (s.eventMod & 16) // contextual menu click retValue = 1 if ( (s.col == 1) || (s.col == 2) ) PopupContextualMenu "Copy This Value to All Peaks of This Type;" if (V_flag > 0) Variable peakNum sscanf parentItem, "Peak %d", peakNum Wave coefs = $(DFPath+":'Peak "+num2istr(peakNum)+" Coefs'") Variable paramNum = s.row-parentRow-1 Variable coefValue = coefs[paramNum] i = 0; do itemRow = WMHL_GetRowNumberForItem(s.win, s.ctrlName, "Peak "+num2str(i)) if (itemRow < 0) break; endif peakType = MPF2_PeakOrBLTypeFromListString(WMHL_GetExtraColumnData(s.win, s.ctrlName, 0, itemRow)) if (CmpStr(peakType, peakorBLtype) == 0) // same type of peak as the one clicked on Wave coefs = $(DFPath+":'Peak "+num2istr(i)+" Coefs'") coefs[paramNum] = coefValue rowIsOpen = WMHL_RowIsOpen(s.win, s.ctrlName, itemRow) if (rowIsOpen) rowItem = WMHL_GetItemForRowNumber(s.win, s.ctrlName, itemRow) WMHL_CloseAContainer(s.win, s.ctrlName, rowItem) // info for this row has changed. Opening the row re-evaluates the info WMHL_OpenAContainer(s.win, s.ctrlName, rowItem) endif endif i += 1 while(1) NVAR negativePeaks = $(DFPath+":negativePeaks") MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1) MPF2_AddFitCurveToGraph(setNumber, wpi, yw, xw, 1, overridePoints=MPF2_getFitCurvePoints(gname+"#MultiPeak2Panel")) endif elseif (s.col == 3) PopupContextualMenu "Hold for All Peaks of This Type;No Hold for All Peaks of This Type;" if (V_flag > 0) Variable doHold = V_flag == 1 Variable nParams = ItemsInList(WMHL_ListChildRows(s.win, s.ctrlName, parentRow)) Variable itemOffset = s.row-parentRow Wave/T HoldStrings= $(DFPath+":HoldStrings") MPF2_RefreshHoldStrings(gname+"#MultiPeak2Panel") i = 0; do itemRow = WMHL_GetRowNumberForItem(s.win, s.ctrlName, "Peak "+num2str(i)) if (itemRow < 0) break; endif peakType = MPF2_PeakOrBLTypeFromListString(WMHL_GetExtraColumnData(s.win, s.ctrlName, 0, itemRow)) if (CmpStr(peakType, peakorBLtype) == 0) // same type of peak as the one clicked on String hs = HoldStrings[i+1] if (strlen(hs) == 0) hs = PadString(hs, nParams, char2num("0")) endif if (doHold) hs[itemOffset-1, itemOffset-1] = "1" else hs[itemOffset-1, itemOffset-1] = "0" endif HoldStrings[i+1] = hs endif i += 1 while(1) SetHoldCheckboxesFromWave(setNumber) endif endif endif else // has no parent, must be a peak type row. Possibly put up a peak type selection menu if (s.col == 2) string selection = MPF2_ContextMenuForBLorPeakType(s.win, s.row) if (strlen(selection) > 0) WMHL_ExtraColumnData(s.win, s.ctrlName, 0, s.row, selection+MENU_ARROW_STRING, 0) if (s.row == 0) MPF2_InfoForBaseline(setnumber, selection) MPF2_UpdateBaselineOnGraph(setnumber, "fit_"+NameOfWave(yw), SavedCoefWave, peakorBLtype, 1, "Res_"+NameOfWave(yw), xw, yw) else MPF2_CoefWaveForListRow(setNumber, s.row, selection) MPF2_UpdateOnePeakOnGraph(setnumber, peakNumber, "fit_"+NameOfWave(yw), SavedCoefWave, peakorBLtype, 1, "Res_"+NameOfWave(yw), xw) endif rowIsOpen = WMHL_RowIsOpen(s.win, s.ctrlName, s.row) if (rowIsOpen) rowItem = WMHL_GetItemForRowNumber(s.win, s.ctrlName, s.row) WMHL_CloseAContainer(s.win, s.ctrlName, rowItem) // info for this row has changed. Opening the row re-evaluates the info endif Wave/T HoldStrings = $(DFpath+":HoldStrings") if (s.row == 0) HoldStrings[0] = "" else HoldStrings[peakNumber+1] = "" endif if (rowIsOpen) WMHL_OpenAContainer(s.win, s.ctrlName, rowItem) endif endif KillWaves SavedCoefWave else if (s.eventMod & 16) // contextual menu click String listOfSelectedPeaks = WMHL_SelectedObjectsList(s.win, s.ctrlName) listOfSelectedPeaks = RemoveNonPeaksFromList(listOfSelectedPeaks) rowItem = WMHL_GetItemForRowNumber(s.win, s.ctrlName, s.row) String menuString Variable doSelection = 0 if (strlen(listOfSelectedPeaks) > 0) menuString = "Delete Selected Peaks;" doSelection = 1 else menuString = "Delete "+rowItem endif PopupContextualMenu menuString if (V_flag > 0) if (doSelection) listOfSelectedPeaks = SortList(listOfSelectedPeaks, ";", 17) // sort alphanumerically in descending order so we delete higher-numbers peaks first for (i = 0; i < ItemsInList(listOfSelectedPeaks); i += 1) MPF2_DeleteAPeak(gname, StringFromList(i, listOfSelectedPeaks)) endfor retvalue = 1 else rowItem = WMHL_GetItemForRowNumber(s.win, s.ctrlName, s.row) retvalue = MPF2_DeleteAPeak(gname, rowItem) endif endif endif endif endif endif endif break; case 2: if (s.eventMod & 16) // contextual menu click retvalue = 1 endif break; case 6: // begin edit NewDataFolder/O tempSavedCoefs Make/D/O/N=0 :tempSavedCoefs:SavedCoefWave Wave SavedCoefWave = :tempSavedCoefs:SavedCoefWave MPF2_GetSavedPeakOrBLInfo(setNumber, s.row, SavedCoefWave, peakorBLtype, peakNumber) Variable/G :tempSavedCoefs:savedPeakNumber = peakNumber String/G :tempSavedCoefs:SavedPeakorBLType = peakorBLtype break; case 7: // finish edit parentitem = WMHL_GetParentItemForRow(s.win, s.ctrlName, s.row) Wave SavedCoefWave = :tempSavedCoefs:SavedCoefWave SVAR SavedPeakorBLType = :tempSavedCoefs:SavedPeakorBLType NVAR savedPeakNumber = :tempSavedCoefs:savedPeakNumber if (CmpStr(parentitem[0,3], "Peak") == 0) String peakName = ParseFilePath(0, parentitem, ":", 1, 0) String wavePath = DFPath+":"+PossiblyQuoteName(peakName+" Coefs") Wave wcoef = $wavePath parentRow = WMHL_GetRowNumberForItem(s.win, s.ctrlName, parentitem) String peakRows = WMHL_ListChildRows(s.win, s.ctrlName, parentRow) for (i = 0; i < ItemsInList(peakRows); i += 1) Variable coefRow = str2num(stringFromList(i, peakRows)) wcoef[i] = str2num(WMHL_GetExtraColumnData(s.win, s.ctrlName, 0, coefRow)) endfor MPF2_UpdateOnePeakOnGraph(setnumber, savedPeakNumber, "fit_"+NameOfWave(yw), SavedCoefWave, SavedPeakorBLType, 1, "Res_"+NameOfWave(yw), xw) elseif (CmpStr(parentitem, "Baseline") == 0) wavePath = DFPath+":"+"'Baseline Coefs'" Wave wcoef = $wavePath parentRow = WMHL_GetRowNumberForItem(s.win, s.ctrlName, parentitem) String baselineRows = WMHL_ListChildRows(s.win, s.ctrlName, parentRow) for (i = 0; i < ItemsInList(baseLineRows); i += 1) coefRow = str2num(stringFromList(i, baselineRows)) wcoef[i] = str2num(WMHL_GetExtraColumnData(s.win, s.ctrlName, 0, coefRow)) endfor MPF2_UpdateBaselineOnGraph(setnumber, "fit_"+NameOfWave(yw), SavedCoefWave, SavedPeakorBLType, 1, "Res_"+NameOfWave(yw), xw, yw) endif KillDataFolder :tempSavedCoefs break; case 12: // keyboard event if ( (s.row == 8) || (s.row == 127) ) // backspace or delete string selections = WMHL_SelectedObjectsList(s.win, s.ctrlName) Variable nItems = ItemsInList(selections) Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") SVAR YWvName = $(DFpath+":YWvName") Wave YData = $YWvName SVAR XWvName = $(DFpath+":XWvName") Wave/Z XData = $XWvName if (WaveExists(wpi)) for (i = nItems-1; i >= 0; i -= 1) String selectedItem = StringFromList(i, selections) MPF2_DeleteAPeak(gname, selectedItem) endfor endif endif break; endswitch MPF2_EnableDisableDoFitButton(setNumber) return retValue end // listbox Function MPF2_CoefWaveForListRow(setNumber, row, peakTypeName) Variable setNumber Variable row String peakTypeName Variable nparams String GaussGuessConversionFuncName String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR gname=$(DFPath+":"+"GraphName") Wave wpi = $(DFPath+":"+"W_AutoPeakInfo") string ContainerPath = WMHL_GetItemForRowNumber(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", row) String peakName = ParseFilePath(0, ContainerPath, ":", 1, 0) String wavePath = DFPath+":"+PossiblyQuoteName(peakName+" Coefs") Make/D/O/N=(DimSize(wpi, 1)) $wavePath Wave w = $wavePath Variable infoWaveRow = str2num(ContainerPath[5, strlen(ContainerPath)-1]) w = wpi[infoWaveRow][p] FUNCREF MPF2_FuncInfoTemplate infoFunc=$(peakTypeName+PEAK_INFO_SUFFIX) GaussGuessConversionFuncName = infoFunc(PeakFuncInfo_GaussConvFName) FUNCREF MPF2_GaussGuessConvTemplate conversionFunc = $GaussGuessConversionFuncName conversionFunc(w) end Function/S MPF2_ContextMenuForBLorPeakType(hostWindow, row) String hostWindow Variable row String selectedItem = "" if (row == 0) // it's the container row for the baseline PopupContextualMenu MPF2_ListBaseLineTypeNames() else if (strlen(WMHL_GetParentItemForRow(hostWindow, "MPF2_PeakList", row)) == 0) PopupContextualMenu MPF2_ListPeakTypeNames() endif endif if (V_flag > 0) selectedItem = S_selection endif return selectedItem end Function MPF2_InfoForBaseline(setnumber, BL_typename) Variable setnumber String BL_typename Variable nParams String ParamNameList FUNCREF MPF2_FuncInfoTemplate blinfo = $(BL_typename + BL_INFO_SUFFIX) ParamNameList = blinfo(BLFuncInfo_ParamNames) // BLFuncName = blinfo(BLFuncInfo_BaselineFName) nparams = ItemsInList(ParamNameList) // NVAR currentSetNumber = root:Packages:MultiPeakFit2:currentSetNumber String DFpath = MPF2_FolderPathFromSetNumber(setnumber) // SVAR gname = $(DFpath+":GraphName") Make/O/D/N=(nparams) $(DFPath+":"+"'Baseline Coefs'") end static Function MPF2_BackupCoefWaves(listofWaveNames, DataFolderName) String listofWaveNames, DataFolderName String saveDF = GetDataFolder(1) SetDataFolder DataFolderName Variable nWaves = ItemsInList(listofWaveNames) Variable i for (i = 0; i < nWaves; i += 1) Wave/Z coefs = $StringFromList(i, listofWaveNames) if (WaveExists(coefs)) // this is actually because the baseline coefficient wave may not exist. Duplicate/O coefs, $("MPF2_CoefsBackup_"+num2istr(i)) endif endfor SetDataFolder saveDF end Static Function/S MPF2_HoldStringForPeakListItem(theItem, DatafolderPath, thePanelWin) String theItem, DatafolderPath, thePanelWin String SaveDF = GetDataFolder(1) SetDataFolder DatafolderPath Wave/T HoldStrings if (!WaveExists(HoldStrings)) SetDataFolder SaveDF return "" endif String holdString = "" Variable isBaseLine = CmpStr(theItem, "Baseline") == 0 String children="" Variable i Variable numItems Variable rownumber Variable HoldStringsRow // It is possible to get into this function without having set the size of the hold strings wave correctly Wave/Z wpi = W_AutoPeakInfo Variable npeaks = 0 if (WaveExists(wpi)) npeaks = DimSize(wpi, 0) endif if (DimSize(HoldStrings, 0) < npeaks+1) Variable oldSize = DimSize(HoldStrings, 0) Redimension/N=(npeaks+1) HoldStrings HoldStrings[oldSize, npeaks] = "" endif if (isBaseLine) HoldStringsRow = 0 else sscanf theItem, "Peak %d", HoldStringsRow HoldStringsRow += 1 // to account for the fact that the baseline holds are in row 0 endif rownumber = WMHL_GetRowNumberForItem(thePanelWin, "MPF2_PeakList", theItem) if (WMHL_RowIsOpen(thePanelWin, "MPF2_PeakList", rownumber)) children = WMHL_ListChildRows(thePanelWin, "MPF2_PeakList", rownumber) numItems = ItemsInList(children) for (i = 0; i < numitems; i += 1) rownumber = str2num(StringFromList(i, children)) Variable SelWaveValue = WMHL_GetExtraColumnSelValue(thePanelWin, "MPF2_PeakList", 1, rownumber) if (SelWaveValue & 0x10) holdString += "1" else holdString += "0" endif endfor else holdString = HoldStrings[HoldStringsRow] endif Variable numchars = strlen(holdString) Variable Char1 = char2num("1") for (i = 0; i < numchars; i += 1) if (char2num(holdString[i]) == Char1) break; // found a 1 endif endfor if (i == numchars) holdString = "" // didn't find a 1 endif SetDataFolder SaveDF return holdString end // finds rows in the peak list that are open and writes the holds from those peaks (or the baseline) into the HoldStrings text wave Static Function MPF2_RefreshHoldStrings(PanelWin) String PanelWin Variable setNumber = GetSetNumberFromWinName(PanelWin) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Wave/T HoldStrings = $(DFPath+":HoldStrings") String listPanel = PanelWin+"#P1" Variable rownumber = WMHL_GetRowNumberForItem(listPanel, "MPF2_PeakList", "Baseline") if (WMHL_RowIsOpen(listPanel, "MPF2_PeakList", rownumber)) HoldStrings[0] = MPF2_HoldStringForPeakListItem("Baseline", DFPath, listPanel) endif Variable i=0 do String peakItem = "Peak "+num2str(i) rownumber = WMHL_GetRowNumberForItem(listPanel, "MPF2_PeakList", peakItem) if (rownumber < 0) break; endif HoldStrings[i+1] = MPF2_HoldStringForPeakListItem(peakItem, DFPath, listPanel) i += 1 while(1) end // Please have the current data folder set to the correct data folder for the current set // // This function makes a theoretical peak using the peak function and parameters for the given row, then applies the algorithm used // by AutoPeakFind to re-construct the auto peak picker info. //static Function MPF2_GetSimulatedAutoPickData(peakCoefRow, listPanelName, tempAutoPickInfo) // Variable peakCoefRow // String listPanelName // Wave tempAutoPickInfo // // String PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(listPanelName, "MPF2_PeakList", 0, peakCoefRow) ) // FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) // String PeakFuncName = infoFunc(PeakFuncInfo_PeakFName) // FUNCREF MPF2_PeakFunctionTemplate peakFunc = $PeakFuncName // String ParamFuncName = infoFunc(PeakFuncInfo_ParameterFunc) // Variable nDerivedParams = ItemsInList(infoFunc(PeakFuncInfo_DerivedParamNames)) // Make/O/N=(nDerivedParams,2) tempParams // // FUNCREF MPF2_ParamFuncTemplate paramFunc=$ParamFuncName // String peakName = WMHL_GetItemForRowNumber(listPanelName, "MPF2_PeakList", peakCoefRow) // Wave coef = $(peakName+" Coefs") // Make/D/N=(numpnts(coef), numpnts(coef))/O dummycov=0 // paramFunc(coef, dummycov, tempParams) // Make/D/N=1001/O/D tempPeakWavey, tempPeakWavex // Variable location, FWHM // location = tempParams[0][0] // FWHM = tempParams[3][0] // SetScale/I x location-10*FWHM, location +10*FWHM, tempPeakWavey // tempPeakWavex = pnt2x(tempPeakWavey, p) // peakFunc(coef, tempPeakWavey, tempPeakWavex) // Duplicate/O tempPeakWavey, difPeakWavey // // // OK, we finally have a synthetic peak // if (tempParams[1][0] < 0) // difPeakWavey = -difPeakWavey // endif // Differentiate difPeakWavey // Differentiate difPeakWavey // WaveStats/Q difPeakWavey // Variable autoPeakLocation = V_minLoc // Variable locP = x2pnt(tempPeakWavey, autoPeakLocation) // // FindLevel/Q/R=[locP,] difPeakWavey,0 // Variable xr= V_LevelX // FindLevel/Q/R=[locP,0] difPeakWavey,0 // note search is from right to left // Variable xl= V_LevelX // Variable baseline = (tempPeakWavey(xr) + tempPeakWavey(xl))/2 // Variable height = 2*(tempPeakWavey(autoPeakLocation) - baseline) // // tempAutoPickInfo = {autoPeakLocation, xr-xl, height, autoPeakLocation-xl, xr-autoPeakLocation} //end Function MPF2_DoFitButtonProc(s) : ButtonControl STRUCT WMButtonAction &s if (s.eventCode != 2) // mouse-up in the control return 0 endif STRUCT MPFitInfoStruct MPStruct Variable setNumber = GetSetNumberFromWinName(s.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") if (!WaveExists(wpi)) DoAlert 0, "There are no peaks to fit." return -1 endif MPStruct.NPeaks = DimSize(wpi, 0) SVAR YWvName = $(DFpath+":YWvName") SVAR XWvName = $(DFpath+":XWvName") Wave MPStruct.yWave = $YWvName Wave/Z MPStruct.xWave = $XWvName SVAR gname = $(DFpath+":GraphName") String listPanelName = gname+"#MultiPeak2Panel#P1" Wave/Z MPStruct.weightWave = $PopupWS_GetSelectionFullPath( gname+"#MultiPeak2Panel#P3", "MPF2_SelectWeightWave") Wave/Z MPStruct.maskWave = $PopupWS_GetSelectionFullPath( gname+"#MultiPeak2Panel#P3", "MPF2_SelectMaskWave") String saveDF = GetDataFolder(1) SetDataFolder DFPath NVAR XPointRangeBegin NVAR XPointRangeEnd NVAR MPF2_FitCurvePoints MPStruct.XPointRangeBegin = XPointRangeBegin MPStruct.XPointRangeEnd = XPointRangeEnd MPStruct.FitCurvePoints = MPF2_FitCurvePoints String/G FuncListString="" Variable nBLParams String ParamNameList String pwname Variable i String OneHoldString = "" Wave/T HoldStrings Variable BaselineRow = WMHL_GetRowNumberForItem(listPanelName, "MPF2_PeakList", "Baseline") String baselineStr = WMHL_GetExtraColumnData(listPanelName, "MPF2_PeakList", 0, BaselineRow) MPStruct.ListOfFunctions = MPF2_PeakOrBLTypeFromListString(baselineStr)+";" Variable doBaseLine = CmpStr(baselineStr, "None"+MENU_ARROW_STRING) != 0 MPStruct.ListOfCWaveNames = "Baseline Coefs;" // if baseline type is "None", this wave probably doesn't exist, but it doesn't matter because it will be ignored if (doBaseLine) MPStruct.ListOfHoldStrings = MPF2_HoldStringForPeakListItem("Baseline", DFpath, listPanelName)+";" else MPStruct.ListOfHoldStrings = ";" endif for (i = 0; i < MPStruct.NPeaks; i += 1) MPStruct.ListOfCWaveNames += "Peak "+num2istr(i)+" Coefs;" String peakItem = "Peak "+num2istr(i) Variable theRow = WMHL_GetRowNumberForItem(listPanelName, "MPF2_PeakList", peakItem) String PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(listPanelName, "MPF2_PeakList", 0, theRow) ) MPStruct.ListOfFunctions += PeakTypeName+";" MPStruct.ListOfHoldStrings += MPF2_HoldStringForPeakListItem(peakItem, DFpath, listPanelName)+";" endfor //print "Function list = ["+FuncListString+"]" //print "Function list has ", strlen(FuncListString), "characters" MPStruct.fitOptions = 4 MPF2_SaveFunctionTypes(gname+"#MultiPeak2Panel") //Variable etime = ticks MPF2_DoMPFit(MPStruct, DFPath+":") //etime = ticks-etime //print "Time for fit: ", etime/60," seconds" FuncListString = MPStruct.FuncListString String alertMsg // if (MPStruct.fitError || ((MPStruct.fitQuitReason > 0) && (MPStruct.fitQuitReason < 3)) ) // Variable doRestore = 1 // if (MPStruct.fitQuitReason == 2) // alertMsg = "Multi-peak Fit cancelled." // else // alertMsg = "Multi-peak fit failed:" // endif // if (MPStruct.fitError & 2) // alertMsg += "\rSingular matrix error." // endif // if (MPStruct.fitError & 4) // alertMsg += "\rOut of memory." // endif // if (MPStruct.fitError & 8) // alertMsg += "\rFunction return NaN or INF." // endif // if (MPStruct.fitQuitReason == 1) // alertMsg += "\rNumber of iterations exceded the limit." // doRestore = 0 // allow the fit to continue from where it left off if Do Fit is clicked again // endif // if (doRestore) // MPF2_RestoreCoefWavesFromBackup(MPStruct.ListOfCWaveNames, DFPath) // endif // DoAlert 0, alertMsg Variable doRestore = 0 if (MPStruct.fitError || MPstruct.fitQuitReason) doRestore = 1 if (MPStruct.fitError) DoAlert 0, "Multi-peak Fit failed: \r\r"+MPstruct.fitErrorMsg else switch (MPstruct.fitQuitReason) case 1: DoAlert 0, "Multi-peak fit exceded the iteration limit. Click Fit again to continue." doRestore = 0 break; case 2: DoAlert 0, "Multi-peak fit cancelled." break; case 3: DoAlert 0, "Multi-peak fit not progressing. Chances are the fit is good." doRestore = 0 break; endswitch endif if (doRestore) MPF2_RestoreCoefWavesFromBackup(MPStruct.ListOfCWaveNames, DFPath) endif endif if (doRestore == 0) //Structure MPFitInfoStruct // Variable NPeaks // Wave yWave // Wave xWave // // Variable XPointRangeBegin // Variable XPointRangeEnd // Variable XPointRangeReversed // Variable FitCurvePoints // Variable fitOptions // this value will be used to set V_fitOptions // // String ListOfFunctions // first item in the list is the baseline type, which might be "None"; the rest will be one peak type per peak to fit. // String ListOfCWaveNames // first is name of coefficient wave for baseline. If baseline function is "None", the first item is ignored (but must be present) // // // outputs // String FuncListString // Variable fitError // Variable fitQuitReason // Variable chisq // Variable fitPnts // Variable dateTimeOfFit //EndStructure Variable/G MPF2_FitDate = MPStruct.dateTimeOfFit Variable/G MPF2_FitPoints = MPStruct.fitPnts Variable/G MPF2_FitChiSq = MPStruct.chisq // Now update the list with the fit results if (doBaseLine) if (WMHL_RowIsOpen(listPanelName, "MPF2_PeakList", BaselineRow)) Wave 'Baseline Coefs' String baselineRows = WMHL_ListChildRows(listPanelName, "MPF2_PeakList", BaselineRow) Variable numBLRows = ItemsInList(baseLineRows) for (i = 0; i < numBLRows; i += 1) Variable BLcoefRow = str2num(stringFromList(i, baselineRows)) WMHL_ExtraColumnData(listPanelName, "MPF2_PeakList", 0, BLcoefRow, num2str('Baseline Coefs'[i]), 1) endfor endif endif for (i = 0; i < MPStruct.NPeaks; i += 1) Variable PeakRow = WMHL_GetRowNumberForItem(listPanelName, "MPF2_PeakList", "Peak "+num2istr(i)) if (WMHL_RowIsOpen(listPanelName, "MPF2_PeakList", PeakRow)) Wave coefs = $("Peak "+num2istr(i)+" Coefs") String coefRows = WMHL_ListChildRows(listPanelName, "MPF2_PeakList", PeakRow) Variable numCoefRows = ItemsInList(coefRows) Variable j for (j = 0; j < numCoefRows; j += 1) Variable peakCoefRow = str2num(stringFromList(j, coefRows)) WMHL_ExtraColumnData(listPanelName, "MPF2_PeakList", 0, peakCoefRow, num2str(coefs[j]), 1) endfor endif // Make/N=5/O tempAutoPickInfo // MPF2_GetSimulatedAutoPickData(PeakRow, listPanelName, tempAutoPickInfo) // wpi[i][] = tempAutoPickInfo[q] endfor MPF2_RefreshPeakResults(setNumber) endif NVAR negativePeaks = negativePeaks MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1) MPF2_AddFitCurveToGraph(setNumber, wpi, MPStruct.yWave, MPStruct.xWave, 1, overridePoints=MPF2_getFitCurvePoints(gname+"#MultiPeak2Panel")) SetDataFolder saveDF End // expects to have the "#" at the end. Makes it easier to use ParseFilePath to extract the base name Function MPF2_HideHelpPanel(basePanel) String basePanel SetWindow $(basePanel +"HelpPanel"),hide=1 SetWindow $(basePanel +"P2"),hide=0 SetWindow $(basePanel+"P3"),hide=0 end Function MPF2_DoFitHelpBoxButtonProc(s) : ButtonControl STRUCT WMButtonAction &s switch( s.eventCode ) case 2: // mouse up MPF2_HideHelpPanel(ParseFilePath(1, s.win, "#", 1, 0)) break endswitch return 0 End Function MPF2_RevertToPreviousButtonProc(s) : ButtonControl STRUCT WMButtonAction &s switch( s.eventCode ) case 2: // mouse up STRUCT MPFitInfoStruct MPStruct Variable setNumber = GetSetNumberFromWinName(s.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR YWvName = $(DFpath+":YWvName") SVAR XWvName = $(DFpath+":XWvName") SVAR gname = $(DFpath+":GraphName") Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") Variable NPeaks = DimSize(wpi, 0) String ListOfCWaveNames = "Baseline Coefs;" // if baseline type is "None", this wave probably doesn't exist, but it doesn't matter because it will be ignored Variable i for (i = 0; i < NPeaks; i += 1) ListOfCWaveNames += "Peak "+num2istr(i)+" Coefs;" endfor MPF2_RestoreCoefWavesFromBackup(ListOfCWaveNames, DFPath) MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1) Wave yWave = $YWvName Wave/Z xWave = $XWvName MPF2_AddFitCurveToGraph(setNumber, wpi, yWave, xWave, 1, overridePoints=MPF2_getFitCurvePoints(gname+"#MultiPeak2Panel")) break endswitch return 0 End //******************************* // Results Display //******************************* static strconstant SMALL_DOWNARROW_STRING=" \Zr075\W523\M" // large-to-small sorting static strconstant SMALL_UPARROW_STRING=" \Zr075\W517\M" // small-to-large sorting //static strconstant SMALL_DOWNARROW_STRING=" \W523" // large-to-small sorting //static strconstant SMALL_UPARROW_STRING=" \W517" // small-to-large sorting static strconstant GRAY_TEXT_STRING="\K(39321,39321,39321)\k(39321,39321,39321)" Function MPF2_ParamFuncTemplate(cw, sw, outWave) Wave cw, sw, outWave end Function MPF2_RefreshPeakResults(setNumber) Variable setNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFpath SVAR/Z MPF2_ResultsPanelName SetDataFolder saveDF if ( SVAR_Exists(MPF2_ResultsPanelName) && (WinType(MPF2_ResultsPanelName) == 7) ) MPF2_DoPeakResults(setNumber) endif end static constant resultsMinWidth=240 static constant resultsMinHeight=176 static constant resultsDefaultWidth=620 static constant resultsDefaultHeight=300 static constant resultsListHeightDif=125 static Function MPF2_AllSamePeakType(setNumber, theType) Variable setNumber String &theType String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFpath SVAR gname=GraphName Variable AllSamePeakType = 1 String lastPeakType = "" Wave wpi = W_AutoPeakInfo Variable npeaks = DimSize(wpi, 0) Variable i String PeakTypeName = "" for (i = 0; i < npeaks; i += 1) Variable theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(i)) PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, theRow) ) if (CmpStr(lastPeakType, PeakTypeName) != 0) if (strlen(lastPeakType) > 0) AllSamePeakType = 0 endif lastPeakType = PeakTypeName endif endfor SetDataFolder saveDF theType = PeakTypeName return AllSamePeakType end Function MPF2_DoPeakResults(setNumber) Variable setNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFpath SVAR gname=GraphName NVAR/Z MPF2_FitDate // if this variable doesn't exist, it means that a fit hasn't been done yet if (!NVAR_Exists(MPF2_FitDate)) DoAlert 0, "No fit results are available yet." return -1 endif Wave wpi = W_AutoPeakInfo Variable npeaks = DimSize(wpi, 0) Variable i, nParamsMax=0, nDerivedParamsMax = 4 // basic set is location, height, area and FWHM Variable nParams Variable j String PeakTypeName Variable AllSamePeakType = MPF2_AllSamePeakType(setNumber, PeakTypeName) // don't actually use the type name returned by PeakTypeName here String MPF2_ResultsPanelName = "MPF2_ResultsPanel"+"_"+num2str(setNumber) if (WinType(MPF2_ResultsPanelName) == 7) ControlInfo/W=$MPF2_ResultsPanelName MPFTResults_BackgroundCheck Variable reportBackground = V_value endif for (i = 0; i < npeaks; i += 1) Wave coefs = $("Peak "+num2istr(i)+" Coefs") if (MPF2_FitDate < modDate(coefs )) DoAlert 0, "The coefficient wave for Peak "+num2istr(i)+" was modified after the last fit, so the results are out of date." SetDataFolder saveDF return -1 endif Variable theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(i)) PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, theRow) ) FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) String ParamNames = infoFunc(PeakFuncInfo_ParamNames) nParamsMax = max(nParamsMax, ItemsInList(ParamNames)) ParamNames = infoFunc(PeakFuncInfo_DerivedParamNames) nDerivedParamsMax = max(nDerivedParamsMax, ItemsInList(ParamNames)) endfor Variable numBLParams = 0 String BL_typename = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, 0) ) if (CmpStr(BL_typename, "None") != 0) // String ParamNameList//, BL_FuncName FUNCREF MPF2_FuncInfoTemplate blinfo = $(BL_typename + BL_INFO_SUFFIX) // ParamNameList = blinfo(BLFuncInfo_ParamNames) // BL_FuncName = blinfo(BLFuncInfo_BaselineFName) numBLParams = ItemsInList(blinfo(BLFuncInfo_ParamNames)) Wave/Z blw = 'Baseline Coefs' if (MPF2_FitDate < modDate(blw )) DoAlert 0, "The coefficient wave for the baseline function was modified after the last fit, so the results are out of date." SetDataFolder saveDF return -1 endif endif Variable nCols = 2 + 2*nDerivedParamsMax + 2*nParamsMax + 2*reportBackground // 2 for Peak Number and Peak Type name; 2* for value and uncertainty. Make/O/T/N=(npeaks, nCols) MPF2_ResultsListWave = "" Make/O/T/N=(nCols) MPF2_ResultsListTitles = "" MPF2_ResultsListTitles[0] = GRAY_TEXT_STRING+SMALL_UPARROW_STRING // up arrow indicates sorting from small to large (like the Macintosh finder). Starts out sorted by peak number which is in column zero. MPF2_ResultsListTitles[1] = "Peak Type" MPF2_ResultsListTitles[2] = "Location" MPF2_ResultsListTitles[4] = "Amplitude" MPF2_ResultsListTitles[6] = "Area" MPF2_ResultsListTitles[8] = "FWHM" if (AllSamePeakType) // put the parameter names into the column titles because all the peaks are the same type theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak 0") PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, theRow) ) FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) ParamNames = infoFunc(PeakFuncInfo_DerivedParamNames) nParams = ItemsInList(ParamNames) if (nParams > 4) for (i = 4; i < nParams; i += 1) MPF2_ResultsListTitles[2+2*i] = StringFromList(i, ParamNames) endfor endif ParamNames = infoFunc(PeakFuncInfo_ParamNames) nParams = ItemsInList(ParamNames) for (i = 0; i < nParams; i += 1) MPF2_ResultsListTitles[2+2*nDerivedParamsMax + 2*i] = StringFromList(i, ParamNames) endfor else // we will be putting the other parameter names into the individual cells because the peak types are variable MPF2_ResultsListTitles[2+2*nDerivedParamsMax] = "Params" endif if (reportBackground) MPF2_ResultsListTitles[2 + 2*nDerivedParamsMax + 2*nParamsMax] = "Background" MPF2_ResultsListTitles[2 + 2*nDerivedParamsMax + 2*nParamsMax + 1] = "Height/Background" endif Variable firstColumn = 2+2*nDerivedParamsMax Variable totalParams = numBLParams Variable totalArea = 0 Variable totalAreaVariance = 0 for (i = 0; i < npeaks; i += 1) Wave coefs = $("Peak "+num2istr(i)+" Coefs") theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(i)) PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, theRow) ) FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) ParamNames = infoFunc(PeakFuncInfo_ParamNames) nParams = ItemsInList(ParamNames) Wave coefs = $("Peak "+num2istr(i)+" Coefs") Variable sigmaSequenceNumber = (numBLParams > 0) ? i+1 : i Wave sigma = $("W_sigma_"+num2istr(sigmaSequenceNumber)) MPF2_ResultsListWave[i][0] = "Peak "+num2str(i) MPF2_ResultsListWave[i][1] = PeakTypeName String ParamFuncName = infoFunc(PeakFuncInfo_ParameterFunc) if (strlen(ParamFuncName) > 0) FUNCREF MPF2_ParamFuncTemplate paramFunc=$ParamFuncName Wave M_covar Make/O/D/N=(nParams, nParams) MPF2_TempCovar Make/O/D/N=(4,2) MPF2_TempParams=NaN // initialize to blanks so that if the function doesn't exist, we just get blanks back- the template function doesn't do anything. MPF2_TempCovar[][] = M_covar[totalParams+p][totalParams+q] paramFunc(coefs, MPF2_TempCovar, MPF2_TempParams) String derivedParamNames = "" if (!AllSamePeakType) derivedParamNames = infoFunc(PeakFuncInfo_DerivedParamNames) endif // the first four parameters are always the same and the names are always in the column titles for (j = 0; j < 4; j += 1) Variable resultColumn = 2*j+2 MPF2_ResultsListWave[i][resultColumn] = num2str(MPF2_TempParams[j][0]) resultColumn += 1 if (numtype(MPF2_TempParams[j][1]) == 2) MPF2_ResultsListWave[i][resultColumn] = "(Not Available)" else MPF2_ResultsListWave[i][resultColumn] = "+/- "+num2str(MPF2_TempParams[j][1]) endif endfor totalArea += MPF2_TempParams[2][0] // area is always in row 2 totalAreaVariance += MPF2_TempParams[2][1]^2 // if there are further derived parameters... for (; j < nDerivedParamsMax; j += 1) if (j < DimSize(MPF2_TempParams, 0)) resultColumn = 2*j+2 if (AllSamePeakType) MPF2_ResultsListWave[i][resultColumn] = "" else MPF2_ResultsListWave[i][resultColumn] = StringFromList(j, derivedParamNames)+"=" endif MPF2_ResultsListWave[i][resultColumn] += num2str(MPF2_TempParams[j][0]) resultColumn += 1 if (numtype(MPF2_TempParams[j][1]) == 2) MPF2_ResultsListWave[i][resultColumn] = "(Not Available)" else MPF2_ResultsListWave[i][resultColumn] = "+/- "+num2str(MPF2_TempParams[j][1]) endif endif endfor endif Variable listColumn for (j = 0; j < nParams; j += 1) listColumn = firstColumn+2*j if (AllSamePeakType) MPF2_ResultsListWave[i][listColumn] = "" else MPF2_ResultsListWave[i][listColumn] = StringFromList(j, ParamNames)+"=" endif MPF2_ResultsListWave[i][listColumn] += num2str(coefs[j]) listColumn += 1 if (numtype(sigma[j]) == 2) MPF2_ResultsListWave[i][listColumn] = "(Not Available)" else MPF2_ResultsListWave[i][listColumn] = "+/- "+num2str(sigma[j]) endif endfor totalParams += nParams if (reportBackground) BL_typename = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, 0) ) if (CmpStr(BL_typename, "None") != 0) String ParamNameList//, BL_FuncName FUNCREF MPF2_FuncInfoTemplate blinfo = $(BL_typename + BL_INFO_SUFFIX) string BL_FuncName = blinfo(BLFuncInfo_BaselineFName) FUNCREF MPF2_BaselineFunctionTemplate blFunc = $BL_FuncName STRUCT MPF2_BLFitStruct BLStruct Wave BLStruct.cWave = 'Baseline Coefs' BLStruct.x = MPF2_TempParams[0][0] SVAR YWvName = $"YWvName" SVAR XWvName = $"XWvName" Wave yw = $YWvName Wave/Z xw = $XWvName NVAR XPointRangeBegin NVAR XPointRangeEnd if (WaveExists(xw)) BLStruct.xStart = xw[XPointRangeBegin] BLStruct.xEnd = xw[XPointRangeEnd] else BLStruct.xStart = pnt2x(yw, XPointRangeBegin) BLStruct.xEnd = pnt2x(yw, XPointRangeEnd) endif Variable bgValue = blFunc(BLStruct) MPF2_ResultsListWave[i][2 + 2*nDerivedParamsMax + 2*nParamsMax] = num2str(bgValue) MPF2_ResultsListWave[i][2 + 2*nDerivedParamsMax + 2*nParamsMax+1] = num2str(MPF2_TempParams[1][0]/bgValue) endif endif endfor KillWaves/Z MPF2_TempCovar, MPF2_TempParams Variable left,top,right,bottom Variable factor = ScreenResolution/72 if (WinType(MPF2_ResultsPanelName) == 7) GetWindow $MPF2_ResultsPanelName wsize left = V_left*factor top = V_top*factor right = V_right*factor bottom = V_bottom*factor DoWindow/K $MPF2_ResultsPanelName else GetWindow $gname wsize left = V_left*factor + 50 top = V_top*factor + 50 right = left + resultsDefaultWidth bottom = top + resultsDefaultHeight endif NewPanel/K=1/N=$MPF2_ResultsPanelName/W=(left,top,right,bottom) as "Multipeak Fit 2 Results" String/G MPF2_Results_DataWavesTitle SVAR YWvName SVAR XWvName Wave/Z xw=$XWvName if (WaveExists(xw)) MPF2_Results_DataWavesTitle = "\f01\K(65535,1,1)Y Wave:\f00\K(0,0,0) "+YWvName //\K(0,0,65535)xxx\K(52428,1,1)yyy\K(0,0,0)zzz MPF2_Results_DataWavesTitle += "\r\f01\K(65535,1,1)X Wave:\f00\K(0,0,0) "+XWvName else MPF2_Results_DataWavesTitle = "\f01\K(52428,1,1)Data Wave:\f00\K(0,0,0) "+YWvName endif TitleBox MPF2_ResultsYWave,pos={355,7},size={259,32},font="Geneva",fSize=9 TitleBox MPF2_ResultsYWave,frame=1 TitleBox MPF2_ResultsYWave,variable=MPF2_Results_DataWavesTitle,anchor= RC String/G MPF2_Results_DateTitle NVAR MPF2_FitDate MPF2_Results_DateTitle = "Multi-peak fit completed " + Secs2time(MPF2_FitDate, 0) MPF2_Results_DateTitle += " " + Secs2Date(MPF2_FitDate, 0) MPF2_Results_DateTitle += "\rMulti-peak Fit Set "+num2str(setNumber) TitleBox MPF2_Results_DataTitle,pos={10,7},size={250,16},fSize=9,frame=1 TitleBox MPF2_Results_DataTitle,variable=MPF2_Results_DateTitle String titlestr NVAR MPF2_FitChiSq sprintf titlestr, "Chi-square = %g",MPF2_FitChiSq TitleBox MPF2_Results_ChiSquare,pos={505,242},size={102,12},title=titlestr TitleBox MPF2_Results_ChiSquare,frame=0,anchor= RC String totalAreaStr sprintf totalAreaStr, "Total Area = %g",totalArea TitleBox MPF2_Results_TotalArea,pos={505,257},size={102,12},title=totalAreaStr TitleBox MPF2_Results_TotalArea,frame=0,anchor= RC String totalAreaStDevStr sprintf totalAreaStDevStr, "+- %g",sqrt(totalAreaVariance) TitleBox MPF2_Results_TotalAreaStdev,pos={505,272},size={102,12},title=totalAreaStDevStr TitleBox MPF2_Results_TotalAreaStdev,frame=0,anchor= RC ListBox MPF2_FitResultsList,pos={1,54},size={right-left-2,bottom-top-resultsListHeightDif},proc=MPF2_resultsListProc,mode=5, selRow=-1 ListBox MPF2_FitResultsList,listWave=MPF2_ResultsListWave,titleWave=MPF2_ResultsListTitles ListBox MPF2_FitResultsList,widths={50,72,100},userColumnResize=1,frame=2 Button MPF2_ResultsDoNotebookButton,pos={12,bottom-top-60},size={260,20},proc=MPF2_ResultsDoNotebookButtnProc,title="Report in Notebook" Button MPF2_TabDelimitedResultsButton,pos={12,bottom-top-30},size={260,20},proc=MPF2_TabDelimitedResultsBtnProc,title="Standard Parameters, Tab-Delimited" Button MPF2Results_GraphButton,pos={314,bottom-top-60},size={100,20},proc=MPF2Results_GraphButtonProc,title="Graph..." Checkbox MPFTResults_BackgroundCheck, pos={314, bottom-top-27}, fsize=12, title="Include Background Info", proc=MPF2_reportBackground, value=reportBackground SetWindow $MPF2_ResultsPanelName, userdata(MPF2_DataSetNumber)=num2str(setnumber) SetWindow $MPF2_ResultsPanelName, hook(MPF2_ResultsResizeHook)=MPF2_ResultsResizeHook STRUCT WMWinHookStruct s s.winName = MPF2_ResultsPanelName s.eventCode = 6 s.eventName = "resize" MPF2_ResultsResizeHook(s) SetDataFolder saveDF end Function MPF2_ResultsResizeHook(s) STRUCT WMWinHookStruct &s if ( s.eventCode == 6) // resize // enforce minimum size Variable factor = ScreenResolution/72 GetWindow $s.winName wsize Variable left = V_left*factor Variable right = V_right*factor Variable top = V_top*factor Variable bottom = V_bottom*factor Variable height = max(resultsMinHeight, bottom-top) Variable width = max(resultsMinWidth, right-left) MoveWindow/W=$s.winName left/factor, top/factor, (left+width)/factor, (top+height)/factor // re-size the listbox ListBox MPF2_FitResultsList,win=$s.winName,size={width-2,height-resultsListHeightDif} // move the buttons Button MPF2_ResultsDoNotebookButton,win=$s.winName,pos={12, height-60} Button MPF2_TabDelimitedResultsButton,win=$s.winName,pos={12, height-30} Button MPF2Results_GraphButton,win=$s.winName,pos={314,height-60} Checkbox MPFTResults_BackgroundCheck, pos={314, height-27} // Move the data waves readout TitleBox MPF2_ResultsYWave,win=$s.winName,pos={width-10,7},size={0,0},anchor=RT // Move the chi-square readout TitleBox MPF2_Results_ChiSquare,win=$s.winName,pos={width-10,height-60},size={0,0},anchor=RT TitleBox MPF2_Results_TotalArea,win=$s.winName,pos={width-10,height-45},size={0,0},anchor=RT TitleBox MPF2_Results_TotalAreaStdev,win=$s.winName,pos={width-10,height-30},size={0,0},anchor=RT endif end Function MPF2_reportBackground(s) : CheckBoxControl STRUCT WMCheckboxAction &s if (s.eventCode == 2) // mouse up MPF2_RefreshPeakResults(GetSetNumberFromWinName(s.win)) endif end Function MPF2_PeakResultsButtonProc(s) : ButtonControl STRUCT WMButtonAction &s if (s.eventCode != 2) // mouse-up in the control return 0 endif // String gname = WinName(0,1) MPF2_DoPeakResults(GetSetNumberFromWinName(s.win)) End Function MPF2_resultsListProc(s) STRUCT WMListboxAction &s Variable i, nRows if (s.eventCode == 1) // mouse-down if (s.row < 0) // click in title cell nRows = DimSize(s.listWave, 0) Make/N=(nRows)/T/O MPF2_TempListSortWave MPF2_TempListSortWave = s.listwave[p][s.col] Make/N=(nRows)/O MPF2_TempListIndexWave String CurrentTitle = s.titleWave[s.col] Variable StartOfArrowString = -1 Variable doReverse = 0 if (strsearch(CurrentTitle, SMALL_UPARROW_STRING, 0) >= 0) // currently sorted small-to-large; use reverse sort doReverse = 1 StartOfArrowString = strsearch(CurrentTitle, GRAY_TEXT_STRING, 0) CurrentTitle = CurrentTitle[0,StartOfArrowString-1] + GRAY_TEXT_STRING + SMALL_DOWNARROW_STRING s.titleWave[s.col] = CurrentTitle elseif (strsearch(CurrentTitle, SMALL_DOWNARROW_STRING, 0) >= 0) // currently sorted large-to-small (reversed); use forward sort doReverse = 0 StartOfArrowString = strsearch(CurrentTitle, GRAY_TEXT_STRING, 0) CurrentTitle = CurrentTitle[0,StartOfArrowString-1] + GRAY_TEXT_STRING + SMALL_UPARROW_STRING s.titleWave[s.col] = CurrentTitle else // currently sorted by a different column; use forward sort, and find the current sort column in order to remove the sort arrow doReverse = 0 Variable numCols = DimSize(s.listWave, 1) // search for current sort column and remove the sort-indicator arrow for (i = 0; i < numCols; i += 1) CurrentTitle = s.titleWave[i] StartOfArrowString = strsearch(CurrentTitle, GRAY_TEXT_STRING, 0) if (StartOfArrowString >= 0) CurrentTitle = CurrentTitle[0, StartOfArrowString-1] s.titleWave[i] = CurrentTitle break; endif endfor // set the up-arrow on the new sort column s.titleWave[s.col] = (s.titleWave[s.col]) + GRAY_TEXT_STRING + SMALL_UPARROW_STRING endif // we now have a sort index, do the sort if (doReverse) MakeIndex/R/A MPF2_TempListSortWave, MPF2_TempListIndexWave else MakeIndex/A MPF2_TempListSortWave, MPF2_TempListIndexWave endif Duplicate/O/T s.listWave, MPF2_TempListSortWave s.listWave[][] = MPF2_TempListSortWave[MPF2_TempListIndexWave[p]][q] KillWaves MPF2_TempListSortWave, MPF2_TempListIndexWave endif endif end Function MPF2_ResultsDoNotebookButtnProc(s) : ButtonControl STRUCT WMButtonAction &s if (s.eventCode != 2) // mouse-up in the control return 0 endif // NVAR currentSetNumber = root:Packages:MultiPeakFit2:currentSetNumber // String gname = WinName(0,1) Variable setNumber = GetSetNumberFromWinName(s.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFpath SVAR gname = GraphName String nb = "MultipeakSet"+num2str(setNumber)+"Report" if (WinType(nb) == 5) DoWindow/K $nb endif NewNotebook/F=1/K=1/N=$nb as "Multipeak Fit Report for Data Set "+num2str(setNumber) String/G MPF2_ReportName = nb Notebook $nb showRuler=0 Wave wpi = W_AutoPeakInfo Variable npeaks = DimSize(wpi, 0) Variable i, nParamsMax=0 Variable j Variable theRow String PeakTypeName String ParamNames String DerivedParamNames // SVAR gname = $(DFpath+":GraphName") SVAR YWvName = $(DFpath+":YWvName") SVAR XWvName = $(DFpath+":XWvName") Wave yw = $YWvName Wave/Z xw = $XWvName NVAR XPointRangeBegin NVAR XPointRangeEnd NVAR MPF2_FitDate NVAR MPF2_FitPoints NVAR MPF2_FitChiSq String MPF2_ResultsPanelName = "MPF2_ResultsPanel"+"_"+num2str(setNumber) ControlInfo/W=$MPF2_ResultsPanelName MPFTResults_BackgroundCheck Variable reportBackground = V_value // for (i = 0; i < npeaks; i += 1) // Wave coefs = $("Peak "+num2istr(i)+" Coefs") // Variable theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(i)) // String PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, theRow) ) // // FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) // String ParamNames = infoFunc(PeakFuncInfo_ParamNames) // nParamsMax = max(nParamsMax, ItemsInList(ParamNames)) // endfor Notebook $nb newRuler=PeakHeaderRuler, justification=0, margins={0,0,468}, spacing={0,0,0}, tabs={}, rulerDefaults={"Geneva",10,1,(0,0,0)} Notebook $nb newRuler=PeakParamsRuler, justification=0, margins={0,0,468}, spacing={0,0,0}, tabs={36,126+3*8192,205+1*8192,234+3*8192}, rulerDefaults={"Geneva",10,0,(0,0,0)} Notebook $nb ruler=PeakParamsRuler, text="Fit completed "+Secs2Time(MPF2_FitDate, 0)+" "+Secs2Date(MPF2_FitDate, 1)+"\r" Notebook $nb ruler=PeakParamsRuler, text="Y data wave: "+GetWavesDataFolder(yw, 2) if ( (XPointRangeBegin != 0) || (XPointRangeEnd != numpnts(yw)-1) ) Notebook $nb ruler=PeakParamsRuler, text="["+num2str(XPointRangeBegin)+", "+num2str(XPointRangeEnd)+"]" endif Notebook $nb text="\r" if (WaveExists(xw)) Notebook $nb ruler=PeakParamsRuler, text="X data wave: "+GetWavesDataFolder(xw, 2)+"\r" endif Notebook $nb ruler=PeakParamsRuler, text="Chi square: "+num2str(MPF2_FitChiSq)+"\r" Notebook $nb ruler=PeakParamsRuler, text="Total fitted points: "+num2str(MPF2_FitPoints)+"\r" Notebook $nb ruler=PeakParamsRuler, text="Multi-peak fit version "+MPF2_VERSIONSTRING+"\r" GetSelection notebook, $nb, 1 Variable paragraphNumberforTotalArea = V_startParagraph Notebook $nb text="\r" Variable numBLParams = 0 String BL_typename = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, 0) ) if (CmpStr(BL_typename, "None") != 0) String ParamNameList//, BL_FuncName FUNCREF MPF2_FuncInfoTemplate blinfo = $(BL_typename + BL_INFO_SUFFIX) ParamNameList = blinfo(BLFuncInfo_ParamNames) // BL_FuncName = blinfo(BLFuncInfo_BaselineFName) numBLParams = ItemsInList(ParamNameList) Notebook $nb ruler=PeakHeaderRuler,fstyle=-1,text="Baseline\tType: "+BL_typename Notebook $nb text="\r\r" Notebook $nb ruler=PeakParamsRuler Notebook $nb fStyle=0 Wave blw = 'Baseline Coefs' Wave ble = W_sigma_0 for (i = 0; i < numBLParams; i += 1) Notebook $nb text= "\t"+StringFromList(i, ParamNameList)+" = \t"+num2str(blw[i])+"\t +/- \t"+num2str(ble[i])+"\r" endfor endif Notebook $nb text="\r" Wave/T MPF2_ResultsListWave Variable firstColumn = 8 Variable totalParams = numBLParams Variable totalArea = 0 Variable totalAreaVariance = 0 for (i = 0; i < npeaks; i += 1) Variable Peak_number sscanf MPF2_ResultsListWave[i][0], "Peak %d", Peak_number Wave coefs = $("Peak "+num2istr(Peak_number)+" Coefs") theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(Peak_number)) PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, theRow) ) FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) ParamNames = infoFunc(PeakFuncInfo_ParamNames) Variable nParams = ItemsInList(ParamNames) DerivedParamNames = infoFunc(PeakFuncInfo_DerivedParamNames) Wave coefs = $("Peak "+num2istr(Peak_number)+" Coefs") Variable sigmaSequenceNumber = (numBLParams > 0) ? Peak_number+1 : Peak_number Wave sigma = $("W_sigma_"+num2istr(sigmaSequenceNumber)) Notebook $nb ruler=PeakHeaderRuler,fstyle=-1,text="Peak "+num2str(Peak_number)+"\t" Notebook $nb text="Type: "+PeakTypeName Notebook $nb text="\r\r" Notebook $nb fStyle=0 Notebook $nb ruler=PeakParamsRuler String ParamFuncName = infoFunc(PeakFuncInfo_ParameterFunc) if (strlen(ParamFuncName) > 0) FUNCREF MPF2_ParamFuncTemplate paramFunc=$ParamFuncName Wave M_covar Make/O/D/N=(nParams, nParams) MPF2_TempCovar Make/O/D/N=(4,2) MPF2_TempParams=NaN // initialize to blanks so that if the function doesn't exist, we just get blanks back- the template function doesn't do anything. MPF2_TempCovar[][] = M_covar[totalParams+p][totalParams+q] paramFunc(coefs, MPF2_TempCovar, MPF2_TempParams) Variable nDerivedParams = ItemsInList(DerivedParamNames) for (j = 0; j < nDerivedParams; j += 1) Notebook $nb text="\t" + StringFromList(j, DerivedParamNames) + " = \t"+num2str(MPF2_TempParams[j][0]) if (numtype(MPF2_TempParams[j][1]) != 2) Notebook $nb text= "\t +/- \t" + num2str(MPF2_TempParams[j][1]) endif Notebook $nb text="\r" endfor endif totalArea += MPF2_TempParams[2][0] // area is always in row 2 totalAreaVariance += MPF2_TempParams[2][1]^2 Notebook $nb text="\r" Notebook $nb text="\tFit function parameters\r" for (j = 0; j < nParams; j += 1) String pname = StringFromList(j, ParamNames) Notebook $nb text= "\t"+pname+" =\t"+num2str(coefs[j]) + "\t+/-\t"+num2str(sigma[j])+"\r" endfor if (reportBackground) Notebook $nb text="\r" Notebook $nb text="\tBacground Info\r" BL_typename = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, 0) ) if (CmpStr(BL_typename, "None") != 0) FUNCREF MPF2_FuncInfoTemplate blinfo = $(BL_typename + BL_INFO_SUFFIX) string BL_FuncName = blinfo(BLFuncInfo_BaselineFName) FUNCREF MPF2_BaselineFunctionTemplate blFunc = $BL_FuncName STRUCT MPF2_BLFitStruct BLStruct Wave BLStruct.cWave = 'Baseline Coefs' BLStruct.x = MPF2_TempParams[0][0] SVAR YWvName = $"YWvName" SVAR XWvName = $"XWvName" Wave yw = $YWvName Wave/Z xw = $XWvName NVAR XPointRangeBegin NVAR XPointRangeEnd if (WaveExists(xw)) BLStruct.xStart = xw[XPointRangeBegin] BLStruct.xEnd = xw[XPointRangeEnd] else BLStruct.xStart = pnt2x(yw, XPointRangeBegin) BLStruct.xEnd = pnt2x(yw, XPointRangeEnd) endif Variable bgValue = blFunc(BLStruct) Notebook $nb text="\tBackground at peak location =\t"+num2str(bgValue)+"\r" Notebook $nb text="\tRatio peak height to background =\t"+num2str(MPF2_TempParams[1][0]/bgValue)+"\r" endif endif totalParams += nParams Notebook $nb text="\r" endfor KillWaves/Z MPF2_TempCovar, MPF2_TempParams Notebook $nb, selection={(paragraphNumberforTotalArea, 0), (paragraphNumberforTotalArea, 0)} Notebook $nb, text = "Total Peak Area = "+num2str(totalArea)+" +/- "+num2str(sqrt(totalAreaVariance))+"\r" Notebook $nb selection={startOfFile,startOfFile},findText={"", 1} SetDataFolder saveDF End Function MPF2_TabDelimitedResultsBtnProc(s) : ButtonControl STRUCT WMButtonAction &s if (s.eventCode != 2) // mouse-up in the control return 0 endif // NVAR currentSetNumber = root:Packages:MultiPeakFit2:currentSetNumber // String gname = WinName(0,1) Variable setNumber = GetSetNumberFromWinName(s.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFpath SVAR gname = GraphName String nb = "MultipeakSet"+num2str(setNumber)+"_TD" if (WinType(nb) == 5) DoWindow/K $nb endif NewNotebook/F=0/K=1/N=$nb String/G MPF2_TDReportName = nb Notebook $nb defaultTab=108 Wave wpi = W_AutoPeakInfo Variable npeaks = DimSize(wpi, 0) Variable i, nParamsMax=0 Variable j Variable theRow String PeakTypeName String ParamNames String DerivedParamNames // SVAR gname = $(DFpath+":GraphName") SVAR YWvName = $(DFpath+":YWvName") SVAR XWvName = $(DFpath+":XWvName") Wave yw = $YWvName Wave/Z xw = $XWvName NVAR XPointRangeBegin NVAR XPointRangeEnd NVAR MPF2_FitDate NVAR MPF2_FitPoints NVAR MPF2_FitChiSq Notebook $nb text="Fit completed "+Secs2Time(MPF2_FitDate, 0)+" "+Secs2Date(MPF2_FitDate, 1)+"\r" Notebook $nb text="Y data wave: "+GetWavesDataFolder(yw, 2) if ( (XPointRangeBegin != 0) || (XPointRangeEnd != numpnts(yw)-1) ) Notebook $nb text="["+num2str(XPointRangeBegin)+", "+num2str(XPointRangeEnd)+"]" endif Notebook $nb text="\r" if (WaveExists(xw)) Notebook $nb text="X data wave: "+GetWavesDataFolder(xw, 2)+"\r" endif Notebook $nb text="Chi square: "+num2str(MPF2_FitChiSq)+"\r" Notebook $nb text="Total fitted points: "+num2str(MPF2_FitPoints)+"\r" Notebook $nb text="Multi-peak fit version "+MPF2_VERSIONSTRING+"\r" GetSelection notebook, $nb, 1 Variable paragraphNumberforTotalArea = V_startParagraph Notebook $nb text="\r" Wave/T MPF2_ResultsListWave Notebook $nb text="Type\tLocation\tLocSigma\tAmplitude\tAmpSigma\tArea\tAreaSigma\tFWHM\tFWHMSigma\r" Variable numBLParams = 0 String BL_typename = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, 0) ) if (CmpStr(BL_typename, "None") != 0) // String ParamNameList, BL_FuncName FUNCREF MPF2_FuncInfoTemplate blinfo = $(BL_typename + BL_INFO_SUFFIX) // ParamNameList = blinfo(BLFuncInfo_ParamNames) // BL_FuncName = blinfo(BLFuncInfo_BaselineFName) numBLParams = ItemsInList(blinfo(BLFuncInfo_ParamNames)) endif Variable totalParams = numBLParams String OneParamText String oneLine Variable totalArea = 0 Variable totalAreaVariance = 0 for (i = 0; i < npeaks; i += 1) oneLine = "" Wave coefs = $("Peak "+num2istr(i)+" Coefs") theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(i)) PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, theRow) ) oneLine = PeakTypeName FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) ParamNames = infoFunc(PeakFuncInfo_ParamNames) Variable nParams = ItemsInList(ParamNames) Wave coefs = $("Peak "+num2istr(i)+" Coefs") Variable sigmaSequenceNumber = (numBLParams > 0) ? i+1 : i Wave sigma = $("W_sigma_"+num2istr(sigmaSequenceNumber)) MPF2_ResultsListWave[i][0] = "Peak "+num2str(i) MPF2_ResultsListWave[i][1] = PeakTypeName String ParamFuncName = infoFunc(PeakFuncInfo_ParameterFunc) if (strlen(ParamFuncName) > 0) FUNCREF MPF2_ParamFuncTemplate paramFunc=$ParamFuncName Wave M_covar Make/O/D/N=(nParams, nParams) MPF2_TempCovar Make/O/D/N=(4,2) MPF2_TempParams=NaN // initialize to blanks so that if the function doesn't exist, we just get blanks back- the template function doesn't do anything. MPF2_TempCovar[][] = M_covar[totalParams+p][totalParams+q] paramFunc(coefs, MPF2_TempCovar, MPF2_TempParams) totalArea += MPF2_TempParams[2][0] // area is always in row 2 totalAreaVariance += MPF2_TempParams[2][1]^2 // the first four parameters are always the same and the names are always in the column titles for (j = 0; j < 4; j += 1) sprintf OneParamText, "\t%g\t%g", MPF2_TempParams[j][0], MPF2_TempParams[j][1] oneLine += OneParamText endfor Notebook $nb text=oneLine+"\r" endif totalParams += nParams endfor Notebook $nb, selection={(paragraphNumberforTotalArea, 0), (paragraphNumberforTotalArea, 0)} Notebook $nb, text = "Total Peak Area = "+num2str(totalArea)+" +/- "+num2str(sqrt(totalAreaVariance))+"\r" SetDataFolder saveDF End Function MPF2Results_GraphButtonProc(s) : ButtonControl STRUCT WMButtonAction &s if (s.eventCode != 2) // mouse-up in the control return 0 endif Variable setNumber = GetSetNumberFromWinName(s.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFpath SVAR gname = GraphName SetDataFolder saveDF MPF2_ResultsGraphPanel(setNumber) return 0 End Function MPF2_ResultsGraphPanel(setNumber) Variable setNumber String panelName = "MakeResultsGraph_Set_"+num2str(setNumber) NewPanel /FLT=1 /K=1 /W=(512,538,775,893) RenameWindow $S_name, $panelName String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) Variable dummy SetDataFolder DFpath dummy = NumVarOrDefault(DFpath+":ResultGraph_IncludeData", 1) Variable/G ResultGraph_IncludeData = dummy dummy = NumVarOrDefault(DFpath+":ResultGraph_IncludeFitCurve", 1) Variable/G ResultGraph_IncludeFitCurve = dummy dummy = NumVarOrDefault(DFpath+":ResultGraph_IncludePeaks", 1) Variable/G ResultGraph_IncludePeaks = dummy dummy = NumVarOrDefault(DFpath+":ResultGraph_IncludeResidual", 1) Variable/G ResultGraph_IncludeResidual = dummy dummy = NumVarOrDefault(DFpath+":ResultGraph_IncludeBackground", 0) Variable/G ResultGraph_IncludeBackground = dummy dummy = NumVarOrDefault(DFpath+":ResultGraph_PeaksOnDataAxis", 0) Variable/G ResultGraph_PeaksOnDataAxis = dummy dummy = NumVarOrDefault(DFpath+":ResultGraph_PeaksAddLines", 0) Variable/G ResultGraph_PeaksAddLines = dummy dummy = NumVarOrDefault(DFpath+":ResultGraph_PeaksAddTags", 0) Variable/G ResultGraph_PeaksAddTags = dummy dummy = NumVarOrDefault(DFpath+":ResultGraph_TagPkNum", 0) Variable/G ResultGraph_TagPkNum = dummy dummy = NumVarOrDefault(DFpath+":ResultGraph_TagRealLoc", 0) Variable/G ResultGraph_TagRealLoc = dummy dummy = NumVarOrDefault(DFpath+":ResultGraph_TagFWHM", 1) Variable/G ResultGraph_TagFWHM = dummy dummy = NumVarOrDefault(DFpath+":ResultGraph_TagPkArea", 1) Variable/G ResultGraph_TagPkArea = dummy dummy = NumVarOrDefault(DFpath+":ResultGraph_TagHeight", 0) Variable/G ResultGraph_TagHeight = dummy SetDataFolder saveDF GroupBox MPF2_Graph_IncludeGroup,pos={8,7},size={249,254},title="Include in Graph" GroupBox MPF2_Graph_IncludeGroup,fSize=12 CheckBox MPF2_Graph_includeData,pos={41,34},size={86,14},title="Your Input Data",Variable = ResultGraph_IncludeData CheckBox MPF2_Graph_IncludeFitCurve,pos={41,54},size={57,14},title="Fit Curve",Variable = ResultGraph_IncludeFitCurve CheckBox MPF2_Graph_IncludePeaks,pos={41,114},size={86,14},title="Individual Peaks",Variable = ResultGraph_IncludePeaks CheckBox MPF2_Graph_IncludeResidual,pos={41,94},size={58,14},title="Residuals",Variable = ResultGraph_IncludeResidual CheckBox MPF2_Graph_IncludeBackground,pos={41,74},size={97,14},title="Background Curve",Variable = ResultGraph_IncludeBackground CheckBox MPF2_Graph_PeakDataAxis,pos={56,134},size={149,14},title="On the Same Axis as Fit Curve",Variable = ResultGraph_PeaksOnDataAxis CheckBox MPF2_Graph_PeaksAddLines,pos={56,154},size={81,14},title="Location Lines",Variable = ResultGraph_PeaksAddLines CheckBox MPF2_Graph_PeaksAddTags,pos={56,174},size={60,14},title="Tags with",Variable = ResultGraph_PeaksAddTags CheckBox MPF2_Graph_TagPkNum,pos={72,194},size={75,14},title="Peak Number",Variable = ResultGraph_TagPkNum CheckBox MPF2_Graph_TagRealLoc,pos={72,214},size={55,14},title="Location",Variable = ResultGraph_TagRealLoc CheckBox MPF2_Graph_TagFWHM,pos={72,235},size={45,14},title="FWHM",Variable = ResultGraph_TagFWHM CheckBox MPF2_Graph_TagPkArea,pos={160,194},size={61,14},title="Peak Area",Variable = ResultGraph_TagPkArea CheckBox MPF2_Graph_TagHeight,pos={160,214},size={46,14},title="Height",Variable = ResultGraph_TagHeight SetVariable MPF2_Graph_GName,pos={31,267},size={217,15},bodyWidth=160,title="Graph Name" SetVariable MPF2_Graph_GName,value= _STR:"MPF2Graph"+num2str(setNumber) SetVariable MPF2_Graph_GTitle,pos={36,290},size={211,15},bodyWidth=160,title="Graph Title" SetVariable MPF2_Graph_GTitle,value= _STR:"Multipeak Fit Set "+num2str(setNumber)+" Results" Button MPF2_Graph_MakeGraph,pos={8,324},size={100,20},proc=MPF2_Graph_MakeGraphButtonProc,title="Make Graph" Button MPF2_Graph_Cancel,pos={157,324},size={100,20},proc=MPF2_Graph_CancelButtonProc,title="Cancel" SetWindow $panelName, userdata(MPF2_DataSetNumber)=num2str(setnumber) //SetActiveSubwindow _endfloat_ EndMacro Function MPF2_Graph_CancelButtonProc(s) : ButtonControl STRUCT WMButtonAction &s switch( s.eventCode ) case 2: // mouse up KillWindow $(s.win) break endswitch return 0 End // returns a bit pattern: // bit 0: bottom or left is autoscaled // bit 1: top or right is autoscaled static Function axisAutoScaleInfo(graphname, axisname) String graphname, axisname String SetScaleCmd = stringbykey("SETAXISCMD", axisinfo(graphname, axisname), ":", ";") if (strsearch(SetScaleCmd, "/A", 0) > 0) return 3 // The /A flag indicates both ends are auto-scaled endif Variable startOfLimits = strsearch(SetScaleCmd, axisname, 0)+strlen(axisname) Variable returnValue = 0 String lowerLimit, upperLimit, theRest String expr = "(?i)[^ ]+ [[:word:]]+ ?([^,]+),(.*)" // case insensitive, one or more non-space characters, a space, one or more word characters, one space, one or more non-comma characters, a comma, the rest of the line SplitString/E=(expr) SetScaleCmd, lowerLimit, upperLimit if (strsearch(lowerLimit, "*", 0) >= 0) returnValue += 1 endif if (strsearch(upperLimit, "*", 0) >= 0) returnValue += 2 endif return returnValue end static Function/S GetAxisRecreation(theGraph, theAxis) String theGraph, theAxis return GetAxisRecreationFromInfoString(AxisInfo(theGraph, theAxis), ":") end static Function/S GetAxisRecreationFromInfoString(info, keySeparator) String info, keySeparator Variable sstop = strsearch(info, "RECREATION"+keySeparator, 0) info= info[sstop+strlen("RECREATION"+keySeparator),1e6] // want just recreation stuff return info end static Function isLogAxis(theGraph, theAxis) String theGraph, theAxis if (strsearch(GetAxisRecreation(theGraph, theAxis), "log(x)=1", 0) >= 0) return 1 endif return 0 end Function MPF2_Graph_MakeGraphButtonProc(s) : ButtonControl STRUCT WMButtonAction &s if (s.eventCode != 2) // mouse-up in the control return 0 endif Variable i Variable setNumber = GetSetNumberFromWinName(s.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFpath SVAR YWvName Wave YData = $YWvName SVAR XWvName Wave/Z XData = $XWvName SVAR gname = GraphName ControlInfo/W=$s.win MPF2_Graph_GName String graphName = S_value ControlInfo/W=$s.win MPF2_Graph_includeData String graphTitle = S_value if (WinType(graphName) == 1) DoAlert 1, "A graph with the name "+graphname+" already exists. Kill it and make the new graph?" if (V_flag != 1) return -1 // something other than Yes was clicked. endif KillWindow $graphName endif Display RenameWindow $S_name, $GraphName ControlInfo/W=$s.win MPF2_Graph_includeData if (V_value) if (WaveExists(XData)) AppendToGraph/W=$GraphName YData vs XData else AppendToGraph/W=$GraphName YData endif endif Variable autoscaleInfo = axisAutoScaleInfo(gname, "bottom") GetAxis/W=$gname/Q bottom Variable low = V_min Variable high = V_max if ( ((autoscaleInfo & 1) == 0) && ((autoscaleInfo & 2) == 0) ) SetAxis/W=$GraphName bottom, low, high elseif ((autoscaleInfo & 1) == 0) SetAxis/W=$GraphName bottom, low, * elseif ((autoscaleInfo & 2) == 0) SetAxis/W=$GraphName bottom, *, high endif if (isLogAxis(gname, "bottom")) ModifyGraph log(bottom)=1 endif ControlInfo/W=$s.win MPF2_Graph_IncludeFitCurve if (V_value) Wave/Z fitwave = $("fit_"+NameOfWave(YData)) if (WaveExists(fitwave)) AppendToGraph/W=$GraphName fitwave endif endif ControlInfo/W=$s.win MPF2_Graph_IncludeBackground if (V_value) Wave/Z bkgwave = $("bkg_"+NameOfWave(YData)) if (WaveExists(bkgwave)) AppendToGraph/W=$GraphName bkgwave endif endif Variable leftAxisBottom = 0 Variable leftAxisTop = 1 Variable theRow ControlInfo/W=$s.win MPF2_Graph_IncludePeaks if (V_value) Wave wpi = W_AutoPeakInfo Variable npeaks = DimSize(wpi, 0) ControlInfo/W=$s.win MPF2_Graph_PeakDataAxis Variable UseLeftAxis = V_value String axisName = "PeaksLeft" String PeakWaveName String PeakTypeName String ParamNames Variable nParams Variable sigmaSequenceNumber String ParamFuncName Variable realLoc for (i = 0; i < npeaks; i += 1) PeakWaveName = "Peak "+num2istr(i) Wave w = $PeakWaveName if (UseLeftAxis) axisName = "left" AppendToGraph/W=$GraphName w else AppendToGraph/W=$GraphName/L=$axisName w endif endfor DoUpdate Wave/T MPF2_ResultsListWave ControlInfo/W=$s.win MPF2_Graph_PeaksAddTags Variable addTags = V_value ControlInfo/W=$s.win MPF2_Graph_PeaksAddLines Variable addLines = V_value Variable numBLParams if (addTags || addLines) String BL_typename = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, 0) ) if (CmpStr(BL_typename, "None") != 0) // String ParamNameList, BL_FuncName FUNCREF MPF2_FuncInfoTemplate blinfo = $(BL_typename + BL_INFO_SUFFIX) // ParamNameList = blinfo(BLFuncInfo_ParamNames) // BL_FuncName = blinfo(BLFuncInfo_BaselineFName) numBLParams = ItemsInList(blinfo(BLFuncInfo_ParamNames)) endif endif if (addTags) for (i = 0; i < npeaks; i += 1) String tagtext = "" String lineEnd = "" Wave coefs = $("Peak "+num2istr(i)+" Coefs") theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(i)) PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, theRow) ) FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) ParamNames = infoFunc(PeakFuncInfo_ParamNames) nParams = ItemsInList(ParamNames) Wave coefs = $("Peak "+num2istr(i)+" Coefs") sigmaSequenceNumber = (numBLParams > 0) ? i+1 : i Wave sigma = $("W_sigma_"+num2istr(sigmaSequenceNumber)) ParamFuncName = infoFunc(PeakFuncInfo_ParameterFunc) if (strlen(ParamFuncName) > 0) FUNCREF MPF2_ParamFuncTemplate paramFunc=$ParamFuncName Wave M_covar Make/O/D/N=(nParams, nParams) MPF2_TempCovar Make/O/D/N=(4,2) MPF2_TempParams=NaN // initialize to blanks so that if the function doesn't exist, we just get blanks back- the template function doesn't do anything. MPF2_TempCovar[][] = M_covar[numBLParams+nParams+p][numBLParams+nParams+q] paramFunc(coefs, MPF2_TempCovar, MPF2_TempParams) realLoc = MPF2_TempParams[0][0] Variable realHeight = MPF2_TempParams[1][0] Variable realArea = MPF2_TempParams[2][0] Variable realFWHM = MPF2_TempParams[3][0] PeakWaveName = "Peak "+num2istr(i) ControlInfo/W=$s.win MPF2_Graph_TagPkNum if (V_value) tagtext += lineEnd + PeakWaveName lineEnd = "\r" endif ControlInfo/W=$s.win MPF2_Graph_TagRealLoc if (V_value) tagtext += lineEnd + "Location: "+num2str(realLoc) lineEnd = "\r" endif ControlInfo/W=$s.win MPF2_Graph_TagFWHM if (V_value) tagtext += lineEnd + "FWHM: "+num2str(realFWHM) lineEnd = "\r" endif ControlInfo/W=$s.win MPF2_Graph_TagPkArea if (V_value) tagtext += lineEnd + "Area: "+num2str(realArea) lineEnd = "\r" endif ControlInfo/W=$s.win MPF2_Graph_TagHeight if (V_value) tagtext += lineEnd + "Height: "+num2str(realHeight) lineEnd = "\r" endif Tag/A=MB/F=2/L=1/N=$("PeakTag"+num2istr(i))/W=$GraphName $PeakWaveName, realLoc, tagtext endif endfor DoUpdate endif if (addLines) for (i = 0; i < npeaks; i += 1) Wave coefs = $("Peak "+num2istr(i)+" Coefs") theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(i)) PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, theRow) ) FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) ParamNames = infoFunc(PeakFuncInfo_ParamNames) nParams = ItemsInList(ParamNames) Wave coefs = $("Peak "+num2istr(i)+" Coefs") sigmaSequenceNumber = (numBLParams > 0) ? i+1 : i Wave sigma = $("W_sigma_"+num2istr(sigmaSequenceNumber)) ParamFuncName = infoFunc(PeakFuncInfo_ParameterFunc) if (strlen(ParamFuncName) > 0) FUNCREF MPF2_ParamFuncTemplate paramFunc=$ParamFuncName Wave M_covar Make/O/D/N=(nParams, nParams) MPF2_TempCovar Make/O/D/N=(4,2) MPF2_TempParams=NaN // initialize to blanks so that if the function doesn't exist, we just get blanks back- the template function doesn't do anything. MPF2_TempCovar[][] = M_covar[numBLParams+nParams+p][numBLParams+nParams+q] paramFunc(coefs, MPF2_TempCovar, MPF2_TempParams) realLoc = MPF2_TempParams[0][0] SetDrawLayer/W=$GraphName userBack SetDrawEnv/W=$GraphName linefgc=(56797,56797,56797), xcoord=bottom, ycoord=prel DrawLine/W=$GraphName realLoc, 0, realLoc, 1 endif endfor endif if (!UseLeftAxis) leftAxisBottom = .3 ModifyGraph/W=$GraphName axisEnab(left)={leftAxisBottom, leftAxisTop}, standoff(left)=0 ModifyGraph/W=$GraphName axisEnab($axisName)={0,.25} ModifyGraph freePos($axisName)={0,kwFraction} endif endif ControlInfo/W=$s.win MPF2_Graph_IncludeResidual if (V_value) Wave rw = $("Res_"+NameOfWave(yData)) if (WaveExists(XData)) AppendToGraph/W=$GraphName/L=Res_left rw vs XData else AppendToGraph/W=$GraphName/L=Res_left rw endif leftAxisTop = .75 ModifyGraph/W=$GraphName axisEnab(left)={leftAxisBottom, leftAxisTop}, standoff(left)=0 ModifyGraph/W=$GraphName axisEnab(Res_left)={leftAxisTop+.05, 1}, standoff(left)=0 ModifyGraph freePos(Res_left)={0,kwFraction} endif KillWindow $s.win KillWaves/Z MPF2_TempCovar, MPF2_TempParams SetDataFolder saveDF return 0 End //******************************* // Add or Edit Peaks Support //******************************* Function/S MPF2_GraphMarqueeDef() if (!DataFolderExists("root:Packages:MultiPeakFit2")) return "" endif String gname = WinName(0,1) Variable gnamelen = strlen(gname) if ( (gnamelen == 0) || (numtype(gnamelen) != 0) ) return "" endif Variable setNumber = GetSetNumberFromWinName(gname) if (numtype(setNumber)) return "" endif String DFpath = MPF2_FolderPathFromSetNumber(setNumber) if (!DataFolderExists(DFpath )) return "" endif SVAR MPF2graph = $(DFpath+":GraphName") if (CmpStr(MPF2graph, WinName(0,1,1)) != 0) return "" endif return "-;Add or Edit Peaks;" end static Function MPF2_FillReadoutList(listwave, peakName, valueWave, valueRow) Wave/T listwave String peakName Wave/Z valueWave Variable valueRow if (numpnts(listwave) != 4) Redimension/N=4 listwave endif if (CmpStr(peakName[0,3], "edit") == 0) listwave[0] = peakName[4, strlen(peakName)-1] elseif (CmpStr(peakName[0,2], "new") == 0) listwave[0] = "New "+peakName[3,strlen(peakName)-1] else listwave[0] = peakName endif if (WaveExists(valueWave)) listwave[1] = "loc= "+num2str(valueWave[valueRow][2]) listwave[2] = "width= "+num2str(valueWave[valueRow][3]) listwave[3] = "height= "+num2str(valueWave[valueRow][1]) else listwave[1] = "loc= " listwave[2] = "width= " listwave[3] = "height= " endif end Function MPF2_MarqueeHandler() GetLastUserMenuInfo String menuStr = S_value String gname = WinName(0,1) GetMarquee bottom, left Variable xleft = V_left Variable xright = V_right Variable setNumber = GetSetNumberFromWinName(gname) if (numtype(setNumber)) return 0 endif String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFPath MPF2_SaveFunctionTypes(gname+"#MultiPeak2Panel") SVAR YWvName = $(DFpath+":YWvName") SVAR XWvName = $(DFpath+":XWvName") Wave yw = $YWvName Wave/Z xw = $XWvName Wave/Z wpi = W_AutoPeakInfo Variable npeaks = 0 if (WaveExists(wpi)) npeaks = DimSize(wpi, 0) endif NVAR negativePeaks NVAR XPointRangeBegin NVAR XPointRangeEnd if (WaveExists(xw)) xleft = max(xleft, xw[XPointRangeBegin]) xright = min(xright, xw[XPointRangeEnd]) else xleft = max(xleft, pnt2x(yw, XPointRangeBegin)) xright = min(xright, pnt2x(yw, XPointRangeEnd)) endif if (WinType("EditOrAddPeaksGraph")) DoWindow/K EditOrAddPeaksGraph endif Variable defLeft = 50 Variable defTop = 70 Variable defRight = 560 Variable defBottom = 420 Variable i String fmt="Display/K=1/W=(%s)/N=EditOrAddPeaksGraph as \"Edit or Add Peaks\"" String cmd = WC_WindowCoordinatesSprintf("EditOrAddPeaksGraph", fmt, defLeft, defTop, defRight, defBottom, 0) Execute cmd if (WaveExists(xw)) AppendToGraph/W=EditOrAddPeaksGraph yw vs xw else AppendToGraph/W=EditOrAddPeaksGraph yw endif SetAxis/W=EditOrAddPeaksGraph bottom, xleft, xright SetAxis/W=EditOrAddPeaksGraph/A=2 left ModifyGraph/W=EditOrAddPeaksGraph standoff=0 ModifyGraph/W=EditOrAddPeaksGraph margin=30 SVAR/Z TraceInfoForAddOrEditData = root:Packages:MultiPeakFit2:TraceInfoForAddOrEditData if (SVAR_Exists(TraceInfoForAddOrEditData)) Variable sstop= strsearch(TraceInfoForAddOrEditData, "RECREATION:", 0) string tinfo= TraceInfoForAddOrEditData[sstop+strlen("RECREATION:"),1e6] // want just recreation stuff String tname = "("+PossiblyQuoteName(NameOfWave(yw))+")" String sitem,xstr i = 0 do sitem= StringFromList(i,tinfo) if( strlen(sitem) == 0 ) break; endif xstr= "ModifyGraph "+ReplaceString("(x)",sitem,tname,1) // replace "(x)" in sitem with, for example, "(left)" Execute xstr i+=1 while(1) endif Variable miny if (WaveExists(xw)) Variable x1 = pnt2x(yw, BinarySearch(xw, xleft)) Variable x2 = pnt2x(yw, BinarySearch(xw, xright)) if (negativePeaks) minY = wavemax(yw, x1, x2) else minY = wavemin(yw, x1, x2) endif else if (negativePeaks) miny = wavemax(yw, xleft, xright) else miny = wavemin(yw, xleft, xright) endif endif NewDataFolder/O EditPeaksStuff Make/O/D/N=(0,4) :EditPeaksStuff:Editwpi Make/O/T/N=0 :EditPeaksStuff:EditPeakList Make/O/N=(0,6) :EditPeaksStuff:UndoInfo // col 0: 1=added, 0=edited; col 1: index into Editwpi; col 2,3,4,5: Editwpi values before editing Variable/G :EditPeaksStuff:UndoIndex = -1 // points to current undo info; when < 0, no undo info available. Wave Editwpi = :EditPeaksStuff:Editwpi Wave/T EditPeakList = :EditPeaksStuff:EditPeakList Variable nPeaksToEdit=0 String/G :EditPeaksStuff:editedPeaksList = "" Variable/G :EditPeaksStuff:numNewPeaks = 0 String/G :EditPeaksStuff:NewPeakTypeList = "" for (i = 0; i < npeaks; i += 1) if ( (wpi[i][0] > xleft) && (wpi[i][0] < xright) ) String peakName = "Peak "+num2str(i) String editPeakName = "EditPeak "+num2str(i) Duplicate/O $PeakName, $(":EditPeaksStuff:"+PossiblyQuoteName(editPeakName)) Wave w = $(":EditPeaksStuff:"+PossiblyQuoteName(editPeakName)) w += minY AppendToGraph/W=EditOrAddPeaksGraph w ModifyGraph rgb($editPeakName)=(0,0,65535) InsertPoints nPeaksToEdit, 1, Editwpi, EditPeakList Editwpi[nPeaksToEdit][1] = wpi[i][2] Editwpi[nPeaksToEdit][2] = wpi[i][0] Editwpi[nPeaksToEdit][3] = wpi[i][1] Editwpi[nPeaksToEdit][0] = minY EditPeakList[nPeaksToEdit] = editPeakName nPeaksToEdit += 1 endif endfor ControlBar 32 ControlBar/L 138 NewPanel/W=(0.2,0.2,0.8,0.8)/FG=(FL,GT,GL,FB)/HOST=# ModifyPanel frameStyle=0 Button MPF2_EditPeaksUndoButton,pos={18,19},size={100,20},proc=MPF2_EditPeaksUndoButtonProc,title="Undo",disable=2 Button MPF2_EditPeaksRedoButton,pos={18,51},size={100,20},proc=MPF2_EditPeaksRedoButtonProc,title="Redo",disable=2 Button MPF2_EditOrAddCancelButton,pos={18,83},size={100,20},proc=MPF2_EditOrAddCancelButtonProc,title="Cancel" Button MPF2_AddOrEditDoneButton,pos={18,116},size={100,20},proc=MPF2_EditOrAddDoneButtonProc,title="Done" Make/O/N=4/T :EditPeaksStuff:MPF2_PeakReadoutListWave Wave MPF2_PeakReadoutListWave = :EditPeaksStuff:MPF2_PeakReadoutListWave ListBox MPF2_AddOrEditReadoutList,pos={11,166},size={117,72},mode=0,listwave=MPF2_PeakReadoutListWave MPF2_FillReadoutList(MPF2_PeakReadoutListWave, "", $"",0) PopupMenu AddOrEdit_NewPeakTypeMenu,pos={28,280},size={100,20} PopupMenu AddOrEdit_NewPeakTypeMenu,mode=1,bodyWidth= 100,value="Ask;"+MPF2_ListPeakTypeNames() String typeName Variable mode=1 if ( (npeaks > 0) && MPF2_AllSamePeakType(setNumber, typeName)) mode = WhichListItem(typeName, "Ask;"+MPF2_ListPeakTypeNames())+1 endif PopupMenu AddOrEdit_NewPeakTypeMenu,mode=mode TitleBox AddOrEdit_NewPeakTypeTitle,pos={14,263},size={90,12},title="Type for New Peaks:" TitleBox AddOrEdit_NewPeakTypeTitle,frame=0 RenameWindow #,PLeft SetActiveSubwindow ## if (DataFolderExists("root:Packages:ManualPeaks")) KillDataFolder root:Packages:ManualPeaks endif string saveDF2 = InitManualPeakPlacePackage() SetDataFolder saveDF2 NewPanel/W=(0.2,0.2,0.8,0)/FG=(FL,FT,GR,)/HOST=# ModifyPanel frameStyle=0 SetVariable MPF2_AddOrEditMessageBox,pos={138,9},size={253,18},title=" " SetVariable MPF2_AddOrEditMessageBox,frame=1,fSize=12,fStyle=0 SetVariable MPF2_AddOrEditMessageBox,value= root:Packages:ManualPeaks:gMessage,noedit= 1 RenameWindow #,PTop SetActiveSubwindow ## SetWindow EditOrAddPeaksGraph, hook(MPF2_EditGraphCursor) = MPF2_EditGraphCursorHook SetWindow EditOrAddPeaksGraph, userdata(MPF2_EditMouseMode) = "up" SetWindow EditOrAddPeaksGraph, userdata(MPF2_DataSetNumber) = num2str(setNumber) SetDataFolder saveDF end static Function/S PossiblyUnquoteName(name) String name if (char2num(name[0]) == char2num("'")) return name[1, strlen(name)-2] endif return name end Function MPF2_EditGraphCursorHook(s) STRUCT WMWinHookStruct &s // If we are in editing or peak placing mode, ignore all events except the keyboard event that will cancel out of peak editing mode if (CmpStr(GetUserData("EditOrAddPeaksGraph", "", "MPF2_EditMouseMode"), "down") == 0) if (s.eventCode == 11) // keycode if (s.keycode == 27) Button MPF2_EditOrAddCancelButton,win=EditOrAddPeaksGraph#PLeft,disable=0 Button MPF2_AddOrEditDoneButton,win=EditOrAddPeaksGraph#PLeft,disable=0 EndManualPeakMode() SetWindow EditOrAddPeaksGraph, userdata(MPF2_EditMouseMode) = "up" return 1 endif endif return 0 endif String highlightedTrace="" // NVAR currentSetNumber = root:Packages:MultiPeakFit2:currentSetNumber Variable setNumber = GetSetNumberFromWinName(s.winName) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF Variable i strswitch (s.eventName) case "mousedown": if (CmpStr(s.winName, "EditOrAddPeaksGraph") != 0) return 0 endif saveDF = GetDataFolder(1) SetDataFolder DFPath Wave/T EditPeakList = :EditPeaksStuff:EditPeakList Variable npeaks = DimSize(EditPeakList, 0) highlightedTrace = GetUserData(s.winName, "", "MPF2_HighlightTrace" ) SetDataFolder saveDF if (s.eventMod & 16) // right-click if ( (strlen(highlightedTrace) > 0) && (npeaks > 0) ) PopupContextualMenu "Remove;" if (V_flag == 1) saveDF = GetDataFolder(1) SetDataFolder DFPath for (i = 0; i < npeaks; i += 1) if (CmpStr(EditPeakList[i], highlightedTrace)==0) break; endif endfor Variable removePeakNumber = i Wave/T EditPeakList = :EditPeaksStuff:EditPeakList Wave Editwpi = :EditPeaksStuff:Editwpi Wave UndoInfo = :EditPeaksStuff:UndoInfo // col 0: 1=added, 0=edited; col 1: index into Editwpi; col 2,3,4,5: Editwpi values before editing NVAR UndoIndex = :EditPeaksStuff:UndoIndex // points to current undo info; when < 0, no undo info available. NVAR numNewPeaks = :EditPeaksStuff:numNewPeaks Variable numOldPeaks = DimSize(EditPeakList, 0) - numNewPeaks SVAR NewPeakTypeList = :EditPeaksStuff:NewPeakTypeList UndoIndex += 1 if (DimSize(UndoInfo, 0) <= UndoIndex) InsertPoints UndoIndex, 1, UndoInfo UndoIndex = DimSize(UndoInfo, 0)-1 // paranoia else UndoInfo[UndoIndex] = 0 Redimension/N=(UndoInfo+1, -1) UndoInfo endif Variable newListItem = removePeakNumber >= numOldPeaks UndoInfo[UndoIndex][0] = newListItem ? 3 : 2 // action is a Remove UndoInfo[UndoIndex][1] = -1 // It doesn't have a position in the Editwpi wave UndoInfo[UndoIndex][2] = Editwpi[removePeakNumber][0] UndoInfo[UndoIndex][3] = Editwpi[removePeakNumber][1] UndoInfo[UndoIndex][4] = Editwpi[removePeakNumber][2] UndoInfo[UndoIndex][5] = Editwpi[removePeakNumber][3] String peakWaveName = EditPeakList[removePeakNumber] if (newListItem) Variable newIndex = removePeakNumber - numOldPeaks Variable removedPeakNumber sscanf highlightedTrace, "NewPeak %d", removedPeakNumber UndoInfo[UndoIndex][1] = removedPeakNumber NewPeakTypeList = RemoveListItem(newIndex, NewPeakTypeList) numNewPeaks -= 1 else String/G :EditPeaksStuff:RemovePeakList SVAR RemovePeakList = :EditPeaksStuff:RemovePeakList sscanf highlightedTrace, "EditPeak %d", removedPeakNumber UndoInfo[UndoIndex][1] = removedPeakNumber RemovePeakList += highlightedTrace+";" endif DeletePoints removePeakNumber, 1, Editwpi, EditPeakList RemoveFromGraph/W=EditOrAddPeaksGraph $peakWaveName KillWaves :EditPeaksStuff:$peakWaveName SetDataFolder saveDF endif return 1 endif break; endif saveDF = GetDataFolder(1) SetDataFolder DFPath SVAR YWvName = $(DFpath+":YWvName") Wave yw = $YWvName Wave/D/Z EditInfo=$"" Wave/T/Z EditTrace=$"" Variable/G :EditPeaksStuff:EditPeakNumber = -1 NVAR EditPeakNumber = :EditPeaksStuff:EditPeakNumber if ( (strlen(highlightedTrace) > 0) && (npeaks > 0) ) Wave Editwpi = :EditPeaksStuff:Editwpi Make/O/D/N=(1, 4) EditInfo Make/O/N=1/T EditTrace for (i = 0; i < npeaks; i += 1) if (CmpStr(EditPeakList[i], highlightedTrace)==0) EditInfo[0][] = Editwpi[i][q] EditTrace[0] = EditPeakList[i] break; endif endfor EditPeakNumber = i endif Button MPF2_EditOrAddCancelButton,win=EditOrAddPeaksGraph#PLeft,disable=1 Button MPF2_AddOrEditDoneButton,win=EditOrAddPeaksGraph#PLeft,disable=1 SetWindow EditOrAddPeaksGraph, userdata(MPF2_EditMouseMode) = "down" SetDataFolder saveDF return StartManualPeakModeEX(yw, "MPF2_PeakEditCallback", EditTrace, EditInfo, s) break; case "mousemoved": if (CmpStr(s.winName, "EditOrAddPeaksGraph") != 0) return 0 endif highlightedTrace = GetUserData(s.winName, "", "MPF2_HighlightTrace" ) // String tname = PossiblyUnquoteName(StringByKey("TRACE", TraceFromPixel(s.mouseLoc.h, s.mouseLoc.v, "" ))) saveDF = GetDataFolder(1) SetDataFolder DFPath Wave/T EditPeakList = :EditPeaksStuff:EditPeakList Wave Editwpi = :EditPeaksStuff:Editwpi Wave/T listWave = :EditPeaksStuff:MPF2_PeakReadoutListWave SetDataFolder saveDF Variable peakNum=0 Variable imax= numpnts(EditPeakList) String tname = "" if (imax > 0) Do String theTrace = TraceFromPixel(s.mouseLoc.h, s.mouseLoc.v, "ONLY:"+PossiblyQuoteName(EditPeakList[peakNum])+";") if( strlen(theTrace) != 0 ) tname = EditPeakList[peakNum] break endif peakNum += 1 while(peakNum < imax) endif SVAR gMessage = root:Packages:ManualPeaks:gMessage if ( (strlen(highlightedTrace) > 0) && (CmpStr(highlightedTrace, tname) != 0) ) if (strlen(TraceInfo(s.winName, highlightedTrace, 0)) > 0) ModifyGraph/W=$(s.winName) lSize($PossiblyQuoteName(highlightedTrace))=1 endif SetWindow $(s.winName) userdata(MPF2_HighlightTrace)="" endif MPF2_FillReadoutList(listWave, "", $"",0) if (strlen(tname) > 0) if ( (CmpStr(tname[0,3], "Edit") == 0) || (CmpStr(tname[0,6], "NewPeak") == 0) ) ModifyGraph/W=$(s.winName) lSize($PossiblyQuoteName(tname))=2 SetWindow $(s.winName) userdata(MPF2_HighlightTrace)=tname gMessage = "Click and drag to edit highlighted peak" MPF2_FillReadoutList(listWave, EditPeakList[peakNum], editwpi, peakNum) else gMessage = "Click and drag to create new peak" endif else gMessage = "Click and drag to create new peak" endif break; case "kill": // treat like the Cancel button String cmd = "KillDataFolder "+DFpath+":EditPeaksStuff" Execute/P/Q/Z cmd String infostr = "EVENT:kill;WINDOW:"+s.winName WC_WindowCoordinatesHook(infostr) SVAR YWvName = $(DFpath+":YWvName") Wave w = $YWvName String/G root:Packages:MultiPeakFit2:TraceInfoForAddOrEditData = TraceInfo(s.winName, NameOfWave(w), 0) break; case "deactivate": highlightedTrace = GetUserData(s.winName, "", "MPF2_HighlightTrace" ) if ( (strlen(highlightedTrace) > 0) ) if (strlen(TraceInfo(s.winName, highlightedTrace, 0)) > 0) ModifyGraph/W=$(s.winName) lSize($PossiblyQuoteName(highlightedTrace))=1 endif SetWindow $(s.winName) userdata(MPF2_HighlightTrace)="" endif break; endswitch return 0 end Static Function/S ExtractRootFromSubwindowName(subWinName) String subWinName Variable poundPos = strsearch(subWinName, "#", 0) if (poundPos > 0) return subWinName[0,poundPos-1] endif return subWinName end Function MPF2_EditPeaksUndoButtonProc(s) : ButtonControl STRUCT WMButtonAction &s if (s.eventCode != 2) // mouse-up in the control return 0 endif Variable setNumber = GetSetNumberFromWinName("EditOrAddPeaksGraph") MPF2_EditOrAddPeaksUndo(setNumber) MPF2_AddOrEditUpdtUndoRedoBtn(setNumber) End Static Function MPF2_AddOrEditUpdtUndoRedoBtn(setNumber) Variable setNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFPath SetDataFolder :EditPeaksStuff NVAR UndoIndex // points to current undo info; when < 0, no undo info available. Wave UndoInfo // col 0: 1=added, 0=edited; col 1: index into Editwpi; col 2,3,4,5: Editwpi values before editing SetDataFolder saveDF Button MPF2_EditPeaksUndoButton,win=EditOrAddPeaksGraph#PLeft,disable= (UndoIndex < 0 ? 2 : 0) Button MPF2_EditPeaksRedoButton,win=EditOrAddPeaksGraph#PLeft,disable= (UndoIndex >= DimSize(UndoInfo, 0)-1 ? 2 : 0) end Function MPF2_EditPeaksRedoButtonProc(s) : ButtonControl STRUCT WMButtonAction &s if (s.eventCode != 2) // mouse-up in the control return 0 endif // String gname = WinName(0,1) Variable setNumber = GetSetNumberFromWinName("EditOrAddPeaksGraph") MPF2_EditOrAddPeaksRedo(setNumber) MPF2_AddOrEditUpdtUndoRedoBtn(setNumber) End // this function doesn't need to kill the graph, because it is called as a result of killing the graph. Function MPF2_AddOrEditModeKillDF(setNumber) Variable setNumber // NVAR currentSetNumber = root:Packages:MultiPeakFit2:currentSetNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) KillDataFolder $(DFpath+":EditPeaksStuff") end Function MPF2_EditOrAddCancelButtonProc(ctrlName) : ButtonControl String ctrlName // Just kill the graph, the hook function will call MPF2_AddOrEditModeKillDF() DoWindow/K EditOrAddPeaksGraph End Function MPF2_EditOrAddDoneButtonProc(s) : ButtonControl STRUCT WMButtonAction &s if (s.eventCode != 2) // mouse-up in the control return 0 endif Variable setNumber = GetSetNumberFromWinName("EditOrAddPeaksGraph") String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFPath Wave/Z wpi = W_AutoPeakInfo SVAR YWvName = $(DFpath+":YWvName") SVAR XWvName = $(DFpath+":XWvName") Wave yw = $YWvName Wave/Z xw = $XWvName SVAR gname = GraphName DoWindow/K EditOrAddPeaksGraph // DoUpdate if (WinType(gname) != 1) DoAlert 0, "The graph and Multi-peak Fit control panel have disappeared!" return -1 endif if (WinType(gname+"#MultiPeak2Panel") != 7) DoAlert 0, "The Multi-peak Fit control panel associated with the graph \""+gname+"\" has disappeared!" return -1 endif SetDataFolder :EditPeaksStuff Wave Editwpi Wave/T EditPeakList NVAR numNewPeaks SVAR NewPeakTypeList Variable numOldPeaks = DimSize(Editwpi, 0) - numNewPeaks SVAR/Z RemovePeakList SVAR editedPeaksList Variable i, peakNumber SetDataFolder :: Wave/Z wpi = W_AutoPeakInfo if (WaveExists(wpi)) Variable npeaks = DimSize(wpi, 0) NewDataFolder/O/S EditDuplicateCoefWaves KillWaves/Z/A for (i = 0; i < npeaks; i += 1) String wvname = "Peak "+num2str(i)+" Coefs" Duplicate/O $("::"+PossiblyQuoteName(wvname)), $wvname endfor SetDataFolder :: endif // We have to perform the actions in the right order- all the peak numbers refer to peak numbers before the Add or Edit graph was put up. // Consequently, we have to store the edited peak values into the wpi wave first, while the row numbers match the peak numbers. // Then we have to remove peaks that were removed in the Add or Edit graph, starting with the highest numbered peak. That's because // as the peaks are removed, higher-numbered peaks move down into lower-numbered rows (effectively, they are re-named). // Finally, add new peaks, since they are added at the end of the wpi wave, which will later be sorted by peak location. SVAR SavedFunctionTypes SavedFunctionTypes += NewPeakTypeList Make/N=(0,2)/O changedPeaks // column 0: peak number; column 1: 1 for changed, 0 for unchanged // First handle edits. If the wpi wave doesn't exist yet, then there can't be edited peaks, but we need to make the wpi wave. if ( !WaveExists(wpi) || (DimSize(wpi, 0) == 0) ) Make/D/N=(0,5)/O W_AutoPeakInfo wave wpi = W_AutoPeakInfo else Redimension/N=(DimSize(wpi, 0), 2) changedPeaks changedPeaks[][0] = p changedPeaks[][1] = 0 for (i = 0; i < numOldPeaks; i += 1) sscanf EditPeakList[i], "EditPeak %d", peakNumber if (WhichListItem(num2str(i), editedPeaksList) >= 0) wpi[peakNumber][0] = Editwpi[i][2] wpi[peakNumber][1] = Editwpi[i][3] wpi[peakNumber][2] = Editwpi[i][1] wpi[peakNumber][3] = Editwpi[i][3]/2 wpi[peakNumber][4] = Editwpi[i][3]/2 changedPeaks[peakNumber][1] = 1 endif endfor endif // next delete removed peaks Variable numRemovedPeaks = 0 if (SVAR_Exists(RemovePeakList)) RemovePeakList = SortList(RemovePeakList, ";", 17) // sort alphanumerically in descending order so the first peak in the list is the highest-numbered numRemovedPeaks = ItemsInList(RemovePeakList) for (i = 0; i < numRemovedPeaks; i += 1) String peakName = StringFromList(i, RemovePeakList) sscanf peakName, "EditPeak %d", peakNumber DeletePoints peakNumber, 1, wpi, changedPeaks endfor endif // finally, add new peaks at the end of the wave. Variable numExistingPeaks = DimSize(wpi, 0) InsertPoints numExistingPeaks, numNewPeaks, wpi, changedPeaks for (i = 0; i < numNewPeaks; i += 1) wpi[numExistingPeaks+i][0] = Editwpi[numOldPeaks+i][2] wpi[numExistingPeaks+i][1] = Editwpi[numOldPeaks+i][3] wpi[numExistingPeaks+i][2] = Editwpi[numOldPeaks+i][1] wpi[numExistingPeaks+i][3] = Editwpi[numOldPeaks+i][3]/2 wpi[numExistingPeaks+i][4] = Editwpi[numOldPeaks+i][3]/2 changedPeaks[numExistingPeaks+i][1] = 1 endfor //DoUpdate Variable newNPeaks = DimSize(wpi, 0) if ( (newNPeaks > 0) || (numRemovedPeaks > 0) ) MPF2_RefreshHoldStrings(gname+"#MultiPeak2Panel") //DoUpdate Wave/T HoldStrings Variable numPeaksBefore = DimSize(HoldStrings, 0) Redimension/N=(newNPeaks) HoldStrings //DoUpdate sscanf EditPeakList[i], "EditPeak %d", peakNumber HoldStrings[numPeaksBefore, ] = "" //DoUpdate String listoftypes = SavedFunctionTypes string indexWaveName = MPF2_SortAutoPeakWave(wpi, listOfTypes=listoftypes, holdwave=HoldStrings, killIndexWave = 0) Wave indexWave = $indexWaveName SavedFunctionTypes = listoftypes for (i = 0; i < DimSize(wpi, 0); i += 1) if (changedPeaks[indexWave[i]][1]) MPF2_CoefWaveForPeak(setNumber, wpi, i, StringFromList(i+1,SavedFunctionTypes)) // i+1: the first of SavedFunctionTypes is for the baseline else String oldWaveName = GetDataFolder(1)+"EditDuplicateCoefWaves:'Peak "+num2str(changedPeaks[indexWave[i]][0])+" Coefs'" String newWaveName = GetDataFolder(1)+"'Peak "+num2str(i)+" Coefs'" Duplicate/O $oldWaveName, $newWaveName endif endfor // KillWaves/Z indexWave // CreateCoefWavesFromAutoPeakInfo(setnumber, wpi, "Gauss") MPF2_PutAutoPeakResultIntoList(setNumber, wpi, 0, listOfPeakTypes=SavedFunctionTypes) NVAR negativePeaks = negativePeaks MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1) MPF2_AddFitCurveToGraph(setNumber, wpi, yw, xw, 1, overridePoints=MPF2_getFitCurvePoints(gname+"#MultiPeak2Panel")) endif KillWaves/Z changedPeaks MPF2_EnableDisableDoFitButton(setNumber) SetDataFolder saveDF End // Undo information is stored in the wave EditPeakStuff:UndoInfo. Each peak that has been edited or added has a row in the UndoInfo wave. // The information in that row is // column 0: type of action: 0 = edited; 1 = added new peak; 2 = remove an old peak; 3 = remove a new peak // Column 1: row in the Editwpi wave. If column 0 is 2 (remove) this column is not meaningful. // Column 2-5: y0, a, x0, w values // An undo of an edit (column 0 is 0): // 1) swap y0, a, x0, w values between appropriate rows in editwpi and UndoInfo waves. // 2) re-compute the peak wave used to display the peak on the Edit or Add graph. // Re-doing an edit is the same as un-doing an edit // An undo of a new wave (column 0 is 1): // 1) objectIndex = UndoInfo[UndoIndex][1] get the index of the added peak in the editwpi wave // 2) peakWaveName = EditPeakList[objectIndex] Get the name of the wave that displays the peak on the graph // 3) DeletePoints objectIndex, 1, Editwpi, EditPeakList Remove the entry in the editwpi wave // 4) RemoveFromGraph/W=EditOrAddPeaksGraph $peakWaveName Remove the peak from the graph // 5) KillWaves $peakWaveName and kill the display wave // 6) numNewPeaks -= 1 Update the number of peaks variable // 7) NewPeakTypeList = RemoveListItem(numNewPeaks, NewPeakTypeList) Remove the appropriate item from the list of new peaks // Re-doing and add // 1) Add row to end of editwpi wave // 2) Populate the new row with values from appropriate row of UndoInfo // 3) Make a new peak wave, fill it with peak values, add it to the graph Function MPF2_PeakEditCallback(pk,y0,a,x0,w) Variable pk,y0,a,x0,w String newType if (pk == 0) ControlInfo/W=EditOrAddPeaksGraph#PLeft AddOrEdit_NewPeakTypeMenu newType = S_value if (CmpStr(newType, "Ask") == 0) Prompt newType, "Type for new peak", popup, MPF2_ListPeakTypeNames() DoPrompt "Choose type for new peak", newType if (V_flag == 1) return 0 endif endif endif w = abs(w) String gname=WinName(0,1) Variable setNumber = GetSetNumberFromWinName(gname) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFPath SetDataFolder :EditPeaksStuff SVAR YWvName = $(DFpath+":YWvName") Wave Editwpi Wave/T EditPeakList NVAR numNewPeaks SVAR NewPeakTypeList Wave UndoInfo // col 0: 1=added, 0=edited; col 1: index into Editwpi; col 2,3,4,5: Editwpi values before editing NVAR UndoIndex // points to current undo info; when < 0, no undo info available. UndoIndex += 1 if (DimSize(UndoInfo, 0) <= UndoIndex) InsertPoints UndoIndex, 1, UndoInfo UndoIndex = DimSize(UndoInfo, 0)-1 // paranoia else UndoInfo[UndoIndex] = 0 Redimension/N=(UndoInfo+1, -1) UndoInfo endif if (pk == 0) // finished creating a new peak Variable nPeaks = DimSize(Editwpi, 0) InsertPoints nPeaks, 1, Editwpi, EditPeakList UndoInfo[UndoIndex][0] = 1 UndoInfo[UndoIndex][1] = nPeaks UndoInfo[UndoIndex][2] = y0 // don't need it for undo, but might for re-do UndoInfo[UndoIndex][3] = a UndoInfo[UndoIndex][4] = x0 UndoInfo[UndoIndex][5] = w Editwpi[nPeaks][0] = y0 Editwpi[nPeaks][1] = a Editwpi[nPeaks][2] = x0 Editwpi[nPeaks][3] = w Variable lastPeakNumber=-1 String previousPeakName = EditPeakList[nPeaks-1] if (CmpStr(previousPeakName[0,7], "NewPeak ") == 0) sscanf previousPeakName, "NewPeak %d", lastPeakNumber endif EditPeakList[nPeaks] = "NewPeak "+num2str(lastPeakNumber+1) Make/O/N=200 $(EditPeakList[nPeaks]) Wave pkwave = $(EditPeakList[nPeaks]) SetScale/I x x0-4*w, x0+4*w, pkwave pkwave = y0+a*exp(-((x-x0)/w)^2) AppendToGraph/W=EditOrAddPeaksGraph pkwave ModifyGraph rgb($NameOfWave(pkwave))=(0,0,65535) NewPeakTypeList += newType+";" numNewPeaks += 1 else // finished editing an existing peak NVAR EditPeakNumber SVAR editedPeaksList pk = EditPeakNumber if (WhichListItem(num2str(pk), editedPeaksList) < 0) editedPeaksList += num2str(pk)+";" endif UndoInfo[UndoIndex][0] = 0 UndoInfo[UndoIndex][1] = pk UndoInfo[UndoIndex][2] = Editwpi[pk][0] UndoInfo[UndoIndex][3] = Editwpi[pk][1] UndoInfo[UndoIndex][4] = Editwpi[pk][2] UndoInfo[UndoIndex][5] = Editwpi[pk][3] Editwpi[pk][0] = y0 Editwpi[pk][1] = a Editwpi[pk][2] = x0 Editwpi[pk][3] = w Wave pkwave = $(EditPeakList[pk]) SetScale/I x x0-4*w, x0+4*w, pkwave pkwave = y0+a*exp(-((x-x0)/w)^2) endif Button MPF2_EditOrAddCancelButton,win=EditOrAddPeaksGraph#PLeft,disable=0 Button MPF2_AddOrEditDoneButton,win=EditOrAddPeaksGraph#PLeft,disable=0 MPF2_AddOrEditUpdtUndoRedoBtn(setNumber) EndManualPeakMode() SetWindow EditOrAddPeaksGraph, userdata(MPF2_EditMouseMode) = "up" SetDataFolder saveDF return 1 end Function MPF2_EditOrAddPeaksUndo(setNumber) Variable setNumber // NVAR currentSetNumber = root:Packages:MultiPeakFit2:currentSetNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFPath SetDataFolder :EditPeaksStuff Wave Editwpi Wave/T EditPeakList NVAR numNewPeaks SVAR NewPeakTypeList Wave UndoInfo // col 0: 1=added, 0=edited; col 1: index into Editwpi; col 2,3,4,5: Editwpi values before editing NVAR UndoIndex // points to current undo info; when < 0, no undo info available. if (UndoIndex < 0) SetDataFolder saveDF return 0 endif Variable objectIndex String peakWaveName // UndoIndex was last left pointing to the last added info, or to the next one to be used Variable undoAction = UndoInfo[UndoIndex][0] switch (undoAction) case 0: // last action was editing a peak objectIndex = UndoInfo[UndoIndex][1] Make/D/N=4/O tempUndoInfoWave tempUndoInfoWave = Editwpi[objectIndex][p] // save values we're about to undo peakWaveName = EditPeakList[objectIndex] Editwpi[objectIndex][] = UndoInfo[UndoIndex][q+2] UndoInfo[UndoIndex][2,] = tempUndoInfoWave[q-2] // save undone values for possible re-do Variable y0 = Editwpi[objectIndex][0] Variable a = Editwpi[objectIndex][1] Variable x0 = Editwpi[objectIndex][2] Variable w = Editwpi[objectIndex][3] Wave pkwave = $peakWaveName SetScale/I x x0-4*w, x0+4*w, pkwave pkwave = y0+a*exp(-((x-x0)/w)^2) break; case 1: // last action was adding a peak objectIndex = UndoInfo[UndoIndex][1] peakWaveName = EditPeakList[objectIndex] DeletePoints objectIndex, 1, Editwpi, EditPeakList RemoveFromGraph/W=EditOrAddPeaksGraph $peakWaveName KillWaves $peakWaveName numNewPeaks -= 1 NewPeakTypeList = RemoveListItem(numNewPeaks, NewPeakTypeList) break; case 2: // last action was removing one of the original peaks case 3: // last action was removing a new peak (one added by dragging on the Add or Edit graph) if (undoAction == 2) Variable removedPeakNumber = UndoInfo[UndoIndex][1] NVAR numNewPeaks SVAR RemovePeakList String removedPeakName = "EditPeak "+num2str(removedPeakNumber) RemovePeakList = RemoveFromList(removedPeakName, RemovePeakList) Variable removedPeakIndex sscanf removedPeakName, "EditPeak %d", removedPeakIndex Variable i Variable numOldPeaks = DimSize(Editwpi, 0)-numNewPeaks for (i = 0; i < numOldPeaks; i += 1) Variable editPeakNumber sscanf EditPeakList[i], "EditPeak %d", editPeakNumber if (editPeakNumber > removedPeakIndex) break; endif endfor Variable editIndex = i InsertPoints editIndex, 1, Editwpi,EditPeakList Editwpi[editIndex][] = UndoInfo[UndoIndex][q+2] EditPeakList[editIndex] = removedPeakName else Variable newIndex = UndoInfo[UndoIndex][1] numOldPeaks = DimSize(Editwpi, 0)-numNewPeaks editIndex = numOldPeaks + newIndex InsertPoints editIndex, 1, Editwpi,EditPeakList Editwpi[editIndex][] = UndoInfo[UndoIndex][q+2] EditPeakList[editIndex] = "NewPeak "+num2str(newIndex) numNewPeaks += 1 endif Make/O/N=200 $(EditPeakList[editIndex]) Wave pkwave = $(EditPeakList[editIndex]) y0 = Editwpi[editIndex][0] a = Editwpi[editIndex][1] x0 = Editwpi[editIndex][2] w = Editwpi[editIndex][3] SetScale/I x x0-4*w, x0+4*w, pkwave pkwave = y0+a*exp(-((x-x0)/w)^2) AppendToGraph/W=EditOrAddPeaksGraph pkwave ModifyGraph rgb($NameOfWave(pkwave))=(0,0,65535) break; endswitch UndoIndex -= 1 SetDataFolder saveDF end Function MPF2_EditOrAddPeaksRedo(setNumber) Variable setNumber // NVAR currentSetNumber = root:Packages:MultiPeakFit2:currentSetNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFPath SetDataFolder :EditPeaksStuff Wave Editwpi Wave/T EditPeakList NVAR numNewPeaks SVAR NewPeakTypeList Wave UndoInfo // col 0: 1=added, 0=edited; col 1: index into Editwpi; col 2,3,4,5: Editwpi values before editing NVAR UndoIndex // points to current undo info; when < 0, no undo info available. if (UndoIndex >= DimSize(UndoInfo, 0)-1) SetDataFolder saveDF return 0 endif UndoIndex += 1 Variable objectIndex String peakWaveName Variable y0 Variable a Variable x0 Variable w // UndoIndex was last left pointing to the last added info, or to the next one to be used Variable undoAction = UndoInfo[UndoIndex][0] switch (undoAction) case 0: // edited a peak objectIndex = UndoInfo[UndoIndex][1] Make/D/N=4/O tempUndoInfoWave tempUndoInfoWave = Editwpi[objectIndex][p] // save values we're about to redo peakWaveName = EditPeakList[objectIndex] Editwpi[objectIndex][] = UndoInfo[UndoIndex][q+2] UndoInfo[UndoIndex][2,] = tempUndoInfoWave[q-2] // save redone values for possible undo y0 = Editwpi[objectIndex][0] a = Editwpi[objectIndex][1] x0 = Editwpi[objectIndex][2] w = Editwpi[objectIndex][3] Wave pkwave = $peakWaveName SetScale/I x x0-4*w, x0+4*w, pkwave pkwave = y0+a*exp(-((x-x0)/w)^2) break; case 1: // added a new peak Variable nPeaks = DimSize(Editwpi, 0) InsertPoints nPeaks, 1, Editwpi, EditPeakList UndoInfo[UndoIndex][1] = nPeaks Editwpi[nPeaks][0] = UndoInfo[UndoIndex][2] Editwpi[nPeaks][1] = UndoInfo[UndoIndex][3] Editwpi[nPeaks][2] = UndoInfo[UndoIndex][4] Editwpi[nPeaks][3] = UndoInfo[UndoIndex][5] Variable lastPeakNumber = -1 String previousPeakName = EditPeakList[nPeaks-1] if (CmpStr(previousPeakName[0,7], "NewPeak ") == 0) sscanf previousPeakName, "NewPeak %d", lastPeakNumber endif EditPeakList[nPeaks] = "NewPeak "+num2str(lastPeakNumber+1) NewPeakTypeList += "Gauss;" Make/O/N=200 $(EditPeakList[nPeaks]) Wave pkwave = $(EditPeakList[nPeaks]) y0 = Editwpi[nPeaks][0] a = Editwpi[nPeaks][1] x0 = Editwpi[nPeaks][2] w = Editwpi[nPeaks][3] SetScale/I x x0-4*w, x0+4*w, pkwave pkwave = y0+a*exp(-((x-x0)/w)^2) AppendToGraph/W=EditOrAddPeaksGraph pkwave ModifyGraph rgb($NameOfWave(pkwave))=(0,0,65535) numNewPeaks += 1 break; case 2: // removed an original peak case 3: // removed an added peak String peakName Variable peakNumber = UndoInfo[UndoIndex][1] Variable numOldPeaks = DimSize(Editwpi, 0)-numNewPeaks Variable i, editRow if (undoAction == 2) peakName = "EditPeak "+num2str(peakNumber) for (i = 0; i < numOldPeaks; i += 1) if (CmpStr(EditPeakList[i], peakName) == 0) break; endif endfor else peakName = "NewPeak "+num2str(peakNumber) for (i = numOldPeaks; i < DimSize(Editwpi, 0); i += 1) if (CmpStr(EditPeakList[i], peakName) == 0) break; endif endfor endif editRow = i DeletePoints editRow, 1, Editwpi, EditPeakList RemoveFromGraph/W=EditOrAddPeaksGraph $peakName KillWaves $peakName if (undoAction == 2) SVAR RemovePeakList = RemovePeakList RemovePeakList = RemoveFromList(peakName, RemovePeakList) else NewPeakTypeList = RemoveListItem(editRow-numOldPeaks, NewPeakTypeList) numNewPeaks -= 1 endif break; endswitch Button MPF2_EditPeaksUndoButton,win=EditOrAddPeaksGraph#PLeft,disable=0 SetDataFolder saveDF end