#if (IgorVersion() < 6.2) #pragma rtGlobals=1 #else #pragma rtGlobals=3 // Use modern global access method. #endif #pragma moduleName= WMMultiPeakFit2 #pragma version=2.15 #pragma IgorVersion=6.10 #include version>=2.00 #include version>=1.10 #include version>=5.07 //*************************************** // 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 g 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. // Added ability to start from a previous set. // If you clicked the Peak Results button before a fit was done, the current data folder was not restored properly. // Added checkpoint menu. // JW 100809: // Fixed out-of-range wave index error if the center of a peak isn't within the range of the X data (most likely a bad fit). // Fixed bug: initializing from a previous set resulted in fitting to the waves in that previous set. // JW 100812: version 2.07 bumped the version number because we released 2.06 with 6.20 beta 4 // Fixed a couple more out-of-range wave index bugs. // JW 100907: // Fixed yet another out-of-range wave index bug, calculating attachment point for peaks that are out of the range of fitting. // JW 100914: Release 2.08 // Fixed bug: reversed X range caused Add or Edit Peaks window to fail to show existing peaks. // Added Background-subtracted data set button to Results window. // JW 101207: Release 2.09 // Fixed bug in MPF2_AddPeaksToGraph where an index-out-of-range error occurs if the estimated peak info is bad. I don't know why // the peak info is bad, but Peter Dedecker sent an example. // Fixed errors when only one cursor is on the graph with the Use Graph Cursors checkbox checked. // Now the Edit or Add Peaks panel ignores a click without a drag, which previously created a peak with zero width. That // fixes the origin of the problem reported by Peter Dedecker. // JW 110111: Release 2.10 // Fixed bug in Add or Edit window: if the X values were backwards (either negative X scaling, or decreasing values in X wave) the // Add or Edit graph didn't use the marquee width as the range. // JW 110217: Release 2.11 // Added Option: Display Peaks Full X Width of Graph checkbox added to the Options area of the main panel. // JW 110714: Version 2.12 // Changed method for updating graph curves after editing coefficients in the list. Old method tried to evaluate the old coefficients for the changed peak or baseline, // then subtract it from the curve, then evaluate the new coefficients and add it to the curve. This was fragile. Now it updates the coefficient wave involved, then // simply re-evaluates the curves from scratch. That way, if something goes wrong at some point, it's self-correcting. // JW 120227: Version 2.13 // Fixed bug: Mask wave popup assigned result to Weight wave global string. This would only affect reconstruction of the panel; the actual mask wave was correctly // recorded in the structure used to transmit the wave to the fit. // Added notation of the weight and mask waves to the results notebook. // Fixed bug: The Graph button would put the baseline wave on the graph even if the current setting for the baseline function was None. // JW 120612: Version 2.14 // Changed behavior (fixed design bug): The function to compute derived parameter values is now called with the correct number of rows in the output wave. // NH 120717: Version 2.15 // Added constraints. New functions at the bottom of the file, plus changes to some existing functions. Added new controls ("Apply Constraints" checkbox and "Inter-Peak Constraints"), // and increased the size of the MultiPeakFit listbox. The "Apply Constraints" checkbox controls the visibility of the new columns in the listbox, the size of the panel and whether or not // the constraints are applied // Fixed a bug in MPF2_RestoreCoefWavesFromBackup(). The baseline coef backup wave might not get created in MPF2_BackupCoefWaves(), but Restore // always tried to create it // Fixed a bug that peaks deleted via the "Add Or Edit Peaks" dialog are not removed from the peak axis on the graph. // Fixed a bug that HoldStrings grew as peaks were added and removed. // Fixed a bug that once Use Graph Cursors was checked the cursors were applied even if Use Graph Cursors is unchecked // Fixed a few more bugs only revealed with heavy use // //*************************************** //*************************************** // 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. // DONE 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 #include static strconstant MPF2_VERSIONSTRING="2.15" static constant MPF2_UPDATEPANELVERSION=2.15 static constant MPF2_NarrowWidth=265 static constant MPF2_PanelWidth=440 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 // Added for version 2.15 Wave /T constraints // NH added 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 Variable xPtRgBgn = MPstruct.XPointRangeBegin //added to avoid FuncFit command being 400 chars - NH Variable xPtRgEnd = MPstruct.XPointRangeEnd if (!WaveExists(MPstruct.Constraints)) Make /T/Free/N=0 MPstruct.Constraints endif FuncFit/Q=1/N=(doUpdates==0?1:0)/M=2 {string=MPstruct.FuncListString} yw[xPtRgBgn,xPtRgEnd]/X=xw[xPtRgBgn,xPtRgEnd]/W=MPstruct.weightWave[xPtRgBgn,xPtRgEnd]/I=1/M=MPstruct.maskWave[xPtRgBgn,xPtRgEnd] /AD=(doAutoDest)/AR=(doAutoResid)/A=1/NWOK/C=MPstruct.constraints;AbortOnRTE catch MPstruct.fitErrorMsg = GetRTErrMessage() Variable semiPos = strsearch(MPstruct.fitErrorMsg, ";", 0) if (semiPos >= 0) String errWithPeaksNamed = MPF2_StringKToPeakNotation(MPstruct.fitErrorMsg[semiPos+1, inf], MPstruct) MPstruct.fitErrorMsg = errWithPeaksNamed // 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 static Function MPF2_BackupCoefWaves(listofWaveNames, DataFolderName, [backupName]) String listofWaveNames, DataFolderName, backupName String saveDF = GetDataFolder(1) SetDataFolder DataFolderName Variable nWaves = ItemsInList(listofWaveNames) if (ParamIsDefault(backupName)) backupName = "MPF2_CoefsBackup_" endif 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, $(backupName+num2istr(i)) endif endfor SetDataFolder saveDF end Function MPF2_RestoreCoefWavesFromBackup(listofWaveNames, DataFolderName, [backupName]) String listofWaveNames, DataFolderName, backupName String saveDF = GetDataFolder(1) SetDataFolder DataFolderName Variable nWaves = ItemsInList(listofWaveNames) if (ParamIsDefault(backupName)) backupName = "MPF2_CoefsBackup_" endif Variable i for (i = 0; i < nWaves; i += 1) Wave/Z coefs = $StringFromList(i, listofWaveNames) if (WaveExists(coefs)) // if this wave doesn't exist, it didn't get made by MPF2_BackupCoefWaves() above - NH Wave backupWave = $(backupName+num2istr(i)) Duplicate/O backupWave, $StringFromList(i, listofWaveNames) endif 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=(50,50,314,285)/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 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:", proc=MPF2_ChooseGraphProc PopupMenu MPF2_ChooseGraph,mode=1,bodyWidth= 140,value= #"\"New Graph;\"+MPF2_ListGraphsWSelectedWaves()" PopupMenu MPF2_InitializeFromSetMenu,pos={6,127},size={247,20},bodyWidth=190,title="Initialization:" PopupMenu MPF2_InitializeFromSetMenu,mode=1,value= #"InitializeMPF2FromMenuString()" PopupMenu MPF2_PanelPositionMenu,pos={50,184},size={123,20},title="Panel Position" PopupMenu MPF2_PanelPositionMenu,mode=2,value= #"\"Below;Right;Left;Above\"" Button MPF2_DataSelectedContinueButton,pos={11,207},size={100,20},proc=MPF2_WaveSelectContinueBtnProc,title="Continue",fSize=10 Button MPF2_StartPanelHelp,pos={187,207},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 = 235*72/screenResolution MoveWindow/W=MultiPeak2StarterPanel scrnWidth/2 - halfWidth, scrnHeight/2 - Height, scrnWidth/2 + halfWidth, scrnHeight/2 end Function MPF2_ChooseGraphProc(s) : PopupMenuControl Struct WMPopupAction &s ControlInfo/W=$(s.win) MPF2_InitializeFromSetMenu Variable menuItem = V_value if ( (CmpStr(s.popStr, "New Graph") == 0) || (strlen(GetUserData(s.popStr, "", "MPF2_DataSetNumber")) == 0) ) if (menuItem == 2) PopupMenu MPF2_InitializeFromSetMenu, win=$(s.win),mode=1 endif endif 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 String DFpath = MPF2_FolderPathFromSetNumber(setNumber) // constraints visible NVAR/Z MPF2ConstraintsShowing = $(DFPath+":MPF2ConstraintsShowing") if (!NVAR_Exists(MPF2ConstraintsShowing)) Variable/G $(DFPath+":MPF2ConstraintsShowing") NVAR MPF2ConstraintsShowing = $(DFPath+":MPF2ConstraintsShowing") MPF2ConstraintsShowing = 0 endif Variable width = MPF2ConstraintsShowing ? MPF2_PanelWidth : MPF2_NarrowWidth Variable left, top, right, bottom switch(position) case 0: // right left=0; top=346; right=width; bottom=346; break; case 1: // left left=width; top=346; right=0; bottom=346; break; case 2: // below left=width; top=0; right=width; bottom=346; break; case 3: // above left=width; top=346; right=width; 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} ///// Panel has had significant changes for version 2.15. Need a way to check for the old version and update the panel ///// It will be done by checking existence of new variables. If they don't exist then check for a userdata version number ///// If the userdata version doesn't exist or < MPF2_UPDATEPANELVERSION then update the panel and additional coefficients SetWindow $panelName userdata(MPF2_UPDATEPANELVERSION)=num2str(MPF2_UPDATEPANELVERSION) Variable/G $(DFPath+":negativePeaks") = NumVarOrDefault(DFPath+":negativePeaks", 0) NVAR negativePeaks = $(DFPath+":negativePeaks") Variable/G $(DFPath+":displayPeaksFullWidth") = NumVarOrDefault(DFPath+":displayPeaksFullWidth", 0) NVAR displayPeaksFullWidth = $(DFPath+":displayPeaksFullWidth") 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={width-60,1},size={50,20},title="Help", proc=MPF2_DoHelpButtonProc //// Panel 0 - Peak location NewPanel/N=P0/W=(0,86,width,149)/FG=(,UGH0,,UGH1)/HOST=$panelName ModifyPanel frameStyle=0, frameInset=0 GroupBox MPF2_LocatePeaksGroupBox,pos={8,2},size={width-16,61},title="Locate Peaks" GroupBox MPF2_LocatePeaksGroupBox,fStyle=1 Button MPF2_AutoLocatePeaksButton,pos={(width-158)/2,21},size={158,20},proc=MPF2_AutoLocatePeaksButtonProc,title="Auto-locate Peaks Now" Button MPF2_AutoLocatePeaksButton,fSize=10 CheckBox MPF2_NegativePeaksCheck,pos={(width-85)/2,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 ## //// Panel 1 - fit controls NewPanel/N=P1/FG=(FL,UGH1,FR,UGH2)/HOST=$panelName ModifyPanel frameStyle=0, frameInset=0 String listPanel = panelName+"#P1" CheckBox MPF2_DiscloseConstraints,win=$listPanel,pos={width-150,2},size={81,15},title="Apply Constraints",value=MPF2ConstraintsShowing,proc=MPF2_DiscloseConstraints,fsize=10 ListBox MPF2_PeakList,win=$listPanel,pos={6,20},size={width-12,119},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", 6) 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) if (MPF2ConstraintsShowing) ListBox MPF2_PeakList win=$listPanel,widths={4,15,16,9,5,10,6,10} else ListBox MPF2_PeakList win=$listPanel,widths={4,15,16,9,0,0,0,0} endif SetActiveSubwindow ## NewPanel/N=P2/W=(65,86,width,260)/FG=(FL,UGH2,FR,UGH3)/HOST=$panelName ModifyPanel frameStyle=0, frameInset=0 String buttonPanel = panelName+"#P2" PopupMenu MPF2_SetAllPeakTypesMenu,win=$buttonPanel,pos={width-195,3},size={185,20},proc=MPF2_SetAllPeakTypesMenuProc,title="Set Peak Type for All Peaks" PopupMenu MPF2_SetAllPeakTypesMenu,win=$buttonPanel,mode=0,value= #"MPF2_ListPeakTypeNames()" Variable leftSideControlOffset = floor(width/15) Button MPF2_DoFitButton,win=$buttonPanel,pos={leftSideControlOffset,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={width-leftSideControlOffset-109 ,35},size={109,20},proc=MPF2_PeakResultsButtonProc,title="Peak Results...",fSize=10 Button MPF2_RevertToGuessesButton,win=$buttonPanel,pos={leftSideControlOffset,65},size={109,20},proc=MPF2_RevertToPreviousButtonProc,title="Revert to Guesses",fSize=10 PopupMenu MPF2_CheckPointMenu,pos={width-leftSideControlOffset-102,66},size={95,20},title="Checkpoint", proc=MPF2_CheckpointMenuProc PopupMenu MPF2_CheckPointMenu,mode=0,value= #"\"Save Checkpoint;Restore Checkpoint;\"" 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={(width-30)/2,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 SVAR /Z interPeakString = $(DFPath+":interPeakConstraints") if (!SVAR_exists(interPeakString)) String /G $(DFPath+":interPeakConstraints") SVAR interPeakString = $(DFPath+":interPeakConstraints") endif SetVariable MPF2_GlobalConstraints,pos={10,33},size={width-20,20},title="Inter-Peak Constraints",fSize=10, value=$(DFPath+":interPeakConstraints"), proc=MPF2_DoValidateConstraint SetVariable MPF2_GlobalConstraints,help={"Identify coefficients by P[PeakNumber]K[LocalCoefNumber]. Each constraint is separated by a Semi-colon."} TitleBox MPF2_ConstraintsExample,pos={90,50},size={width-50,20},title="Example: P1K0>P0K0;P2K1 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 NVAR displayPeaksFullWidth = $(DFpath+":displayPeaksFullWidth") 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, displayPeaksFullWidth) 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 "activate": Variable MultiPeakPanelLocation = strsearch(s.winName, "#MultiPeak2Panel", 0) String MPFpanelName if (MultiPeakPanelLocation < 0) // graph was the activated window MPFpanelName = (s.winName)+"#MultiPeak2Panel" else MPFpanelName = (s.winName)[0, MultiPeakPanelLocation+15] endif String updatePanelStr = GetUserData(MPFpanelName, "", "MPF2_UPDATEPANELVERSION") Variable updatePanelNum = str2num(updatePanelStr) if (numtype(updatePanelNum)==2) updatePanelNum = 0 endif Variable setNum = GetSetNumberFromWinName(s.winName) Variable lastUpdate = NumVarOrDefault("root:Packages:MultiPeakFit2:lastPanelUpdateNotification", 0) Variable panelVersion = NumVarOrDefault("root:Packages:MultiPeakFit2:MPF2_PanelVersion", 0) if (updatePanelNum < MPF2_UPDATEPANELVERSION) UpdateMultiPeak2Panel(MPFpanelName, setNum) Variable /G root:Packages:MultiPeakFit2:MPF2_PanelVersion = MPF2_UPDATEPANELVERSION if (lastUpdate && lastUpdate < panelVersion) DoAlert /T="Multi-peak Fitting 2.0 update" 0, "This Multipeak Fit Panel was has been updated. Your fit set-up has not been affected. Opening this experiment with the older version will result in an aesthetically flawed but still usable panel." Variable /G root:Packages:MultiPeakFit2:lastPanelUpdateNotification = MPF2_UPDATEPANELVERSION endif endif break; case "cursormoved": ControlInfo/W=$panelName MPF2_UserCursorsCheckbox if (!V_value) return 0 endif NVAR XPointRangeBegin = $(DFPath+":XPointRangeBegin") NVAR XPointRangeEnd = $(DFPath+":XPointRangeEnd") if ( (strlen(CsrInfo(A, gname)) == 0) || (strlen(CsrInfo(B, gname)) == 0) ) break // need both cursors on the graph endif Variable pl = pcsr(A) Variable pr = pcsr(B) Variable oldpl = XPointRangeBegin Variable oldpr = XPointRangeEnd XPointRangeEnd = max(pr, pl) XPointRangeBegin = min(pr, 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") NVAR displayPeaksFullWidth = $(DFpath+":displayPeaksFullWidth") MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1, displayPeaksFullWidth) 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 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 ControlInfo/W=$(s.winName+"#P3") MPF2_DiscloseOptions if (V_value) height = minHeight+150 endif Variable setNumber = GetSetNumberFromWinName(s.winName) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) NVAR position = $(DFPath+":panelPosition") height = max(height, bottom-top) NVAR MPF2ConstraintsShowing = $(DFPath+":MPF2ConstraintsShowing") Variable baseWidth = MPF2ConstraintsShowing ? MPF2_PanelWidth : MPF2_NarrowWidth Variable width = max(baseWidth, right-left) 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-16,height-23} SetVariable MPF2_GlobalConstraints,win=$(s.winName+"#P3"),size={width-20,20} 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_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 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 static Function isMonotonic(wx) Wave wx Variable smallestXIncrement Variable isMonotonic=0 Duplicate/O/Free wx, diff Differentiate/DIM=0/EP=0/METH=1/P diff WaveStats/Q/M=0 diff isMonotonic= (V_min > 0) == (V_max > 0) diff= abs(diff[p]) WaveStats/Q/M=0 diff return isMonotonic && V_Min != 0 End Function MPF2_WaveSelectContinueBtnProc(ctrlName) : ButtonControl String ctrlName 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 if (WaveExists(xw)) if (!isMonotonic(xw)) DoAlert 0, "Your X data wave is not monotonic." return -1 endif endif 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 theGraph = S_value ControlInfo/W=MultiPeak2StarterPanel MPF2_InitializeFromSetMenu Variable initializeFrom = min(V_value, 3) Variable menuSetNumber sscanf S_value, "Set Number %d", menuSetnumber MPF2_StartNewMPFit(Panelposition, theGraph, yWName, xWName, initializeFrom, menuSetNumber) end Function MPF2_StartNewMPFit(Panelposition, theGraph, yWName, xWName, initializeFrom, initializeFromSet) Variable Panelposition // 0: right, 1: left, 2: below, 3: above String theGraph // a graph name or "New Graph" String yWName, xWName Variable initializeFrom // 1: Start Fresh; 2: Previous for this graph; 3: from set number... Variable initializeFromSet // if initializeFrom is 2, then this has the set number to initialize from Variable setnumber Variable buildingFromOldData = 0 NVAR currentSetNumber = root:Packages:MultiPeakFit2:currentSetNumber Wave/Z yw = $yWName Wave/Z xw = $xWName String SaveDF = GetDataFolder(2) String gname = theGraph 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 // ********* EXIT *********** endif endif Variable panelAlreadyExists = 0 if (CmpStr(gname, "New Graph") == 0) if (WaveExists(xw)) Display yw vs xw else Display yw endif String newGName = "MultipeakFit_Set"+num2str(currentSetNumber+1) RenameWindow $S_name, $newGName gname = newGName elseif (WinType(gname+"#MultiPeak2Panel")) panelAlreadyExists = 1 endif if (initializeFrom == 1) // start fresh if (panelAlreadyExists) DoAlert 1, "You selected \"Start Fresh\" but there is a Multipeak Fit panel already active for that graph. Close it and continue?" if (V_flag == 1) // Yes was clicked KillWindow $(gname+"#MultiPeak2Panel") else return 0 // ********* EXIT *********** endif endif currentSetNumber += 1 setnumber = currentSetNumber 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 elseif (initializeFrom > 1) // "Previous Set for This Graph" or "Set N" String fName String graphSetNumberStr = GetUserData(gname, "", "MPF2_DataSetNumber" ) Variable previousSetnumber = str2num(graphSetNumberStr) if (initializeFrom == 2 || previousSetnumber == initializeFromSet) // "Previous Set for This Graph" if (panelAlreadyExists) DoWindow/F $gname return 0 // ********* EXIT *********** endif setnumber = previousSetnumber fName = "root:Packages:MultiPeakFit2:"+MPF2_FolderNameFromSetNumber(setnumber) SetDataFolder $fName else // "Set N" if (panelAlreadyExists) DoAlert 1, "You selected Set Number "+num2str(initializeFromSet)+" but there is a Multipeak Fit panel already active for that graph. Close it and continue?" if (V_flag == 1) // Yes was clicked KillWindow $(gname+"#MultiPeak2Panel") else return 0 // ********* EXIT *********** endif endif Variable copySetNumber = initializeFromSet String copyfName = "root:Packages:MultiPeakFit2:"+MPF2_FolderNameFromSetNumber(copySetNumber) currentSetNumber += 1 setnumber = currentSetNumber fName = "root:Packages:MultiPeakFit2:"+MPF2_FolderNameFromSetNumber(setnumber) DuplicateDataFolder $copyfName, $fName SetDataFolder $fName SVAR YWvName SVAR XWvName YWvName = GetWavesDataFolder(yw, 2) if (WaveExists(xw)) XWvName = GetWavesDataFolder(xw, 2) else XWvName = "" endif endif if (initializeFrom == 2) 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 // ********* EXIT *********** 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 // ********* EXIT *********** 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 // ********* EXIT *********** endif endif endif buildingFromOldData = 1 endif // Just in case we're starting from a previously-used graph, remove old result traces. // If they aren't there, /Z makes it OK to do anyway. MPF2_RemoveMPTracesFromGraph(gname) 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 /Z wpi = $(DFPath+":W_AutoPeakInfo") Variable nPeaks=0 if (WaveExists(wpi)) npeaks = DimSize(wpi, 0) endif SVAR SavedFunctionTypes = $(DFPath+":SavedFunctionTypes") 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", 6) 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 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 NVAR/Z MPF2ConstraintsShowing = $(DFPath+":MPF2ConstraintsShowing") if (!NVAR_Exists(MPF2ConstraintsShowing)) Variable/G $(DFPath+":MPF2ConstraintsShowing") NVAR MPF2ConstraintsShowing = $(DFPath+":MPF2ConstraintsShowing") MPF2ConstraintsShowing = 0 endif if (MPF2ConstraintsShowing) ListBox MPF2_PeakList win=$gname#MultiPeak2Panel#P1,widths={4,15,16,9,5,10,6,10} else ListBox MPF2_PeakList win=$gname#MultiPeak2Panel#P1,widths={4,15,16,9,0,0,0,0} endif NVAR displayPeaksFullWidth = $(DFpath+":displayPeaksFullWidth") if (waveExists(wpi)) MPF2_AddFitCurveToGraph(setNumber, wpi, yw, xw, 1) MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1, displayPeaksFullWidth) endif 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 NVAR MPF2ConstraintsShowing = $(DFPath+":MPF2ConstraintsShowing") Variable panelWidth = MPF2ConstraintsShowing ? MPF2_PanelWidth : MPF2_NarrowWidth 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 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,limits={0,inf,1},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,limits={0,inf,1},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,limits={0,inf,1},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 Function MPF2_DiscloseConstraints(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 MPF2ConstraintsShowing = $(DFPath+":MPF2ConstraintsShowing") MPF2ConstraintsShowing = 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 panelWidth = s.checked ? panelWidth+(MPF2_PanelWidth-MPF2_NarrowWidth)*(panelWidth/MPF2_NarrowWidth) : panelWidth-(MPF2_PanelWidth-MPF2_NarrowWidth)*(panelWidth/MPF2_PanelWidth) 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 Variable baseWidth = MPF2_PanelWidth if (!s.checked) baseWidth = MPF2_NarrowWidth ListBox MPF2_PeakList, win=$(panelName+"#P1"), widths={4,15,16,9,0,0,0,0} else ListBox MPF2_PeakList, win=$(panelName+"#P1"), widths={4,15,16,9,5,10,6,10} endif Button MPF2_HelpButton,win=$panelName, pos={baseWidth-60,1} //P0 MoveSubwindow /W=$(panelName+"#P0"), fnum=(0,86,baseWidth,149) GroupBox MPF2_LocatePeaksGroupBox, win=$(panelName+"#P0"),size={baseWidth-16,61} Button MPF2_AutoLocatePeaksButton, win=$(panelName+"#P0"),pos={(baseWidth-158)/2,21},size={158,20} CheckBox MPF2_NegativePeaksCheck, win=$(panelName+"#P0"),pos={(baseWidth-85)/2,44} //P1 CheckBox MPF2_DiscloseConstraints,win=$(panelName+"#P1"),pos={baseWidth-150,5} ListBox MPF2_PeakList, win=$(panelName+"#P1"),size={baseWidth-12,119} //P2 PopupMenu MPF2_SetAllPeakTypesMenu, win=$(panelName+"#P2"),pos={baseWidth-195,3} Variable leftSideControlOffset = floor(baseWidth/15) Button MPF2_DoFitButton, win=$(panelName+"#P2"),pos={leftSideControlOffset,35},size={50,20} Button MPF2_PeakResultsButton, win=$(panelName+"#P2"),pos={baseWidth-leftSideControlOffset-109 ,35} Button MPF2_RevertToGuessesButton, win=$(panelName+"#P2"),pos={leftSideControlOffset,65},size={109,20} PopupMenu MPF2_CheckPointMenu, win=$(panelName+"#P2"),pos={baseWidth-leftSideControlOffset-102,66} //P3 SetVariable MPF2_GlobalConstraints,win=$(panelName+"#P3"),size={baseWidth-20,20} TitleBox MPF2_ConstraintsExample,win=$(panelName+"#P3"),size={baseWidth-50,20} SetVariable MPF2_SetFitCurvePoints,win=$(panelName+"#P3"),pos={(baseWidth-143)/2,125} CheckBox MPF2_DisplayPeakXWidthCheck,win=$(panelName+"#P3"),pos={(baseWidth-188)/2,148} MoveSubwindow/W=$panelName fnum=(left, top, right, bottom) end 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+150 : panelHeight-150 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,-173} 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 MPF2MaskWaveName = $(DFPath+":MPF2MaskWaveName") Wave/Z w = $wavepath if (WaveExists(w)) MPF2MaskWaveName = wavepath else MPF2MaskWaveName = "" 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+":MPF2WeightWaveName") 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 B cursor is not on the graph." checkAxis = 1 endif if (checkAxis == 0) RangeBegin= pcsr(A, gname) RangeEnd= pcsr(B, gname) endif else checkAxis = 1 endif if (checkAxis) GetAxis /Q bottom if(!WaveExists(XData)) RangeBegin= max(x2pnt(YData,V_min), 0) RangeEnd= min(x2pnt(YData,V_max), numpnts(YData)-1) 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 if (DimSize(wpi, 0)==0) // If columns dimension disappears problems will occur if peaks are added later Redimension /N=(0,5) wpi endif 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 NVAR displayPeaksFullWidth = $(DFpath+":displayPeaksFullWidth") MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1, displayPeaksFullWidth) 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/S ListExistingSets() DFREF savedDF = GetDataFolderDFR() SetDataFolder root:Packages:MultiPeakFit2: String theList = "" Variable numFolders = CountObjects(":", 4) Variable i for (i = 0; i < numFolders; i += 1) String oneFolder = GetIndexedObjName(":", 4, i) Variable nameLen = strlen(oneFolder) if (nameLen > 14) if (CmpStr(oneFolder[0,13], "MPF_SetFolder_") == 0) theList += oneFolder[14,nameLen-1]+";" endif endif endfor SetDataFolder savedDF return theList end Function/S InitializeMPF2FromMenuString() String theList = "Start Fresh;" ControlInfo/W=MultiPeak2StarterPanel MPF2_ChooseGraph if (CmpStr(S_value, "New Graph") != 0) String graphSetNumberStr = GetUserData(S_value, "", "MPF2_DataSetNumber" ) if (strlen(graphSetNumberStr) > 0) theList += "Previous Set for This Graph;" else theList += "\\M1(Previous Set for This Graph;" endif else theList += "\\M1(Previous Set for This Graph;" endif String SetList = ListExistingSets() Variable i Variable nSets = ItemsInList(SetList) for (i = 0; i < nSets; i += 1) theList += "Set Number "+StringFromList(i, SetList)+";" endfor return theList end Function MPF2_GetExternalPanelPosition(gname) String gname String recMacro = winRecreation(gname, 0) Variable asPos = strsearch(recMacro, "as \"Multi-peak Fit Set", 0) Variable beginPos = strsearch(recMacro, "/EXT=", asPos, 1) // search backwards Variable extPosCode sscanf recMacro[beginPos, beginPos+10], "/EXT=%d", extPosCode return extPosCode end Function MPF2_RemoveMPTracesFromGraph(gname) String gname String setNumberStr = GetUserData(gname, "", "MPF2_DataSetNumber") if (strlen(setNumberStr) == 0) return 0 endif Variable setNumber = str2num(setNumberStr) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR YWvName = $(DFpath+":YWvName") Wave yw = $YWvName RemoveFromGraph/W=$gname/Z $("fit_"+NameOfWave(yw)) RemoveFromGraph/W=$gname/Z $("res_"+NameOfWave(yw)) RemoveFromGraph/W=$gname/Z $("Bkg_"+NameOfWave(yw)) DoUpdate String tlist = TraceNameList(gname, ";", 1) Variable ntraces = ItemsInList(tlist) Variable i for (i = 0; i < ntraces; i += 1) String oneTrace = StringFromList(i, tlist) Wave w = TraceNameToWaveRef(gname, oneTrace) String wdf = RemoveEnding(GetWavesDataFolder(w, 1), ":") if (CmpStr(wdf, DFPath) == 0) RemoveFromGraph/W=$gname $oneTrace endif endfor end Function MPF2_CheckpointMenuProc(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 panelName = gname+"#MultiPeak2Panel" String listPanelName = panelName+"#P1" Variable i strswitch (s.popStr) case "Save Checkpoint": MPF2_RefreshHoldStrings(panelName) MPF2_SaveFunctionTypes(panelName) Variable/G $(DFPath+":MPF2_UserCursors") NVAR useCursors = $(DFPath+":MPF2_UserCursors") ControlInfo/W=$(panelName) MPF2_UserCursorsCheckbox useCursors = V_value if (DataFolderExists(DFpath+"CP")) KillDataFolder $(DFpath+"CP") endif DuplicateDataFolder $DFpath, $(DFpath+"CP") break; case "Restore Checkpoint": if (DataFolderExists(DFpath+"CP")) DoAlert 1, "The present panel and its information will be killed and restored with the saved data. Proceed?" if (V_flag == 1) Variable panelPosition = MPF2_GetExternalPanelPosition(gname) KillWindow $panelName MPF2_RemoveMPTracesFromGraph(gname) String graphname = gname // save the graph name before the global variable stored in the datafolder is killed. KillDataFolder $DFpath DuplicateDataFolder $(DFpath+"CP"), $DFpath SVAR YWvName = $(DFpath+":YWvName") SVAR XWvName = $(DFpath+":XWvName") // "2" is to use previous set for this graph MPF2_StartNewMPFit(panelPosition, graphname, YWvName, XWvName, 2, 0) endif else DoAlert 0, "There is no checkpoint data available." endif break; endswitch 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 panelName = gname+"#MultiPeak2Panel" String listPanelName = panelName+"#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)) String prevFunc = WMHL_GetExtraColumnData(listPanelName, "MPF2_PeakList", 0, i) prevFunc = RemoveEnding(prevFunc, MENU_ARROW_STRING) WMHL_ExtraColumnData(listPanelName, "MPF2_PeakList", 0, i, s.popStr+MENU_ARROW_STRING, 0) MPF2_CoefWaveForListRow(setNumber, i, s.popStr) String theItem="" if (WMHL_RowIsOpen(listPanelName, "MPF2_PeakList", i)) 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 endif //NH: If the type has changed then the hold and constraint strings should be reset. if (CmpStr(prevFunc, s.popStr)) String currPeakName = WMHL_GetItemForRowNumber(listPanelName, "MPF2_PeakList", i) Variable peakNumber sscanf currPeakName, "Peak %d", peakNumber Wave/T HoldStrings = $(DFpath+":HoldStrings") HoldStrings[peakNumber+1] = "" resetPeakConstraints(setNumber, peakNumber+1) endif //NH: needed to put this after the close so that clearing out Hold and Constraint data (if necessary) sticks if (strlen(theItem)) // if this was set, then the container was open earlier 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") NVAR displayPeaksFullWidth = $(DFpath+":displayPeaksFullWidth") MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1, displayPeaksFullWidth) 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, constraintswave, killIndexWave]) Wave wpi String &listOfTypes Wave/Z/T holdwave Wave/Z/T constraintswave Variable killIndexWave if (ParamIsDefault(killIndexWave)) killIndexWave = 1 endif Variable i String indexWaveName = MPF2_MakeIndexAutoPeakWave(wpi) Wave MPF2_indexwave = $indexWaveName Duplicate/O wpi, MPF2_TempWave if (DimSize(wpi,0)) // without this the following line crashes if there are no peaks wpi = MPF2_TempWave[MPF2_indexwave[p]][q] endif //DoUpdate Variable tempIndex if (!ParamIsDefault(holdwave)) if (numpnts(holdwave) > 1) Duplicate/O/T holdwave, MPF2_TempHoldWave holdwave = "" for (i = 1; i <= numpnts(MPF2_indexwave); i += 1) tempIndex = MPF2_indexwave[i-1]+1 if (tempIndex < numpnts(holdwave)) holdwave[i] = MPF2_TempHoldWave[tempIndex] endif endfor KillWaves MPF2_TempHoldWave endif endif if (!ParamIsDefault(constraintswave)) if (numpnts(constraintswave) > 1) Duplicate/O/T constraintswave, MPF2_TempConstraintsWave constraintswave = "" for (i = 1; i <= numpnts(MPF2_indexwave); i += 1) tempIndex = MPF2_indexwave[i-1]+1 if (tempIndex < numpnts(constraintswave)) constraintswave[i] = MPF2_TempConstraintsWave[tempIndex] endif endfor KillWaves MPF2_TempConstraintsWave endif endif //DoUpdate if (!ParamIsDefault(listOfTypes)) 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 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, theRow if (!WaveExists(autoPeakInfo) || (DimSize(autoPeakInfo, 0) == 0)) // If there's any peaks in the listbox get rid of them if (!initializeBaseline) i = 0 do 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) endif return 0 endif 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 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", 6) 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 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 Variable updateRejected = NumVarOrDefault("root:Packages:MultiPeakFit2:updateRejected", 0) if (!updateRejected) NVAR MPF2ConstraintsShowing = $(DFPath+":MPF2ConstraintsShowing") if (MPF2ConstraintsShowing) ListBox MPF2_PeakList win=$gname#MultiPeak2Panel#P1,widths={4,15,16,9,5,10,6,10} else ListBox MPF2_PeakList win=$gname#MultiPeak2Panel#P1,widths={4,15,16,9,0,0,0,0} endif else ListBox MPF2_PeakList win=$gname#MultiPeak2Panel#P1,widths={4,15,16,10} endif 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) // Get the fit function ParamNameList = BLinfoFunc(BLFuncInfo_ParamNames) // fit function parameter names nparams = ItemsInList(ParamNameList) // number of params in the fit function 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) // Get the fit function ParamNameList = PKinfoFunc(PeakFuncInfo_ParamNames) // fit function parameter names nparams = ItemsInList(ParamNameList) // number of params in the fit function 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 = $(DFPath+":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) ////////// Constraint information Variable waveConstraintIndx = DoingBaseline ? 0 : PeakNumber+1 WMHL_ExtraColumnData(HostWindow, ListControlName, 2, theRow+i+1, "Min:", 0) WMHL_ExtraColumnData(HostWindow, ListControlName, 3, theRow+i+1, getPeakConstraints(setNumber, waveConstraintIndx, i, "Min"), 1) WMHL_ExtraColumnData(HostWindow, ListControlName, 4, theRow+i+1, "Max:", 0) WMHL_ExtraColumnData(HostWindow, ListControlName, 5, theRow+i+1, getPeakConstraints(setNumber, waveConstraintIndx, i, "Max"), 1) 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)) SetDataFolder saveDF 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 ////////// Constraints /////////// Variable peakWaveIndx if (DoingBaseline) peakWaveIndx = 0 else peakWaveIndx = peakNumber+1 endif String aVal aVal = WMHL_GetExtraColumnData(HostWindow, ListControlName, 3, i) setPeakConstraints(setNumber, peakWaveIndx, i-FirstChildRow, minVal=str2num(aVal)) aVal = WMHL_GetExtraColumnData(HostWindow, ListControlName, 5, i) setPeakConstraints(setNumber, peakWaveIndx, i-FirstChildRow, maxVal=str2num(aVal)) 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 SetDataFolder saveDF 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 // NH: removing peaks by wave name rather than by wpi size. Cases exist where size of wpi != number of traces // Also possible to have cases where the peak name's number portion is greater than the number of current peaks, // which also will not get deleted with the original logic String allTraces = TraceNameList(gname, ";", 1) nPeaks = ItemsInList(allTraces) for (i=0; i= npeaks) return -1 endif DeletePoints peakNumber, 1, wpi if (DimSize(wpi, 0)==0) // If columns dimension disappears problems will occur if peaks are added later Redimension /N=(0,5) wpi endif Wave/T HoldStrings DeletePoints peakNumber+1, 1, HoldStrings removePeakConstraints(setNumber, peakNumber+1) //NH 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, doFullwidth) Variable setNumber Wave/Z wfi Variable doTags Variable doGrayLines Variable doFullwidth // if non-zero, make the peaks the full width of the graph 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 if (XStart > XEnd) Variable temp = XStart XStart = XEnd XEnd = temp 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=(doFullWidth?1000:200)/O/D MPF2_TempXWave for (i = 0; i < nPeaks; i += 1) if ( (wfi[i][1] == 0) || (wfi[i][2] == 0) ) // width or height estimate is zero- bad info; skip it. // fixes index-out-of-range error that results from trying to set up the peak trace with bad data. But how did the bad data get into the wfi wave? continue endif String PeakWaveName = "Peak "+num2istr(i) Make/D/N=(doFullWidth?1000: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 center = coefs[0] if (doFullWidth) SetScale/I x XStart, XEnd, "", w else Variable width = wfi[i][1] SetScale/I x max(center-4*width, XStart), min(center+4*width, XEnd), "", w endif 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" Variable attachPoint = center < XStart ? XStart : (center > XEnd ? XEnd : center) if (w(attachPoint) < 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 DoUpdate 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 DoUpdate 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 Variable itemOffset, paramNum 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'") 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") NVAR displayPeaksFullWidth = $(DFpath+":displayPeaksFullWidth") MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1, displayPeaksFullWidth) 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)) 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 elseif (s.col==4 || s.col==5) PopupContextualMenu "Copy This Value to All Peaks of This Type;" if (V_flag > 0) MPF2_RefreshConstraintStrings(setNumber) paramNum = s.row-parentRow-1 Variable minVal = str2num(getPeakConstraints(setNumber, peakNumber+1, paramNum, "Min")) 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 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 endif setPeakConstraints(setNumber, i+1, paramNum, minVal=minVal) if (rowIsOpen) WMHL_OpenAContainer(s.win, s.ctrlName, rowItem) endif endif i += 1 while(1) endif elseif (s.col==6 || s.col==7) PopupContextualMenu "Copy This Value to All Peaks of This Type;" if (V_flag > 0) MPF2_RefreshConstraintStrings(setNumber) paramNum = s.row-parentRow-1 Variable maxVal = str2num(getPeakConstraints(setNumber, peakNumber+1, paramNum, "Max")) 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 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 endif setPeakConstraints(setNumber, i+1, paramNum, maxVal=maxVal) if (rowIsOpen) WMHL_OpenAContainer(s.win, s.ctrlName, rowItem) endif endif i += 1 while(1) 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] = "" resetPeakConstraints(setNumber, 0) else HoldStrings[peakNumber+1] = "" resetPeakConstraints(setNumber, peakNumber+1) endif if (rowIsOpen) WMHL_OpenAContainer(s.win, s.ctrlName, rowItem) endif endif 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 KillWaves SavedCoefWave endif break; case 2: // mouse up if (s.eventMod & 16) // contextual menu click retvalue = 1 endif break; case 6: // begin edit break; case 7: // finish edit // first, update the edited coefficient wave parentitem = WMHL_GetParentItemForRow(s.win, s.ctrlName, s.row) 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 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 endif // now re-evaluate the graph curves NVAR displayPeaksFullWidth = $(DFpath+":displayPeaksFullWidth") MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1, displayPeaksFullWidth) MPF2_AddFitCurveToGraph(setNumber, wpi, yw, xw, 1, overridePoints=MPF2_getFitCurvePoints(gname+"#MultiPeak2Panel")) 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/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 //// do a check on the Use Graph Cursors checkbox to determine xPointRangeBegin&End String multiPeakPanelName = gname+"#MultiPeak2Panel" ControlInfo /W=$multiPeakPanelName MPF2_UserCursorsCheckbox if (V_value) // use cursors NVAR XPointRangeBegin NVAR XPointRangeEnd MPStruct.XPointRangeBegin = XPointRangeBegin MPStruct.XPointRangeEnd = XPointRangeEnd else // don't use cursors - use visible graph range size Variable RangeBegin, RangeEnd, RangeReversed MPF2_SetDataPointRange(gname, MPStruct.yWave, MPStruct.xWave, RangeBegin, RangeEnd, RangeReversed) MPStruct.XPointRangeBegin = RangeBegin MPStruct.XPointRangeEnd = RangeEnd endif NVAR MPF2_FitCurvePoints 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" //// Check inter-peak constraints //// NVAR/Z MPF2ConstraintsShowing = $(DFPath+":MPF2ConstraintsShowing") if (!NVAR_Exists(MPF2ConstraintsShowing)) Variable/G $(DFPath+":MPF2ConstraintsShowing") NVAR MPF2ConstraintsShowing = $(DFPath+":MPF2ConstraintsShowing") MPF2ConstraintsShowing = 0 endif if (MPF2ConstraintsShowing) SVAR /Z interPeakString = $(DFPath+":interPeakConstraints") if (SVAR_exists(interPeakString)) if (!MPF2_ValidateConstraint(setNumber, s.win, interPeakString)) return -1 endif endif Wave /T MPStruct.constraints = getGlobalConstraintsWave(setNumber) endif 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) 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 NVAR displayPeaksFullWidth = $(DFpath+":displayPeaksFullWidth") MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1, displayPeaksFullWidth) 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) NVAR displayPeaksFullWidth = $(DFpath+":displayPeaksFullWidth") MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1, displayPeaksFullWidth) 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=327 static constant resultsListHeightDif=152 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." SetDataFolder saveDF 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 Make/FREE/N=(npeaks)/O ndp 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) ndp[i] = ItemsInList(ParamNames) nDerivedParamsMax = max(nDerivedParamsMax, ndp[i]) 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 // a wave of waves, each of which is nParams for the correspoding peak. Values are 0|1 indicating if the coefficient is constrained Wave /Wave contraintsSetWave = MPF2_isConstrainedWave(setNumber) 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=(ndp[i],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 Wave coefConstrained = contraintsSetWave[i+1] // constraintsSetWave includes baseline info at constraintsSetWave[0] 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]) if (coefConstrained[j]) MPF2_ResultsListWave[i][listColumn] += " (constrained)" endif 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-87},size={260,20},proc=MPF2_ResultsDoNotebookButtnProc,title="Report in Notebook" Button MPF2_TabDelimitedResultsButton,pos={12,bottom-top-57},size={260,20},proc=MPF2_TabDelimitedResultsBtnProc,title="Standard Parameters, Tab-Delimited" Button MPF2Results_GraphButton,pos={314,bottom-top-87},size={100,20},proc=MPF2Results_GraphButtonProc,title="Graph..." Checkbox MPFTResults_BackgroundCheck, pos={314, bottom-top-54}, fsize=12, title="Include Background Info", proc=MPF2_reportBackground, value=reportBackground Button MPF2_BaselineSubtracted,pos={12,299},size={200,20},title="Baseline-Subtracted Data",proc=MPF2_BLSubtractedDataButtonProc Variable numPathElements = ItemsInList(YWvName, ":") String newWaveName = StringFromList(numPathElements-1, YWvName, ":") if (CmpStr(newWaveName[0], "'") == 0) newWaveName = newWaveName[1,strlen(newWaveName)-2] endif newWaveName = newWaveName[0, min(strlen(newWaveName), 25)]+"_BlSub" SetVariable MPF2_BLSubtractedWaveName,pos={220,300},size={375,19},bodyWidth=160,title="Wave Name for BL Subtracted Data:" SetVariable MPF2_BLSubtractedWaveName,fSize=12,value= _STR:newWaveName 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-87} Button MPF2_TabDelimitedResultsButton,win=$s.winName,pos={12, height-57} Button MPF2Results_GraphButton,win=$s.winName,pos={314,height-87} Checkbox MPFTResults_BackgroundCheck, pos={314, height-54} Button MPF2_BaselineSubtracted, pos={12, height-28} Variable SVWidth = max(min(width-460, 300), 100) // Variable SVWidth = width-460 SetVariable MPF2_BLSubtractedWaveName, bodywidth=SVWidth ControlUpdate MPF2_BLSubtractedWaveName SetVariable MPF2_BLSubtractedWaveName, pos={220, 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-87},size={0,0},anchor=RT TitleBox MPF2_Results_TotalArea,win=$s.winName,pos={width-10,height-72},size={0,0},anchor=RT TitleBox MPF2_Results_TotalAreaStdev,win=$s.winName,pos={width-10,height-57},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") SVAR WeightWaveName = $(DFpath+":MPF2WeightWaveName") SVAR MaskWaveName = $(DFpath+":MPF2MaskWaveName") Wave yw = $YWvName Wave/Z xw = $XWvName Wave/Z wt = $WeightWaveName Wave/Z mask = $MaskWaveName Wave/Z constraints 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,504}, spacing={0,0,0}, tabs={}, rulerDefaults={"Geneva",10,1,(0,0,0)} Notebook $nb newRuler=PeakParamsRuler, justification=0, margins={0,0,504}, spacing={0,0,0}, tabs={36,126+3*8192,205+1*8192,234+3*8192,305,394}, 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 if (WaveExists(wt)) Notebook $nb ruler=PeakParamsRuler, text="Weight wave: "+GetWavesDataFolder(wt, 2)+"\r" endif if (WaveExists(mask)) Notebook $nb ruler=PeakParamsRuler, text="Mask wave: "+GetWavesDataFolder(mask, 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]) String blConstraint = getPeakConstraints(setNumber, 0, i, "Min") if (strlen(blConstraint)) Notebook $nb text="\tMin: "+blConstraint endif blConstraint = getPeakConstraints(setNumber, 0, i, "Max") if (strlen(blConstraint)) Notebook $nb text="\tMax: "+blConstraint endif Notebook $nb text="\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]) String paramConstraint = getPeakConstraints(setNumber, i+1, j, "Min") if (strlen(paramConstraint)) Notebook $nb text="\tMin: "+paramConstraint endif paramConstraint = getPeakConstraints(setNumber, i+1, j, "Max") if (strlen(paramConstraint)) Notebook $nb text="\tMax: "+paramConstraint endif Notebook $nb text="\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 ///// Print global constraints ///// SVAR /Z interPeakString = $(DFPath+":interPeakConstraints") if (SVAR_exists(interPeakString) && strlen(interPeakString)) String spelledOutIPString = interPeakString Notebook $nb fstyle=1,text="Inter Peak Constraints:\r\r" Notebook $nb fstyle=0,ruler=PeakParamsRuler//, text=interPeakString String peakConstraints = MPF2_interPeakToStringList(setNumber, interPeakString) Variable nVals = ItemsInList(peakConstraints) for (i=0; i 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 MPF2_BLSubtractedDataButtonProc(s) : ButtonControl STRUCT WMButtonAction &s if (s.eventCode != 2) // mouse-up in the control return 0 endif Variable setNumber = GetSetNumberFromWinName(s.win) ControlInfo/W=$(s.win) MPF2_BLSubtractedWaveName String newWaveName = S_value MPF2_BLSubtractedData(setNumber, newWaveName) return 0 end Function MPF2_BLSubtractedData(setNumber, newWaveName) Variable setNumber String newWaveName String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String saveDF = GetDataFolder(1) SetDataFolder DFpath SVAR gname = GraphName SVAR YWvName SVAR XWvName Wave yw = $YWvName Wave/Z xw=$XWvName NVAR XPointRangeBegin NVAR XPointRangeEnd Variable xpstart = min(XPointRangeBegin, XPointRangeEnd) Variable xpend = max(XPointRangeBegin, XPointRangeEnd) SetDataFolder saveDF Duplicate/O/R=[xpstart, xpend] yw, $newWaveName/WAVE=newW SetDataFolder DFpath 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) string BL_FuncName = blinfo(BLFuncInfo_BaselineFName) FUNCREF MPF2_BaselineFunctionTemplate blFunc = $BL_FuncName STRUCT MPF2_BLFitStruct BLStruct Wave BLStruct.cWave = 'Baseline Coefs' if (WaveExists(xw)) BLStruct.xStart = xw[XPointRangeBegin] BLStruct.xEnd = xw[XPointRangeEnd] else BLStruct.xStart = pnt2x(yw, XPointRangeBegin) BLStruct.xEnd = pnt2x(yw, XPointRangeEnd) endif Variable i, endp endp = xpend - xpstart + 1 for (i = 0; i < endp; i += 1) Variable point = i + xpstart if (WaveExists(xw)) BLStruct.x = xw[point] else BLStruct.x = pnt2x(yw, point) endif newW[i] -= blFunc(BLStruct) endfor else DoAlert 0,"No baseline function was used in fitting this data set" endif 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) SVAR SavedFunctionTypes = $(DFPath+":SavedFunctionTypes") if (CmpStr(StringFromList(0,SavedFunctionTypes), "None") != 0) Wave/Z bkgwave = $("bkg_"+NameOfWave(YData)) if (WaveExists(bkgwave)) AppendToGraph/W=$GraphName bkgwave endif 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 temp 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 Variable xb, xe if (WaveExists(xw)) xb = xw[XPointRangeBegin] xe = xw[XPointRangeEnd] if (xb > xe) temp = xb xb = xe xe = temp endif xleft = max(xleft, xb) xright = min(xright, xe) else xb = pnt2x(yw, XPointRangeBegin) xe = pnt2x(yw, XPointRangeEnd) if (xb > xe) temp = xb xb = xe xe = temp endif xleft = max(xleft, xb) xright = min(xright, xe) endif if (xleft > xright) temp = xleft xleft = xright xright = temp 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 if (DimSize(Editwpi, 0)==0) // If columns dimension disappears problems will occur if peaks are added later Redimension /N=(0,4) Editwpi endif 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 Variable peaksWereChanged = 0 // NH added this to go through the re-order function even if peaks were moved but not added or removed // 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 peaksWereChanged = 1 endif endfor endif Wave/T HoldStrings MPF2_RefreshHoldStrings(gname+"#MultiPeak2Panel") // Update the hold strings MPF2_RefreshConstraintStrings(setNumber) // 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 DeletePoints peakNumber+1, 1, HoldStrings //HoldStrings is peakNumber+1 because baseline is HoldStrings[0] if (DimSize(wpi, 0)==0) // If columns dimension disappears problems will occur if peaks are added later Redimension /N=(0,5) wpi endif if (DimSize(wpi, 0)==0) // If columns dimension disappears problems will occur if peaks are changed later Redimension /N=(0,2) changedPeaks endif //keep constraints consistent removePeakConstraints(setNumber, peakNumber+1) endfor endif // finally, add new peaks at the end of the wave. Variable numExistingPeaks = DimSize(wpi, 0) InsertPoints numExistingPeaks, numNewPeaks, wpi, changedPeaks Variable numCurrentPeaks = DimSize(HoldStrings,0) Redimension /N=(numCurrentPeaks+numNewPeaks) HoldStrings 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 insertPeakConstraints(setNumber, numCurrentPeaks+i+1) endfor Variable newNPeaks = DimSize(wpi, 0)-numExistingPeaks if ( (newNPeaks > 0) || (numRemovedPeaks > 0) || peaksWereChanged) // Did peaks get added or removed? Wave /T constraintsTextWave if (newNPeaks > 0 && i < DimSize(EditPeakList, 0)) sscanf EditPeakList[i], "EditPeak %d", peakNumber endif String listoftypes = SavedFunctionTypes String indexWaveName = MPF2_SortAutoPeakWave(wpi, listOfTypes=listoftypes, holdwave=HoldStrings, constraintswave=constraintsTextWave, 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 MPF2_RemoveAllPeaksFromGraph(gname) // fixes issue with some peaks lingering after delete MPF2_PutAutoPeakResultIntoList(setNumber, wpi, 0, listOfPeakTypes=SavedFunctionTypes) NVAR negativePeaks = negativePeaks NVAR displayPeaksFullWidth = $(DFpath+":displayPeaksFullWidth") MPF2_AddPeaksToGraph(setNumber, wpi, 1, 1, displayPeaksFullWidth) 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 if (w == 0) // happens if you click in the graph without dragging. return 0 // tells calling code to ignore this. endif 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 if (nPeaks > 0) String previousPeakName = EditPeakList[nPeaks-1] if (CmpStr(previousPeakName[0,7], "NewPeak ") == 0) sscanf previousPeakName, "NewPeak %d", lastPeakNumber endif 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 if (DimSize(Editwpi, 0)==0) // If columns dimension disappears problems will occur if peaks are added later Redimension /N=(0,4) Editwpi endif 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 if (DimSize(Editwpi, 0)==0) // If columns dimension disappears problems will occur if peaks are added later Redimension /N=(0,4) Editwpi endif 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 ////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////// Constraints Functions /////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////// // When constraints are included in the peak fit the coefficients indicies are globally numbered. So if there is a baseline // function is a constant then constraints are expressed in terms of K0. If there are 2 peaks and one is gaussian (3 coefs) // and one is Voigt (4 coefs) then they will be expressed in terms of K1-K3 and K4-K7. To keep from having to renumber // every time a peak is added or removed or a peak function changes indices will be saved in a local sense. So the situation // described above will be numbered locally as baseline: K0, Peak0: K0-K2, Peak1: K0-K3. To get the full text wave with // global indices as required by FuncFit use getGlobalConstraintsString(setNumber). // // The local constraint text wave is not FuncFit ready even if converted to global strings. It contains key-value pairs. Keys and // values are separated by ":", and key-value pairs are separated by ";". Current Keys are "MIN[local coef name]:[number]", // "MAX[local coef name]:[number]" and "GENERAL:[locally indexed but otherwise FitFunc ready format constraint string] Function resetPeakConstraints(setNumber, peakNumberP1) Variable setNumber, peakNumberP1 String DFpath = MPF2_FolderPathFromSetNumber(setNumber) //// get the number of peaks Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") Variable npeaks = waveExists(wpi) ? DimSize(wpi, 0) : 0 Wave /T /Z constraintsTextWave = $(DFPath+":constraintsTextWave") if (!WaveExists(constraintsTextWave)) Make /T /N=(npeaks+1) $(DFPath+":constraintsTextWave") Wave /T constraintsTextWave = $(DFPath+":constraintsTextWave") endif if (DimSize(constraintsTextWave, 0) < peakNumberP1+1) Redimension /N=(peakNumberP1+1) constraintsTextWave endif constraintsTextWave[peakNumberP1] = "" End // peakNumberP1 means peak number plus 1. Plus 1 because of the baseline function // localCoefIndex is locally indexed. Not used for general // for no min set minVal=NaN; same for maxVal Function setPeakConstraints(setNumber, peakNumberP1, localCoefIndex, [minVal, maxVal, general]) Variable setNumber, peakNumberP1, localCoefIndex, minVal, maxVal String general String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Variable i //// get the number of peaks Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") Variable npeaks = waveExists(wpi) ? DimSize(wpi, 0) : 0 //// if the peakNumberP1 is valid, make sure there is room for it in the constraintsWave - also make sure the constraintsTextWave exists! if (peakNumberP1 > npeaks) return -1 endif Wave /T /Z constraintsTextWave = $(DFPath+":constraintsTextWave") if (!WaveExists(constraintsTextWave)) Make /T /N=(npeaks+1) $(DFPath+":constraintsTextWave") Wave /T constraintsTextWave = $(DFPath+":constraintsTextWave") endif if (DimSize(constraintsTextWave, 0) < peakNumberP1+1) Redimension /N=(peakNumberP1+1) constraintsTextWave endif //// get the current peak constraint string and work on it. String currConstraints = constraintsTextWave[peakNumberP1] //// for each type see if it already exists for the given constraint. If not, add it. If so, replace it if (!ParamIsDefault(minVal)) if (numType(minVal)==2) currConstraints = ReplaceStringByKey("MINK"+num2str(localCoefIndex), currConstraints, "") else currConstraints = ReplaceStringByKey("MINK"+num2str(localCoefIndex), currConstraints, num2Str(minVal)) endif endif if (!ParamIsDefault(maxVal)) if (numType(maxVal)==2) currConstraints = ReplaceStringByKey("MAXK"+num2str(localCoefIndex), currConstraints, "") else currConstraints = ReplaceStringByKey("MAXK"+num2str(localCoefIndex), currConstraints, num2Str(maxVal)) endif endif if (!ParamIsDefault(general)) currConstraints = ReplaceStringByKey("GENERAL", currConstraints, general) endif //// replace the current constraint with the new one constraintsTextWave[peakNumberP1] = currConstraints End // localCoefIndex is locally indexed. Not applicable for type="General" // type is a string - either "Min", "Max" or "General" Function /S getPeakConstraints(setNumber, peakNumberP1, localCoefIndex, type) Variable setNumber, peakNumberP1, localCoefIndex String type String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String ret="", keyStr="" Wave /T /Z constraintsTextWave = $(DFPath+":constraintsTextWave") if (WaveExists(constraintsTextWave) && DimSize(constraintsTextWave, 0)>peakNumberP1) strswitch (type) case "Min": keyStr = "MINK"+num2str(localCoefIndex) break case "Max": keyStr = "MAXK"+num2str(localCoefIndex) break case "General": keyStr = "GENERAL" break default: break endswitch ret = StringByKey(keyStr, constraintsTextWave[peakNumberP1]) endif return ret End Function removePeakConstraints(setNumber, peakNumberP1) Variable setNumber, peakNumberP1 String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Variable i Wave /T /Z constraintsTextWave = $(DFPath+":constraintsTextWave") if (waveExists(constraintsTextWave)) DeletePoints peakNumberP1, 1, constraintsTextWave endif End Function insertPeakConstraints(setNumber, newPeakNumberP1) Variable setNumber, newPeakNumberP1 String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Variable i Wave /T /Z constraintsTextWave = $(DFPath+":constraintsTextWave") if (!waveExists(constraintsTextWave)) Make /T /N=(newPeakNumberP1) $(DFPath+":constraintsTextWave") Wave /T constraintsTextWave = $(DFPath+":constraintsTextWave") endif InsertPoints newPeakNumberP1, 1, constraintsTextWave End /// Constraint data saved when Hierarchical container is closed. Thus need to check the open containers when getting global constraints Function /Wave getGlobalConstraintsWave(setNumber) Variable setNumber String utilStr, constraintsList="" String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR gname = $(DFpath+":GraphName") String listPanelName = gname+"#MultiPeak2Panel#P1" Variable i, j, aVal ///// Update the constraint strings to reflect peaks open on the multi-peak panel MPF2_RefreshConstraintStrings(setNumber) Wave nParamsSum = MPF2_GetNParamsForFuncs(setNumber) Variable nPeaks = DimSize(nParamsSum,0) // includes baseline Variable BaselineRow = WMHL_GetRowNumberForItem(listPanelName, "MPF2_PeakList", "Baseline") String baselineStr = WMHL_GetExtraColumnData(listPanelName, "MPF2_PeakList", 0, BaselineRow) Variable doBaseLine = CmpStr(baselineStr, "None"+MENU_ARROW_STRING) != 0 if (doBaseLine) //// Get baseline coef min and maxs for each coefficient //// for (i=0; i0) constraintsList+="K"+num2str(i)+">"+utilStr+";" endif utilStr = getPeakConstraints(setNumber, 0, i, "Max") if (strLen(utilStr)>0) constraintsList+="K"+num2str(i)+"<"+utilStr+";" endif endfor endif Variable iPeak for (iPeak=1; iPeak < nPeaks; iPeak+=1) Variable nCoefs = nParamsSum[iPeak]-nParamsSum[iPeak-1] for (i=0; i0) constraintsList+="K"+num2str(i+nParamsSum[iPeak-1])+">"+utilStr+";" endif utilStr = getPeakConstraints(setNumber, iPeak, i, "Max") if (strLen(utilStr)>0) constraintsList+="K"+num2str(i+nParamsSum[iPeak-1])+"<"+utilStr+";" endif endfor endfor ///// Get the Inter-Peak constraints ///// SVAR /Z interPeakString = $(DFPath+":interPeakConstraints") if (SVAR_exists(interPeakString)) constraintsList += MPF2_AllPeaksToFitFuncStr(setNumber, interPeakString) endif Variable nConstraints = ItemsInList(constraintsList) Make /N=(nConstraints)/Free/T constraintsWave for (i=0; i k) currPeak = j if (j==0) currCoef = k else currCoef = k-(nConstraintsWave[j-1]) endif Wave currWave = ret[currPeak] currWave[currCoef] = 1 break endif endfor else break endif while (1) endfor return ret End //// Modeled after MPF2_RefreshHoldStrings() //// Only covers min and max. Static Function MPF2_RefreshConstraintStrings(setNumber) Variable setNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR gname = $(DFpath+":GraphName") String listPanel = gname+"#MultiPeak2Panel#P1" Variable i, numitems, rownumber String children, minStr, maxStr rownumber = WMHL_GetRowNumberForItem(listPanel, "MPF2_PeakList", "Baseline") if (WMHL_RowIsOpen(listPanel, "MPF2_PeakList", rownumber)) children = WMHL_ListChildRows(listPanel, "MPF2_PeakList", rownumber) numItems = ItemsInList(children) for (i = 0; i < numitems; i += 1) rownumber = str2num(StringFromList(i, children)) minStr = WMHL_GetExtraColumnData(listPanel, "MPF2_PeakList", 3, rownumber) maxStr = WMHL_GetExtraColumnData(listPanel, "MPF2_PeakList", 5, rownumber) setPeakConstraints(setNumber, 0, i, minVal=str2num(minStr), maxVal=str2num(maxStr)) endfor endif Variable iPeak=0 do String peakItem = "Peak "+num2str(iPeak) rownumber = WMHL_GetRowNumberForItem(listPanel, "MPF2_PeakList", peakItem) if (rownumber < 0) break; endif if (WMHL_RowIsOpen(listPanel, "MPF2_PeakList", rownumber)) children = WMHL_ListChildRows(listPanel, "MPF2_PeakList", rownumber) numItems = ItemsInList(children) for (i = 0; i < numitems; i += 1) rownumber = str2num(StringFromList(i, children)) minStr = WMHL_GetExtraColumnData(listPanel, "MPF2_PeakList", 3, rownumber) maxStr = WMHL_GetExtraColumnData(listPanel, "MPF2_PeakList", 5, rownumber) setPeakConstraints(setNumber, iPeak+1, i, minVal=str2num(minStr), maxVal=str2num(maxStr)) endfor endif iPeak += 1 while(1) End // Global constraint syntax is simple: P#K#, where the number following P is the peak number (indexed from 0) and the number following the K is the coefficient number (from 0) // Individual constraints are ";" separated. Valid operators are "<", "<=", ">", ">=" // This function will translate a global string into the correct syntax for use with FitFunc Static Function /S MPF2_AllPeaksToFitFuncStr(setNumber, interPeakString) Variable setNumber String interPeakString String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String ret=interPeakString String currPeakString = interPeakString String regExprStr = "([Pp|Bb][0-9|Ll]+[Kk][0-9]+)(.*)" String BLregExprStr = "([Bb][Ll][Kk][0-9]+)(.*)" Wave nParamsSum = MPF2_GetNParamsForFuncs(setNumber) Variable i String substring1, substring2 Variable peakNum, coefNum String newVal do // check for baseline coefs SplitString /E=(BLregExprStr) currPeakString, substring1, substring2 currPeakString = substring2 if (strlen(substring1)) sscanf substring1, "%*[Bb]%*[Ll]%*[kK]%i", coefNum newVal = "K"+num2str(coefNum) ret = ReplaceString (substring1, ret, newVal) else break endif while(1) currPeakString = interPeakString do // check for peak coefs SplitString /E=(regExprStr) currPeakString, substring1, substring2 currPeakString = substring2 if (strlen(substring1)) sscanf substring1, "%*[pP]%i%*[kK]%i", peakNum, coefNum newVal = "K"+num2str(nParamsSum[peakNum]+coefNum) ret = ReplaceString (substring1, ret, newVal) else break endif while(1) return ret End Static Function /S MPF2_StringKToPeakNotation(kString, MPStruct) String kString STRUCT MPFitInfoStruct &MPStruct Variable i, j Make /Free /N=(MPStruct.nPeaks+1) totalNCoefs /// get the total number of coefficients for each peak Variable doBaseLine = CmpStr(MPStruct.listOfFunctions[0], "None") != 0 if (doBaseLine) FUNCREF MPF2_FuncInfoTemplate blinfo = $(StringFromList(0, MPStruct.listOfFunctions)+BL_INFO_SUFFIX) totalNCoefs[0] = ItemsInList(blinfo(BLFuncInfo_ParamNames)) else totalNCoefs[0] = 0 endif for (i = 1; i <= MPStruct.nPeaks; i += 1) String peakItem = "Peak "+num2istr(i-1) FUNCREF MPF2_FuncInfoTemplate peakInfoFunc=$(StringFromList(i, MPStruct.listOfFunctions)+PEAK_INFO_SUFFIX) totalNCoefs[i] = totalNCoefs[i-1] + ItemsInList(peakInfoFunc(PeakFuncInfo_ParamNames)) endfor String regExprStr = "( [Kk][0-9]+ )(.*)" Variable peakNum, coefNum String ParamNames, replacementStr String currPeakString = kString, ret = kString String substring1, substring2 do // do baseline SplitString /E=(regExprStr) currPeakString, substring1, substring2 currPeakString = substring2 if (strlen(substring1)) sscanf substring1, " %*[kK]%i ", coefNum for (j=0; j coefNum) if (j==0) FUNCREF MPF2_FuncInfoTemplate blinfo = $(StringFromList(0, MPStruct.listOfFunctions)+BL_INFO_SUFFIX) ParamNames = blinfo(BLFuncInfo_ParamNames) replacementStr = "Baseline "+StringFromList(coefNum, ParamNames) else FUNCREF MPF2_FuncInfoTemplate peakInfoFunc=$(StringFromList(j, MPStruct.listOfFunctions)+PEAK_INFO_SUFFIX) ParamNames = peakInfoFunc(PeakFuncInfo_ParamNames) replacementStr = " Peak "+num2str(j-1)+" "+StringFromList(coefNum-totalNCoefs[j-1], ParamNames)+" " endif break endif endfor ret = ReplaceString(substring1, ret, replacementStr) else break endif while(1) return ret End Static Function /S MPF2_interPeakToStringList(setNumber, interPeakString) Variable setNumber String interPeakString String ret = "" String DFpath = MPF2_FolderPathFromSetNumber(setNumber) SVAR gname = $(DFpath+":GraphName") String currPeakString = interPeakString, substring1, substring2 String regExprStr = "([Pp][0-9]+[Kk][0-9]+)(.*)" String BLregExprStr = "([Bb][Ll][Kk][0-9]+)(.*)" Variable peakNum, coefNum, theRow String ParamNames do // do baseline SplitString /E=(BLregExprStr) currPeakString, substring1, substring2 currPeakString = substring2 if (strlen(substring1)) sscanf substring1, "%*[Bb]%*[Ll]%*[kK]%i", coefNum 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) ParamNames = blinfo(BLFuncInfo_ParamNames) ret=ReplaceStringByKey(substring1, ret, "Baseline, "+StringFromList(coefNum, ParamNames)) endif else break endif while(1) currPeakString = interPeakString do // do peaks SplitString /E=(regExprStr) currPeakString, substring1, substring2 currPeakString = substring2 if (strlen(substring1)) sscanf substring1, "%*[pP]%i%*[kK]%i", peakNum, coefNum Wave coefs = $("Peak "+num2istr(peakNum)+" Coefs") theRow = WMHL_GetRowNumberForItem(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", "Peak "+num2istr(peakNum)) String PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(gname+"#MultiPeak2Panel#P1", "MPF2_PeakList", 0, theRow) ) FUNCREF MPF2_FuncInfoTemplate infoFunc=$(PeakTypeName+PEAK_INFO_SUFFIX) ParamNames = infoFunc(PeakFuncInfo_ParamNames) ret=ReplaceStringByKey(substring1, ret, "Peak "+num2str(peakNum)+", "+StringFromList(coefNum, ParamNames)) else break endif while(1) return ret End // Get a list of peak function info from the UI Static Function /WAVE MPF2_GetNParamsForFuncs(setNumber) Variable setNumber String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Wave/Z wpi = $(DFPath+":"+"W_AutoPeakInfo") Variable nPeaks if (WaveExists(wpi)) nPeaks = DimSize(wpi, 0) else nPeaks = 0 endif Make /O/FREE/N=(nPeaks+1) ret SVAR gname = $(DFpath+":GraphName") String listPanelName = gname+"#MultiPeak2Panel#P1" String saveDF = GetDataFolder(1) SetDataFolder DFPath String/G FuncListString="" Variable nBLParams String ParamNameList String pwname Variable i Variable BaselineRow = WMHL_GetRowNumberForItem(listPanelName, "MPF2_PeakList", "Baseline") String baselineStr = WMHL_GetExtraColumnData(listPanelName, "MPF2_PeakList", 0, BaselineRow) String listOfFunctions = MPF2_PeakOrBLTypeFromListString(baselineStr)+";" Variable doBaseLine = CmpStr(baselineStr, "None"+MENU_ARROW_STRING) != 0 if (doBaseLine) FUNCREF MPF2_FuncInfoTemplate blinfo = $(StringFromList(0, listOfFunctions)+BL_INFO_SUFFIX) ret[0] = ItemsInList(blinfo(BLFuncInfo_ParamNames)) else ret[0] = 0 endif for (i = 1; i <= nPeaks; i += 1) String peakItem = "Peak "+num2istr(i-1) Variable theRow = WMHL_GetRowNumberForItem(listPanelName, "MPF2_PeakList", peakItem) String PeakTypeName = MPF2_PeakOrBLTypeFromListString( WMHL_GetExtraColumnData(listPanelName, "MPF2_PeakList", 0, theRow) )+PEAK_INFO_SUFFIX FUNCREF MPF2_FuncInfoTemplate peakInfoFunc=$PeakTypeName ret[i] = ret[i-1] + ItemsInList(peakInfoFunc(PeakFuncInfo_ParamNames)) endfor return ret End Function MPF2_DoValidateConstraint(SV_Struct) : SetVariableControl STRUCT WMSetVariableAction &SV_Struct if (SV_Struct.eventcode==2) Variable setNumber = GetSetNumberFromWinName(SV_Struct.win) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) MPF2_ValidateConstraint(setNumber, SV_Struct.win, SV_Struct.sval) endif End // Validate an interpeak constraint string. Pop up an alert if there's Function MPF2_ValidateConstraint(setNumber, win, interPeakConstraints) Variable setNumber String win, interPeakConstraints Wave nPeaksNCoefs = MPF2_GetNParamsForFuncs(setNumber) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) String constraintsStr = interPeakConstraints String errList = "" String keyList = "" String currPeakString = constraintsStr, substring1, substring2 String regExprStr = "([Pp][0-9]+[Kk][0-9]+)(.*)" String BLregExprStr = "([Bb][Ll][Kk][0-9]+)(.*)" Variable peakNum, coefNum, theRow String ParamNames do // do baseline SplitString /E=(BLregExprStr) currPeakString, substring1, substring2 currPeakString = substring2 if (strlen(substring1)) sscanf substring1, "%*[Bb]%*[Ll]%*[kK]%i", coefNum if (coefNum >= nPeaksNCoefs[0]) errList += substring1+":Baseline Function coeficient number "+num2str(coefNum)+" is out of range;" if (!StringMatch(keyList, substring1+";")) keyList += substring1+";" endif endif else break endif while(1) currPeakString = constraintsStr do // do peaks SplitString /E=(regExprStr) currPeakString, substring1, substring2 currPeakString = substring2 if (strlen(substring1)) sscanf substring1, "%*[pP]%i%*[kK]%i", peakNum, coefNum if (peakNum+1 >= DimSize(nPeaksNCoefs, 0)) errList = ReplaceStringByKey(substring1,errList,"There is no Peak "+num2str(peakNum)) if (!StringMatch(keyList, substring1+";")) keyList += substring1+";" endif elseif (coefNum >= (nPeaksNCoefs[peakNum+1]-nPeaksNCoefs[peakNum])) errList = ReplaceStringByKey(substring1,errList,"Peak "+num2str(peakNum)+" coeficient number "+num2str(coefNum)+" is out of range") if (!StringMatch(keyList, substring1+";")) keyList += substring1+";" endif endif else break endif while(1) Variable i if (strlen(errList)) String errorString="" for (i=0; i0) errorString += "\r" endif errorString += StringByKey(currKey, errList) endfor String /G $(DFpath+":MPF2InterPeakErrStr") SVAR interPeakErrStr = $(DFpath+":MPF2InterPeakErrStr") interPeakErrStr = errorString Variable middleX, middleY Variable height = 200 Variable width = 320 if (strlen(win)) String panelName = ParseFilePath(1, win, "#", 1, 0) panelName = panelName[0, strlen(panelName)-2] GetWindow $panelName wsize middleX = (V_left + V_right)/2 middleY = (V_top + V_bottom)/2 else 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 middleX = scrnWidth/2 middleY = scrnHeight/2 endif Variable panelFSize = 12 DoWindow/F InterPeakConstraintErrorPanel if (V_flag == 0) // Panel doesn't exist NewPanel /N=InterPeakConstraintErrorPanel /W=(middleX-width/2, middleY-height/2, middleX+width/2, middleY+height/2)/K=1 as "Inter-Peak Constraints Error" TitleBox ConstraintStringTitle win=InterPeakConstraintErrorPanel, pos={10, 5}, size={width-20, 15}, title="Inter-Peak Constraints String:", fstyle=1, frame=0, fsize=panelFSize TitleBox ConstraintString win=InterPeakConstraintErrorPanel, pos={10, 25}, size={width-20, 40}, title=constraintsStr, fsize=panelFSize, frame=5 TitleBox ErrorsTitle win=InterPeakConstraintErrorPanel, pos={10, 55}, size={width-20, 15}, title="Errors:", fstyle=1, frame=0, fsize=panelFSize TitleBox Errors win=InterPeakConstraintErrorPanel, pos={10, 75}, size={width-20, 60}, variable=interPeakErrStr, fsize=panelFSize, frame=5 Button OKButton win=InterPeakConstraintErrorPanel, pos={width/2-30, 170}, size={60,20}, title="OK", proc=InterPeakErrorOK, fsize=panelFSize, fColor=(1,34817,52428) SetWindow InterPeakConstraintErrorPanel hook(MPF2_OK)=MPF2_OKProcedure else TitleBox ConstraintString win=InterPeakConstraintErrorPanel, title=constraintsStr endif return 0 else DoWindow/F InterPeakConstraintErrorPanel if (V_flag == 0) DoWindow /K InterPeakConstraintErrorPanel endif endif return 1 End Function MPF2_OKProcedure(s) STRUCT WMWinHookStruct &s Variable statusCode= 0 strswitch( s.eventName ) case "keyboard": if (s.keycode==13) // Carriage Return, or Enter Key DoWindow /K $(s.winName) endif break endswitch return statusCode End Function InterPeakErrorOK(ba) : ButtonControl STRUCT WMButtonAction &ba switch( ba.eventCode ) case 2: // mouse up DoWindow /K $(ba.win) break case -1: // control being killed break endswitch return 0 End Function UpdateMultiPeak2Panel(srcPanelName, setNumber) String srcPanelName Variable setNumber Variable MultiPeakPanelLocation = strsearch(srcPanelName, "#MultiPeak2Panel", 0) String panelName = srcPanelName[0, MultiPeakPanelLocation+15] String updatePanelStr = GetUserData(panelName, "", "MPF2_UPDATEPANELVERSION") if (!strlen(updatePanelStr) || str2num(updatePanelStr) < MPF2_UPDATEPANELVERSION) String DFpath = MPF2_FolderPathFromSetNumber(setNumber) Variable/G $(DFPath+":MPF2ConstraintsShowing") NVAR MPF2ConstraintsShowing = $(DFPath+":MPF2ConstraintsShowing") MPF2ConstraintsShowing = 0 Variable width = MPF2ConstraintsShowing ? MPF2_PanelWidth : MPF2_NarrowWidth Button MPF2_HelpButton,win=$panelName,pos={width-60,1} //// Panel 0 - Peak location GroupBox MPF2_LocatePeaksGroupBox,win=$(panelName+"#P0"),size={width-16,61} Button MPF2_AutoLocatePeaksButton,win=$(panelName+"#P0"),pos={(width-158)/2,21} CheckBox MPF2_NegativePeaksCheck,win=$(panelName+"#P0"),pos={(width-85)/2,44} //// Panel 1 - fit controls String listPanel = panelName+"#P1" CheckBox MPF2_DiscloseConstraints,win=$listPanel,pos={width-150,2},size={81,15},title="Apply Constraints",value=MPF2ConstraintsShowing,proc=MPF2_DiscloseConstraints,fsize=10 ListBox MPF2_PeakList,win=$listPanel,pos={6,20},size={width-12,119} WMHL_AddColumns(listPanel, "MPF2_PeakList", 4) if (MPF2ConstraintsShowing) ListBox MPF2_PeakList win=$listPanel,widths={4,15,16,9,5,10,6,10} else ListBox MPF2_PeakList win=$listPanel,widths={4,15,16,9,0,0,0,0} endif // NewPanel/N=P2/W=(65,86,width,260)/FG=(FL,UGH2,FR,UGH3)/HOST=$panelName String buttonPanel = panelName+"#P2" PopupMenu MPF2_SetAllPeakTypesMenu,win=$buttonPanel,pos={width-195,3} Variable leftSideControlOffset = floor(width/15) Button MPF2_DoFitButton,win=$buttonPanel,pos={leftSideControlOffset,35} Button MPF2_PeakResultsButton,win=$buttonPanel,pos={width-leftSideControlOffset-109 ,35} Button MPF2_RevertToGuessesButton,win=$buttonPanel,pos={leftSideControlOffset,65} PopupMenu MPF2_CheckPointMenu,win=$buttonPanel,pos={width-leftSideControlOffset-102,66} // NewPanel/W=(65,86,196,346)/FG=(FL,UGH3,FR,FB)/HOST=# SVAR /Z interPeakString = $(DFPath+":interPeakConstraints") if (!SVAR_exists(interPeakString)) String /G $(DFPath+":interPeakConstraints") SVAR interPeakString = $(DFPath+":interPeakConstraints") endif SetVariable MPF2_GlobalConstraints,win=$(panelName+"#P3"),pos={10,33},size={width-20,20},title="Inter-Peak Constraints",fSize=10, value=$(DFPath+":interPeakConstraints"), proc=MPF2_DoValidateConstraint SetVariable MPF2_GlobalConstraints,win=$(panelName+"#P3"),help={"Identify coefficients by P[PeakNumber]K[LocalCoefNumber]. Each constraint is separated by a Semi-colon."} TitleBox MPF2_ConstraintsExample,win=$(panelName+"#P3"),pos={90,50},size={width-50,20},title="Example: P1K0>P0K0;P2K1