Skip to content

Commit 40d1e61

Browse files
authored
Merge pull request #2387 from AllenInstitute/feature/2387-sf-enhancements-range-concat
SF: Fix range and add concat
2 parents 9bd75f0 + f6e2894 commit 40d1e61

9 files changed

Lines changed: 383 additions & 44 deletions

Packages/MIES/MIES_Constants.ipf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2109,6 +2109,7 @@ StrConstant SF_DATATYPE_AVG = "Average"
21092109
StrConstant SF_DATATYPE_MAX = "Max"
21102110
StrConstant SF_DATATYPE_MIN = "Min"
21112111
StrConstant SF_DATATYPE_RANGE = "Range"
2112+
StrConstant SF_DATATYPE_CONCAT = "Concat"
21122113
StrConstant SF_DATATYPE_EPOCHS = "Epochs"
21132114
StrConstant SF_DATATYPE_TP = "TestPulse"
21142115
StrConstant SF_DATATYPE_TPSS = "TestPulseMode_SteadyState"

Packages/MIES/MIES_JSONWaveNotes.ipf

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,34 @@
99
static Constant JWN_DEFAULT_RELEASE = 1
1010

1111
/// @brief Gets the JSON wave note part as string
12-
threadsafe Function/S JWN_GetWaveNoteAsString(WAVE wv)
12+
threadsafe static Function/S JWN_GetWaveNoteAsString(string noteStr)
1313

1414
variable pos, len
15-
string noteStr
1615

17-
ASSERT_TS(WaveExists(wv), "Missing wave")
18-
noteStr = note(wv)
19-
pos = strsearch(noteStr, WAVE_NOTE_JSON_SEPARATOR, 0)
20-
len = strlen(WAVE_NOTE_JSON_SEPARATOR)
16+
pos = strsearch(noteStr, WAVE_NOTE_JSON_SEPARATOR, 0)
17+
len = strlen(WAVE_NOTE_JSON_SEPARATOR)
2118
if(pos >= 0 && strlen(noteStr) > (pos + len))
2219
return noteStr[pos + len, Inf]
2320
endif
2421

2522
return WAVE_NOTE_EMPTY_JSON
2623
End
2724

25+
threadsafe static Function JWN_Parse(string noteStr)
26+
27+
return JSON_Parse(JWN_GetWaveNoteAsString(noteStr))
28+
End
29+
2830
/// @brief Gets the JSON wave note part as JSON object
2931
/// The caller is responsible to release the returned jsonId after use.
3032
threadsafe Function JWN_GetWaveNoteAsJSON(WAVE wv)
3133

34+
string noteStr
35+
3236
ASSERT_TS(WaveExists(wv), "Missing wave")
37+
noteStr = note(wv)
3338

34-
return JSON_Parse(JWN_GetWaveNoteAsString(wv))
39+
return JWN_Parse(noteStr)
3540
End
3641

3742
/// @brief Set the JSON json document as JSON wave note. Releases json if `release` is true (default).
@@ -176,12 +181,17 @@ End
176181
/// @returns the value on success. An empty string is returned if it could not be found
177182
threadsafe Function/S JWN_GetStringFromWaveNote(WAVE wv, string jsonPath)
178183

184+
return JWN_GetStringFromNote(note(wv), jsonPath)
185+
End
186+
187+
threadsafe Function/S JWN_GetStringFromNote(string noteStr, string jsonPath)
188+
179189
variable jsonID
180190
string str
181191

182192
ASSERT_TS(!IsEmpty(jsonPath), "Empty jsonPath")
183193

184-
jsonID = JWN_GetWaveNoteAsJSON(wv)
194+
jsonID = JWN_Parse(noteStr)
185195
str = JSON_GetString(jsonID, jsonPath, ignoreErr = 1)
186196
JSON_Release(jsonID)
187197

Packages/MIES/MIES_SweepFormula.ipf

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ static StrConstant SF_OP_PLUS = "+"
6464
static StrConstant SF_OP_MULT = "*"
6565
static StrConstant SF_OP_DIV = "~1"
6666
static StrConstant SF_OP_RANGE = "range"
67+
static StrConstant SF_OP_CONCAT = "concat"
6768
static StrConstant SF_OP_RANGESHORT = "…"
6869
static StrConstant SF_OP_MIN = "min"
6970
static StrConstant SF_OP_MAX = "max"
@@ -255,7 +256,7 @@ Function/WAVE SF_GetNamedOperations()
255256
SF_OP_MERGE, SF_OP_FIT, SF_OP_FITLINE, SF_OP_DATASET, SF_OP_SELECTVIS, SF_OP_SELECTCM, SF_OP_SELECTSTIMSET, \
256257
SF_OP_SELECTIVSCCSWEEPQC, SF_OP_SELECTIVSCCSETQC, SF_OP_SELECTRANGE, SF_OP_SELECTEXP, SF_OP_SELECTDEV, \
257258
SF_OP_SELECTEXPANDSCI, SF_OP_SELECTEXPANDRAC, SF_OP_SELECTSETCYCLECOUNT, SF_OP_SELECTSETSWEEPCOUNT, \
258-
SF_OP_SELECTSCIINDEX, SF_OP_SELECTRACINDEX, SF_OP_ANAFUNCPARAM}
259+
SF_OP_SELECTSCIINDEX, SF_OP_SELECTRACINDEX, SF_OP_ANAFUNCPARAM, SF_OP_CONCAT}
259260

260261
return wt
261262
End
@@ -1044,6 +1045,9 @@ static Function/WAVE SF_FormulaExecutor(string graph, variable jsonID, [string j
10441045
case SF_OP_RANGESHORT:
10451046
WAVE out = SF_OperationRange(jsonId, jsonPath, graph)
10461047
break
1048+
case SF_OP_CONCAT:
1049+
WAVE out = SF_OperationConcat(jsonId, jsonPath, graph)
1050+
break
10471051
case SF_OP_MIN:
10481052
WAVE out = SF_OperationMin(jsonId, jsonPath, graph)
10491053
break
@@ -4023,37 +4027,68 @@ End
40234027
/// range (start[, stop[, step]])
40244028
static Function/WAVE SF_OperationRange(variable jsonId, string jsonPath, string graph)
40254029

4026-
variable numArgs
4030+
variable start, stop, step, stopDefault
40274031

4028-
numArgs = SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_RANGE, 1, maxArgs = 3)
4032+
SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_RANGE, 1, maxArgs = 3)
40294033

4030-
WAVE arg0 = SFH_ResolveDatasetElementFromJSON(jsonId, jsonpath, graph, SF_OP_RANGE, 0, checkExist = 1)
4031-
if(numArgs == 1)
4032-
SFH_ASSERT(IsNumericWave(arg0), "range first argument must be numeric")
4033-
Make/FREE/D/N=(abs(trunc(arg0[0]))) range
4034-
MultiThread range = p
4035-
elseif(numArgs == 2)
4036-
WAVE arg1 = SFH_ResolveDatasetElementFromJSON(jsonId, jsonpath, graph, SF_OP_RANGE, 1, checkExist = 1)
4037-
SFH_ASSERT(IsNumericWave(arg1), "range second argument must be numeric")
4038-
Make/FREE/D/N=(abs(trunc(arg0[0]) - trunc(arg1[0]))) range
4039-
MultiThread range[] = arg0[0] + p
4040-
SFH_CleanUpInput(arg1)
4041-
elseif(numArgs == 3)
4042-
WAVE arg1 = SFH_ResolveDatasetElementFromJSON(jsonId, jsonpath, graph, SF_OP_RANGE, 1, checkExist = 1)
4043-
WAVE arg2 = SFH_ResolveDatasetElementFromJSON(jsonId, jsonpath, graph, SF_OP_RANGE, 2, checkExist = 1)
4044-
SFH_ASSERT(IsNumericWave(arg1), "range second argument must be numeric")
4045-
SFH_ASSERT(IsNumericWave(arg2), "range third argument must be numeric")
4046-
Make/FREE/D/N=(ceil(abs((arg0[0] - arg1[0]) / arg2[0]))) range
4047-
MultiThread range[] = arg0[0] + p * arg2[0]
4048-
SFH_CleanUpInput(arg1)
4049-
SFH_CleanUpInput(arg2)
4034+
start = SFH_GetArgumentAsNumeric(jsonId, jsonpath, graph, SF_OP_RANGE, 0)
4035+
stop = SFH_GetArgumentAsNumeric(jsonId, jsonpath, graph, SF_OP_RANGE, 1, defValue = NaN)
4036+
step = SFH_GetArgumentAsNumeric(jsonId, jsonpath, graph, SF_OP_RANGE, 2, defValue = 1)
4037+
4038+
if(IsNaN(stop))
4039+
stop = 0
4040+
stopDefault = 1
40504041
endif
40514042

4052-
SFH_CleanUpInput(arg0)
4043+
Make/FREE/D/N=(ceil(abs((start - stop) / step))) range
4044+
4045+
if(stopDefault)
4046+
MultiThread range[] = p * step
4047+
else
4048+
MultiThread range[] = start + p * step
4049+
endif
40534050

40544051
return SFH_GetOutputForExecutorSingle(range, graph, SF_OP_RANGE, dataType = SF_DATATYPE_RANGE)
40554052
End
40564053

4054+
/// concat(array0, array1, array2, ...)
4055+
static Function/WAVE SF_OperationConcat(variable jsonId, string jsonPath, string graph)
4056+
4057+
variable numArgs, i, err, majorType, sliceMajorType
4058+
variable constantDataType
4059+
string refDataType, dataType, wvNote, errMsg
4060+
4061+
numArgs = SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_CONCAT, 1)
4062+
4063+
WAVE result = SFH_GetArgumentAsWave(jsonId, jsonpath, graph, SF_OP_CONCAT, 0, copy = 1, singleResult = 1, wvNote = wvNote)
4064+
majorType = WaveType(result, 1)
4065+
4066+
refDataType = JWN_GetStringFromNote(wvNote, SF_META_DATATYPE)
4067+
dataType = refDataType
4068+
constantDataType = !IsEmpty(refDataType)
4069+
Note/K result
4070+
4071+
AssertOnAndClearRTError()
4072+
for(i = 1; i < numArgs; i += 1)
4073+
WAVE slice = SFH_GetArgumentAsWave(jsonId, jsonpath, graph, SF_OP_CONCAT, i, singleResult = 1, wvNote = wvNote)
4074+
sliceMajorType = WaveType(slice, 1)
4075+
4076+
if(majorType != sliceMajorType)
4077+
sprintf errMsg, "Concatenate failed as the wave types of the first argument and #%d don't match: %s vs %s", i, WaveTypeToStringSelectorOne(majorType), WaveTypeToStringSelectorOne(sliceMajorType)
4078+
SFH_ASSERT(0, errMsg)
4079+
endif
4080+
4081+
dataType = JWN_GetStringFromNote(wvNote, SF_META_DATATYPE)
4082+
constantDataType = constantDataType && !CmpStr(refDataType, dataType)
4083+
4084+
Concatenate/FREE/NP {slice}, result; errMsg = GetRTErrMessage(); err = GetRTError(1)
4085+
SFH_ASSERT(!err, "Error concatenating waves: " + errMsg)
4086+
endfor
4087+
4088+
dataType = SelectString(constantDataType, SF_DATATYPE_CONCAT, dataType)
4089+
return SFH_GetOutputForExecutorSingle(result, graph, SF_OP_CONCAT, discardOpStack = 1, dataType = dataType)
4090+
End
4091+
40574092
static Function/WAVE SF_OperationMin(variable jsonId, string jsonPath, string graph)
40584093

40594094
WAVE/WAVE input = SF_GetNumericVarArgs(jsonId, jsonPath, graph, SF_OP_MIN)

Packages/MIES/MIES_SweepFormula_Helpers.ipf

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -235,14 +235,34 @@ End
235235
/// returned.
236236
///
237237
/// The second argument `birdTypes` is optional, if not present the operation `birdTypes()` is called and its result returned. Alternatively `defWave` can be supplied which is then returned if the argument is not present.
238-
Function/WAVE SFH_GetArgumentAsWave(variable jsonId, string jsonPath, string graph, string opShort, variable argNum, [string defOp, WAVE/Z defWave, variable singleResult, variable expectedMinorType, variable expectedMajorType, variable copy])
239-
240-
variable checkExist, numArgs, checkMinorType, checkMajorType
238+
///
239+
/// @param jsonId JSON identifier
240+
/// @param jsonPath Location in the AST
241+
/// @param graph Databrowser graph
242+
/// @param opShort Short name of the operation
243+
/// @param argNum Argument index
244+
/// @param defOp [optional, defaults to None] SF code to execute in case the argument is not present
245+
/// @param defWave [optional] wave to return in case the argument is not present and defOp is not supplied
246+
/// @param singleResult [optional, defaults to 0] Return the first wave of the dataset if it has only one entry
247+
/// @param expectedMinorType [optional, defaults to None] Expected minor wave type, possible values are from WaveType(wv, 0)
248+
/// @param expectedMajorType [optional, defaults to None] Expected major wave type, possible values are from WaveType(wv, 1)
249+
/// @param copy [optional, defaults to 0] If the returned data should be safe for modification (true) or is only read (false)
250+
/// @param[out] wvNote [optional, defaults to None] Wave note of the dataset, useful for single result cases where you still need
251+
/// to query JSON wave note entries
252+
Function/WAVE SFH_GetArgumentAsWave(variable jsonId, string jsonPath, string graph, string opShort, variable argNum, [string defOp, WAVE/Z defWave, variable singleResult, variable expectedMinorType, variable expectedMajorType, variable copy, string &wvNote])
253+
254+
variable checkExist, numArgs, checkMinorType, checkMajorType, result
241255
string msg
242256

257+
if(!ParamIsDefault(wvNote))
258+
ASSERT(ParamIsDefault(defOp) && ParamIsDefault(defWave), \
259+
"The optional parameters wvNote and defOp/defWave can't be used together")
260+
endif
261+
243262
if(ParamIsDefault(defOp) && ParamIsDefault(defWave))
244263
checkExist = 1
245264
else
265+
ASSERT((ParamIsDefault(defOp) + ParamIsDefault(defWave)) == 1, "Can only supply one of defOp and defWave")
246266
checkExist = 0
247267
endif
248268

@@ -265,6 +285,10 @@ Function/WAVE SFH_GetArgumentAsWave(variable jsonId, string jsonPath, string gra
265285
sprintf msg, "Argument #%d of operation %s: Too many input values", argNum, opShort
266286
SFH_ASSERT(DimSize(input, ROWS) == 1, msg)
267287

288+
if(!ParamIsDefault(wvNote))
289+
wvNote = note(input)
290+
endif
291+
268292
WAVE/Z data = input[0]
269293
SFH_CleanUpInput(input)
270294
else
@@ -278,12 +302,18 @@ Function/WAVE SFH_GetArgumentAsWave(variable jsonId, string jsonPath, string gra
278302
WAVE/WAVE dataAsRef = data
279303
Make/FREE/N=(DimSize(data, ROWS)) types = WaveType(dataAsRef[p])
280304
endif
281-
sprintf msg, "Argument #%d of operation %s: Expected minor wave type %d", argNum, opShort, expectedMinorType
282-
if(expectedMinorType)
305+
306+
if(expectedMinorType > 0)
283307
types[] = !!(types[p] & expectedMinorType)
284-
SFH_ASSERT(sum(types) == DimSize(types, ROWS), msg)
308+
result = sum(types) == DimSize(types, ROWS)
285309
else
286-
SFH_ASSERT(IsConstant(types, expectedMinorType), msg)
310+
result = IsConstant(types, expectedMinorType)
311+
endif
312+
313+
if(!result)
314+
Make/T/FREE/N=(DimSize(types, ROWS)) typesText = WaveTypeToStringSelectorZero(types[p])
315+
sprintf msg, "Argument #%d of operation %s: Expected minor wave type %s but got %s", argNum, opShort, WaveTypeToStringSelectorZero(expectedMinorType), TextWaveToList(typesText, ", ", trailSep = 0)
316+
SFH_ASSERT(result, msg)
287317
endif
288318
endif
289319
if(checkMajorType)
@@ -294,8 +324,11 @@ Function/WAVE SFH_GetArgumentAsWave(variable jsonId, string jsonPath, string gra
294324
Make/FREE/N=(DimSize(data, ROWS)) types = WaveType(dataAsRef[p], 1)
295325
endif
296326

297-
sprintf msg, "Argument #%d of operation %s: Expected major wave type %d", argNum, opShort, expectedMajorType
298-
SFH_ASSERT(IsConstant(types, expectedMajorType), msg)
327+
if(!IsConstant(types, expectedMajorType))
328+
Make/T/FREE/N=(DimSize(types, ROWS)) typesText = WaveTypeToStringSelectorOne(types[p])
329+
sprintf msg, "Argument #%d of operation %s: Expected major wave type %s but got %s", argNum, opShort, WaveTypeToStringSelectorOne(expectedMajorType), TextWaveToList(typesText, ", ", trailSep = 0)
330+
SFH_ASSERT(0, msg)
331+
endif
299332
endif
300333

301334
return SFH_CopyDataIfRequired(copy, input, data)

Packages/MIES/MIES_Utilities_Strings.ipf

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,3 +514,66 @@ threadsafe Function/S UpperCaseFirstChar(string str)
514514

515515
return UpperStr(str[0]) + str[1, len - 1]
516516
End
517+
518+
/// @brief Human readable name for possible return values of WaveType(wv, 0)
519+
///
520+
/// We don't do any error checking if the given type really exists.
521+
threadsafe Function/S WaveTypeToStringSelectorZero(variable type)
522+
523+
string result = ""
524+
variable trim
525+
526+
if(type == IGOR_TYPE_TEXT_WREF_DFR)
527+
return "non-numeric (text, wave ref, dfref)"
528+
endif
529+
530+
if(type & IGOR_TYPE_COMPLEX)
531+
result += "complex "
532+
endif
533+
534+
if(type & IGOR_TYPE_32BIT_FLOAT)
535+
result += "32-bit float"
536+
elseif(type & IGOR_TYPE_64BIT_FLOAT)
537+
result += "64-bit float"
538+
elseif(type & IGOR_TYPE_8BIT_INT)
539+
result += "8-bit int"
540+
elseif(type & IGOR_TYPE_16BIT_INT)
541+
result += "16-bit int"
542+
elseif(type & IGOR_TYPE_32BIT_INT)
543+
result += "32-bit int"
544+
elseif(type & IGOR_TYPE_64BIT_INT)
545+
result += "64-bit int"
546+
else
547+
// do nothing here
548+
trim = 1
549+
endif
550+
551+
if(type & IGOR_TYPE_UNSIGNED)
552+
result += " unsigned"
553+
endif
554+
555+
if(trim)
556+
return trimstring(result)
557+
endif
558+
559+
return result
560+
End
561+
562+
/// @brief Human readable name for possible return values of WaveType(wv, 1)
563+
threadsafe Function/S WaveTypeToStringSelectorOne(variable type)
564+
565+
switch(type)
566+
case IGOR_TYPE_NULL_WAVE:
567+
return "null"
568+
case IGOR_TYPE_NUMERIC_WAVE:
569+
return "numeric"
570+
case IGOR_TYPE_TEXT_WAVE:
571+
return "text"
572+
case IGOR_TYPE_DFREF_WAVE:
573+
return "datafolder reference"
574+
case IGOR_TYPE_WAVEREF_WAVE:
575+
return "wave reference"
576+
default:
577+
ASSERT_TS(0, "Unknown constant: " + num2str(type, "%d"))
578+
endswitch
579+
End

Packages/MIES/SweepFormulaHelp.ifn

1.9 KB
Binary file not shown.

Packages/doc/SweepFormula.rst

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,13 +1722,13 @@ the Unicode Character 'HORIZONTAL ELLIPSIS' (U+2026). Writing "..." is automatic
17221722
The function generally accepts 1 to 3 arguments. The operation is intended to be
17231723
used with two arguments.
17241724
1725-
The operation accepts also multiple data waves as first argument. Each data wave content must follow the operation argument order and size in that case.
1726-
For this case the operation is applied on each input data wave independently and returns the same number of data waves.
17271725
The returned data type is `SF_DATATYPE_RANGE`.
17281726
17291727
.. code-block:: bash
17301728
17311729
range(1, 5, 0.7) == [1, 1.7, 2.4, 3.1, 3.8, 4.5]
1730+
range(3) == [0, 1, 2]
1731+
range(1, 4) == [1, 2, 3]
17321732
17331733
epochs
17341734
""""""
@@ -1983,6 +1983,26 @@ The operation currently throws away all metadata.
19831983
19841984
merge(4, 7, 8) == [4, 7, 8]
19851985
1986+
concat
1987+
""""""
1988+
1989+
The concat operation allows to concatenate multiple arrays together.
1990+
1991+
.. code-block:: bash
1992+
1993+
concat(array data1, array data2, ...)
1994+
1995+
data1, data2, ...
1996+
data waves (numeric and text)
1997+
1998+
The operation accepts 1 to unlimited arguments. The dimensionality of all input
1999+
waves and their types must match.
2000+
2001+
.. code-block:: bash
2002+
2003+
concat([1, 5, [3, 8]) == [1, 5, 3, 8]
2004+
concat(["a", "b], ["e", "f"]) == [a, b, e, f]
2005+
19862006
dataset
19872007
"""""""
19882008

0 commit comments

Comments
 (0)