@aics/vole-app 2.13.6 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1112 @@
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator["return"] && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, "catch": function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
3
+ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
4
+ function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
5
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
6
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
7
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
8
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
9
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
10
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
11
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
12
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
13
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
14
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
15
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
16
+ import { describe, expect, it } from "@jest/globals";
17
+ import { getDefaultCameraState, getDefaultChannelState, getDefaultViewerState } from "../../constants";
18
+ import { ImageType, RenderMode, ViewMode } from "../../enums";
19
+ import { CONTROL_POINTS_REGEX, deserializeViewerChannelSetting, deserializeViewerState, LEGACY_CONTROL_POINTS_REGEX, parseHexColorAsColorArray, parseKeyValueList, parseStringEnum, parseStringFloat, parseStringInt, parseViewerUrlParams, serializeCameraState, serializeViewerChannelSetting, serializeViewerState, serializeViewerUrlParams } from "../urlParsing";
20
+ var defaultSettings = {
21
+ match: 0,
22
+ color: undefined,
23
+ enabled: undefined,
24
+ surfaceEnabled: undefined,
25
+ isovalue: undefined,
26
+ surfaceOpacity: undefined,
27
+ colorizeEnabled: undefined,
28
+ colorizeAlpha: undefined
29
+ };
30
+
31
+ //// VALUE PARSING ///////////////////////////////////////
32
+
33
+ describe("LEGACY_CONTROL_POINTS_REGEX", function () {
34
+ it("accepts single control points", function () {
35
+ var data = "1:0.5:ffffff";
36
+ expect(LEGACY_CONTROL_POINTS_REGEX.test(data)).toBe(true);
37
+ });
38
+ it("accepts multiple control points", function () {
39
+ var data = "1:0.5:ff0000,128:0.7:ffff00,255:1:ff0000";
40
+ expect(LEGACY_CONTROL_POINTS_REGEX.test(data)).toBe(true);
41
+ });
42
+ it("accepts negative numbers", function () {
43
+ var data = "-1:0.5:ff0000,-255:1:ff0000";
44
+ expect(LEGACY_CONTROL_POINTS_REGEX.test(data)).toBe(true);
45
+ });
46
+ it("allows 'w' as a placeholder for color strings", function () {
47
+ var data = "1:0.5:1,255:1:1";
48
+ expect(LEGACY_CONTROL_POINTS_REGEX.test(data)).toBe(true);
49
+ });
50
+ });
51
+ describe("CONTROL_POINTS_REGEX", function () {
52
+ it("accepts single control points", function () {
53
+ var data = "1:0.5:ffffff";
54
+ expect(CONTROL_POINTS_REGEX.test(data)).toBe(true);
55
+ });
56
+ it("accepts multiple control points", function () {
57
+ var data = "1:0.5:ff0000:128:0.7:ffff00:255:1:ff0000";
58
+ expect(CONTROL_POINTS_REGEX.test(data)).toBe(true);
59
+ });
60
+ it("accepts negative numbers", function () {
61
+ var data = "-1:0.5:ff0000:-255:1:ff0000";
62
+ expect(CONTROL_POINTS_REGEX.test(data)).toBe(true);
63
+ });
64
+ it("allows empty color strings", function () {
65
+ var data = "1:0.5:1:255:1:1";
66
+ expect(CONTROL_POINTS_REGEX.test(data)).toBe(true);
67
+ });
68
+ });
69
+ describe("parseKeyValueList", function () {
70
+ it("returns expected key value pairs", function () {
71
+ var data = "key1:value1,key2:value2";
72
+ var result = parseKeyValueList(data);
73
+ expect(result).toEqual({
74
+ key1: "value1",
75
+ key2: "value2"
76
+ });
77
+ });
78
+ it("decodes encoded key and value string names", function () {
79
+ var data = "ampersand%26:ampersand%26,comma%2C:comma%2C,colon%3A:colon%3A";
80
+ var result = parseKeyValueList(data);
81
+ expect(result).toEqual({
82
+ "ampersand&": "ampersand&",
83
+ "comma,": "comma,",
84
+ "colon:": "colon:"
85
+ });
86
+ });
87
+ it("ignores repeat colons in value string", function () {
88
+ var data = "key1:value1:extra:extra,key2:value2";
89
+ var result = parseKeyValueList(data);
90
+ expect(result).toEqual({
91
+ key1: "value1:extra:extra",
92
+ key2: "value2"
93
+ });
94
+ });
95
+ it("returns empty object for empty string", function () {
96
+ var data = "";
97
+ var result = parseKeyValueList(data);
98
+ expect(result).toEqual({});
99
+ });
100
+ it("parses example viewer channel settings", function () {
101
+ var data = "col:FF0000,clz:1,cza:0.5,isa:0.75,ven:1,sen:1,isv:128,lut:autoij:";
102
+ var result = parseKeyValueList(data);
103
+ expect(result).toEqual({
104
+ col: "FF0000",
105
+ clz: "1",
106
+ cza: "0.5",
107
+ isa: "0.75",
108
+ ven: "1",
109
+ sen: "1",
110
+ isv: "128",
111
+ lut: "autoij:"
112
+ });
113
+ });
114
+ it("removes trailing and leading whitespace", function () {
115
+ var data = " key1 : value1 , key2 : value2 , key3: value 3";
116
+ var result = parseKeyValueList(data);
117
+ expect(result).toEqual({
118
+ key1: "value1",
119
+ key2: "value2",
120
+ key3: "value 3"
121
+ });
122
+ });
123
+ });
124
+ describe("parseHexColorAsColorArray", function () {
125
+ it("parses hex values", function () {
126
+ expect(parseHexColorAsColorArray("000000")).toEqual([0, 0, 0]);
127
+ expect(parseHexColorAsColorArray("FFFFFF")).toEqual([255, 255, 255]);
128
+ expect(parseHexColorAsColorArray("ff0000")).toEqual([255, 0, 0]);
129
+ expect(parseHexColorAsColorArray("123456")).toEqual([18, 52, 86]);
130
+ expect(parseHexColorAsColorArray("7890ab")).toEqual([120, 144, 171]);
131
+ expect(parseHexColorAsColorArray("cdefef")).toEqual([205, 239, 239]);
132
+ expect(parseHexColorAsColorArray("ABCDEF")).toEqual([171, 205, 239]);
133
+ });
134
+ it("ignores unrecognized formats", function () {
135
+ expect(parseHexColorAsColorArray("f")).toBeUndefined();
136
+ expect(parseHexColorAsColorArray("fff")).toBeUndefined();
137
+ expect(parseHexColorAsColorArray("fffffff")).toBeUndefined();
138
+ expect(parseHexColorAsColorArray("hjklmn")).toBeUndefined();
139
+ });
140
+ it("parses the '1' code as white", function () {
141
+ expect(parseHexColorAsColorArray("1")).toEqual([255, 255, 255]);
142
+ });
143
+ });
144
+ describe("parseStringEnum", function () {
145
+ var TestEnum = /*#__PURE__*/function (TestEnum) {
146
+ TestEnum["SOME_VALUE"] = "some_value";
147
+ TestEnum["ANOTHER_VALUE"] = "another_value";
148
+ return TestEnum;
149
+ }({});
150
+ it("recognizes enum values", function () {
151
+ expect(parseStringEnum("some_value", TestEnum)).toEqual(TestEnum.SOME_VALUE);
152
+ expect(parseStringEnum("another_value", TestEnum)).toEqual(TestEnum.ANOTHER_VALUE);
153
+ });
154
+ it("returns default values for unrecognized enum values", function () {
155
+ expect(parseStringEnum("unexpected", TestEnum, undefined)).toEqual(undefined);
156
+ expect(parseStringEnum("unexpected", TestEnum, TestEnum.SOME_VALUE)).toEqual(TestEnum.SOME_VALUE);
157
+ });
158
+ });
159
+ describe("parseStringInt", function () {
160
+ it("parses int and undefined values", function () {
161
+ expect(parseStringInt("0", 0, 1000)).toEqual(0);
162
+ expect(parseStringInt("1", 0, 1000)).toEqual(1);
163
+ expect(parseStringInt("100", 0, 1000)).toEqual(100);
164
+ expect(parseStringInt("255", 0, 1000)).toEqual(255);
165
+ expect(parseStringInt("-1", -1000, 1000)).toEqual(-1);
166
+ });
167
+ it("returns undefined for undefined and NaN values", function () {
168
+ expect(parseStringInt("bad", -1000, 1000)).toEqual(undefined);
169
+ expect(parseStringInt("NaN", -1000, 1000)).toEqual(undefined);
170
+ expect(parseStringInt(undefined, -1000, 1000)).toEqual(undefined);
171
+ });
172
+ it("casts float values to integers", function () {
173
+ expect(parseStringInt("0.5", 0, 1000)).toEqual(0);
174
+ expect(parseStringInt("99.5", 0, 1000)).toEqual(99);
175
+ expect(parseStringInt("-10.5", -1000, 1000)).toEqual(-10);
176
+ });
177
+ it("applies clamping", function () {
178
+ expect(parseStringInt("0", 0, 255)).toEqual(0);
179
+ expect(parseStringInt("-1", 0, 255)).toEqual(0);
180
+ expect(parseStringInt("128", 0, 255)).toEqual(128);
181
+ expect(parseStringInt("255", 0, 255)).toEqual(255);
182
+ expect(parseStringInt("256", 0, 255)).toEqual(255);
183
+ });
184
+ });
185
+ describe("parseStringFloat", function () {
186
+ it("parses strings to float values", function () {
187
+ expect(parseStringFloat("0", -1000, 1000)).toEqual(0);
188
+ expect(parseStringFloat("1.0", -1000, 1000)).toEqual(1.0);
189
+ expect(parseStringFloat("0.5", -1000, 1000)).toEqual(0.5);
190
+ expect(parseStringFloat("128.5", -1000, 1000)).toEqual(128.5);
191
+ expect(parseStringFloat("495.344", -1000, 1000)).toEqual(495.344);
192
+ expect(parseStringFloat("-1", -1000, 1000)).toEqual(-1);
193
+ });
194
+ it("returns undefined for NaN or undefined values", function () {
195
+ expect(parseStringFloat("bad", -1000, 1000)).toEqual(undefined);
196
+ expect(parseStringFloat("NaN", -1000, 1000)).toEqual(undefined);
197
+ expect(parseStringFloat(undefined, -1000, 1000)).toEqual(undefined);
198
+ });
199
+ it("applies clamping", function () {
200
+ expect(parseStringFloat("0.5", 0, 1.0)).toEqual(0.5);
201
+ expect(parseStringFloat("1.0", 0, 1.0)).toEqual(1.0);
202
+ expect(parseStringFloat("1.1", 0, 1.0)).toEqual(1.0);
203
+ expect(parseStringFloat("0", 0, 1.0)).toEqual(0);
204
+ expect(parseStringFloat("-0.1", 0, 1.0)).toEqual(0);
205
+ });
206
+ });
207
+
208
+ //// SERIALIZE STATES ///////////////////////
209
+
210
+ describe("Channel state serialization", function () {
211
+ var DEFAULT_CHANNEL_STATE = {
212
+ name: "",
213
+ displayName: "",
214
+ color: [255, 0, 0],
215
+ volumeEnabled: true,
216
+ isosurfaceEnabled: true,
217
+ isovalue: 128,
218
+ opacity: 0.75,
219
+ colorizeEnabled: true,
220
+ colorizeAlpha: 0.5,
221
+ useControlPoints: false,
222
+ controlPoints: [{
223
+ x: 0,
224
+ opacity: 0.5,
225
+ color: [255, 255, 255]
226
+ }, {
227
+ x: 255,
228
+ opacity: 1.0,
229
+ color: [255, 255, 255]
230
+ }],
231
+ ramp: [0, 255],
232
+ plotMin: 0,
233
+ plotMax: 255
234
+ };
235
+ var DEFAULT_SERIALIZED_CHANNEL_STATE = {
236
+ col: "ff0000",
237
+ ven: "1",
238
+ sen: "1",
239
+ isv: "128",
240
+ isa: "0.75",
241
+ clz: "1",
242
+ cza: "0.5",
243
+ cpe: "0",
244
+ cps: "0:0.5:1:255:1:1",
245
+ rmp: "0:255"
246
+ };
247
+
248
+ // Note that the serialization + deserialization are NOT direct inverses.
249
+ // Serializing converts a ChannelState object to a ViewerChannelSettingParams object,
250
+ // deserializing converts a ViewerChannelSettingParams object to a **ViewerChannelSetting** object.
251
+
252
+ // ChannelStates are per-channel and are used internally, while ViewerChannelSettings are
253
+ // input into the viewer and support grouping/matching to multiple channels.
254
+ // TODO: refactor these to all be the same state?
255
+
256
+ describe("deserializeViewerChannelSetting", function () {
257
+ it("returns default settings for empty objects", function () {
258
+ var data = {};
259
+ var result = deserializeViewerChannelSetting(0, data);
260
+ expect(result).toEqual(defaultSettings);
261
+ });
262
+ it("ignores unexpected keys", function () {
263
+ var data = {
264
+ badKey: "badValue",
265
+ ven: "1",
266
+ sen: "1"
267
+ };
268
+ var result = deserializeViewerChannelSetting(0, data);
269
+ expect(result).toEqual(_objectSpread(_objectSpread({}, defaultSettings), {}, {
270
+ enabled: true,
271
+ surfaceEnabled: true
272
+ }));
273
+ });
274
+ it("parses settings correctly", function () {
275
+ var data = {
276
+ col: "FF0000",
277
+ clz: "1",
278
+ cza: "0.5",
279
+ isa: "0.75",
280
+ ven: "1",
281
+ sen: "1",
282
+ isv: "128",
283
+ lut: "0:255"
284
+ };
285
+ expect(deserializeViewerChannelSetting(0, data)).toEqual({
286
+ match: 0,
287
+ color: "FF0000",
288
+ enabled: true,
289
+ surfaceEnabled: true,
290
+ isovalue: 128,
291
+ surfaceOpacity: 0.75,
292
+ colorizeEnabled: true,
293
+ colorizeAlpha: 0.5,
294
+ lut: ["0", "255"]
295
+ });
296
+ });
297
+ it("handles expected lut formatting", function () {
298
+ var luts = [["autoij:0", ["autoij", "0"]], ["0:autoij", ["0", "autoij"]], [":autoij", ["", "autoij"]], ["autoij:", ["autoij", ""]], ["0:255", ["0", "255"]], ["0.5:1.0", ["0.5", "1.0"]], ["0.50:1.00", ["0.50", "1.00"]], ["m99:m100", ["m99", "m100"]], ["p10:p90", ["p10", "p90"]], ["p10: p90", ["p10", "p90"]] // handle spaces
299
+ ];
300
+ for (var _i = 0, _luts = luts; _i < _luts.length; _i++) {
301
+ var _luts$_i = _slicedToArray(_luts[_i], 2),
302
+ encodedLut = _luts$_i[0],
303
+ decodedLut = _luts$_i[1];
304
+ var data = {
305
+ lut: encodedLut
306
+ };
307
+ var result = deserializeViewerChannelSetting(0, data);
308
+ expect(result.lut).toEqual(decodedLut);
309
+ }
310
+ });
311
+ it("ignores unexpected lut formats", function () {
312
+ var luts = ["!:0", "0:9:93", "255", ""];
313
+ for (var _i2 = 0, _luts2 = luts; _i2 < _luts2.length; _i2++) {
314
+ var lut = _luts2[_i2];
315
+ var data = {
316
+ lut: lut
317
+ };
318
+ var result = deserializeViewerChannelSetting(0, data);
319
+ expect(result.lut).toBeUndefined();
320
+ }
321
+ });
322
+ it("handles hex color formats", function () {
323
+ var colors = ["000000", "FFFFFF", "ffffff", "012345", "6789AB", "CDEF01", "abcdef"];
324
+ for (var _i3 = 0, _colors = colors; _i3 < _colors.length; _i3++) {
325
+ var color = _colors[_i3];
326
+ var data = {
327
+ col: color
328
+ };
329
+ var result = deserializeViewerChannelSetting(0, data);
330
+ expect(result.color).toEqual(color);
331
+ }
332
+ });
333
+ it("ignores bad color formats", function () {
334
+ var badColors = ["f", "ff00", "red", "rgb(255,0,0)"];
335
+ for (var _i4 = 0, _badColors = badColors; _i4 < _badColors.length; _i4++) {
336
+ var color = _badColors[_i4];
337
+ var data = {
338
+ col: color
339
+ };
340
+ var result = deserializeViewerChannelSetting(0, data);
341
+ expect(result.color).toBeUndefined();
342
+ }
343
+ });
344
+ it("ignores bad float data", function () {
345
+ var data = {
346
+ cza: "NaN",
347
+ isa: "bad",
348
+ isv: "f8"
349
+ };
350
+ var result = deserializeViewerChannelSetting(0, data);
351
+ expect(result.colorizeAlpha).toBeUndefined();
352
+ expect(result.surfaceOpacity).toBeUndefined();
353
+ expect(result.isovalue).toBeUndefined();
354
+ });
355
+ });
356
+ describe("serializeViewerChannelSetting", function () {
357
+ it("serializes channel settings", function () {
358
+ expect(serializeViewerChannelSetting(DEFAULT_CHANNEL_STATE, false)).toEqual(DEFAULT_SERIALIZED_CHANNEL_STATE);
359
+ });
360
+ it("serializes custom channel settings", function () {
361
+ var customChannelState = {
362
+ name: "a",
363
+ displayName: "a",
364
+ color: [3, 255, 157],
365
+ volumeEnabled: false,
366
+ isosurfaceEnabled: false,
367
+ isovalue: 0,
368
+ opacity: 0.54,
369
+ colorizeEnabled: false,
370
+ colorizeAlpha: 1.0,
371
+ useControlPoints: false,
372
+ controlPoints: [],
373
+ ramp: [0, 255],
374
+ // TODO: the settings below are not serialized. should they be? (see #384)
375
+ plotMin: 0,
376
+ plotMax: 255
377
+ };
378
+ var serializedCustomChannelState = {
379
+ col: "03ff9d",
380
+ ven: "0",
381
+ sen: "0",
382
+ isv: "0",
383
+ isa: "0.54",
384
+ clz: "0",
385
+ cza: "1",
386
+ cpe: "0",
387
+ cps: "",
388
+ rmp: "0:255"
389
+ };
390
+ expect(serializeViewerChannelSetting(customChannelState, false)).toEqual(serializedCustomChannelState);
391
+ });
392
+ });
393
+ });
394
+ describe("Viewer state", function () {
395
+ var DEFAULT_VIEWER_STATE = {
396
+ viewMode: ViewMode.threeD,
397
+ // "XY", "XZ", "YZ"
398
+ renderMode: RenderMode.volumetric,
399
+ // "pathtrace", "maxproject"
400
+ imageType: ImageType.segmentedCell,
401
+ showAxes: false,
402
+ showBoundingBox: false,
403
+ backgroundColor: [0, 0, 0],
404
+ boundingBoxColor: [255, 255, 255],
405
+ autorotate: false,
406
+ maskAlpha: 0,
407
+ brightness: 70,
408
+ density: 50,
409
+ levels: [35, 140, 255],
410
+ interpolationEnabled: true,
411
+ region: {
412
+ x: [0, 1],
413
+ y: [0, 1],
414
+ z: [0, 1]
415
+ },
416
+ slice: {
417
+ x: 0.5,
418
+ y: 0.5,
419
+ z: 0.5
420
+ },
421
+ time: 0,
422
+ scene: 0,
423
+ cameraState: undefined
424
+ };
425
+ var SERIALIZED_DEFAULT_VIEWER_STATE = {
426
+ mode: "volumetric",
427
+ view: "3D",
428
+ image: "cell",
429
+ axes: "0",
430
+ bb: "0",
431
+ bgcol: "000000",
432
+ bbcol: "ffffff",
433
+ rot: "0",
434
+ mask: "0",
435
+ bright: "70",
436
+ dens: "50",
437
+ lvl: "35,140,255",
438
+ interp: "1",
439
+ reg: "0:1,0:1,0:1",
440
+ slice: "0.5,0.5,0.5",
441
+ t: "0",
442
+ scene: "0"
443
+ };
444
+ var CUSTOM_VIEWER_STATE = {
445
+ renderMode: RenderMode.pathTrace,
446
+ viewMode: ViewMode.xy,
447
+ imageType: ImageType.fullField,
448
+ showAxes: true,
449
+ showBoundingBox: true,
450
+ backgroundColor: [255, 0, 0],
451
+ boundingBoxColor: [0, 255, 0],
452
+ autorotate: true,
453
+ maskAlpha: 55,
454
+ brightness: 100,
455
+ density: 75,
456
+ levels: [0, 250, 251],
457
+ interpolationEnabled: false,
458
+ region: {
459
+ x: [0, 0.5],
460
+ y: [0, 1],
461
+ z: [0, 1]
462
+ },
463
+ slice: {
464
+ x: 0.25,
465
+ y: 0.75,
466
+ z: 0.5
467
+ },
468
+ time: 100,
469
+ scene: 3,
470
+ cameraState: {
471
+ position: [-1.05, -4, 45],
472
+ target: [0, 0, 0],
473
+ up: [0, 1, 0],
474
+ orthoScale: 3.534,
475
+ fov: 43.5
476
+ }
477
+ };
478
+ var SERIALIZED_CUSTOM_VIEWER_STATE = {
479
+ mode: "pathtrace",
480
+ view: "Z",
481
+ image: "fov",
482
+ axes: "1",
483
+ bb: "1",
484
+ bgcol: "ff0000",
485
+ bbcol: "00ff00",
486
+ rot: "1",
487
+ mask: "55",
488
+ bright: "100",
489
+ dens: "75",
490
+ lvl: "0,250,251",
491
+ interp: "0",
492
+ reg: "0:0.5,0:1,0:1",
493
+ slice: "0.25,0.75,0.5",
494
+ t: "100",
495
+ scene: "3",
496
+ cam: "pos:-1.05:-4:45,tar:0:0:0,up:0:1:0,ort:3.534,fov:43.5"
497
+ };
498
+ describe("serializeViewerState", function () {
499
+ it("serializes the default viewer settings", function () {
500
+ expect(serializeViewerState(DEFAULT_VIEWER_STATE, false)).toEqual(SERIALIZED_DEFAULT_VIEWER_STATE);
501
+ });
502
+ it("serializes custom viewer settings", function () {
503
+ expect(serializeViewerState(CUSTOM_VIEWER_STATE, false)).toEqual(SERIALIZED_CUSTOM_VIEWER_STATE);
504
+ });
505
+ it("shortens long numbers in the slice and region parameters", function () {
506
+ // Floats should be rounded to 7 significant digits or less
507
+ var state = {
508
+ region: {
509
+ x: [0.4566666666, 0.8667332],
510
+ y: [0.49999999, 0.8999999],
511
+ z: [0.3000000001, 0.16467883]
512
+ },
513
+ slice: {
514
+ x: 0.41111186,
515
+ y: 0.49999999,
516
+ z: 0.677402
517
+ }
518
+ };
519
+ var serializedState = serializeViewerState(state, true);
520
+ expect(serializedState.reg).toEqual("0.4566667:0.8667332,0.5:0.8999999,0.3:0.1646788");
521
+ expect(serializedState.slice).toEqual("0.4111119,0.5,0.677402");
522
+ });
523
+ });
524
+ describe("deserializeViewerState", function () {
525
+ it("returns an empty object for empty input", function () {
526
+ expect(deserializeViewerState({})).toEqual({});
527
+ });
528
+ it("deserializes the default viewer settings", function () {
529
+ var params = SERIALIZED_DEFAULT_VIEWER_STATE;
530
+ expect(deserializeViewerState(params)).toEqual(DEFAULT_VIEWER_STATE);
531
+ });
532
+ it("deserializes custom viewer settings", function () {
533
+ var params = SERIALIZED_CUSTOM_VIEWER_STATE;
534
+ expect(deserializeViewerState(params)).toEqual(CUSTOM_VIEWER_STATE);
535
+ });
536
+ it("handles all ViewMode values", function () {
537
+ var viewModes = Object.values(ViewMode);
538
+ for (var _i5 = 0, _viewModes = viewModes; _i5 < _viewModes.length; _i5++) {
539
+ var viewMode = _viewModes[_i5];
540
+ var state = _objectSpread(_objectSpread({}, DEFAULT_VIEWER_STATE), {}, {
541
+ viewMode: viewMode
542
+ });
543
+ expect(deserializeViewerState(serializeViewerState(state, false)).viewMode).toEqual(viewMode);
544
+ }
545
+ });
546
+ it("handles all RenderMode values", function () {
547
+ var renderModes = Object.values(RenderMode);
548
+ for (var _i6 = 0, _renderModes = renderModes; _i6 < _renderModes.length; _i6++) {
549
+ var renderMode = _renderModes[_i6];
550
+ var state = _objectSpread(_objectSpread({}, DEFAULT_VIEWER_STATE), {}, {
551
+ renderMode: renderMode
552
+ });
553
+ expect(deserializeViewerState(serializeViewerState(state, false)).renderMode).toEqual(renderMode);
554
+ }
555
+ });
556
+ it("deserializes partial camera settings", function () {
557
+ var state = {
558
+ cameraState: {
559
+ position: [1.0, -1.4, 45],
560
+ up: [0, 1, 0],
561
+ fov: 43.5
562
+ }
563
+ };
564
+ var serializedState = "pos:1:-1.4:45,up:0:1:0,fov:43.5";
565
+ expect(deserializeViewerState({
566
+ cam: serializedState
567
+ })).toEqual(state);
568
+ });
569
+ });
570
+ });
571
+ describe("Camera state", function () {
572
+ it("uses default camera state when choosing elements to exclude/ignore", function () {
573
+ var cameraState = _objectSpread({}, getDefaultCameraState());
574
+ // No changes from default
575
+ expect(serializeCameraState(cameraState, true)).toEqual(undefined);
576
+ cameraState = _objectSpread(_objectSpread({}, cameraState), {}, {
577
+ position: [1, 2, 3]
578
+ });
579
+ expect(serializeCameraState(cameraState, true)).toEqual("pos:1:2:3");
580
+ });
581
+ it("default camera state has not been changed", function () {
582
+ // The default camera state should NOT change unless backwards compatibility
583
+ // is added to ensure old links still maintain the same camera orientation;
584
+ // otherwise, cameras will appear in the new default orientation unexpectedly.
585
+ expect(getDefaultCameraState()).toEqual({
586
+ position: [0, 0, 5],
587
+ target: [0, 0, 0],
588
+ up: [0, 1, 0],
589
+ fov: 20,
590
+ orthoScale: 0.5
591
+ });
592
+ });
593
+ });
594
+
595
+ //// DESERIALIZE STATES ///////////////////////
596
+
597
+ describe("Channel state deserialization", function () {
598
+ var DEFAULT_CONTROL_POINTS = [{
599
+ x: -10,
600
+ opacity: 0,
601
+ color: [0, 0, 0]
602
+ }, {
603
+ x: 50,
604
+ opacity: 0,
605
+ color: [0, 0, 0]
606
+ }, {
607
+ x: 100,
608
+ opacity: 0.3,
609
+ color: [0, 16, 255]
610
+ }, {
611
+ x: 140,
612
+ opacity: 0.8,
613
+ color: [0, 255, 255]
614
+ }, {
615
+ x: 260,
616
+ opacity: 1,
617
+ color: [0, 255, 180]
618
+ }];
619
+ it("parses comma-separated control points", function () {
620
+ var result = deserializeViewerChannelSetting(0, {
621
+ cps: "-10:0:000000,50:0:000000,100:0.3:0010ff,140:0.8:00ffff,260:1:00ffb4"
622
+ });
623
+ expect(result.controlPoints).toEqual(DEFAULT_CONTROL_POINTS);
624
+ });
625
+ it("parses colon-separated control points", function () {
626
+ var result = deserializeViewerChannelSetting(0, {
627
+ cps: "-10:0:000000:50:0:000000:100:0.3:0010ff:140:0.8:00ffff:260:1:00ffb4"
628
+ });
629
+ expect(result.controlPoints).toEqual(DEFAULT_CONTROL_POINTS);
630
+ });
631
+ it("replaces '1' color strings with default color #ffffff", function () {
632
+ var result = deserializeViewerChannelSetting(0, {
633
+ cps: "0:0:1:50:1:1"
634
+ });
635
+ expect(result.controlPoints).toEqual([{
636
+ x: 0,
637
+ opacity: 0,
638
+ color: [255, 255, 255]
639
+ }, {
640
+ x: 50,
641
+ opacity: 1,
642
+ color: [255, 255, 255]
643
+ }]);
644
+ });
645
+ });
646
+
647
+ //// URL parsing /////////////////////////////////
648
+
649
+ describe("parseViewerUrlParams", function () {
650
+ // Tests will try parsing both unencoded and encoded URL params.
651
+ var channelParamToSetting = [["c3=ven:1,col:ff00ff,clz:0,cza:0.9,isa:0.4,lut:p50:p99,sen:1,isv:129", "c3=ven%3A1%2Ccol%3Aff00ff%2Cclz%3A0%2Ccza%3A0.9%2Cisa%3A0.4%2Clut%3Ap50%3Ap99%2Csen%3A1%2Cisv%3A129", {
652
+ color: "ff00ff",
653
+ name: undefined,
654
+ match: 3,
655
+ enabled: true,
656
+ surfaceEnabled: true,
657
+ surfaceOpacity: 0.4,
658
+ colorizeEnabled: false,
659
+ colorizeAlpha: 0.9,
660
+ isovalue: 129,
661
+ lut: ["p50", "p99"]
662
+ }], ["c1=ven:0,col:ff0000,clz:1,cza:0.5,isa:0.75,lut:0:255,sen:0,isv:0", "c1=ven%3A0%2Ccol%3Aff0000%2Cclz%3A1%2Ccza%3A0.5%2Cisa%3A0.75%2Clut%3A0%3A255%2Csen%3A0%2Cisv%3A0", {
663
+ color: "ff0000",
664
+ name: undefined,
665
+ match: 1,
666
+ colorizeEnabled: true,
667
+ colorizeAlpha: 0.5,
668
+ surfaceOpacity: 0.75,
669
+ surfaceEnabled: false,
670
+ enabled: false,
671
+ isovalue: 0,
672
+ lut: ["0", "255"]
673
+ }], ["c5=ven:0,col:00ff00,clz:0,cza:0,isa:1,lut:autoij:,sen:1,isv:100", "c5=ven%3A0%2Ccol%3A00ff00%2Cclz%3A0%2Ccza%3A0%2Cisa%3A1%2Clut%3Aautoij%3A%2Csen%3A1%2Cisv%3A100", {
674
+ color: "00ff00",
675
+ name: undefined,
676
+ match: 5,
677
+ colorizeEnabled: false,
678
+ colorizeAlpha: 0,
679
+ surfaceOpacity: 1,
680
+ surfaceEnabled: true,
681
+ enabled: false,
682
+ isovalue: 100,
683
+ lut: ["autoij", ""]
684
+ }]];
685
+ it("parses unencoded per-channel setting", /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
686
+ var _i7, _channelParamToSettin, _args$viewerChannelSe, _channelParamToSettin2, queryString, expected, params, _yield$parseViewerUrl, args, channelSetting;
687
+ return _regeneratorRuntime().wrap(function _callee$(_context) {
688
+ while (1) switch (_context.prev = _context.next) {
689
+ case 0:
690
+ _i7 = 0, _channelParamToSettin = channelParamToSetting;
691
+ case 1:
692
+ if (!(_i7 < _channelParamToSettin.length)) {
693
+ _context.next = 13;
694
+ break;
695
+ }
696
+ _channelParamToSettin2 = _slicedToArray(_channelParamToSettin[_i7], 3), queryString = _channelParamToSettin2[0], expected = _channelParamToSettin2[2];
697
+ params = new URLSearchParams(queryString);
698
+ _context.next = 6;
699
+ return parseViewerUrlParams(params);
700
+ case 6:
701
+ _yield$parseViewerUrl = _context.sent;
702
+ args = _yield$parseViewerUrl.args;
703
+ channelSetting = (_args$viewerChannelSe = args.viewerChannelSettings) === null || _args$viewerChannelSe === void 0 ? void 0 : _args$viewerChannelSe.groups[0].channels[0];
704
+ expect(channelSetting).toEqual(expected);
705
+ case 10:
706
+ _i7++;
707
+ _context.next = 1;
708
+ break;
709
+ case 13:
710
+ case "end":
711
+ return _context.stop();
712
+ }
713
+ }, _callee);
714
+ })));
715
+ it("parses encoded per-channel settings", /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2() {
716
+ var _i8, _channelParamToSettin3, _args$viewerChannelSe2, _channelParamToSettin4, queryString, expected, params, _yield$parseViewerUrl2, args, channelSetting;
717
+ return _regeneratorRuntime().wrap(function _callee2$(_context2) {
718
+ while (1) switch (_context2.prev = _context2.next) {
719
+ case 0:
720
+ _i8 = 0, _channelParamToSettin3 = channelParamToSetting;
721
+ case 1:
722
+ if (!(_i8 < _channelParamToSettin3.length)) {
723
+ _context2.next = 13;
724
+ break;
725
+ }
726
+ _channelParamToSettin4 = _slicedToArray(_channelParamToSettin3[_i8], 3), queryString = _channelParamToSettin4[1], expected = _channelParamToSettin4[2];
727
+ params = new URLSearchParams(queryString);
728
+ _context2.next = 6;
729
+ return parseViewerUrlParams(params);
730
+ case 6:
731
+ _yield$parseViewerUrl2 = _context2.sent;
732
+ args = _yield$parseViewerUrl2.args;
733
+ channelSetting = (_args$viewerChannelSe2 = args.viewerChannelSettings) === null || _args$viewerChannelSe2 === void 0 ? void 0 : _args$viewerChannelSe2.groups[0].channels[0];
734
+ expect(channelSetting).toEqual(expected);
735
+ case 10:
736
+ _i8++;
737
+ _context2.next = 1;
738
+ break;
739
+ case 13:
740
+ case "end":
741
+ return _context2.stop();
742
+ }
743
+ }, _callee2);
744
+ })));
745
+ it("parses multiple per-channel settings", /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee3() {
746
+ var i, _args$viewerChannelSe3, queryString, params, _yield$parseViewerUrl3, args, channelSettings;
747
+ return _regeneratorRuntime().wrap(function _callee3$(_context3) {
748
+ while (1) switch (_context3.prev = _context3.next) {
749
+ case 0:
750
+ i = 0;
751
+ case 1:
752
+ if (!(i < 2)) {
753
+ _context3.next = 15;
754
+ break;
755
+ }
756
+ queryString = channelParamToSetting[0][i] + "&" + channelParamToSetting[1][i] + "&" + channelParamToSetting[2][i];
757
+ params = new URLSearchParams(queryString);
758
+ _context3.next = 6;
759
+ return parseViewerUrlParams(params);
760
+ case 6:
761
+ _yield$parseViewerUrl3 = _context3.sent;
762
+ args = _yield$parseViewerUrl3.args;
763
+ channelSettings = (_args$viewerChannelSe3 = args.viewerChannelSettings) === null || _args$viewerChannelSe3 === void 0 ? void 0 : _args$viewerChannelSe3.groups[0].channels; // Order is not guaranteed, so check if any of the expected settings are present
764
+ expect(channelSettings).toContainEqual(channelParamToSetting[0][2]);
765
+ expect(channelSettings).toContainEqual(channelParamToSetting[1][2]);
766
+ expect(channelSettings).toContainEqual(channelParamToSetting[2][2]);
767
+ case 12:
768
+ i++;
769
+ _context3.next = 1;
770
+ break;
771
+ case 15:
772
+ case "end":
773
+ return _context3.stop();
774
+ }
775
+ }, _callee3);
776
+ })));
777
+ it("overrides ch settings when per-channel settings are included", /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee4() {
778
+ var _args$viewerChannelSe4;
779
+ var queryString, params, _yield$parseViewerUrl4, args, groups, channelSetting;
780
+ return _regeneratorRuntime().wrap(function _callee4$(_context4) {
781
+ while (1) switch (_context4.prev = _context4.next) {
782
+ case 0:
783
+ queryString = "?ch=0&lut=1,2&c1=ven:1,lut:4:5";
784
+ params = new URLSearchParams(queryString);
785
+ _context4.next = 4;
786
+ return parseViewerUrlParams(params);
787
+ case 4:
788
+ _yield$parseViewerUrl4 = _context4.sent;
789
+ args = _yield$parseViewerUrl4.args;
790
+ groups = (_args$viewerChannelSe4 = args.viewerChannelSettings) === null || _args$viewerChannelSe4 === void 0 ? void 0 : _args$viewerChannelSe4.groups[0];
791
+ expect(groups.channels).toHaveLength(1);
792
+ channelSetting = groups.channels[0];
793
+ expect(channelSetting.match).toEqual(1);
794
+ expect(channelSetting.enabled).toEqual(true);
795
+ expect(channelSetting.lut).toEqual(["4", "5"]);
796
+ case 12:
797
+ case "end":
798
+ return _context4.stop();
799
+ }
800
+ }, _callee4);
801
+ })));
802
+ it("skips missing channel indices", /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee5() {
803
+ var _args$viewerChannelSe5;
804
+ var queryString, params, _yield$parseViewerUrl5, args, groups, channelSetting1, channelSetting2;
805
+ return _regeneratorRuntime().wrap(function _callee5$(_context5) {
806
+ while (1) switch (_context5.prev = _context5.next) {
807
+ case 0:
808
+ queryString = "?c0=ven:1&c15=ven:1,lut:4:5";
809
+ params = new URLSearchParams(queryString);
810
+ _context5.next = 4;
811
+ return parseViewerUrlParams(params);
812
+ case 4:
813
+ _yield$parseViewerUrl5 = _context5.sent;
814
+ args = _yield$parseViewerUrl5.args;
815
+ groups = (_args$viewerChannelSe5 = args.viewerChannelSettings) === null || _args$viewerChannelSe5 === void 0 ? void 0 : _args$viewerChannelSe5.groups[0];
816
+ expect(groups.channels).toHaveLength(2);
817
+ channelSetting1 = groups.channels[0];
818
+ channelSetting2 = groups.channels[1];
819
+ expect(channelSetting1.match).toEqual(0);
820
+ expect(channelSetting1.enabled).toEqual(true);
821
+ expect(channelSetting2.match).toEqual(15);
822
+ expect(channelSetting2.enabled).toEqual(true);
823
+ expect(channelSetting2.lut).toEqual(["4", "5"]);
824
+ case 15:
825
+ case "end":
826
+ return _context5.stop();
827
+ }
828
+ }, _callee5);
829
+ })));
830
+ it("creates empty default data for bad per-channel setting formats", /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee6() {
831
+ var _args$viewerChannelSe6;
832
+ var queryString, params, _yield$parseViewerUrl6, args, channelSettings, i, channelSetting;
833
+ return _regeneratorRuntime().wrap(function _callee6$(_context6) {
834
+ while (1) switch (_context6.prev = _context6.next) {
835
+ case 0:
836
+ queryString = "c1=bad&c0=ultrabad:bad&c2=,,,,,,";
837
+ params = new URLSearchParams(queryString);
838
+ _context6.next = 4;
839
+ return parseViewerUrlParams(params);
840
+ case 4:
841
+ _yield$parseViewerUrl6 = _context6.sent;
842
+ args = _yield$parseViewerUrl6.args;
843
+ channelSettings = (_args$viewerChannelSe6 = args.viewerChannelSettings) === null || _args$viewerChannelSe6 === void 0 ? void 0 : _args$viewerChannelSe6.groups[0].channels;
844
+ expect(channelSettings).toHaveLength(3);
845
+ for (i = 0; i < channelSettings.length; i++) {
846
+ channelSetting = channelSettings[i]; // Match with default on everything except match number
847
+ expect(channelSetting).toEqual(_objectSpread(_objectSpread({}, defaultSettings), {}, {
848
+ match: channelSetting.match
849
+ }));
850
+ }
851
+ case 9:
852
+ case "end":
853
+ return _context6.stop();
854
+ }
855
+ }, _callee6);
856
+ })));
857
+ it("enables first three channels by default if no channel settings are provided", /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee7() {
858
+ var _args$viewerChannelSe7;
859
+ var queryString, params, _yield$parseViewerUrl7, args, channelSettingsGroups, channelSettings;
860
+ return _regeneratorRuntime().wrap(function _callee7$(_context7) {
861
+ while (1) switch (_context7.prev = _context7.next) {
862
+ case 0:
863
+ queryString = "url=https://example.com/image.tiff";
864
+ params = new URLSearchParams(queryString);
865
+ _context7.next = 4;
866
+ return parseViewerUrlParams(params);
867
+ case 4:
868
+ _yield$parseViewerUrl7 = _context7.sent;
869
+ args = _yield$parseViewerUrl7.args;
870
+ // Should have one group
871
+ channelSettingsGroups = (_args$viewerChannelSe7 = args.viewerChannelSettings) === null || _args$viewerChannelSe7 === void 0 ? void 0 : _args$viewerChannelSe7.groups;
872
+ expect(channelSettingsGroups).toHaveLength(1);
873
+ channelSettings = channelSettingsGroups[0].channels;
874
+ expect(channelSettings[0]).toEqual({
875
+ match: [0, 1, 2],
876
+ enabled: true
877
+ });
878
+ expect(channelSettings[1]).toEqual({
879
+ match: "(.+)",
880
+ enabled: false
881
+ });
882
+ case 11:
883
+ case "end":
884
+ return _context7.stop();
885
+ }
886
+ }, _callee7);
887
+ })));
888
+ it("parses default nucmorph settings", /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee8() {
889
+ var _args$viewerChannelSe8;
890
+ var queryString, params, _yield$parseViewerUrl8, args, viewerSettings, channelSettings;
891
+ return _regeneratorRuntime().wrap(function _callee8$(_context8) {
892
+ while (1) switch (_context8.prev = _context8.next) {
893
+ case 0:
894
+ queryString = "url=https://example.com/image1.ome.zarr,https://example.com/image2.ome.zarr&c0=ven:0&c1=ven:1,lut:autoij:&c2=ven:1,clz:1&view=Z";
895
+ params = new URLSearchParams(queryString);
896
+ _context8.next = 4;
897
+ return parseViewerUrlParams(params);
898
+ case 4:
899
+ _yield$parseViewerUrl8 = _context8.sent;
900
+ args = _yield$parseViewerUrl8.args;
901
+ viewerSettings = _yield$parseViewerUrl8.viewerSettings;
902
+ expect(viewerSettings.viewMode).toEqual(ViewMode.xy);
903
+ expect(args.imageUrl).toEqual({
904
+ scenes: [["https://example.com/image1.ome.zarr", "https://example.com/image2.ome.zarr"]]
905
+ });
906
+
907
+ // Check that channel settings have been loaded in.
908
+ // Should be one group with three channels.
909
+ channelSettings = (_args$viewerChannelSe8 = args.viewerChannelSettings) === null || _args$viewerChannelSe8 === void 0 ? void 0 : _args$viewerChannelSe8.groups[0];
910
+ expect(channelSettings).toBeDefined();
911
+ channelSettings = channelSettings;
912
+ expect(channelSettings.channels).toHaveLength(3);
913
+ expect(channelSettings.channels[0].match).toEqual(0);
914
+ expect(channelSettings.channels[0].enabled).toEqual(false);
915
+ expect(channelSettings.channels[1].match).toEqual(1);
916
+ expect(channelSettings.channels[1].enabled).toEqual(true);
917
+ expect(channelSettings.channels[1].lut).toEqual(["autoij", ""]);
918
+ expect(channelSettings.channels[2].match).toEqual(2);
919
+ expect(channelSettings.channels[2].enabled).toEqual(true);
920
+ expect(channelSettings.channels[2].colorizeEnabled).toEqual(true);
921
+ case 21:
922
+ case "end":
923
+ return _context8.stop();
924
+ }
925
+ }, _callee8);
926
+ })));
927
+ it("parses viewer settings", /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee9() {
928
+ var queryString, params, _yield$parseViewerUrl9, viewerSettings;
929
+ return _regeneratorRuntime().wrap(function _callee9$(_context9) {
930
+ while (1) switch (_context9.prev = _context9.next) {
931
+ case 0:
932
+ queryString = "mask=30&view=X";
933
+ params = new URLSearchParams(queryString);
934
+ _context9.next = 4;
935
+ return parseViewerUrlParams(params);
936
+ case 4:
937
+ _yield$parseViewerUrl9 = _context9.sent;
938
+ viewerSettings = _yield$parseViewerUrl9.viewerSettings;
939
+ expect(viewerSettings.viewMode).toEqual(ViewMode.yz);
940
+ expect(viewerSettings.maskAlpha).toEqual(30);
941
+ case 8:
942
+ case "end":
943
+ return _context9.stop();
944
+ }
945
+ }, _callee9);
946
+ })));
947
+ it("returns an empty object when no params are passed in", /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee10() {
948
+ var queryString, params, _yield$parseViewerUrl10, args, viewerSettings;
949
+ return _regeneratorRuntime().wrap(function _callee10$(_context10) {
950
+ while (1) switch (_context10.prev = _context10.next) {
951
+ case 0:
952
+ queryString = "";
953
+ params = new URLSearchParams(queryString);
954
+ _context10.next = 4;
955
+ return parseViewerUrlParams(params);
956
+ case 4:
957
+ _yield$parseViewerUrl10 = _context10.sent;
958
+ args = _yield$parseViewerUrl10.args;
959
+ viewerSettings = _yield$parseViewerUrl10.viewerSettings;
960
+ expect(Object.keys(args).length === 0);
961
+ expect(args).toEqual({});
962
+ expect(Object.keys(viewerSettings).length === 0);
963
+ expect(viewerSettings).toEqual({});
964
+ case 11:
965
+ case "end":
966
+ return _context10.stop();
967
+ }
968
+ }, _callee10);
969
+ })));
970
+ });
971
+ describe("serializeViewerUrlParams", function () {
972
+ it("serializes channel settings to key-value list format", function () {
973
+ var channelStates = [{
974
+ name: "channel0",
975
+ displayName: "channel0",
976
+ color: [255, 0, 0],
977
+ volumeEnabled: true,
978
+ isosurfaceEnabled: true,
979
+ isovalue: 128,
980
+ opacity: 0.75,
981
+ colorizeEnabled: true,
982
+ colorizeAlpha: 0.5,
983
+ useControlPoints: false,
984
+ controlPoints: [{
985
+ x: 0,
986
+ opacity: 0,
987
+ color: [128, 128, 128]
988
+ }, {
989
+ x: 1,
990
+ opacity: 1,
991
+ color: [255, 0, 0]
992
+ }],
993
+ ramp: [-10, 260.1],
994
+ plotMin: 0,
995
+ plotMax: 255
996
+ }, {
997
+ name: "channel1",
998
+ displayName: "channel1",
999
+ color: [128, 128, 128],
1000
+ volumeEnabled: false,
1001
+ isosurfaceEnabled: false,
1002
+ isovalue: 57,
1003
+ opacity: 0.0,
1004
+ colorizeEnabled: false,
1005
+ colorizeAlpha: 0.2,
1006
+ useControlPoints: true,
1007
+ controlPoints: [{
1008
+ x: -10,
1009
+ opacity: 0,
1010
+ color: [0, 0, 0]
1011
+ }, {
1012
+ x: 50,
1013
+ opacity: 0,
1014
+ color: [0, 0, 0]
1015
+ }, {
1016
+ x: 100,
1017
+ opacity: 0.3,
1018
+ color: [0, 16, 255]
1019
+ }, {
1020
+ x: 140,
1021
+ opacity: 0.8,
1022
+ color: [0, 255, 255]
1023
+ }, {
1024
+ x: 260,
1025
+ opacity: 1.0,
1026
+ color: [0, 255, 180]
1027
+ }],
1028
+ ramp: [50, 140],
1029
+ plotMin: 0,
1030
+ plotMax: 255
1031
+ }];
1032
+ var serialized = serializeViewerUrlParams({
1033
+ channelSettings: channelStates
1034
+ }, false);
1035
+ // Format should look like "ven:1,col:ff0000,clz:1,cza:0.75,isa:0.5,sen:1,isv:128", but ordering
1036
+ // is not guaranteed. Parse the string and check that the values match the expected values.
1037
+ // Note that `lut` is not included when serializing from existing viewer state.
1038
+ var expectedChannel0 = {
1039
+ ven: "1",
1040
+ col: "ff0000",
1041
+ clz: "1",
1042
+ cza: "0.5",
1043
+ isa: "0.75",
1044
+ sen: "1",
1045
+ isv: "128",
1046
+ rmp: "-10:260.1",
1047
+ cps: "0:0:808080:1:1:ff0000",
1048
+ cpe: "0"
1049
+ };
1050
+ var expectedChannel1 = {
1051
+ ven: "0",
1052
+ col: "808080",
1053
+ clz: "0",
1054
+ cza: "0.2",
1055
+ isa: "0",
1056
+ sen: "0",
1057
+ isv: "57",
1058
+ rmp: "50:140",
1059
+ cps: "-10:0:000000:50:0:000000:100:0.3:0010ff:140:0.8:00ffff:260:1:00ffb4",
1060
+ cpe: "1"
1061
+ };
1062
+ expect(serialized["c0"]).toBeDefined();
1063
+ expect(parseKeyValueList(serialized["c0"])).toEqual(expectedChannel0);
1064
+ expect(serialized["c1"]).toBeDefined();
1065
+ expect(parseKeyValueList(serialized["c1"])).toEqual(expectedChannel1);
1066
+ });
1067
+ it("can remove viewer settings that match the default", function () {
1068
+ var _defaultViewerSetting, _defaultViewerSetting2;
1069
+ var defaultViewerSettings = getDefaultViewerState();
1070
+ var customViewerState = {
1071
+ viewMode: ViewMode.xy,
1072
+ density: 100,
1073
+ time: 40,
1074
+ cameraState: {
1075
+ position: [1.2, 3.4, 5.6],
1076
+ target: (_defaultViewerSetting = defaultViewerSettings.cameraState) === null || _defaultViewerSetting === void 0 ? void 0 : _defaultViewerSetting.target,
1077
+ up: (_defaultViewerSetting2 = defaultViewerSettings.cameraState) === null || _defaultViewerSetting2 === void 0 ? void 0 : _defaultViewerSetting2.up
1078
+ }
1079
+ };
1080
+ var serializedParams = serializeViewerUrlParams(_objectSpread(_objectSpread({}, defaultViewerSettings), customViewerState), true);
1081
+ var urlParams = new URLSearchParams(serializedParams);
1082
+ // Pos is 1.2:3.4:5.6 but escaped
1083
+ var expectedCameraPosition = encodeURIComponent("pos:1.2:3.4:5.6");
1084
+ expect(urlParams.toString()).toEqual("dens=100&t=40&cam=".concat(expectedCameraPosition, "&view=Z"));
1085
+ });
1086
+ it("can remove channel state fields that matches the default", function () {
1087
+ // Note that this unit test will break if the
1088
+ var customChannelState = {
1089
+ color: [255, 0, 0],
1090
+ useControlPoints: true,
1091
+ isovalue: 49
1092
+ };
1093
+ var serializedParams = serializeViewerUrlParams(_objectSpread(_objectSpread({}, getDefaultViewerState()), {}, {
1094
+ channelSettings: [_objectSpread(_objectSpread({}, getDefaultChannelState()), customChannelState)]
1095
+ }), true);
1096
+ var urlParams = new URLSearchParams(serializedParams);
1097
+ var expectedEncodedParams = encodeURIComponent("isv:49,col:ff0000,cpe:1");
1098
+ expect(urlParams.toString()).toEqual("c0=" + expectedEncodedParams);
1099
+ });
1100
+ it("does not use object reference comparison on control points when excluding defaults", function () {
1101
+ // Expand control points so it isn't comparing an object reference
1102
+ var defaultChannelState = getDefaultChannelState();
1103
+ var customChannelState = {
1104
+ controlPoints: [_objectSpread({}, defaultChannelState.controlPoints[0]), _objectSpread({}, defaultChannelState.controlPoints[1])]
1105
+ };
1106
+ var serializedParams = serializeViewerUrlParams(_objectSpread(_objectSpread({}, getDefaultViewerState()), {}, {
1107
+ channelSettings: [_objectSpread(_objectSpread({}, defaultChannelState), customChannelState)]
1108
+ }), true);
1109
+ var urlParams = new URLSearchParams(serializedParams);
1110
+ expect(urlParams.toString()).toEqual("c0=");
1111
+ });
1112
+ });