From b51c39b1321f69ba967484c50754ab112ae351fa Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 22 Dec 2025 15:14:46 +0100 Subject: [PATCH 1/8] SF: Move part of the argument parsing for operation apfrequency to own function This will allow to parse the same argument block as part of another operation later --- .../MIES/MIES_SweepFormula_Operations.ipf | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 301cb47a02..559b36f65d 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -235,6 +235,25 @@ static Function/WAVE SFO_OperationAnaFuncParamImplAllNames(WAVE/T names, WAVE/WA return GetUniqueEntries(allNames) End +static Function [variable method, variable level, string timeFreq, string normalize, string xAxisType] SFO_GetApFrequencyArguments(STRUCT SF_ExecutionData &exd, string opShort, variable offset) + + method = SFH_GetArgumentAsNumeric(exd, opShort, offset, defValue = SF_APFREQUENCY_FULL, allowedValues = {SF_APFREQUENCY_FULL, SF_APFREQUENCY_INSTANTANEOUS, SF_APFREQUENCY_APCOUNT, SF_APFREQUENCY_INSTANTANEOUS_PAIR}) + level = SFH_GetArgumentAsNumeric(exd, opShort, offset + 1, defValue = 0) + timeFreq = SFH_GetArgumentAsText(exd, opShort, offset + 2, defValue = SF_OP_APFREQUENCY_Y_FREQ, allowedValues = {SF_OP_APFREQUENCY_Y_TIME, SF_OP_APFREQUENCY_Y_FREQ}) + normalize = SFH_GetArgumentAsText(exd, opShort, offset + 3, defValue = SF_OP_APFREQUENCY_NONORM, allowedValues = { \ + SF_OP_APFREQUENCY_NONORM, \ + SF_OP_APFREQUENCY_NORMOVERSWEEPSMIN, \ + SF_OP_APFREQUENCY_NORMOVERSWEEPSMAX, \ + SF_OP_APFREQUENCY_NORMOVERSWEEPSAVG, \ + SF_OP_APFREQUENCY_NORMWITHINSWEEPMIN, \ + SF_OP_APFREQUENCY_NORMWITHINSWEEPMAX, \ + SF_OP_APFREQUENCY_NORMWITHINSWEEPAVG \ + }) + xAxisType = SFH_GetArgumentAsText(exd, opShort, offset + 4, defValue = SF_OP_APFREQUENCY_X_TIME, allowedValues = {SF_OP_APFREQUENCY_X_TIME, SF_OP_APFREQUENCY_X_COUNT}) + + return [method, level, timeFreq, normalize, xAxisType] +End + // apfrequency(data, [frequency calculation method], [spike detection crossing level], [result value type], [normalize], [x-axis type]) Function/WAVE SFO_OperationApFrequency(STRUCT SF_ExecutionData &exd) @@ -249,19 +268,7 @@ Function/WAVE SFO_OperationApFrequency(STRUCT SF_ExecutionData &exd) SFH_ASSERT(numArgs >= numArgsMin, "ApFrequency needs at least " + num2istr(numArgsMin) + " argument(s).") WAVE/WAVE input = SF_ResolveDatasetFromJSON(exd, 0) - method = SFH_GetArgumentAsNumeric(exd, opShort, 1, defValue = SF_APFREQUENCY_FULL, allowedValues = {SF_APFREQUENCY_FULL, SF_APFREQUENCY_INSTANTANEOUS, SF_APFREQUENCY_APCOUNT, SF_APFREQUENCY_INSTANTANEOUS_PAIR}) - level = SFH_GetArgumentAsNumeric(exd, opShort, 2, defValue = 0) - timeFreq = SFH_GetArgumentAsText(exd, opShort, 3, defValue = SF_OP_APFREQUENCY_Y_FREQ, allowedValues = {SF_OP_APFREQUENCY_Y_TIME, SF_OP_APFREQUENCY_Y_FREQ}) - normalize = SFH_GetArgumentAsText(exd, opShort, 4, defValue = SF_OP_APFREQUENCY_NONORM, allowedValues = { \ - SF_OP_APFREQUENCY_NONORM, \ - SF_OP_APFREQUENCY_NORMOVERSWEEPSMIN, \ - SF_OP_APFREQUENCY_NORMOVERSWEEPSMAX, \ - SF_OP_APFREQUENCY_NORMOVERSWEEPSAVG, \ - SF_OP_APFREQUENCY_NORMWITHINSWEEPMIN, \ - SF_OP_APFREQUENCY_NORMWITHINSWEEPMAX, \ - SF_OP_APFREQUENCY_NORMWITHINSWEEPAVG \ - }) - xAxisType = SFH_GetArgumentAsText(exd, opShort, 5, defValue = SF_OP_APFREQUENCY_X_TIME, allowedValues = {SF_OP_APFREQUENCY_X_TIME, SF_OP_APFREQUENCY_X_COUNT}) + [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 1) WAVE/T argSetup = SFH_GetNewArgSetupWave(numArgsMax - 1) From 834a5c32f08fa8d5fa47f7ea96f324f0f3445687 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 16 Dec 2025 17:50:23 +0100 Subject: [PATCH 2/8] SF: Adapt SF Plotter to insert plots from operations - the plotting loop was adapted to allow the insertion of plots returned from operations - the plotting functions arguments were wrapped in the SF_PlotterGraphStruct structure and the functions adapted --- Packages/MIES/MIES_Constants.ipf | 1 + Packages/MIES/MIES_SweepFormula.ipf | 498 +++++++++++++++++----------- 2 files changed, 310 insertions(+), 189 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 4b8990d79b..4d7c00b8cb 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2141,6 +2141,7 @@ StrConstant SF_META_TRACETOFRONT = "/TraceToFront" // number, boolean, StrConstant SF_META_DONOTPLOT = "/DoNotPlot" // number, boolean, defaults to false (0) StrConstant SF_META_WINDOW_HOOK = "/WindowHook" // string StrConstant SF_META_FORMULA = "/Formula" // string +StrConstant SF_META_PLOT = "/Plot" // number, boolean, defaults to false (0) /// A color group allows to have matching colors for sweep data with the same channel type/number and sweep. /// It is applied before the matching headstage/average colors in #SF_GetTraceColor(). diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index f03c706930..1669056ec8 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -162,22 +162,27 @@ Function SF_FormulaWaveScaleTransfer(WAVE source, WAVE dest, variable dimSource, endswitch End -static Function [WAVE/WAVE formulaResults, WAVE/T plotMetaData] SF_GatherFormulaResults(string xFormula, string yFormula, string graph, variable lineNr, variable offset) +/// @brief Retrieves the plot meta data from the JSON wave note or other sources and stores it in the plotMetaData wave +static Function/WAVE SF_FillPlotMetaData(WAVE wvYRef, variable useXLabel, string dataUnits) + + WAVE/T plotMetaData = GetSFPlotMetaData() + plotMetaData[%DATATYPE] = JWN_GetStringFromWaveNote(wvYRef, SF_META_DATATYPE) + plotMetaData[%OPSTACK] = JWN_GetStringFromWaveNote(wvYRef, SF_META_OPSTACK) + plotMetaData[%ARGSETUPSTACK] = JWN_GetStringFromWaveNote(wvYRef, SF_META_ARGSETUPSTACK) + plotMetaData[%XAXISLABEL] = SelectString(useXLabel, SF_XLABEL_USER, JWN_GetStringFromWaveNote(wvYRef, SF_META_XAXISLABEL)) + plotMetaData[%YAXISLABEL] = JWN_GetStringFromWaveNote(wvYRef, SF_META_YAXISLABEL) + dataUnits + + return plotMetaData +End + +static Function [WAVE/WAVE formulaResults, WAVE/T plotMetaData] SF_FillFormulaResults(WAVE/Z/WAVE wvYRef, WAVE/Z/WAVE wvXRef, string yFormula) variable i, numResultsY, numResultsX variable useXLabel, addDataUnitsInAnnotation string dataUnits, dataUnitCheck - WAVE/WAVE formulaResults = GetFormulaGatherWave() - WAVE/T plotMetaData = GetSFPlotMetaData() - - WAVE/Z/WAVE wvXRef = $"" - if(!IsEmpty(xFormula)) - WAVE/WAVE wvXRef = SFE_ExecuteFormula(xFormula, graph, useVariables = 0, line = lineNr, offset = offset) - SFH_ASSERT(WaveExists(wvXRef), "x part of formula returned no result.") - endif - WAVE/WAVE wvYRef = SFE_ExecuteFormula(yFormula, graph, useVariables = 0, line = lineNr, offset = 0) SFH_ASSERT(WaveExists(wvYRef), "y part of formula returned no result.") + numResultsY = DimSize(wvYRef, ROWS) if(WaveExists(wvXRef)) numResultsX = DimSize(wvXRef, ROWS) @@ -186,6 +191,7 @@ static Function [WAVE/WAVE formulaResults, WAVE/T plotMetaData] SF_GatherFormula useXLabel = 1 addDataUnitsInAnnotation = 1 + WAVE/WAVE formulaResults = GetFormulaGatherWave() Redimension/N=(numResultsY, -1) formulaResults if(DimSize(wvYRef, ROWS) > 0 && DimSize(formulaResults, ROWS) > 0) @@ -233,11 +239,21 @@ static Function [WAVE/WAVE formulaResults, WAVE/T plotMetaData] SF_GatherFormula dataUnits = SelectString(addDataUnitsInAnnotation && !IsEmpty(dataUnitCheck), "", SF_FormatUnit(dataUnitCheck)) endif - plotMetaData[%DATATYPE] = JWN_GetStringFromWaveNote(wvYRef, SF_META_DATATYPE) - plotMetaData[%OPSTACK] = JWN_GetStringFromWaveNote(wvYRef, SF_META_OPSTACK) - plotMetaData[%ARGSETUPSTACK] = JWN_GetStringFromWaveNote(wvYRef, SF_META_ARGSETUPSTACK) - plotMetaData[%XAXISLABEL] = SelectString(useXLabel, SF_XLABEL_USER, JWN_GetStringFromWaveNote(wvYRef, SF_META_XAXISLABEL)) - plotMetaData[%YAXISLABEL] = JWN_GetStringFromWaveNote(wvYRef, SF_META_YAXISLABEL) + dataUnits + WAVE/T plotMetaData = SF_FillPlotMetaData(wvyRef, useXLabel, dataUnits) + + return [formulaResults, plotMetaData] +End + +static Function [WAVE/WAVE formulaResults, WAVE/T plotMetaData] SF_GatherFormulaResults(string xFormula, string yFormula, string graph, variable lineNr, variable offset) + + WAVE/Z/WAVE wvXRef = $"" + if(!IsEmpty(xFormula)) + WAVE/WAVE wvXRef = SFE_ExecuteFormula(xFormula, graph, useVariables = 0, line = lineNr, offset = offset) + SFH_ASSERT(WaveExists(wvXRef), "x part of formula returned no result.") + endif + WAVE/WAVE wvYRef = SFE_ExecuteFormula(yFormula, graph, useVariables = 0, line = lineNr, offset = 0) + + [WAVE/WAVE formulaResults, WAVE/T plotMetaData] = SF_FillFormulaResults(wvYRef, wvXRef, yFormula) return [formulaResults, plotMetaData] End @@ -491,12 +507,12 @@ End /// /// @retval traces generated trace names /// @retval traceCnt total count of all traces (input *and* output) -static Function [WAVE/T traces, variable traceCnt] SF_CreateTraceNames(variable numTraces, variable dataNum, WAVE/T plotMetaData, WAVE data) +static Function [WAVE/T traces, STRUCT SF_PlotterGraphStruct pg] SF_CreateTraceNames(variable numTraces, variable dataNum, WAVE/T plotMetaData, WAVE data) string traceAnnotation if(!numTraces) - return [$"", traceCnt] + return [$"", pg] endif traceAnnotation = SF_GetTraceAnnotationText(plotMetaData, data) @@ -505,9 +521,10 @@ static Function [WAVE/T traces, variable traceCnt] SF_CreateTraceNames(variable Make/T/N=(numTraces)/FREE traces - traces[] = GetTraceNamePrefix(traceCnt + p) + "d" + num2istr(dataNum) + "_" + traceAnnotation + traces[] = GetTraceNamePrefix(pg.traceCnt + p) + "d" + num2istr(dataNum) + "_" + traceAnnotation + pg.traceCnt += numTraces - return [traces, traceCnt + numTraces] + return [traces, pg] End /// Reduces a multi line legend to a single line if only the sweep number changes. @@ -898,7 +915,17 @@ static Function SF_IsDataForTableDisplay(WAVE wvY) return IsNaN(useTable) ? 0 : !!useTable End -static Function [variable dataCnt, variable traceCnt, variable gdIndex, string annotation, variable formulaAddedOncePerDataset, variable showLegend] SF_CreateTracesForResultsImpl(string graph, WAVE wvResultY, WAVE/Z wvResultX, WAVE/T plotMetaData, variable dataNum, WAVE/Z colorGroups, variable showInTable, string win, WAVE/T tableFormulas, WAVE plotFormData) +/// @brief Returns 1 if the result is flagged as full plotting specification, 0 otherwise +static Function SF_IsDataForFullPlotting(WAVE wv) + + variable plot + + plot = JWN_GetNumberFromWaveNote(wv, SF_META_PLOT) + + return IsNaN(plot) ? 0 : !!plot +End + +static Function [variable dataCnt, STRUCT SF_PlotterGraphStruct pg, variable gdIndex, string annotation, variable formulaAddedOncePerDataset] SF_CreateTracesForResultsImpl(WAVE wvResultY, WAVE/Z wvResultX, variable dataNum, variable showInTable, WAVE plotFormData) STRUCT RGBColor color variable numTraces, yPoints, xPoints, yMxN, xMxN, idx, splitTraces @@ -909,11 +936,11 @@ static Function [variable dataCnt, variable traceCnt, variable gdIndex, string a SFH_ASSERT(!(IsTextWave(wvResultY) && WaveDims(wvResultY) > 1), "Plotter got 2d+ text wave as y data.") - DFREF dfr = SF_GetBrowserDF(graph) + DFREF dfr = SF_GetBrowserDF(pg.graph) - [color] = SF_GetTraceColor(graph, plotMetaData[%OPSTACK], wvResultY, colorGroups) + [color] = SF_GetTraceColor(pg.graph, pg.plotMetaData[%OPSTACK], wvResultY, pg.colorGroups) - if(!WaveExists(wvResultX) && !IsEmpty(plotMetaData[%XAXISLABEL])) + if(!WaveExists(wvResultX) && !IsEmpty(pg.plotMetaData[%XAXISLABEL])) WAVE/Z wvResultX = JWN_GetNumericWaveFromWaveNote(wvResultY, SF_META_XVALUES) if(!WaveExists(wvResultX)) @@ -936,26 +963,26 @@ static Function [variable dataCnt, variable traceCnt, variable gdIndex, string a if(showInTable) if(HasDimLabels(wvY, ROWS) || HasDimLabels(wvY, COLS)) - AppendToTable/W=$win wvY.ld + AppendToTable/W=$pg.win wvY.ld else - AppendToTable/W=$win wvY.d + AppendToTable/W=$pg.win wvY.d endif if(!formulaAddedOncePerDataset) - idx = GetNumberFromWaveNote(tableFormulas, NOTE_INDEX) - EnsureLargeEnoughWave(tableFormulas, indexShouldExist = idx) - tableFormulas[idx] = JWN_GetStringFromWaveNote(wvY, SF_META_FORMULA) - SetNumberInWaveNote(tableFormulas, NOTE_INDEX, idx + 1) + idx = GetNumberFromWaveNote(pg.tableFormulas, NOTE_INDEX) + EnsureLargeEnoughWave(pg.tableFormulas, indexShouldExist = idx) + pg.tableFormulas[idx] = JWN_GetStringFromWaveNote(wvY, SF_META_FORMULA) + SetNumberInWaveNote(pg.tableFormulas, NOTE_INDEX, idx + 1) formulaAddedOncePerDataset = 1 endif dataCnt += 1 - return [dataCnt, traceCnt, gdIndex, annotation, formulaAddedOncePerDataset, showLegend] + return [dataCnt, pg, gdIndex, annotation, formulaAddedOncePerDataset] endif if(IsTextWave(wvY)) SFH_ASSERT(WaveExists(wvX), "Cannot plot a single text wave") - ModifyGraph/W=$win swapXY=1 + ModifyGraph/W=$pg.win swapXY=1 WAVE dummy = wvY WAVE wvY = wvX WAVE wvX = dummy @@ -963,48 +990,48 @@ static Function [variable dataCnt, variable traceCnt, variable gdIndex, string a if(!WaveExists(wvX)) numTraces = yMxN - SF_CheckNumTraces(graph, numTraces) - [WAVE/T traces, traceCnt] = SF_CreateTraceNames(numTraces, dataNum, plotMetaData, wvResultY) + SF_CheckNumTraces(pg.graph, numTraces) + [WAVE/T traces, pg] = SF_CreateTraceNames(numTraces, dataNum, pg.plotMetaData, wvResultY) for(i = 0; i < numTraces; i += 1) SF_CollectTraceData(gdIndex, plotFormData, traces[i], wvX, wvY) - AppendTograph/W=$win/C=(color.red, color.green, color.blue) wvY[][i]/TN=$traces[i] - annotation += SF_GetMetaDataAnnotationText(plotMetaData, wvResultY, traces[i]) + AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[][i]/TN=$traces[i] + annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) endfor elseif((xMxN == 1) && (yMxN == 1)) // 1D if(yPoints == 1) // 0D vs 1D numTraces = xPoints - SF_CheckNumTraces(graph, numTraces) - [WAVE/T traces, traceCnt] = SF_CreateTraceNames(numTraces, dataNum, plotMetaData, wvResultY) + SF_CheckNumTraces(pg.graph, numTraces) + [WAVE/T traces, pg] = SF_CreateTraceNames(numTraces, dataNum, pg.plotMetaData, wvResultY) for(i = 0; i < numTraces; i += 1) SF_CollectTraceData(gdIndex, plotFormData, traces[i], wvX, wvY) - AppendTograph/W=$win/C=(color.red, color.green, color.blue) wvY[][0]/TN=$traces[i] vs wvX[i][] - annotation += SF_GetMetaDataAnnotationText(plotMetaData, wvResultY, traces[i]) + AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[][0]/TN=$traces[i] vs wvX[i][] + annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) endfor elseif(xPoints == 1) // 1D vs 0D numTraces = yPoints - SF_CheckNumTraces(graph, numTraces) - [WAVE/T traces, traceCnt] = SF_CreateTraceNames(numTraces, dataNum, plotMetaData, wvResultY) + SF_CheckNumTraces(pg.graph, numTraces) + [WAVE/T traces, pg] = SF_CreateTraceNames(numTraces, dataNum, pg.plotMetaData, wvResultY) for(i = 0; i < numTraces; i += 1) SF_CollectTraceData(gdIndex, plotFormData, traces[i], wvX, wvY) - AppendTograph/W=$win/C=(color.red, color.green, color.blue) wvY[i][]/TN=$traces[i] vs wvX[][0] - annotation += SF_GetMetaDataAnnotationText(plotMetaData, wvResultY, traces[i]) + AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[i][]/TN=$traces[i] vs wvX[][0] + annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) endfor else // 1D vs 1D splitTraces = min(yPoints, xPoints) numTraces = floor(max(yPoints, xPoints) / splitTraces) - SF_CheckNumTraces(graph, numTraces) - [WAVE/T traces, traceCnt] = SF_CreateTraceNames(numTraces, dataNum, plotMetaData, wvResultY) + SF_CheckNumTraces(pg.graph, numTraces) + [WAVE/T traces, pg] = SF_CreateTraceNames(numTraces, dataNum, pg.plotMetaData, wvResultY) if(mod(max(yPoints, xPoints), splitTraces) == 0) DebugPrint("Unmatched Data Alignment in ROWS.") endif for(i = 0; i < numTraces; i += 1) - if(WindowExists(win) && WhichListItem("bottom", AxisList(win)) >= 0) - info = AxisInfo(win, "bottom") + if(WindowExists(pg.win) && WhichListItem("bottom", AxisList(pg.win)) >= 0) + info = AxisInfo(pg.win, "bottom") isCategoryAxis = NumberByKey("ISCAT", info) == 1 if(isCategoryAxis) @@ -1023,19 +1050,19 @@ static Function [variable dataCnt, variable traceCnt, variable gdIndex, string a SF_CollectTraceData(gdIndex, plotFormData, traces[i], wvX, wvY) splitY = SF_SplitPlotting(wvY, ROWS, i, splitTraces) splitX = SF_SplitPlotting(wvX, ROWS, i, splitTraces) - AppendTograph/W=$win/C=(color.red, color.green, color.blue) wvY[splitY, splitY + splitTraces - 1][0]/TN=$traces[i] vs wvX[splitX, splitX + splitTraces - 1][0] - annotation += SF_GetMetaDataAnnotationText(plotMetaData, wvResultY, traces[i]) + AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[splitY, splitY + splitTraces - 1][0]/TN=$traces[i] vs wvX[splitX, splitX + splitTraces - 1][0] + annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) endfor endif elseif(yMxN == 1) // 1D vs 2D numTraces = xMxN - SF_CheckNumTraces(graph, numTraces) - [WAVE/T traces, traceCnt] = SF_CreateTraceNames(numTraces, dataNum, plotMetaData, wvResultY) + SF_CheckNumTraces(pg.graph, numTraces) + [WAVE/T traces, pg] = SF_CreateTraceNames(numTraces, dataNum, pg.plotMetaData, wvResultY) for(i = 0; i < numTraces; i += 1) SF_CollectTraceData(gdIndex, plotFormData, traces[i], wvX, wvY) - AppendTograph/W=$win/C=(color.red, color.green, color.blue) wvY[][0]/TN=$traces[i] vs wvX[][i] - annotation += SF_GetMetaDataAnnotationText(plotMetaData, wvResultY, traces[i]) + AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[][0]/TN=$traces[i] vs wvX[][i] + annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) endfor elseif(xMxN == 1) // 2D vs 1D or 0D if(xPoints == 1) // 2D vs 0D -> extend X to 1D with constant value @@ -1044,18 +1071,18 @@ static Function [variable dataCnt, variable traceCnt, variable gdIndex, string a wvX = wvX[0] endif numTraces = yMxN - SF_CheckNumTraces(graph, numTraces) - [WAVE/T traces, traceCnt] = SF_CreateTraceNames(numTraces, dataNum, plotMetaData, wvResultY) + SF_CheckNumTraces(pg.graph, numTraces) + [WAVE/T traces, pg] = SF_CreateTraceNames(numTraces, dataNum, pg.plotMetaData, wvResultY) for(i = 0; i < numTraces; i += 1) SF_CollectTraceData(gdIndex, plotFormData, traces[i], wvX, wvY) - AppendTograph/W=$win/C=(color.red, color.green, color.blue) wvY[][i]/TN=$traces[i] vs wvX - annotation += SF_GetMetaDataAnnotationText(plotMetaData, wvResultY, traces[i]) + AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[][i]/TN=$traces[i] vs wvX + annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) endfor else // 2D vs 2D numTraces = WaveExists(wvX) ? max(1, max(yMxN, xMxN)) : max(1, yMxN) - SF_CheckNumTraces(graph, numTraces) - [WAVE/T traces, traceCnt] = SF_CreateTraceNames(numTraces, dataNum, plotMetaData, wvResultY) + SF_CheckNumTraces(pg.graph, numTraces) + [WAVE/T traces, pg] = SF_CreateTraceNames(numTraces, dataNum, pg.plotMetaData, wvResultY) if(yPoints != xPoints) DebugPrint("Size mismatch in data rows for plotting waves.") @@ -1066,22 +1093,22 @@ static Function [variable dataCnt, variable traceCnt, variable gdIndex, string a for(i = 0; i < numTraces; i += 1) SF_CollectTraceData(gdIndex, plotFormData, traces[i], wvX, wvY) if(WaveExists(wvX)) - AppendTograph/W=$win/C=(color.red, color.green, color.blue) wvY[][min(yMxN - 1, i)]/TN=$traces[i] vs wvX[][min(xMxN - 1, i)] + AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[][min(yMxN - 1, i)]/TN=$traces[i] vs wvX[][min(xMxN - 1, i)] else - AppendTograph/W=$win/C=(color.red, color.green, color.blue) wvY[][i]/TN=$traces[i] + AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[][i]/TN=$traces[i] endif - annotation += SF_GetMetaDataAnnotationText(plotMetaData, wvResultY, traces[i]) + annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) endfor endif - showLegend = showLegend && SF_GetShowLegend(wvY) + pg.showLegend = pg.showLegend && SF_GetShowLegend(wvY) dataCnt += 1 - return [dataCnt, traceCnt, gdIndex, annotation, formulaAddedOncePerDataset, showLegend] + return [dataCnt, pg, gdIndex, annotation, formulaAddedOncePerDataset] End -static Function [variable dataCnt, variable traceCnt, WAVE/Z colorGroups, variable showLegend] SF_CreateTracesForResults(string graph, WAVE/WAVE formulaResults, variable formulaCounter, WAVE/T plotMetaData, string win, WAVE/T tableFormulas, WAVE/T wAnnotations, WAVE/T formulaArgSetup, WAVE/WAVE collPlotFormData) +static Function [variable dataCnt, STRUCT SF_PlotterGraphStruct pg] SF_CreateTracesForResults() variable i, idx, showInTable, numData, formulaAddedOncePerDataset variable gdIndex // indexes in tracesInGraph wave and dataInGraph wave in SF_CollectTraceData(), both waves are stored in plotformData @@ -1089,21 +1116,21 @@ static Function [variable dataCnt, variable traceCnt, WAVE/Z colorGroups, variab WAVE/WAVE plotFormData = SF_CreatePlotFormulaDataWave() - SF_FormulaPlotterExtendResultsIfCompatible(formulaResults) + SF_FormulaPlotterExtendResultsIfCompatible(pg.formulaResults) - if(WaveExists(colorGroups)) - Duplicate/FREE colorGroups, previousColorGroups + if(WaveExists(pg.colorGroups)) + Duplicate/FREE pg.colorGroups, previousColorGroups else WAVE/ZZ previousColorGroups endif - WAVE/Z colorGroups = SF_GetColorGroups(formulaResults, previousColorGroups) - showInTable = SF_IsDataForTableDisplay(formulaResults) + WAVE/Z pg.colorGroups = SF_GetColorGroups(pg.formulaResults, previousColorGroups) + showInTable = SF_IsDataForTableDisplay(pg.formulaResults) - numData = DimSize(formulaResults, ROWS) + numData = DimSize(pg.formulaResults, ROWS) for(i = 0; i < numData; i += 1) - WAVE/Z wvResultX = formulaResults[i][%FORMULAX] - WAVE/Z wvResultY = formulaResults[i][%FORMULAY] + WAVE/Z wvResultX = pg.formulaResults[i][%FORMULAX] + WAVE/Z wvResultY = pg.formulaResults[i][%FORMULAY] if(!WaveExists(wvResultY)) continue endif @@ -1111,26 +1138,28 @@ static Function [variable dataCnt, variable traceCnt, WAVE/Z colorGroups, variab continue endif - [dataCnt, traceCnt, gdIndex, annotation, formulaAddedOncePerDataset, showLegend] = SF_CreateTracesForResultsImpl(graph, wvResultY, wvResultX, plotMetaData, i, colorGroups, showInTable, win, tableFormulas, plotFormData) + [dataCnt, pg, gdIndex, annotation, formulaAddedOncePerDataset] = SF_CreateTracesForResultsImpl(wvResultY, wvResultX, i, showInTable, plotFormData) endfor if(!IsEmpty(annotation)) - idx = GetNumberFromWaveNote(wAnnotations, NOTE_INDEX) - EnsureLargeEnoughWave(wAnnotations, indexShouldExist = idx) - wAnnotations[idx] = annotation - SetNumberInWaveNote(wAnnotations, NOTE_INDEX, idx + 1) + idx = GetNumberFromWaveNote(pg.wAnnotations, NOTE_INDEX) + EnsureLargeEnoughWave(pg.wAnnotations, indexShouldExist = idx) + pg.wAnnotations[idx] = annotation + SetNumberInWaveNote(pg.wAnnotations, NOTE_INDEX, idx + 1) - idx = GetNumberFromWaveNote(formulaArgSetup, NOTE_INDEX) - EnsureLargeEnoughWave(formulaArgSetup, indexShouldExist = idx) - formulaArgSetup[idx] = plotMetaData[%ARGSETUPSTACK] - SetNumberInWaveNote(formulaArgSetup, NOTE_INDEX, idx + 1) + idx = GetNumberFromWaveNote(pg.formulaArgSetup, NOTE_INDEX) + EnsureLargeEnoughWave(pg.formulaArgSetup, indexShouldExist = idx) + pg.formulaArgSetup[idx] = pg.plotMetaData[%ARGSETUPSTACK] + SetNumberInWaveNote(pg.formulaArgSetup, NOTE_INDEX, idx + 1) endif - EnsureLargeEnoughWave(collPlotFormData, indexShouldExist = formulaCounter) + EnsureLargeEnoughWave(pg.collPlotFormData, indexShouldExist = pg.formulaCounter) WAVE/T tracesInGraph = plotFormData[0] WAVE/WAVE dataInGraph = plotFormData[1] Redimension/N=(gdIndex, -1) tracesInGraph, dataInGraph - collPlotFormData[formulaCounter] = plotFormData + pg.collPlotFormData[pg.formulaCounter] = plotFormData + + return [dataCnt, pg] End static Function SF_RestorePlotProperties(WAVE/WAVE prevPlotProperties) @@ -1259,6 +1288,79 @@ static Function/S SF_CreateDataDisplayWindow(string graph, WAVE/WAVE formulaResu return win End +static Structure SF_PlotterGraphStruct + + /// Name of the sweep formula graph this struct is associated with + string graph + /// Name of the window in which the plot/table is displayed + string win + /// Number of traces currently plotted + variable traceCnt + /// flag to call PSX plot creation when evaluated formulas contained psx operation + variable postPlotPSX + /// Non-zero if the legend should be shown for this plot + variable showLegend + /// Counter tracking how many formulas have been processed + variable formulaCounter + /// Wave holding x-axis labels used for the plotted data + WAVE xAxisLabels + /// Wave holding y-axis labels used for the plotted data + WAVE yAxisLabels + /// Text wave storing annotations to be displayed + WAVE/T wAnnotations + /// Text wave describing the argument setup for each formula + WAVE/T formulaArgSetup + /// Text wave containing the formulas for data displayed in tables + WAVE/T tableFormulas + /// Wave of waves storing collected plot formatting data for each formula + WAVE/WAVE collPlotFormData + /// Wave tracking which panels (graph/table) have been created + WAVE panelsCreated + /// Wave of waves with the evaluation results of a formula + WAVE/WAVE formulaResults + /// Text wave holding meta data about the plotted formulas and traces + WAVE/T plotMetaData + /// Wave assigning color groups to plotted traces + WAVE colorGroups +EndStructure + +static Function [STRUCT SF_PlotterGraphStruct pg] SF_InitPlotterGraphStruct(string graph) + + pg.graph = graph + pg.win = "" + + WAVE/Z pg.colorGroups = $"" + pg.traceCnt = 0 + pg.postPlotPSX = 0 + pg.showLegend = 1 + pg.formulaCounter = 0 + + Make/FREE/T/N=0 xAxisLabels, yAxisLabels + WAVE pg.xAxisLabels = xAxisLabels + WAVE pg.yAxisLabels = yAxisLabels + + Make/FREE=1/T/N=(MINIMUM_WAVE_SIZE) wAnnotations, formulaArgSetup, tableFormulas + SetNumberInWaveNote(wAnnotations, NOTE_INDEX, 0) + SetNumberInWaveNote(formulaArgSetup, NOTE_INDEX, 0) + SetNumberInWaveNote(tableFormulas, NOTE_INDEX, 0) + WAVE/T pg.wAnnotations = wAnnotations + WAVE/T pg.formulaArgSetup = formulaArgSetup + WAVE/T pg.tableFormulas = tableFormulas + + Make/FREE=1/WAVE/N=(MINIMUM_WAVE_SIZE) collPlotFormData + WAVE pg.collPlotFormData = collPlotFormData + + Make/FREE=1/D/N=2 panelsCreated + SetDimLabel ROWS, 0, GRAPH, panelsCreated + SetDimLabel ROWS, 1, TABLE, panelsCreated + WAVE pg.panelsCreated = panelsCreated + + WAVE/Z/WAVE pg.formulaResults = $"" + WAVE/Z/T pg.plotMetaData = $"" + + return [pg] +End + /// @brief Plot the formula using the data from graph /// /// @param graph graph to pass to SF_FormulaExecutor @@ -1267,13 +1369,12 @@ End /// @param lineVars [optional, default NaN] number of lines in the SF notebook with variable assignments in front of the formula static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode, variable lineVars]) - variable i, dataCnt, splitTraces, numGraphs, traceCnt - variable winDisplayMode, showLegend, line, lineGraph, lineGraphFormula - variable keepUserSelection, formulasAreDifferent, postPlotPSX - variable formulaCounter, xFormulaOffset - variable numTableFormulas, formulaAddedOncePerDataset, showInTable - string win, wList, xAxis - string formulasRemain, moreFormulas, yAndXFormula, xFormula, yFormula, winHook + variable i, j, k, dataCnt, numGraphs, numPlotAND, numPlotWITH + variable winDisplayMode, line, lineGraph, lineGraphFormula, xFormulaOffset + variable keepUserSelection, showInTable, isFullPlot + string wList + string formulasRemain, moreFormulas, yAndXFormula, xFormula, yFormula + STRUCT SF_PlotterGraphStruct pg winDisplayMode = ParamIsDefault(dmMode) ? SF_DM_SUBWINDOWS : dmMode lineVars = ParamIsDefault(lineVars) ? NaN : lineVars @@ -1294,30 +1395,12 @@ static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode WAVE/T winGraphs = outputWindows[%GRAPH] WAVE/WAVE prevPlotProperties = GetSFPlotProperties() - Make/FREE/D/N=2 panelsCreated - SetDimLabel ROWS, 0, GRAPH, panelsCreated - SetDimLabel ROWS, 1, TABLE, panelsCreated - for(i = 0; i < numGraphs; i += 1) - traceCnt = 0 - postPlotPSX = 0 - showLegend = 1 - formulaCounter = 0 - formulasAreDifferent = 0 - WAVE/Z colorGroups = $"" - FastOp panelsCreated = 0 - - Make/FREE/T/N=0 xAxisLabels, yAxisLabels - formulasRemain = graphCode[i][%GRAPHCODE] lineGraph = str2num(graphCode[i][%LINE]) - Make/FREE=1/T/N=(MINIMUM_WAVE_SIZE) wAnnotations, formulaArgSetup, tableFormulas - SetNumberInWaveNote(wAnnotations, NOTE_INDEX, 0) - SetNumberInWaveNote(formulaArgSetup, NOTE_INDEX, 0) - SetNumberInWaveNote(tableFormulas, NOTE_INDEX, 0) - Make/FREE=1/WAVE/N=(MINIMUM_WAVE_SIZE) collPlotFormData + [pg] = SF_InitPlotterGraphStruct(graph) do @@ -1334,73 +1417,70 @@ static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode try [WAVE/WAVE formulaResults, WAVE/T plotMetaData] = SF_GatherFormulaResults(xFormula, yFormula, graph, line, xFormulaOffset) + WAVE/WAVE pg.formulaResults = formulaResults + WAVE/T pg.plotMetaData = plotMetaData catch SF_KillEmptyDataWindows(winGraphs) SF_KillEmptyDataWindows(winTables) Abort endtry - SF_GatherAxisLabels(formulaResults, plotMetaData[%XAXISLABEL], "FORMULAX", xAxisLabels) - SF_GatherAxisLabels(formulaResults, plotMetaData[%YAXISLABEL], "FORMULAY", yAxisLabels) - - showInTable = SF_IsDataForTableDisplay(formulaResults) - if(!panelsCreated[%GRAPH] && !showInTable) - win = SF_CreateDataDisplayWindow(graph, formulaResults, outputWindows, winDisplayMode, prevPlotProperties) - panelsCreated[%GRAPH] = 1 - if(winDisplaymode == SF_DM_NORMAL) - wList = AddListItem(win, wList) - endif - elseif(!panelsCreated[%TABLE] && showInTable) - win = SF_CreateDataDisplayWindow(graph, formulaResults, outputWindows, winDisplayMode, prevPlotProperties) - panelsCreated[%TABLE] = 1 - if(winDisplaymode == SF_DM_NORMAL) - wList = AddListItem(win, wList) + isFullPlot = SF_IsDataForFullPlotting(formulaResults) + numPlotAND = isFullPlot ? DimSize(formulaResults, ROWS) : 1 + for(j = 0; j < numPlotAND; j += 1) + if(isFullPlot) + WAVE/WAVE plotsWITH = formulaResults[j][%FORMULAY] endif - elseif(!showInTable) - win = winGraphs[GetNumberFromWaveNote(winGraphs, NOTE_INDEX) - 1] - else - win = winTables[GetNumberFromWaveNote(winTables, NOTE_INDEX) - 1] - endif - - if(!cmpstr(plotMetaData[%DATATYPE], SF_DATATYPE_PSX)) - PSX_Plot(win, graph, formulaResults, plotMetaData) - postPlotPSX = 1 - continue - endif - - [dataCnt, traceCnt, colorGroups, showLegend] = SF_CreateTracesForResults(graph, formulaResults, formulaCounter, plotMetaData, win, tableFormulas, wAnnotations, formulaArgSetup, collPlotFormData) - formulaCounter += 1 - while(1) + numPlotWITH = isFullPlot ? DimSize(plotsWITH, ROWS) : 1 + for(k = 0; k < numPlotWITH; k += 1) + if(isFullPlot) + WAVE/Z/WAVE wvYRef = plotsWITH[k][%FORMULAY] + WAVE/Z/WAVE wvXRef = plotsWITH[k][%FORMULAX] + [WAVE/WAVE formulaResultsInner, WAVE/T plotMetaDataInner] = SF_FillFormulaResults(wvYRef, wvXRef, yFormula) + WAVE/WAVE pg.formulaResults = formulaResultsInner + WAVE/T pg.plotMetaData = plotMetaDataInner + endif - numTableFormulas = GetNumberFromWaveNote(tableFormulas, NOTE_INDEX) - if(numTableFormulas) - Redimension/N=(numTableFormulas) tableFormulas - SetWindow $win, userdata($SF_UDATA_TABLEFORMULAS)=WaveToJSON(tableFormulas) - endif + SF_GatherAxisLabels(pg.formulaResults, plotMetaData[%XAXISLABEL], "FORMULAX", pg.xAxisLabels) + SF_GatherAxisLabels(pg.formulaResults, plotMetaData[%YAXISLABEL], "FORMULAY", pg.yAxisLabels) - if(panelsCreated[%GRAPH]) - win = winGraphs[GetNumberFromWaveNote(winGraphs, NOTE_INDEX) - 1] - if(showLegend) - formulasAreDifferent = SF_AddPlotLegend(win, wAnnotations, formulaArgSetup, formulaResults) - endif - - SF_AddPlotTicks(graph, win, formulaResults) + showInTable = SF_IsDataForTableDisplay(pg.formulaResults) + if(!pg.panelsCreated[%GRAPH] && !showInTable) + pg.win = SF_CreateDataDisplayWindow(pg.graph, pg.formulaResults, outputWindows, winDisplayMode, prevPlotProperties) + pg.panelsCreated[%GRAPH] = 1 + if(winDisplaymode == SF_DM_NORMAL) + wList = AddListItem(pg.win, wList) + endif + elseif(!pg.panelsCreated[%TABLE] && showInTable) + pg.win = SF_CreateDataDisplayWindow(pg.graph, pg.formulaResults, outputWindows, winDisplayMode, prevPlotProperties) + pg.panelsCreated[%TABLE] = 1 + if(winDisplaymode == SF_DM_NORMAL) + wList = AddListItem(pg.win, wList) + endif + elseif(!showInTable) + pg.win = winGraphs[GetNumberFromWaveNote(winGraphs, NOTE_INDEX) - 1] + else + pg.win = winTables[GetNumberFromWaveNote(winTables, NOTE_INDEX) - 1] + endif - winHook = JWN_GetStringFromWaveNote(formulaResults, SF_META_WINDOW_HOOK) - if(!IsEmpty(winHook)) - SetWindow $win, tooltipHook(SweepFormulaTraceValue)=$winHook - endif + if(!cmpstr(plotMetaData[%DATATYPE], SF_DATATYPE_PSX)) + PSX_Plot(pg.win, graph, pg.formulaResults, pg.plotMetaData) + pg.postPlotPSX = 1 + break + endif - SF_AddPlotTraceStyle(graph, win, formulaCounter, collPlotFormData, formulasAreDifferent) + [dataCnt, pg] = SF_CreateTracesForResults() + pg.formulaCounter += 1 + endfor - if(traceCnt > 0) - SF_AddPlotLabels(win, xAxisLabels, yAxisLabels) - endif - endif + if(j < (numPlotAND - 1)) + [pg] = SF_FinishPlotWindow(winGraphs) + [pg] = SF_InitPlotterGraphStruct(graph) + endif + endfor + while(1) - if(postPlotPSX) - PSX_PostPlot(win) - endif + [pg] = SF_FinishPlotWindow(winGraphs) endfor @@ -1417,13 +1497,51 @@ static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode SF_KillOldDataDisplayWindows(graph, winDisplayMode, wList, outputWindows) End -static Function SF_AddPlotTraceStyle(string graph, string win, variable formulaCounter, WAVE/WAVE collPlotFormData, variable formulasAreDifferent) +static Function [STRUCT SF_PlotterGraphStruct pg] SF_FinishPlotWindow(WAVE/T winGraphs) + + variable formulasAreDifferent, numTableFormulas + string winHook + + numTableFormulas = GetNumberFromWaveNote(pg.tableFormulas, NOTE_INDEX) + if(numTableFormulas) + Redimension/N=(numTableFormulas) pg.tableFormulas + SetWindow $pg.win, userdata($SF_UDATA_TABLEFORMULAS)=WaveToJSON(pg.tableFormulas) + endif + + if(pg.panelsCreated[%GRAPH]) + pg.win = winGraphs[GetNumberFromWaveNote(winGraphs, NOTE_INDEX) - 1] + if(pg.showLegend) + [formulasAreDifferent, pg] = SF_AddPlotLegend() + endif + + SF_AddPlotTicks(pg.graph, pg.win, pg.formulaResults) + + winHook = JWN_GetStringFromWaveNote(pg.formulaResults, SF_META_WINDOW_HOOK) + if(!IsEmpty(winHook)) + SetWindow $pg.win, tooltipHook(SweepFormulaTraceValue)=$winHook + endif + + [pg] = SF_AddPlotTraceStyle(formulasAreDifferent) + + if(pg.traceCnt > 0) + SF_AddPlotLabels(pg.win, pg.xAxisLabels, pg.yAxisLabels) + endif + endif + + if(pg.postPlotPSX) + PSX_PostPlot(pg.win) + endif + + return [pg] +End + +static Function [STRUCT SF_PlotterGraphStruct pg] SF_AddPlotTraceStyle(variable formulasAreDifferent) variable i, j, numTraces, markerCode, lineCode, isCategoryAxis, tagCounter, lineStyle, overrideMarker, traceToFront string trace, info, tagText, name, wvName - for(i = 0; i < formulaCounter; i += 1) - WAVE/WAVE plotFormData = collPlotFormData[i] + for(i = 0; i < pg.formulaCounter; i += 1) + WAVE/WAVE plotFormData = pg.collPlotFormData[i] WAVE/T tracesInGraph = plotFormData[0] WAVE/WAVE dataInGraph = plotFormData[1] numTraces = DimSize(tracesInGraph, ROWS) @@ -1437,7 +1555,7 @@ static Function SF_AddPlotTraceStyle(string graph, string win, variable formulaC WAVE wvY = dataInGraph[j][%WAVEY] trace = tracesInGraph[j] - info = AxisInfo(win, "left") + info = AxisInfo(pg.win, "left") isCategoryAxis = (NumberByKey("ISCAT", info) == 1) if(isCategoryAxis) @@ -1450,10 +1568,10 @@ static Function SF_AddPlotTraceStyle(string graph, string win, variable formulaC if(WaveExists(traceColor)) switch(DimSize(traceColor, ROWS)) case 3: - ModifyGraph/W=$win rgb($trace)=(traceColor[0], traceColor[1], traceColor[2]) + ModifyGraph/W=$pg.win rgb($trace)=(traceColor[0], traceColor[1], traceColor[2]) break case 4: - ModifyGraph/W=$win rgb($trace)=(traceColor[0], traceColor[1], traceColor[2], traceColor[3]) + ModifyGraph/W=$pg.win rgb($trace)=(traceColor[0], traceColor[1], traceColor[2], traceColor[3]) break default: FATAL_ERROR("Invalid size of trace color wave") @@ -1463,25 +1581,25 @@ static Function SF_AddPlotTraceStyle(string graph, string win, variable formulaC tagText = JWN_GetStringFromWaveNote(wvY, SF_META_TAG_TEXT) if(!IsEmpty(tagText)) name = "tag" + num2str(tagCounter++) - Tag/C/N=$name/W=$win/F=0/L=0/X=0.00/Y=0.00 $trace, 0, tagText + Tag/C/N=$name/W=$pg.win/F=0/L=0/X=0.00/Y=0.00 $trace, 0, tagText endif - ModifyGraph/W=$win mode($trace)=SF_DeriveTraceDisplayMode(wvX, wvY) + ModifyGraph/W=$pg.win mode($trace)=SF_DeriveTraceDisplayMode(wvX, wvY) lineStyle = JWN_GetNumberFromWaveNote(wvY, SF_META_LINESTYLE) if(IsValidTraceLineStyle(lineStyle)) - ModifyGraph/W=$win lStyle($trace)=lineStyle + ModifyGraph/W=$pg.win lStyle($trace)=lineStyle elseif(formulasAreDifferent) - ModifyGraph/W=$win lStyle($trace)=lineCode + ModifyGraph/W=$pg.win lStyle($trace)=lineCode endif WAVE/Z customMarkerAsFree = JWN_GetNumericWaveFromWaveNote(wvY, SF_META_MOD_MARKER) if(WaveExists(customMarkerAsFree)) - DFREF dfrWork = SFH_GetWorkingDF(graph) + DFREF dfrWork = SFH_GetWorkingDF(pg.graph) wvName = "customMarker_" + NameOfWave(wvY) WAVE customMarker = MoveFreeWaveToPermanent(customMarkerAsFree, dfrWork, wvName) ASSERT(DimSize(wvY, ROWS) == DimSize(customMarker, ROWS), "Marker size mismatch") - ModifyGraph/W=$win zmrkNum($trace)={customMarker} + ModifyGraph/W=$pg.win zmrkNum($trace)={customMarker} else overrideMarker = JWN_GetNumberFromWaveNote(wvY, SF_META_MOD_MARKER) @@ -1489,13 +1607,13 @@ static Function SF_AddPlotTraceStyle(string graph, string win, variable formulaC markerCode = overrideMarker endif - ModifyGraph/W=$win marker($trace)=markerCode + ModifyGraph/W=$pg.win marker($trace)=markerCode endif traceToFront = JWN_GetNumberFromWaveNote(wvY, SF_META_TRACETOFRONT) traceToFront = IsNaN(traceToFront) ? 0 : !!traceToFront if(traceToFront) - ReorderTraces/W=$win _front_, {$trace} + ReorderTraces/W=$pg.win _front_, {$trace} endif endfor @@ -1541,30 +1659,32 @@ static Function SF_AddPlotTicks(string graph, string win, WAVE formulaResults) endif End -static Function SF_AddPlotLegend(string win, WAVE/T wAnnotations, WAVE formulaArgSetup, WAVE formulaResults) +static Function [variable formulasAreDifferent, STRUCT SF_PlotterGraphStruct pg] SF_AddPlotLegend() - variable numAnnotations, formulasAreDifferent - string customLegend + variable numAnnotations + string customLegend string annotation = "" - numAnnotations = GetNumberFromWaveNote(wAnnotations, NOTE_INDEX) - customLegend = JWN_GetStringFromWaveNote(formulaResults, SF_META_CUSTOM_LEGEND) + formulasAreDifferent = 0 + + numAnnotations = GetNumberFromWaveNote(pg.wAnnotations, NOTE_INDEX) + customLegend = JWN_GetStringFromWaveNote(pg.formulaResults, SF_META_CUSTOM_LEGEND) if(!IsEmpty(customLegend)) annotation = customLegend elseif(numAnnotations > 0) - wAnnotations[0, numAnnotations - 1] = SF_ShrinkLegend(wAnnotations[p]) - Redimension/N=(numAnnotations) wAnnotations, formulaArgSetup - formulasAreDifferent = SFH_EnrichAnnotations(wAnnotations, formulaArgSetup) - annotation = TextWaveToList(wAnnotations, "\r") + pg.wAnnotations[0, numAnnotations - 1] = SF_ShrinkLegend(pg.wAnnotations[p]) + Redimension/N=(numAnnotations) pg.wAnnotations, pg.formulaArgSetup + formulasAreDifferent = SFH_EnrichAnnotations(pg.wAnnotations, pg.formulaArgSetup) + annotation = TextWaveToList(pg.wAnnotations, "\r") annotation = UnPadString(annotation, char2num("\r")) endif if(!IsEmpty(annotation)) - Legend/W=$win/C/N=metadata/F=2 annotation + Legend/W=$pg.win/C/N=metadata/F=2 annotation endif - return formulasAreDifferent + return [formulasAreDifferent, pg] End static Function SF_AddPlotLabels(string win, WAVE xAxisLabels, WAVE yAxisLabels) From 8fc356ef9306a7e719bd3b077c7a67fbd43144ae Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 15 Dec 2025 20:47:25 +0100 Subject: [PATCH 3/8] SF: Add operation ivscc_apfrequency - Add plot property support for axisOffsets and axisPercent An operation can set these through SF_META_XAXISOFFSET, SF_META_YAXISOFFSET, SF_META_XAXISPERCENT and SF_META_YAXISPERCENT in the JSON wave note of the result wave. The plotter uses the settings from the last result in a "with" block. e.g. op_that_sets_axisoffet() with 1 would ignore the plot properties set by the first operation, whereas 1 with op_that_sets_axisoffet() would apply it. --- Packages/MIES/MIES_Constants.ipf | 5 + Packages/MIES/MIES_SweepFormula.ipf | 42 ++++- Packages/MIES/MIES_SweepFormula_Executor.ipf | 3 + Packages/MIES/MIES_SweepFormula_Helpers.ipf | 37 +++++ .../MIES/MIES_SweepFormula_Operations.ipf | 151 ++++++++++++++++++ Packages/MIES/MIES_WaveDataFolderGetters.ipf | 4 +- 6 files changed, 239 insertions(+), 3 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 4d7c00b8cb..9db77cff89 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2142,6 +2142,10 @@ StrConstant SF_META_DONOTPLOT = "/DoNotPlot" // number, boolean, StrConstant SF_META_WINDOW_HOOK = "/WindowHook" // string StrConstant SF_META_FORMULA = "/Formula" // string StrConstant SF_META_PLOT = "/Plot" // number, boolean, defaults to false (0) +StrConstant SF_META_XAXISOFFSET = "/XAxisOffset" // number +StrConstant SF_META_YAXISOFFSET = "/YAxisOffset" // number +StrConstant SF_META_XAXISPERCENT = "/XAxisPercent" // number +StrConstant SF_META_YAXISPERCENT = "/YAxisPercent" // number /// A color group allows to have matching colors for sweep data with the same channel type/number and sweep. /// It is applied before the matching headstage/average colors in #SF_GetTraceColor(). @@ -2542,6 +2546,7 @@ StrConstant SF_OP_TPINST = "tpinst" StrConstant SF_OP_TPBASE = "tpbase" StrConstant SF_OP_TPFIT = "tpfit" StrConstant SF_OP_EXTRACT = "extract" +StrConstant SF_OP_IVSCCAPFREQUENCY = "ivscc_apfrequency" ///@} StrConstant SF_PROPERTY_TABLE = "Table" diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 1669056ec8..d86decc570 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -96,7 +96,8 @@ Function/WAVE SF_GetNamedOperations() SF_OP_MERGE, SF_OP_FIT, SF_OP_FITLINE, SF_OP_DATASET, SF_OP_SELECTVIS, SF_OP_SELECTCM, SF_OP_SELECTSTIMSET, \ SF_OP_SELECTIVSCCSWEEPQC, SF_OP_SELECTIVSCCSETQC, SF_OP_SELECTRANGE, SF_OP_SELECTEXP, SF_OP_SELECTDEV, \ SF_OP_SELECTEXPANDSCI, SF_OP_SELECTEXPANDRAC, SF_OP_SELECTSETCYCLECOUNT, SF_OP_SELECTSETSWEEPCOUNT, \ - SF_OP_SELECTSCIINDEX, SF_OP_SELECTRACINDEX, SF_OP_ANAFUNCPARAM, SF_OP_CONCAT, SF_OP_TABLE, SF_OP_EXTRACT} + SF_OP_SELECTSCIINDEX, SF_OP_SELECTRACINDEX, SF_OP_ANAFUNCPARAM, SF_OP_CONCAT, SF_OP_TABLE, SF_OP_EXTRACT, \ + SF_OP_IVSCCAPFREQUENCY} return wt End @@ -171,6 +172,10 @@ static Function/WAVE SF_FillPlotMetaData(WAVE wvYRef, variable useXLabel, string plotMetaData[%ARGSETUPSTACK] = JWN_GetStringFromWaveNote(wvYRef, SF_META_ARGSETUPSTACK) plotMetaData[%XAXISLABEL] = SelectString(useXLabel, SF_XLABEL_USER, JWN_GetStringFromWaveNote(wvYRef, SF_META_XAXISLABEL)) plotMetaData[%YAXISLABEL] = JWN_GetStringFromWaveNote(wvYRef, SF_META_YAXISLABEL) + dataUnits + plotMetaData[%XAXISOFFSET] = num2str(JWN_GetNumberFromWaveNote(wvYRef, SF_META_XAXISOFFSET), "%f") + plotMetaData[%YAXISOFFSET] = num2str(JWN_GetNumberFromWaveNote(wvYRef, SF_META_YAXISOFFSET), "%f") + plotMetaData[%XAXISPERCENT] = num2str(JWN_GetNumberFromWaveNote(wvYRef, SF_META_XAXISPERCENT), "%f") + plotMetaData[%YAXISPERCENT] = num2str(JWN_GetNumberFromWaveNote(wvYRef, SF_META_YAXISPERCENT), "%f") return plotMetaData End @@ -1497,6 +1502,31 @@ static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode SF_KillOldDataDisplayWindows(graph, winDisplayMode, wList, outputWindows) End +/// @brief Sets axis properties for plots of the SF formula plotter. The properties are stored in the plotMetaData wave. +static Function [STRUCT SF_PlotterGraphStruct pg] SF_SetAxisProperties() + + variable xaxisOffset, yaxisOffset, xaxisPercent, yaxisPercent + + xaxisOffset = str2num(pg.plotMetaData[%XAXISOFFSET]) + if(!IsNaN(xaxisOffset)) + ModifyGraph/W=$pg.win axOffset(bottom)=xaxisOffset + endif + yaxisOffset = str2num(pg.plotMetaData[%YAXISOFFSET]) + if(!IsNaN(yaxisOffset)) + ModifyGraph/W=$pg.win axOffset(left)=yaxisOffset + endif + xaxisPercent = str2num(pg.plotMetaData[%XAXISPERCENT]) + if(!IsNaN(xaxisPercent)) + ModifyGraph/W=$pg.win axisEnab(bottom)={0, xaxisPercent * PERCENT_TO_ONE} + endif + yaxisPercent = str2num(pg.plotMetaData[%YAXISPERCENT]) + if(!IsNaN(yaxisPercent)) + ModifyGraph/W=$pg.win axisEnab(left)={0, yaxisPercent * PERCENT_TO_ONE} + endif + + return [pg] +End + static Function [STRUCT SF_PlotterGraphStruct pg] SF_FinishPlotWindow(WAVE/T winGraphs) variable formulasAreDifferent, numTableFormulas @@ -1509,7 +1539,11 @@ static Function [STRUCT SF_PlotterGraphStruct pg] SF_FinishPlotWindow(WAVE/T win endif if(pg.panelsCreated[%GRAPH]) + pg.win = winGraphs[GetNumberFromWaveNote(winGraphs, NOTE_INDEX) - 1] + + [pg] = SF_SetAxisProperties() + if(pg.showLegend) [formulasAreDifferent, pg] = SF_AddPlotLegend() endif @@ -2825,3 +2859,9 @@ Function SF_TableWindowHook(STRUCT WMWinHookStruct &s) return 0 End + +/// @brief Adds an expression to a formula string with the proper termination character +Function/S SF_AddExpressionToFormula(string formula, string expr) + + return formula + expr + SF_CHAR_CR +End diff --git a/Packages/MIES/MIES_SweepFormula_Executor.ipf b/Packages/MIES/MIES_SweepFormula_Executor.ipf index e6ba507602..5559358041 100644 --- a/Packages/MIES/MIES_SweepFormula_Executor.ipf +++ b/Packages/MIES/MIES_SweepFormula_Executor.ipf @@ -517,6 +517,9 @@ Function/WAVE SFE_FormulaExecutor(STRUCT SF_ExecutionData &exd, [variable srcLoc case SF_OP_TABLE: WAVE out = SFO_OperationTable(exdop) break + case SF_OP_IVSCCAPFREQUENCY: + WAVE out = SFO_OperationIVSCCApFrequency(exdop) + break default: SFH_FATAL_ERROR("Undefined Operation", jsonId = exdop.jsonId) endswitch diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index d0e9ba1753..d47242fd25 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -2173,3 +2173,40 @@ Function/WAVE SFH_GetDatasetArrayAsResolvedWaverefs(STRUCT SF_ExecutionData &exd return dataFromEachGroup End + +/// @brief Executes a formula from within an operation with low overhead +/// - the currently active variable storage is used +/// - the formula string is not preprocessed +Function/WAVE SFH_ExecuteFormulaInternal(string graph, string formula) + + STRUCT SF_ExecutionData exd + variable jsonId, srcLocId + + exd.graph = graph + [jsonId, srcLocId] = SFP_ParseFormulaToJSON(formula) + exd.jsonId = jsonId + WAVE dataRef = SFE_FormulaExecutor(exd, srcLocId = srcLocId) + + JSON_Release(exd.jsonId) + JSON_Release(srcLocId) + + WAVE resolved = SF_ResolveDataset(dataRef) + + return resolved +End + +/// @brief Adds a variable to the variable storage. If the variable already exists it is overwritten. +Function SFH_AddVariableToStorage(string graph, string name, WAVE result) + + variable size, idx + + WAVE/WAVE varStorage = GetSFVarStorage(graph) + idx = FindDimLabel(varStorage, ROWS, name) + if(idx == -2) + size = DimSize(varStorage, ROWS) + Redimension/N=(size + 1) varStorage + idx = size + endif + varStorage[idx] = result + SetDimLabel ROWS, idx, $name, varStorage +End diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 559b36f65d..d39a91c365 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -30,6 +30,11 @@ static StrConstant SF_OP_APFREQUENCY_NONORM = "nonorm" static StrConstant SF_OP_APFREQUENCY_X_COUNT = "count" static StrConstant SF_OP_APFREQUENCY_X_TIME = "time" +static StrConstant SF_OP_IVSCCAPFREQUENCY_FIRST = "first" +static StrConstant SF_OP_IVSCCAPFREQUENCY_MIN = "min" +static StrConstant SF_OP_IVSCCAPFREQUENCY_MAX = "max" +static StrConstant SF_OP_IVSCCAPFREQUENCY_NONE = "none" + static StrConstant SF_OP_AVG_INSWEEPS = "in" static StrConstant SF_OP_AVG_OVERSWEEPS = "over" static StrConstant SF_OP_AVG_GROUPS = "group" @@ -2488,3 +2493,149 @@ Function/WAVE SFO_OperationTable(STRUCT SF_ExecutionData &exd) return SFH_GetOutputForExecutor(output, exd.graph, SF_OP_TABLE) End + +/// @brief Sets the plot meta data for the ivscc_apfrequency operation +static Function SFO_OperationIVSCCApFrequencySetPlotProperties(WAVE wvY, variable xAxisPercentage, variable yAxisPercentage) + + JWN_SetNumberInWaveNote(wvY, SF_META_XAXISPERCENT, xAxisPercentage) + JWN_SetNumberInWaveNote(wvY, SF_META_YAXISPERCENT, yAxisPercentage) +End + +// ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage]) +Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) + + string opShort = SF_OP_IVSCCAPFREQUENCY + variable numArgsMin = 0 + variable numArgsMax = 9 + string formula, expr, exprPart + variable i, numArgs, col, size, numExp + variable xAxisPercentage, yAxisPercentage + string xaxisOffset, yaxisOffset + variable method, level + string timeFreq, normalize, xAxisType + STRUCT RGBColor s + + SFH_ASSERT(BSP_IsSweepBrowser(exd.graph), "ivscc_apfrequency only works with sweepbrowser") + + numArgs = SFH_GetNumberOfArguments(exd) + SFH_ASSERT(numArgs <= numArgsMax, "ivscc_apfrequency has " + num2istr(numArgsMax) + " arguments at most.") + SFH_ASSERT(numArgs >= numArgsMin, "ivscc_apfrequency needs at least " + num2istr(numArgsMin) + " argument(s).") + + xaxisOffset = SFH_GetArgumentAsText(exd, opShort, 0, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) + yaxisOffset = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) + xAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 2, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) + yAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 3, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) + [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 4) + + WAVE/T sweepMap = SB_GetSweepMap(exd.graph) + col = FindDimlabel(sweepMap, COLS, "FileName") + size = GetNumberFromWaveNote(sweepMap, NOTE_INDEX) + Duplicate/FREE/RMD=[0, size - 1][col] sweepMap, fileNames + WAVE/T uniqueFiles = GetUniqueEntries(fileNames, dontDuplicate = 1) + Sort uniqueFiles, uniqueFiles + numExp = DimSize(uniqueFiles, ROWS) + SFH_ASSERT(numExp > 0, "ivscc_apfrequency: data from at least one experiment has to be loaded") + + Make/FREE/T/N=(numExp) elems + + formula = "sel = select(selsweeps(), selstimset(\"*rheo*\", \"*supra*\"), selvis(all), selivsccsweepqc(passed))\r" + for(i = 0; i < numExp; i += 1) + sprintf expr, "selexpAD%d = select(selexp(\"%s\"), $sel, selchannels(AD0), selrange(E1))", i, uniqueFiles[i] + formula = SF_AddExpressionToFormula(formula, expr) + sprintf expr, "selexpDA%d = select(selexp(\"%s\"), $sel, selchannels(DA0), selrange(E1))", i, uniqueFiles[i] + formula = SF_AddExpressionToFormula(formula, expr) + sprintf expr, "freq%d = apfrequency(data($selexpAD%d), %d, %f, %s, %s, %s)", i, i, method, level, timeFreq, normalize, xAxisType + formula = SF_AddExpressionToFormula(formula, expr) + sprintf expr, "current%d = max(data($selexpDA%d))", i, i + formula = SF_AddExpressionToFormula(formula, expr) + if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) + sprintf expr, "currentNorm%d = $current%d - extract($current%d, 0)", i, i, i + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) + sprintf expr, "currentNorm%d = $current%d - min(merge($current%d))", i, i, i + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) + sprintf expr, "currentNorm%d = $current%d - max(merge($current%d))", i, i, i + else // SF_OP_IVSCCAPFREQUENCY_NONE + sprintf expr, "currentNorm%d = $current%d", i, i + endif + formula = SF_AddExpressionToFormula(formula, expr) + endfor + + elems[] = "$freq" + num2istr(p) + expr = "ivsccavg = avg([" + TextWaveToList(elems, ",", trailSep = 0) + "], group)" + formula = SF_AddExpressionToFormula(formula, expr) + + elems[] = "$currentNorm" + num2istr(p) + expr = "ivscccurrentavg = avg([" + TextWaveToList(elems, ",", trailSep = 0) + "], group)" + formula = SF_AddExpressionToFormula(formula, expr) + + elems[] = "\"" + uniqueFiles[p] + "\"" + expr = "ivscc_apfrequency_explist = [" + TextWaveToList(elems, ",", trailSep = 0) + "]" + formula = SF_AddExpressionToFormula(formula, expr) + + WAVE/WAVE varStorage = GetSFVarStorage(exd.graph) + Duplicate/FREE varStorage, varBackup + SFE_ExecuteVariableAssignments(exd.graph, formula) + + WAVE/WAVE varStorageOp = GetSFVarStorage(exd.graph) + WAVE wvResult = varStorageOp[%ivscc_apfrequency_explist] + + WAVE/WAVE plotAND = SFH_CreateSFRefWave(exd.graph, opShort, 1) + Make/FREE/WAVE/N=(numExp + 1, 2) plotWITH + SetDimlabel COLS, 0, FORMULAX, plotWITH + SetDimlabel COLS, 1, FORMULAY, plotWITH + plotAND[0] = plotWITH + + for(i = 0; i < numExp; i += 1) + if(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) + sprintf formula, "merge($freq%d - extract($freq%d, 0))", i, i + elseif(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) + sprintf formula, "merge($freq%d - min(merge($freq%d)))", i, i + elseif(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) + sprintf formula, "merge($freq%d - max(merge($freq%d)))", i, i + else // SF_OP_IVSCCAPFREQUENCY_NONE + sprintf formula, "merge($freq%d)", i + endif + WAVE/WAVE wvY = SFH_ExecuteFormulaInternal(exd.graph, formula) + [s] = GetTraceColor(i) + Make/FREE/W/U traceColor = {s.red, s.green, s.blue} + JWN_SetWaveInWaveNote(wvY[0], SF_META_TRACECOLOR, traceColor) + JWN_SetNumberInWaveNote(wvY[0], SF_META_MOD_MARKER, 17) + JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, uniqueFiles[i]) + plotWITH[i][%FORMULAY] = wvY + SFO_OperationIVSCCApFrequencySetPlotProperties(plotWITH[i][%FORMULAY], xAxisPercentage, yAxisPercentage) + sprintf formula, "merge($currentNorm%d)", i + plotWITH[i][%FORMULAX] = SFH_ExecuteFormulaInternal(exd.graph, formula) + endfor + + if(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) + formula = "merge($ivsccavg - extract($ivsccavg, 0))" + elseif(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) + formula = "merge($ivsccavg - min(merge($ivsccavg)))" + elseif(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) + formula = "merge($ivsccavg - max(merge($ivsccavg)))" + else // SF_OP_IVSCCAPFREQUENCY_NONE + formula = "merge($ivsccavg)" + endif + WAVE/WAVE wvY = SFH_ExecuteFormulaInternal(exd.graph, formula) + JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, "ivscc_apfrequency average") + plotWITH[i][%FORMULAY] = wvY + SFO_OperationIVSCCApFrequencySetPlotProperties(plotWITH[i][%FORMULAY], xAxisPercentage, yAxisPercentage) + + if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) + formula = "merge($ivscccurrentavg - extract($ivscccurrentavg, 0))" + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) + formula = "merge($ivscccurrentavg - min(merge($ivscccurrentavg)))" + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) + formula = "merge($ivscccurrentavg - max(merge($ivscccurrentavg)))" + else // SF_OP_IVSCCAPFREQUENCY_NONE + formula = "merge($ivscccurrentavg)" + endif + plotWITH[i][%FORMULAX] = SFH_ExecuteFormulaInternal(exd.graph, formula) + + Duplicate/O varBackup, varStorage + SFH_AddVariableToStorage(exd.graph, "ivscc_apfrequency_explist", wvResult) + + JWN_SetNumberInWaveNote(plotAND, SF_META_PLOT, 1) + + return SFH_GetOutputForExecutor(plotAND, exd.graph, opShort) +End diff --git a/Packages/MIES/MIES_WaveDataFolderGetters.ipf b/Packages/MIES/MIES_WaveDataFolderGetters.ipf index ec9685c2b9..d32227d065 100644 --- a/Packages/MIES/MIES_WaveDataFolderGetters.ipf +++ b/Packages/MIES/MIES_WaveDataFolderGetters.ipf @@ -9284,8 +9284,8 @@ End /// @brief Wave storing sf plot meta information per formularesult, filled in SF_GatherFormulaResults Function/WAVE GetSFPlotMetaData() - Make/FREE/T/N=(5) wv - SetDimensionLabels(wv, "DATATYPE;OPSTACK;ARGSETUPSTACK;XAXISLABEL;YAXISLABEL;", ROWS) + Make/FREE/T/N=(9) wv + SetDimensionLabels(wv, "DATATYPE;OPSTACK;ARGSETUPSTACK;XAXISLABEL;YAXISLABEL;XAXISOFFSET;YAXISOFFSET;XAXISPERCENT;YAXISPERCENT;", ROWS) return wv End From bc9c7ab5008d894e855004c44b012204c17ee37e Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 19 Dec 2025 16:46:23 +0100 Subject: [PATCH 4/8] SF: bugfix for avg operation with group mode The avg operation in group mode does a wavenote and meta data transfer. This transfer works only if the number of datasets in the output waves are equal to the number of datasets of the input waves. (If there would be less input than output waves, there would be no data for some output waves) The group mode did transfer always from the first group. If the first group contained less datasets than the group with the greatest number of datasets then the transfer failed. As the result of the group averaging always has the same number of datasets as the greatest group the wave note and meta data transfer is now done from the greatest input group. Since: 272c26b0 (SF: operation avg add mode to average over groups of data, 2025-11-12) --- Packages/MIES/MIES_SweepFormula_Operations.ipf | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index d39a91c365..b3c6183839 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -525,7 +525,6 @@ Function/WAVE SFO_OperationAvg(STRUCT SF_ExecutionData &exd) elseif(!CmpStr(mode, SF_OP_AVG_GROUPS)) WAVE/WAVE dataFromEachGroup = SFH_GetDatasetArrayAsResolvedWaverefs(exd, 0, resolveSelect = 1) WAVE/WAVE averagedGroup = SFO_OperationAvgImplSweepGroups(dataFromEachGroup, exd.graph, opShort) - SFH_TransferFormulaDataWaveNoteAndMeta(dataFromEachGroup[0], averagedGroup, opShort, SF_DATATYPE_AVG) return SFH_GetOutputForExecutor(averagedGroup, exd.graph, opShort) else @@ -535,7 +534,7 @@ End static Function/WAVE SFO_OperationAvgImplSweepGroups(WAVE/WAVE sweepsFromEachSelection, string graph, string opShort) - variable numData, numMaxSweeps, numGroups, i, j + variable numData, numMaxSweeps, numGroups, i, j, maxIdx STRUCT RGBColor s [s] = GetTraceColorForAverage() @@ -543,7 +542,9 @@ static Function/WAVE SFO_OperationAvgImplSweepGroups(WAVE/WAVE sweepsFromEachSel numGroups = DimSize(sweepsFromEachSelection, ROWS) Make/FREE/D/N=(numGroups) sweepCnts = DimSize(sweepsFromEachSelection[p], ROWS) - numMaxSweeps = WaveMax(sweepCnts) + WaveStats/Q/M=1 sweepCnts + numMaxSweeps = V_max + maxIdx = V_maxRowLoc WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, numMaxSweeps) for(i = 0; i < numMaxSweeps; i += 1) Make/FREE/WAVE/N=(numGroups) avgSet @@ -561,6 +562,7 @@ static Function/WAVE SFO_OperationAvgImplSweepGroups(WAVE/WAVE sweepsFromEachSel JWN_SetNumberInWaveNote(output[i], SF_META_TRACETOFRONT, 1) JWN_SetNumberInWaveNote(output[i], SF_META_LINESTYLE, 0) endfor + SFH_TransferFormulaDataWaveNoteAndMeta(sweepsFromEachSelection[maxIdx], output, opShort, SF_DATATYPE_AVG) return output End From 7bf96f540ef117bccf7e341c6caa9092030447c7 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sat, 20 Dec 2025 04:21:01 +0100 Subject: [PATCH 5/8] SF: Add always legendPrefix from meta data to annotation if present --- Packages/MIES/MIES_SweepFormula.ipf | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index d86decc570..c6c2989c77 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -292,7 +292,8 @@ static Function/S SF_GetTraceAnnotationText(WAVE/T plotMetaData, WAVE data) string channelId, prefix, legendPrefix string traceAnnotation, annotationPrefix - prefix = RemoveEnding(ReplaceString(";", plotMetaData[%OPSTACK], " "), " ") + prefix = RemoveEnding(ReplaceString(";", plotMetaData[%OPSTACK], " "), " ") + legendPrefix = JWN_GetStringFromWaveNote(data, SF_META_LEGEND_LINE_PREFIX) strswitch(plotMetaData[%DATATYPE]) case SF_DATATYPE_EPOCHS: // fallthrough @@ -300,8 +301,7 @@ static Function/S SF_GetTraceAnnotationText(WAVE/T plotMetaData, WAVE data) case SF_DATATYPE_LABNOTEBOOK: // fallthrough case SF_DATATYPE_ANAFUNCPARAM: // fallthrough case SF_DATATYPE_TP: - sweepNo = JWN_GetNumberFromWaveNote(data, SF_META_SWEEPNO) - legendPrefix = JWN_GetStringFromWaveNote(data, SF_META_LEGEND_LINE_PREFIX) + sweepNo = JWN_GetNumberFromWaveNote(data, SF_META_SWEEPNO) if(!IsEmpty(legendPrefix)) legendPrefix = " " + legendPrefix + " " @@ -320,7 +320,11 @@ static Function/S SF_GetTraceAnnotationText(WAVE/T plotMetaData, WAVE data) break default: if(WhichListItem(SF_OP_DATA, plotMetaData[%OPSTACK]) == -1) - sprintf traceAnnotation, "%s", prefix + if(!IsEmpty(legendPrefix)) + sprintf traceAnnotation, "%s %s", prefix, legendPrefix + else + sprintf traceAnnotation, "%s", prefix + endif else channelNumber = JWN_GetNumberFromWaveNote(data, SF_META_CHANNELNUMBER) channelType = JWN_GetNumberFromWaveNote(data, SF_META_CHANNELTYPE) From 4f5c3b06ad9fb3cbf024cfc0499763f042883e57 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 22 Dec 2025 21:14:38 +0100 Subject: [PATCH 6/8] SF: Add transfer of plotting meta data in merge operation --- Packages/MIES/MIES_SweepFormula_Helpers.ipf | 11 +++++++++++ Packages/MIES/MIES_SweepFormula_Operations.ipf | 2 ++ 2 files changed, 13 insertions(+) diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index d47242fd25..e57634b5de 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -2210,3 +2210,14 @@ Function SFH_AddVariableToStorage(string graph, string name, WAVE result) varStorage[idx] = result SetDimLabel ROWS, idx, $name, varStorage End + +/// @brief Copy plot meta data JSON properties from a source to a target wave +Function SFH_CopyPlotMetaData(WAVE input, WAVE output) + + WAVE/Z wv = JWN_GetNumericWaveFromWaveNote(input, SF_META_TRACECOLOR) + if(WaveExists(wv)) + JWN_SetWaveInWaveNote(output, SF_META_TRACECOLOR, wv) + endif + JWN_SetNumberInWaveNote(output, SF_META_TRACETOFRONT, JWN_GetNumberFromWaveNote(input, SF_META_TRACETOFRONT)) + JWN_SetNumberInWaveNote(output, SF_META_LINESTYLE, JWN_GetNumberFromWaveNote(input, SF_META_LINESTYLE)) +End diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index b3c6183839..e21e269a50 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -1579,6 +1579,8 @@ Function/WAVE SFO_OperationMerge(STRUCT SF_ExecutionData &exd) output[0] = content + SFH_CopyPlotMetaData(input[0], output[0]) + return SFH_GetOutputForExecutor(output, exd.graph, SF_OP_MERGE) End From 7dfe0bcf36dbac55f3ad518f02a5c7d8e1ae6837 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 23 Dec 2025 20:44:33 +0100 Subject: [PATCH 7/8] SF: Add bins mode to avg operation --- .../MIES/MIES_SweepFormula_Operations.ipf | 110 +++++++++++++++++- 1 file changed, 104 insertions(+), 6 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index e21e269a50..46456b85ac 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -38,6 +38,7 @@ static StrConstant SF_OP_IVSCCAPFREQUENCY_NONE = "none" static StrConstant SF_OP_AVG_INSWEEPS = "in" static StrConstant SF_OP_AVG_OVERSWEEPS = "over" static StrConstant SF_OP_AVG_GROUPS = "group" +static StrConstant SF_OP_AVG_BINS = "bins" static StrConstant SF_OP_EPOCHS_TYPE_RANGE = "range" static StrConstant SF_OP_EPOCHS_TYPE_NAME = "name" @@ -500,13 +501,13 @@ End Function/WAVE SFO_OperationAvg(STRUCT SF_ExecutionData &exd) - variable numArgs - string mode + variable numArgs, binWidth + string mode string opShort = SF_OP_AVG - numArgs = SFH_CheckArgumentCount(exd, opShort, 1, maxArgs = 2) + numArgs = SFH_CheckArgumentCount(exd, opShort, 1, maxArgs = 5) - mode = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_AVG_INSWEEPS, allowedValues = {SF_OP_AVG_INSWEEPS, SF_OP_AVG_OVERSWEEPS, SF_OP_AVG_GROUPS}) + mode = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_AVG_INSWEEPS, allowedValues = {SF_OP_AVG_INSWEEPS, SF_OP_AVG_OVERSWEEPS, SF_OP_AVG_GROUPS, SF_OP_AVG_BINS}) if(!CmpStr(mode, SF_OP_AVG_INSWEEPS) || !CmpStr(mode, SF_OP_AVG_OVERSWEEPS)) WAVE/WAVE input = SFH_GetArgumentAsWave(exd, opShort, 0, resolveSelect = 1) strswitch(mode) @@ -527,11 +528,108 @@ Function/WAVE SFO_OperationAvg(STRUCT SF_ExecutionData &exd) WAVE/WAVE averagedGroup = SFO_OperationAvgImplSweepGroups(dataFromEachGroup, exd.graph, opShort) return SFH_GetOutputForExecutor(averagedGroup, exd.graph, opShort) - else - FATAL_ERROR("Unhandled avg operation mode") + elseif(!CmpStr(mode, SF_OP_AVG_BINS)) + WAVE/WAVE dataFromEachGroup = SFH_GetDatasetArrayAsResolvedWaverefs(exd, 0, resolveSelect = 1) + WAVE/WAVE wTmp = SFH_GetArgumentAsWave(exd, opShort, 2) + WAVE binRange = wTmp[0] + SFH_ASSERT(DimSize(binRange, ROWS) == 2, "Expected range in the form of [start, end]") + SFH_ASSERT(binRange[1] > binRange[0], "The end of the bin range must be greater than the start") + binWidth = SFH_GetArgumentAsNumeric(exd, opShort, 3, checkFunc = IsStrictlyPositiveAndFinite) + WAVE/WAVE binData = SFH_GetDatasetArrayAsResolvedWaverefs(exd, 4, resolveSelect = 1) + SFH_ASSERT(DimSize(dataFromEachGroup, ROWS) == DimSize(binData, ROWS), "input data and bin data must have the same number of groups") + WAVE/WAVE averagedBins = SFO_OperationAvgImplBins(dataFromEachGroup, exd.graph, opShort, binData, binRange, binWidth) + return SFH_GetOutputForExecutor(averagedBins, exd.graph, opShort) endif End +static Function/WAVE SFO_OperationAvgImplBins(WAVE/WAVE input, string graph, string opShort, WAVE/WAVE binData, WAVE binRange, variable binWidth) + + variable i, j, numBins, binStart, binEnd, numGroups, numDataSets, binValue, binPos, idx + STRUCT RGBColor s + + [s] = GetTraceColorForAverage() + Make/FREE/W/U traceColor = {s.red, s.green, s.blue} + + numGroups = DimSize(input, ROWS) + binStart = binRange[0] + binEnd = binRange[1] + numBins = ceil((binEnd - binStart) / binWidth) + SFH_ASSERT(numBins < 1E6, "Maximum number of bins is 1E6.") + + // Gather + Make/FREE/WAVE/N=(numBins, numGroups) binnedPerGroup + for(i = 0; i < numGroups; i += 1) + WAVE/WAVE dataSets = input[i] + WAVE/WAVE binDataSets = binData[i] + numDataSets = DimSize(dataSets, ROWS) + SFH_ASSERT(numDataSets == DimSize(binDataSets, ROWS), "The number of datasets of the input and bins are not the same for group " + num2istr(i)) + for(j = 0; j < numDataSets; j += 1) + SFH_ASSERT(DimSize(binDataSets[j], ROWS) == 1, "A bin dataset must have exactly one value") + binValue = WaveRef(binDataSets, row = j)[0] + if(binValue < binStart || binValue >= binEnd) + continue + endif + binPos = floor((binValue - binStart) / binWidth) + // Add to bin + WAVE/Z/WAVE wavesInBin = binnedPerGroup[binPos][i] + if(!WaveExists(wavesInBin)) + Make/FREE/WAVE wavesInBin = {dataSets[j]} + SetNumberInWaveNote(wavesInBin, NOTE_INDEX, 1) + binnedPerGroup[binPos][i] = wavesInBin + continue + endif + idx = GetNumberFromWaveNote(wavesInBin, NOTE_INDEX) + EnsureLargeEnoughWave(wavesInBin, indexShouldExist = idx) + wavesInBin[idx] = dataSets[j] + SetNumberInWaveNote(wavesInBin, NOTE_INDEX, idx + 1) + endfor + endfor + // avg bins with multiple filling + for(i = 0; i < numBins; i += 1) + for(j = 0; j < numGroups; j += 1) + if(!WaveExists(binnedPerGroup[i][j])) + continue + endif + WAVE/WAVE wavesInBin = binnedPerGroup[i][j] + idx = GetNumberFromWaveNote(wavesInBin, NOTE_INDEX) + Redimension/N=(idx) wavesInBin + if(idx == 1) + continue + endif + WAVE/WAVE avg = MIES_fWaveAverage(wavesInBin, 1, IGOR_TYPE_64BIT_FLOAT) + Redimension/N=(1) wavesInBin + wavesInBin[0] = avg[0] + endfor + endfor + // avg same bins + WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, numBins) + for(i = 0; i < numBins; i += 1) + Make/FREE/WAVE/N=(numGroups) sameBin + idx = 0 + for(j = 0; j < numGroups; j += 1) + if(!WaveExists(binnedPerGroup[i][j])) + continue + endif + WAVE/WAVE wavesInBin = binnedPerGroup[i][j] + sameBin[idx] = wavesInBin[0] + idx += 1 + endfor + if(idx == 0) + Make/FREE/D tmp = {NaN} + output[i] = tmp + else + Redimension/N=(idx) sameBin + WAVE/WAVE avg = MIES_fWaveAverage(sameBin, 1, IGOR_TYPE_64BIT_FLOAT) + output[i] = avg[0] + endif + JWN_SetWaveInWaveNote(output[i], SF_META_TRACECOLOR, traceColor) + JWN_SetNumberInWaveNote(output[i], SF_META_TRACETOFRONT, 1) + JWN_SetNumberInWaveNote(output[i], SF_META_LINESTYLE, 0) + endfor + + return output +End + static Function/WAVE SFO_OperationAvgImplSweepGroups(WAVE/WAVE sweepsFromEachSelection, string graph, string opShort) variable numData, numMaxSweeps, numGroups, i, j, maxIdx From bc32099c2726d6f65bb10ef790ef3e9c05d887cd Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 23 Dec 2025 21:12:50 +0100 Subject: [PATCH 8/8] SF: use avg bins mode in ivscc_apfrequency --- .../MIES/MIES_SweepFormula_Operations.ipf | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 46456b85ac..10ddeb9951 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -2613,8 +2613,8 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) variable i, numArgs, col, size, numExp variable xAxisPercentage, yAxisPercentage string xaxisOffset, yaxisOffset - variable method, level - string timeFreq, normalize, xAxisType + variable method, level, binWidth, numBins + string timeFreq, normalize, xAxisType, freqList, currentList, expList, binList STRUCT RGBColor s SFH_ASSERT(BSP_IsSweepBrowser(exd.graph), "ivscc_apfrequency only works with sweepbrowser") @@ -2623,11 +2623,14 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) SFH_ASSERT(numArgs <= numArgsMax, "ivscc_apfrequency has " + num2istr(numArgsMax) + " arguments at most.") SFH_ASSERT(numArgs >= numArgsMin, "ivscc_apfrequency needs at least " + num2istr(numArgsMin) + " argument(s).") - xaxisOffset = SFH_GetArgumentAsText(exd, opShort, 0, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) - yaxisOffset = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) - xAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 2, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) - yAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 3, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) - [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 4) + xaxisOffset = SFH_GetArgumentAsText(exd, opShort, 0, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) + yaxisOffset = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) + xAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 2, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) + yAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 3, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) + WAVE/WAVE wTmp = SFH_GetArgumentAsWave(exd, opShort, 4) + WAVE binRange = wTmp[0] + binWidth = SFH_GetArgumentAsNumeric(exd, opShort, 5, checkFunc = IsStrictlyPositiveAndFinite) + [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 6) WAVE/T sweepMap = SB_GetSweepMap(exd.graph) col = FindDimlabel(sweepMap, COLS, "FileName") @@ -2638,8 +2641,6 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) numExp = DimSize(uniqueFiles, ROWS) SFH_ASSERT(numExp > 0, "ivscc_apfrequency: data from at least one experiment has to be loaded") - Make/FREE/T/N=(numExp) elems - formula = "sel = select(selsweeps(), selstimset(\"*rheo*\", \"*supra*\"), selvis(all), selivsccsweepqc(passed))\r" for(i = 0; i < numExp; i += 1) sprintf expr, "selexpAD%d = select(selexp(\"%s\"), $sel, selchannels(AD0), selrange(E1))", i, uniqueFiles[i] @@ -2662,16 +2663,21 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) formula = SF_AddExpressionToFormula(formula, expr) endfor - elems[] = "$freq" + num2istr(p) - expr = "ivsccavg = avg([" + TextWaveToList(elems, ",", trailSep = 0) + "], group)" + Make/FREE/T/N=(numExp) freqs, currents, exps + freqs[] = "$freq" + num2istr(p) + freqList = TextWaveToList(freqs, ",", trailSep = 0) + currents[] = "$currentNorm" + num2istr(p) + currentList = TextWaveToList(currents, ",", trailSep = 0) + exps[] = "\"" + uniqueFiles[p] + "\"" + expList = TextWaveToList(exps, ",", trailSep = 0) + + sprintf expr, "ivsccavg = avg([%s], bins, [%f,%f],%f,[%s])", freqList, binRange[0], binRange[1], binWidth, currentList formula = SF_AddExpressionToFormula(formula, expr) - elems[] = "$currentNorm" + num2istr(p) - expr = "ivscccurrentavg = avg([" + TextWaveToList(elems, ",", trailSep = 0) + "], group)" + sprintf expr, "ivscccurrentavg = avg([%s], bins, [%f,%f],%f,[%s])", currentList, binRange[0], binRange[1], binWidth, currentList formula = SF_AddExpressionToFormula(formula, expr) - elems[] = "\"" + uniqueFiles[p] + "\"" - expr = "ivscc_apfrequency_explist = [" + TextWaveToList(elems, ",", trailSep = 0) + "]" + sprintf expr, "ivscc_apfrequency_explist = [%s]", expList formula = SF_AddExpressionToFormula(formula, expr) WAVE/WAVE varStorage = GetSFVarStorage(exd.graph) @@ -2720,7 +2726,7 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) endif WAVE/WAVE wvY = SFH_ExecuteFormulaInternal(exd.graph, formula) JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, "ivscc_apfrequency average") - plotWITH[i][%FORMULAY] = wvY + plotWITH[numExp][%FORMULAY] = wvY SFO_OperationIVSCCApFrequencySetPlotProperties(plotWITH[i][%FORMULAY], xAxisPercentage, yAxisPercentage) if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) @@ -2732,7 +2738,13 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) else // SF_OP_IVSCCAPFREQUENCY_NONE formula = "merge($ivscccurrentavg)" endif - plotWITH[i][%FORMULAX] = SFH_ExecuteFormulaInternal(exd.graph, formula) + + numBins = ceil((binRange[1] - binRange[0]) / binWidth) + Make/FREE/D/N=(numBins) binValues + binValues[] = binRange[0] + p * binWidth + binWidth / 2 + binList = NumericWaveToList(binValues, ",", format = "%f", trailSep = 0) + sprintf formula, "[%s]", binList + plotWITH[numExp][%FORMULAX] = SFH_ExecuteFormulaInternal(exd.graph, formula) Duplicate/O varBackup, varStorage SFH_AddVariableToStorage(exd.graph, "ivscc_apfrequency_explist", wvResult)