diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 2e77837f5e..5cee83a1eb 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2146,6 +2146,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(). @@ -2207,6 +2208,9 @@ StrConstant SF_DATATYPE_ANAFUNCPARAM = "AnaFunc" StrConstant SF_WREF_MARKER = "\"WREF@\":" StrConstant SF_VARIABLE_MARKER = "/SF_IsVariable" // numeric + +StrConstant SF_ANNOTATION_NAME = "metadata" +StrConstant SF_VARNAME_REGEXP = "[A-Z]{1}[A-Z0-9_]*" ///@} /// @name Constants for SweepFormula Clampmode codes returned by operation selcm() @@ -2548,6 +2552,10 @@ StrConstant SF_OP_TPINST = "tpinst" StrConstant SF_OP_TPBASE = "tpbase" StrConstant SF_OP_TPFIT = "tpfit" StrConstant SF_OP_EXTRACT = "extract" + +#ifdef AUTOMATED_TESTING +StrConstant SF_OP_TESTOP = "testop" +#endif // AUTOMATED_TESTING ///@} StrConstant SF_PROPERTY_TABLE = "Table" diff --git a/Packages/MIES/MIES_GlobalStringAndVariableAccess.ipf b/Packages/MIES/MIES_GlobalStringAndVariableAccess.ipf index 65add9a172..86beb49252 100644 --- a/Packages/MIES/MIES_GlobalStringAndVariableAccess.ipf +++ b/Packages/MIES/MIES_GlobalStringAndVariableAccess.ipf @@ -824,3 +824,14 @@ Function/S GetSweepFormulaLastRightClickedDisplayWindow() return GetSVARAsString(GetSweepFormulaPath(), "LastRightClickedDisplayWindow", initialValue = "") End + +#ifdef AUTOMATED_TESTING +/// @brief Returns the full path to the global with the name of the function that is declared in a test +/// that is called when testop is executed in a formula, used in testing +Function/S GetSFTestopName(string graph) + + DFREF dfr = SF_GetBrowserDF(graph) + + return GetSVARAsString(dfr, "TestopFunctionName", initialValue = "") +End +#endif // AUTOMATED_TESTING diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index e6318a42c1..c6c8ed7408 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -35,6 +35,42 @@ static Constant SF_SWEEPFORMULA_AXIS_Y = 1 static StrConstant SF_UDATA_TABLEFORMULAS = "formulas" +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 + Menu "GraphPopup" "Bring browser to front", /Q, SF_BringBrowserToFront() End @@ -94,6 +130,10 @@ Function/WAVE SF_GetNamedOperations() 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} +#ifdef AUTOMATED_TESTING + Make/FREE/T wtTest = {SF_OP_TESTOP} + Concatenate/NP/T {wtTest}, wt +#endif // AUTOMATED_TESTING return wt End @@ -112,6 +152,19 @@ Function/S SF_EscapeJsonPath(string str) return ReplaceString("/", str, "~1") End +/// @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 + /// @brief transfer the wave scaling from one wave to another /// /// Note: wave scale transfer requires wave units for the first wave or second wave @@ -159,22 +212,14 @@ 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) +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) @@ -183,6 +228,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) @@ -230,11 +276,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 @@ -268,7 +324,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 @@ -276,8 +333,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 + " " @@ -296,7 +352,8 @@ static Function/S SF_GetTraceAnnotationText(WAVE/T plotMetaData, WAVE data) break default: if(WhichListItem(SF_OP_DATA, plotMetaData[%OPSTACK]) == -1) - sprintf traceAnnotation, "%s", prefix + sprintf traceAnnotation, "%s %s", prefix, legendPrefix + traceAnnotation = TrimString(traceAnnotation) else channelNumber = JWN_GetNumberFromWaveNote(data, SF_META_CHANNELNUMBER) channelType = JWN_GetNumberFromWaveNote(data, SF_META_CHANNELTYPE) @@ -488,12 +545,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) @@ -502,9 +559,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. @@ -886,6 +944,16 @@ static Function/WAVE SF_PrepareResultWaveForPlotting(DFREF dfr, WAVE wvResult, v return plotWave End +/// @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 SF_IsDataForTableDisplay(WAVE wvY) variable useTable @@ -895,7 +963,7 @@ 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) +static Function [variable dataCnt, variable gdIndex, string annotation, variable formulaAddedOncePerDataset] SF_CreateTracesForResultsImpl(STRUCT SF_PlotterGraphStruct &pg, WAVE wvResultY, WAVE/Z wvResultX, variable dataNum, variable showInTable, WAVE plotFormData) STRUCT RGBColor color variable numTraces, yPoints, xPoints, yMxN, xMxN, idx, splitTraces @@ -906,11 +974,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)) @@ -933,26 +1001,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, 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 @@ -960,48 +1028,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) @@ -1020,19 +1088,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 @@ -1041,18 +1109,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.") @@ -1063,22 +1131,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, 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] SF_CreateTracesForResults(STRUCT SF_PlotterGraphStruct &pg) variable i, idx, showInTable, numData, formulaAddedOncePerDataset variable gdIndex // indexes in tracesInGraph wave and dataInGraph wave in SF_CollectTraceData(), both waves are stored in plotformData @@ -1086,21 +1154,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 @@ -1108,26 +1176,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, gdIndex, annotation, formulaAddedOncePerDataset] = SF_CreateTracesForResultsImpl(pg, 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] End static Function SF_RestorePlotProperties(WAVE/WAVE prevPlotProperties) @@ -1256,6 +1326,43 @@ static Function/S SF_CreateDataDisplayWindow(string graph, WAVE/WAVE formulaResu return win End +static Function [STRUCT SF_PlotterGraphStruct pg] SF_ResetPlotterGraphStruct(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 @@ -1264,13 +1371,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 @@ -1291,30 +1397,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_ResetPlotterGraphStruct(graph) do @@ -1331,73 +1419,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) - - numTableFormulas = GetNumberFromWaveNote(tableFormulas, NOTE_INDEX) - if(numTableFormulas) - Redimension/N=(numTableFormulas) tableFormulas - SetWindow $win, userdata($SF_UDATA_TABLEFORMULAS)=WaveToJSON(tableFormulas) - endif + 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 - if(panelsCreated[%GRAPH]) - win = winGraphs[GetNumberFromWaveNote(winGraphs, NOTE_INDEX) - 1] - if(showLegend) - formulasAreDifferent = SF_AddPlotLegend(win, wAnnotations, formulaArgSetup, formulaResults) - endif + SF_GatherAxisLabels(pg.formulaResults, pg.plotMetaData[%XAXISLABEL], "FORMULAX", pg.xAxisLabels) + SF_GatherAxisLabels(pg.formulaResults, pg.plotMetaData[%YAXISLABEL], "FORMULAY", pg.yAxisLabels) - 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(pg.plotMetaData[%DATATYPE], SF_DATATYPE_PSX)) + PSX_Plot(pg.win, pg.graph, pg.formulaResults, pg.plotMetaData) + pg.postPlotPSX = 1 + break + endif - SF_AddPlotTraceStyle(graph, win, formulaCounter, collPlotFormData, formulasAreDifferent) + [dataCnt] = SF_CreateTracesForResults(pg) + pg.formulaCounter += 1 + endfor - if(traceCnt > 0) - SF_AddPlotLabels(win, xAxisLabels, yAxisLabels) - endif - endif + if(j < (numPlotAND - 1)) + SF_FinishPlotWindow(pg, winGraphs) + [pg] = SF_ResetPlotterGraphStruct(graph) + endif + endfor + while(1) - if(postPlotPSX) - PSX_PostPlot(win) - endif + SF_FinishPlotWindow(pg, winGraphs) endfor @@ -1414,13 +1499,49 @@ 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 SF_FinishPlotWindow(STRUCT SF_PlotterGraphStruct &pg, 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 = SF_AddPlotLegend(pg) + 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 + + SF_AddPlotTraceStyle(pg, formulasAreDifferent) + + if(pg.traceCnt > 0) + SF_AddPlotLabels(pg.win, pg.xAxisLabels, pg.yAxisLabels) + endif + endif + + if(pg.postPlotPSX) + PSX_PostPlot(pg.win) + endif +End + +static Function SF_AddPlotTraceStyle(STRUCT SF_PlotterGraphStruct &pg, 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) @@ -1434,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) @@ -1447,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") @@ -1460,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) @@ -1486,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 @@ -1538,27 +1659,27 @@ 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 SF_AddPlotLegend(STRUCT SF_PlotterGraphStruct &pg) variable numAnnotations, formulasAreDifferent string customLegend string annotation = "" - numAnnotations = GetNumberFromWaveNote(wAnnotations, NOTE_INDEX) - customLegend = JWN_GetStringFromWaveNote(formulaResults, SF_META_CUSTOM_LEGEND) + 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") - annotation = UnPadString(annotation, char2num("\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, "") + annotation = TrimString(annotation) endif if(!IsEmpty(annotation)) - Legend/W=$win/C/N=metadata/F=2 annotation + Legend/W=$pg.win/C/N=$SF_ANNOTATION_NAME/F=2 annotation endif return formulasAreDifferent @@ -2365,7 +2486,7 @@ End static Function [string varName, string formula] SF_SplitVariableAssignment(string line) - string regex = "^(?i)\\s*([A-Z]{1}[A-Z0-9_]*)\\s*=(.+)$" + string regex = "^(?i)\\s*(" + SF_VARNAME_REGEXP + ")\\s*=(.+)$" SplitString/E=regex line, varName, formula if(V_flag != 2) diff --git a/Packages/MIES/MIES_SweepFormula_Executor.ipf b/Packages/MIES/MIES_SweepFormula_Executor.ipf index a4dce70cb5..241aad4c5f 100644 --- a/Packages/MIES/MIES_SweepFormula_Executor.ipf +++ b/Packages/MIES/MIES_SweepFormula_Executor.ipf @@ -25,7 +25,11 @@ static Constant SFE_VARIABLE_PREFIX = 36 /// @param useVariables [optional, default 1], when not set, hint the function that the formula string contains only an expression and no variable definitions /// @param line [optional, default NaN], line number of formula in SF notebook, when set, stores the information for the case of an SFH_ASSERT /// @param offset [optional, default NaN], offset of a formula in SF notebook in characters from the start of the line (x-formulas), when set, stores the information for the case of an SFH_ASSERT -Function/WAVE SFE_ExecuteFormula(string formula, string graph, [variable singleResult, variable checkExist, variable useVariables, variable line, variable offset]) +/// @param preProcess [optional, default 1], when set to 0 then the formula is not in any way preprocessed and must not contain any variable definitions. +/// Also the current error information for SFH_ASSERT is kept as is. The current variable storage is used. +/// This allows to internally execute a formula where a triggered SFH_ASSERT should result in the marking +/// of the "outer" formula in the current SF notebook. +Function/WAVE SFE_ExecuteFormula(string formula, string graph, [variable singleResult, variable checkExist, variable useVariables, variable line, variable offset, variable preProcess]) STRUCT SF_ExecutionData exd variable jsonId, srcLocId @@ -37,12 +41,15 @@ Function/WAVE SFE_ExecuteFormula(string formula, string graph, [variable singleR useVariables = ParamIsDefault(useVariables) ? 1 : !!useVariables line = ParamIsDefault(line) ? NaN : line offset = ParamIsDefault(offset) ? NaN : offset + preProcess = ParamIsDefault(preProcess) ? 1 : !!preProcess - formula = SF_PreprocessInput(formula) - if(useVariables) - formula = SFE_ExecuteVariableAssignments(graph, formula) + if(preProcess) + formula = SF_PreprocessInput(formula) + if(useVariables) + formula = SFE_ExecuteVariableAssignments(graph, formula) + endif + SFH_StoreAssertInfoParser(line, offset) endif - SFH_StoreAssertInfoParser(line, offset) [jsonId, srcLocId] = SFP_ParseFormulaToJSON(formula) exd.jsonId = jsonId WAVE/Z result = SFE_FormulaExecutor(exd, srcLocId = srcLocId) @@ -61,12 +68,20 @@ Function/WAVE SFE_ExecuteFormula(string formula, string graph, [variable singleR return out End -Function/S SFE_ExecuteVariableAssignments(string graph, string preProcCode) +/// @brief Executes each variable assignment expression and stores the result in the variable storage +/// +/// @param graph SweepBrowser graph +/// @param preProcCode preprocessed sweep formula notebook text +/// @param allowEmptyCode [optional, default 0] when set then the check for empty formula code is disabled, such that +/// input that contains only variable expressions can be evaluated +Function/S SFE_ExecuteVariableAssignments(string graph, string preProcCode, [variable allowEmptyCode]) STRUCT SF_ExecutionData exd variable i, numAssignments, jsonId, srcLocId, line, offset string code, sfWin, nbText + allowEmptyCode = ParamisDefault(allowEmptyCode) ? 0 : !!allowEmptyCode + exd.graph = graph WAVE/WAVE varStorage = GetSFVarStorage(graph) @@ -96,7 +111,7 @@ Function/S SFE_ExecuteVariableAssignments(string graph, string preProcCode) JSON_Release(srcLocId) endfor - if(IsEmpty(code)) + if(!allowEmptyCode && IsEmpty(code)) if(!StringEndsWith(preProcCode, SF_CHAR_CR)) sfWin = BSP_GetSFFormula(graph) nbText = GetNotebookText(sfWin, mode = 2) @@ -532,6 +547,11 @@ Function/WAVE SFE_FormulaExecutor(STRUCT SF_ExecutionData &exd, [variable srcLoc case SF_OP_TABLE: WAVE out = SFO_OperationTable(exdop) break +#ifdef AUTOMATED_TESTING + case SF_OP_TESTOP: + WAVE out = SFO_OperationTestop(exdop) + break +#endif // AUTOMATED_TESTING 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 5f59e84f2c..846dcc62e0 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -2177,3 +2177,46 @@ 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 + string varName + string regex = "^(?i)(" + SF_VARNAME_REGEXP + ")$" + + SplitString/E=regex name, varName + ASSERT(V_flag == 1, "Invalid SF variable name") + + WAVE/WAVE varStorage = GetSFVarStorage(graph) + idx = FindDimLabel(varStorage, ROWS, varName) + if(idx == -2) + size = DimSize(varStorage, ROWS) + Redimension/N=(size + 1) varStorage + idx = size + SetDimLabel ROWS, size, $varName, varStorage + endif + JWN_SetNumberInWaveNote(result, SF_VARIABLE_MARKER, 1) + varStorage[idx] = result +End diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 301cb47a02..e4dfdf22ff 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -2481,3 +2481,24 @@ Function/WAVE SFO_OperationTable(STRUCT SF_ExecutionData &exd) return SFH_GetOutputForExecutor(output, exd.graph, SF_OP_TABLE) End + +#ifdef AUTOMATED_TESTING +Function/WAVE SFO_OperationTestop_PROTO(STRUCT SF_ExecutionData &exd) + + INFO("TestOp PROTO function was called. A proper implementation function for testop must be registered by setting the SVAR from GetSFTestopName") + FAIL() +End + +/// testop(...) +Function/WAVE SFO_OperationTestop(STRUCT SF_ExecutionData &exd) + + string funcName = ROStr(GetSFTestopName(exd.graph)) + + REQUIRE_PROPER_STR(funcName) + + FUNCREF SFO_OperationTestop_PROTO func = $funcName + WAVE wv = func(exd) + + return wv +End +#endif // AUTOMATED_TESTING diff --git a/Packages/MIES/MIES_Utilities_Conversions.ipf b/Packages/MIES/MIES_Utilities_Conversions.ipf index fe35c257e8..65c433bc81 100644 --- a/Packages/MIES/MIES_Utilities_Conversions.ipf +++ b/Packages/MIES/MIES_Utilities_Conversions.ipf @@ -54,24 +54,17 @@ threadsafe Function/S TextWaveToList(WAVE/Z/T txtWave, string rowSep, [string co endif ASSERT_TS(IsTextWave(txtWave), "Expected a text wave") - ASSERT_TS(!IsEmpty(rowSep), "Expected a non-empty row list separator") if(ParamIsDefault(colSep)) colSep = "," - else - ASSERT_TS(!IsEmpty(colSep), "Expected a non-empty column list separator") endif if(ParamIsDefault(layerSep)) layerSep = ":" - else - ASSERT_TS(!IsEmpty(layerSep), "Expected a non-empty layer list separator") endif if(ParamIsDefault(chunkSep)) chunkSep = "/" - else - ASSERT_TS(!IsEmpty(chunkSep), "Expected a non-empty chunk list separator") endif if(ParamIsDefault(maxElements)) diff --git a/Packages/MIES/MIES_WaveDataFolderGetters.ipf b/Packages/MIES/MIES_WaveDataFolderGetters.ipf index 490d8c3e9d..a656b51a1d 100644 --- a/Packages/MIES/MIES_WaveDataFolderGetters.ipf +++ b/Packages/MIES/MIES_WaveDataFolderGetters.ipf @@ -9382,3 +9382,26 @@ Function/WAVE GetSFPlotProperties() return wv End + +/// @brief Returns a permanent SF wave reference wave for the AND branch of a full plotting specification. +/// The elements of this wave must be filled with WITH branches from GetFullPlottingWITH. +/// At the end of the operation code this wave can be returned with +/// return SFH_GetOutputForExecutor(plotAND, exd.graph, opShort) +Function/WAVE GetFullPlottingAND(string graph, string opShort, variable size) + + WAVE/WAVE plotAND = SFH_CreateSFRefWave(graph, opShort, size) + JWN_SetNumberInWaveNote(plotAND, SF_META_PLOT, 1) + + return plotAND +End + +/// @brief Returns a free wave for the WITH branch of a full plotting specification +/// At least the FORMULAY element must be filled by the caller +Function/WAVE GetFullPlottingWITH(variable size) + + Make/FREE/WAVE/N=(size, 2) plotWITH + SetDimlabel COLS, 0, FORMULAX, plotWITH + SetDimlabel COLS, 1, FORMULAY, plotWITH + + return plotWITH +End diff --git a/Packages/doc/SweepFormula.rst b/Packages/doc/SweepFormula.rst index 2e33fff0f4..5f8c91df21 100644 --- a/Packages/doc/SweepFormula.rst +++ b/Packages/doc/SweepFormula.rst @@ -2423,6 +2423,180 @@ Thus, `SF_GetArgument` sees with the resolved `wave` operation `[17]`, whereas ` More complex operation such as `data` build the output wave reference wave dynamically. See `SF_GetSweepsForFormula` how the output wave is build depending on selectData and the found sweeps. +Operations returning a Full Plotting Specification +"""""""""""""""""""""""""""""""""""""""""""""""""" + +Sweep formula operations can return a result wave that is tagged as full plotting specification with the `SF_META_PLOT` tag. A full plotting specification +is a wave reference wave that stores formula results as if they would have been specified with the AND / WITH keyword syntax in a sweepformula notebook. +This represents an evaluated formula specification for plotting that is not yet plotted. An operation can return such full plotting specification as Y-formula +and the formula plotter will insert this in the output panel with the plots as if the formulas would have been in the sweepformula notebook. + +The full plotting specification is a wave reference wave tree with two levels. The topmost level indexes over evaluated formula results as if +specified through the AND keyword. The second level stores evaluated formulas as if specified through the WITH keyword. For each level a wave getter is +implemented. + +A simple operation that creates and returns a full plotting specification looks like this: + +.. code-block:: igorpro + + Function/WAVE FullPlottingOp(STRUCT SF_ExecutionData &exd) + + string opShort = SF_OP_MYOP + string formula + + WAVE/WAVE plotAND = GetFullPlottingAND(exd.graph, opShort, 1) + WAVE/WAVE plotWITH = GetFullPlottingWITH(1) + + formula = "[1, 2, 3]" + WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess=0) + plotWITH[0][%FORMULAY] = wvY + formula = "[10, 20, 30]" + WAVE/WAVE wvX = SFE_ExecuteFormula(formula, exd.graph, preProcess=0) + plotWITH[0][%FORMULAX] = wvX + plotAND[0] = plotWITH + + return SFH_GetOutputForExecutor(plotAND, exd.graph, opShort) + End + +This is equivalent to a formula of `[1, 2, 3] vs [10, 20, 30]`. Note that when plotAND is created the `SF_META_PLOT` tag is automatically set. + +Multiple plots with AND (for simplicity no optional X-formula set here): + +.. code-block:: igorpro + + WAVE/WAVE plotAND = GetFullPlottingAND(exd.graph, opShort, 2) + + WAVE/WAVE plotWITH = GetFullPlottingWITH(1) + formula = "[1, 2, 3]" + WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess=0) + plotWITH[0][%FORMULAY] = wvY + plotAND[0] = plotWITH + + WAVE/WAVE plotWITH = GetFullPlottingWITH(1) + formula = "[4, 5, 6]" + WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess=0) + plotWITH[0][%FORMULAY] = wvY + plotAND[1] = plotWITH + +This is equivalent to a formula of + +.. code-block:: igorpro + + [1, 2, 3] + and + [4, 5, 6] + +Multiple traces with WITH (for simplicity no optional X-formula set here): + +.. code-block:: igorpro + + WAVE/WAVE plotAND = GetFullPlottingAND(exd.graph, opShort, 1) + + WAVE/WAVE plotWITH = GetFullPlottingWITH(2) + formula = "[1, 2, 3]" + WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess=0) + plotWITH[0][%FORMULAY] = wvY + formula = "[4, 5, 6]" + WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess=0) + plotWITH[1][%FORMULAY] = wvY + plotAND[0] = plotWITH + +This is equivalent to a formula of + +.. code-block:: igorpro + + [1, 2, 3] + with + [4, 5, 6] + +The evaluated formula result of the full plotting specification will be inserted in the formula specification of the sweep formula notebook. +For example if there is a `FullPlottingOp()` defined that returns `[1, 2, 3]` then the following sweepformula notebook code results in three plots with one trace each: + +.. code-block:: igorpro + + [4, 5, 6] + and + FullPlottingOp() + and + [4, 5, 6] + +If the connection is done through `with` then there will be only two plots where the trace from the full plotting specification is either added to the first or +second plot, depending on if `with` is before or after `FullPlottingOp()`. + +.. code-block:: igorpro + + # two plots, first plot has two traces, second plot one trace + [4, 5, 6] + with + FullPlottingOp() + and + [4, 5, 6] + + # upper code is equivalent to + [4, 5, 6] + with + [1, 2, 3] + and + [4, 5, 6] + +.. code-block:: igorpro + + # two plots, first plot has one trace, second plot two traces + [4, 5, 6] + and + FullPlottingOp() + with + [4, 5, 6] + + # upper code is equivalent to + [4, 5, 6] + and + [1, 2, 3] + with + [4, 5, 6] + +Note: If `FullPlottingOp()` would return more than one entry in the plotAND wave then it would itself create more plots and an `with` keyword +in the sweepformula notebook would only connect to the first/last plot. + +To keep the construction of the full plotting specification simple it is recommended to put most of the formula evaluation in variables. +The evaluation call to `SFE_ExecuteFormula` with `preProcess=0` uses the current variable storage. This allows to provide already evaluated variables. +The following code snippet illustrates this: + +.. code-block:: igorpro + + // save previous variables + WAVE/WAVE varStorage = GetSFVarStorage(exd.graph) + Duplicate/FREE varStorage, varBackup + + // evaluate own variables + formula = "var1 = 1\rvar2 = $var1 * 2" + SFE_ExecuteVariableAssignments(exd.graph, formula, allowEmptyCode = 1) + + // create plotting specification, use own variables + WAVE/WAVE plotAND = GetFullPlottingAND(exd.graph, opShort, 1) + WAVE/WAVE plotWITH = GetFullPlottingWITH(1) + formula = "$var2" + WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess=0) + plotWITH[0][%FORMULAY] = wvY + plotAND[0] = plotWITH + + // restore previous variables + Duplicate/O varBackup, varStorage + +Return Operation Results in Variable +"""""""""""""""""""""""""""""""""""" + +In some situations it is useful that the operation returns results in a variable. +This is done with the utility function `SFH_AddVariableToStorage`. + +.. code-block:: igorpro + + SFH_AddVariableToStorage(graph, "varName", result) + +`varName` is the name of the new variable. If the variable is already present then it is replaced with the new result. +`result` is a wave from an evaluated formula. The utility function works on the current variable storage. +The variable content is available for all later formula evaluations. + Meta Data Handling """""""""""""""""" @@ -2517,3 +2691,17 @@ need to be shown with a different marker or line style. It also adapts the legen apfrequency(data(select(selrange(ST), selchannels(AD), selvis(all))), 3, 100, freq, normoversweepsavg, count) with apfrequency(data(select(selrange(ST), selchannels(AD), selvis(all))), 3, 100, time, norminsweepsavg, count) + +Dynamic Operation for Testing +""""""""""""""""""""""""""""" + +When `AUTOMATED_TESTING` is defined then the additional operation `testop` is available. The operation function content can be defined specifically for a test case. +The test case has to implement a non-static function with the same function signature as a sweepformula operation. Then the test case code can set +the implemented function as body for `testop` by updating a SVAR global: + +.. code-block:: igorpro + + SVAR funcName = $GetSFTestopName(graph) + funcName = "MySpecialTestCaseOperation" + +Formulas that use `testop` will then execute `MySpecialTestCaseOperation` in that case. The global is set per SweepBrowser. diff --git a/Packages/tests/Basic/UTF_SweepFormula.ipf b/Packages/tests/Basic/UTF_SweepFormula.ipf index 94c07802f9..08518470ed 100644 --- a/Packages/tests/Basic/UTF_SweepFormula.ipf +++ b/Packages/tests/Basic/UTF_SweepFormula.ipf @@ -2692,3 +2692,229 @@ static Function TestSourceLocationContent([WAVE/WAVE wv]) Redimension/N=(size, -1) srcLocs CHECK_EQUAL_WAVES(srcLocs, wSrcLocs, mode = WAVE_DATA) End + +Function/WAVE TestFullPlottingOp(STRUCT SF_ExecutionData &exd) + + string opShort = SF_OP_TESTOP + string formula + + formula = "var = 1" + + WAVE/WAVE varStorage = GetSFVarStorage(exd.graph) + Duplicate/FREE varStorage, varBackup + SFE_ExecuteVariableAssignments(exd.graph, formula, allowEmptyCode = 1) + + WAVE/WAVE plotAND = GetFullPlottingAND(exd.graph, opShort, 2) + // build the following construct for the full plot specification, $var is created independently in the code above beforehand + // $var vs 2 + // with + // 3 vs 4 + // and + // 5 vs 6 + // with + // 7 vs 8 + + WAVE/WAVE plotWITH = GetFullPlottingWITH(2) + formula = "$var" + WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, "legend") + JWN_SetStringInWaveNote(wvY, SF_META_YAXISLABEL, "yaxislabel") + plotWITH[0][%FORMULAY] = wvY + formula = "2" + WAVE/WAVE wvX = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + plotWITH[0][%FORMULAX] = wvX + + formula = "3" + WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + plotWITH[1][%FORMULAY] = wvY + formula = "4" + WAVE/WAVE wvX = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + plotWITH[1][%FORMULAX] = wvX + + plotAND[0] = plotWITH + + WAVE/WAVE plotWITH = GetFullPlottingWITH(2) + formula = "5" + WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + plotWITH[0][%FORMULAY] = wvY + formula = "6" + WAVE/WAVE wvX = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + plotWITH[0][%FORMULAX] = wvX + + formula = "7" + WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + plotWITH[1][%FORMULAY] = wvY + formula = "8" + WAVE/WAVE wvX = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + plotWITH[1][%FORMULAX] = wvX + + plotAND[1] = plotWITH + + Duplicate/O varBackup, varStorage + + return SFH_GetOutputForExecutor(plotAND, exd.graph, opShort) +End + +static Function TestFullPlottingSpecificationCheckTrace(string win, variable traceIndex, variable yRef, variable xRef) + + string traces = TraceNameList(win, ";", 0x1) + WAVE wvY = TraceNameToWaveRef(win, StringFromList(traceIndex, traces)) + WAVE wvX = XWaveRefFromTrace(win, StringFromList(traceIndex, traces)) + Make/FREE/D wvRef = {{yRef}} + CHECK_EQUAL_WAVES(wvY, wvRef, mode = WAVE_DATA) + wvRef[] = xRef + CHECK_EQUAL_WAVES(wvX, wvRef, mode = WAVE_DATA) +End + +static Function TestFullPlottingSpecification() + + string win, traces, lbl, annoText + string graph, winResultBase + + graph = CreateFakeSweepBrowser_IGNORE() + DFREF dfr = BSP_GetFolder(graph, MIES_BSP_PANEL_FOLDER) + winResultBase = BSP_GetFormulaGraph(graph) + + SVAR funcName = $GetSFTestopName(graph) + funcName = "TestFullPlottingOp" + + MIES_SF#SF_FormulaPlotter(graph, "testop()") + win = winResultBase + "_" + SF_WINNAME_SUFFIX_GRAPH + "#" + SF_WINNAME_SUFFIX_GRAPH + "0" + REQUIRE_EQUAL_VAR(WindowExists(win), 1) + traces = TraceNameList(win, ";", 0x1) + CHECK_EQUAL_VAR(ItemsInList(traces), 2) + TestFullPlottingSpecificationCheckTrace(win, 0, 1, 2) + TestFullPlottingSpecificationCheckTrace(win, 1, 3, 4) + lbl = AxisLabel(win, "left") + CHECK_EQUAL_STR(lbl, "yaxislabel") + annoText = StringByKey("TEXT", AnnotationInfo(win, SF_ANNOTATION_NAME)) + CHECK_GT_VAR(strsearch(annoText, " legend", 0), 0) + win = winResultBase + "_" + SF_WINNAME_SUFFIX_GRAPH + "#" + SF_WINNAME_SUFFIX_GRAPH + "1" + REQUIRE_EQUAL_VAR(WindowExists(win), 1) + traces = TraceNameList(win, ";", 0x1) + CHECK_EQUAL_VAR(ItemsInList(traces), 2) + TestFullPlottingSpecificationCheckTrace(win, 0, 5, 6) + TestFullPlottingSpecificationCheckTrace(win, 1, 7, 8) + + MIES_SF#SF_FormulaPlotter(graph, "9 vs 9\rand\rtestop()") + win = winResultBase + "_" + SF_WINNAME_SUFFIX_GRAPH + "#" + SF_WINNAME_SUFFIX_GRAPH + "0" + REQUIRE_EQUAL_VAR(WindowExists(win), 1) + traces = TraceNameList(win, ";", 0x1) + CHECK_EQUAL_VAR(ItemsInList(traces), 1) + TestFullPlottingSpecificationCheckTrace(win, 0, 9, 9) + win = winResultBase + "_" + SF_WINNAME_SUFFIX_GRAPH + "#" + SF_WINNAME_SUFFIX_GRAPH + "1" + REQUIRE_EQUAL_VAR(WindowExists(win), 1) + traces = TraceNameList(win, ";", 0x1) + CHECK_EQUAL_VAR(ItemsInList(traces), 2) + TestFullPlottingSpecificationCheckTrace(win, 0, 1, 2) + TestFullPlottingSpecificationCheckTrace(win, 1, 3, 4) + win = winResultBase + "_" + SF_WINNAME_SUFFIX_GRAPH + "#" + SF_WINNAME_SUFFIX_GRAPH + "2" + REQUIRE_EQUAL_VAR(WindowExists(win), 1) + traces = TraceNameList(win, ";", 0x1) + CHECK_EQUAL_VAR(ItemsInList(traces), 2) + TestFullPlottingSpecificationCheckTrace(win, 0, 5, 6) + TestFullPlottingSpecificationCheckTrace(win, 1, 7, 8) + + MIES_SF#SF_FormulaPlotter(graph, "9 vs 9\rwith\rtestop()") + win = winResultBase + "_" + SF_WINNAME_SUFFIX_GRAPH + "#" + SF_WINNAME_SUFFIX_GRAPH + "0" + REQUIRE_EQUAL_VAR(WindowExists(win), 1) + traces = TraceNameList(win, ";", 0x1) + CHECK_EQUAL_VAR(ItemsInList(traces), 3) + TestFullPlottingSpecificationCheckTrace(win, 0, 9, 9) + TestFullPlottingSpecificationCheckTrace(win, 1, 1, 2) + TestFullPlottingSpecificationCheckTrace(win, 2, 3, 4) + win = winResultBase + "_" + SF_WINNAME_SUFFIX_GRAPH + "#" + SF_WINNAME_SUFFIX_GRAPH + "1" + REQUIRE_EQUAL_VAR(WindowExists(win), 1) + traces = TraceNameList(win, ";", 0x1) + CHECK_EQUAL_VAR(ItemsInList(traces), 2) + TestFullPlottingSpecificationCheckTrace(win, 0, 5, 6) + TestFullPlottingSpecificationCheckTrace(win, 1, 7, 8) + + MIES_SF#SF_FormulaPlotter(graph, "testop()\rand\r9 vs 9") + win = winResultBase + "_" + SF_WINNAME_SUFFIX_GRAPH + "#" + SF_WINNAME_SUFFIX_GRAPH + "0" + REQUIRE_EQUAL_VAR(WindowExists(win), 1) + traces = TraceNameList(win, ";", 0x1) + CHECK_EQUAL_VAR(ItemsInList(traces), 2) + TestFullPlottingSpecificationCheckTrace(win, 0, 1, 2) + TestFullPlottingSpecificationCheckTrace(win, 1, 3, 4) + win = winResultBase + "_" + SF_WINNAME_SUFFIX_GRAPH + "#" + SF_WINNAME_SUFFIX_GRAPH + "1" + REQUIRE_EQUAL_VAR(WindowExists(win), 1) + traces = TraceNameList(win, ";", 0x1) + CHECK_EQUAL_VAR(ItemsInList(traces), 2) + TestFullPlottingSpecificationCheckTrace(win, 0, 5, 6) + TestFullPlottingSpecificationCheckTrace(win, 1, 7, 8) + win = winResultBase + "_" + SF_WINNAME_SUFFIX_GRAPH + "#" + SF_WINNAME_SUFFIX_GRAPH + "2" + REQUIRE_EQUAL_VAR(WindowExists(win), 1) + traces = TraceNameList(win, ";", 0x1) + CHECK_EQUAL_VAR(ItemsInList(traces), 1) + TestFullPlottingSpecificationCheckTrace(win, 0, 9, 9) + + MIES_SF#SF_FormulaPlotter(graph, "testop()\rwith\r9 vs 9") + win = winResultBase + "_" + SF_WINNAME_SUFFIX_GRAPH + "#" + SF_WINNAME_SUFFIX_GRAPH + "0" + REQUIRE_EQUAL_VAR(WindowExists(win), 1) + traces = TraceNameList(win, ";", 0x1) + CHECK_EQUAL_VAR(ItemsInList(traces), 2) + TestFullPlottingSpecificationCheckTrace(win, 0, 1, 2) + TestFullPlottingSpecificationCheckTrace(win, 1, 3, 4) + win = winResultBase + "_" + SF_WINNAME_SUFFIX_GRAPH + "#" + SF_WINNAME_SUFFIX_GRAPH + "1" + REQUIRE_EQUAL_VAR(WindowExists(win), 1) + traces = TraceNameList(win, ";", 0x1) + CHECK_EQUAL_VAR(ItemsInList(traces), 3) + TestFullPlottingSpecificationCheckTrace(win, 0, 5, 6) + TestFullPlottingSpecificationCheckTrace(win, 1, 7, 8) + TestFullPlottingSpecificationCheckTrace(win, 2, 9, 9) +End + +static Function TestAddVariableToStorage() + + string graph + + graph = CreateFakeSweepBrowser_IGNORE() + + WAVE/WAVE varStorage = GetSFVarStorage(graph) + Make/FREE result1, result2 + + try + SFH_AddVariableToStorage(graph, "", result1) + FAIL() + catch + PASS() + endtry + + try + SFH_AddVariableToStorage(graph, "0VAR", result1) + FAIL() + catch + PASS() + endtry + + try + SFH_AddVariableToStorage(graph, " VAR", result1) + FAIL() + catch + PASS() + endtry + + try + SFH_AddVariableToStorage(graph, "VAR ", result1) + FAIL() + catch + PASS() + endtry + + result2[] = 2 + SFH_AddVariableToStorage(graph, "result1", result1) + CHECK_EQUAL_VAR(FindDimLabel(varStorage, ROWS, "result1"), 0) + WAVE wv = varStorage[%result1] + CHECK_EQUAL_WAVES(result1, wv) + + SFH_AddVariableToStorage(graph, "result2", result2) + CHECK_EQUAL_VAR(FindDimLabel(varStorage, ROWS, "result2"), 1) + WAVE wv = varStorage[%result2] + CHECK_EQUAL_WAVES(result2, wv) + + SFH_AddVariableToStorage(graph, "result1", result2) + CHECK_EQUAL_VAR(FindDimLabel(varStorage, ROWS, "result1"), 0) + WAVE wv = varStorage[%result1] + CHECK_EQUAL_WAVES(result2, wv) +End diff --git a/Packages/tests/Basic/UTF_Utils_Conversions.ipf b/Packages/tests/Basic/UTF_Utils_Conversions.ipf index 39f3fa6ef4..6c10090bb2 100644 --- a/Packages/tests/Basic/UTF_Utils_Conversions.ipf +++ b/Packages/tests/Basic/UTF_Utils_Conversions.ipf @@ -44,35 +44,6 @@ Function TWTLChecksParams() PASS() endtry - // empty separators - try - list = TextWaveToList(w, "") - FAIL() - catch - PASS() - endtry - - try - list = TextWaveToList(w, ";", colSep = "") - FAIL() - catch - PASS() - endtry - - try - list = TextWaveToList(w, ";", layerSep = "") - FAIL() - catch - PASS() - endtry - - try - list = TextWaveToList(w, ";", chunkSep = "") - FAIL() - catch - PASS() - endtry - // invalid max elements try list = TextWaveToList(w, ";", maxElements = -1) @@ -94,6 +65,11 @@ Function TWTLChecksParams() catch PASS() endtry + + // empty separators + Make/FREE/T wt = {{{{"a"}, {"e"}}, {{"c"}, {"g"}}}, {{{"b"}, {"f"}}, {{"d"}, {"h"}}}} + list = TextWaveToList(wt, "", colSep = "", layerSep = "", chunkSep = "") + CHECK_EQUAL_STR(list, "abcdefgh") End // UTF_TD_GENERATOR DataGenerators#TrailSepOptions diff --git a/Packages/tests/HardwareBasic/UTF_SweepFormulaHardware.ipf b/Packages/tests/HardwareBasic/UTF_SweepFormulaHardware.ipf index 18a3c63a2b..22d9c15986 100644 --- a/Packages/tests/HardwareBasic/UTF_SweepFormulaHardware.ipf +++ b/Packages/tests/HardwareBasic/UTF_SweepFormulaHardware.ipf @@ -72,7 +72,7 @@ static Function TestSweepFormulaAnnotations(string device) formula = "data(select(selrange(cursors(A,B)),selchannels(AD),selsweeps(),selvis(all)))" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) - annoInfo = AnnotationInfo(plotWin, "metadata", 1) + annoInfo = AnnotationInfo(plotWin, SF_ANNOTATION_NAME, 1) typeRef = "Legend" flagsRef = "/N=metadata/J/I=0/V=1/D=1/LS=0/O=0/F=2/S=0/H=0/Q/Z=0/G=(0,0,0)/B=(65535,65535,65535)/T=36/A=RT/X=5.00/Y=5.00" textRef = "\\s(T000000d0_Sweep_0_AD1) Sweep 0 AD1\r\\s(T000001d1_Sweep_0_AD2) Sweep 0 AD2\r\\s(T000002d2_Sweep_1_AD1) Sweep 1 AD1\r\\s(T000003d3_Sweep_1_AD2) Sweep 1 AD2\r\\s(T000004d4_Sweep_2_AD1) Sweep 2 AD1\r\\s(T000005d5_Sweep_2_AD2) Sweep 2 AD2" @@ -86,7 +86,7 @@ static Function TestSweepFormulaAnnotations(string device) formula = "avg(data(select(selrange(cursors(A,B)),selchannels(AD),selsweeps(),selvis(all))))" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) - annoInfo = AnnotationInfo(plotWin, "metadata", 1) + annoInfo = AnnotationInfo(plotWin, SF_ANNOTATION_NAME, 1) typeRef = "Legend" flagsRef = "/N=metadata/J/I=0/V=1/D=1/LS=0/O=0/F=2/S=0/H=0/Q/Z=0/G=(0,0,0)/B=(65535,65535,65535)/T=36/A=RT/X=5.00/Y=5.00" textRef = "\\s(T000000d0_avg_data_Sweep_0_AD1) avg data Sweep 0 AD1\r\\s(T000001d1_avg_data_Sweep_0_AD2) avg data Sweep 0 AD2\r\\s(T000002d2_avg_data_Sweep_1_AD1) avg data Sweep 1 AD1\r\\s(T000003d3_avg_data_Sweep_1_AD2) avg data Sweep 1 AD2\r\\s(T000004d4_avg_data_Sweep_2_AD1) avg data Sweep 2 AD1\r\\s(T000005d5_avg_data_Sweep_2_AD2) avg data Sweep 2 AD2" @@ -100,7 +100,7 @@ static Function TestSweepFormulaAnnotations(string device) formula = "avg(avg(data(select(selrange(cursors(A,B)),selchannels(AD),selsweeps(),selvis(all)))))" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) - annoInfo = AnnotationInfo(plotWin, "metadata", 1) + annoInfo = AnnotationInfo(plotWin, SF_ANNOTATION_NAME, 1) typeRef = "Legend" flagsRef = "/N=metadata/J/I=0/V=1/D=1/LS=0/O=0/F=2/S=0/H=0/Q/Z=0/G=(0,0,0)/B=(65535,65535,65535)/T=36/A=RT/X=5.00/Y=5.00" textRef = "\\s(T000000d0_avg_avg_data_Sweep_0_AD1) avg avg data Sweep 0 AD1\r\\s(T000001d1_avg_avg_data_Sweep_0_AD2) avg avg data Sweep 0 AD2\r\\s(T000002d2_avg_avg_data_Sweep_1_AD1) avg avg data Sweep 1 AD1\r\\s(T000003d3_avg_avg_data_Sweep_1_AD2) avg avg data Sweep 1 AD2\r\\s(T000004d4_avg_avg_data_Sweep_2_AD1) avg avg data Sweep 2 AD1\r\\s(T000005d5_avg_avg_data_Sweep_2_AD2) avg avg data Sweep 2 AD2" diff --git a/Packages/tests/UTF_DataGenerators.ipf b/Packages/tests/UTF_DataGenerators.ipf index d417b0ee32..dde442d934 100644 --- a/Packages/tests/UTF_DataGenerators.ipf +++ b/Packages/tests/UTF_DataGenerators.ipf @@ -576,6 +576,10 @@ static Function/WAVE TestHelpNotebookGetter_IGNORE() SetDimensionLabels(wt, TextWaveToList(wt, ";"), ROWS) +#ifdef AUTOMATED_TESTING + RemoveTextWaveEntry1D(wt, SF_OP_TESTOP) +#endif // AUTOMATED_TESTING + return wt End