@aics/vole-app 2.13.0 → 2.13.2
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.
- package/es/aics-image-viewer/components/App/index.js +211 -479
- package/es/aics-image-viewer/components/AxisClipSliders/index.js +2 -2
- package/es/aics-image-viewer/components/useVolume.js +327 -0
- package/es/aics-image-viewer/shared/constants.js +9 -0
- package/es/aics-image-viewer/shared/utils/hooks.js +1 -24
- package/es/aics-image-viewer/shared/utils/sliderValuesToImageValues.js +15 -9
- package/package.json +2 -2
- package/type-declarations/aics-image-viewer/components/useVolume.d.ts +49 -0
- package/type-declarations/aics-image-viewer/shared/constants.d.ts +4 -1
- package/type-declarations/aics-image-viewer/shared/utils/hooks.d.ts +0 -2
- package/type-declarations/aics-image-viewer/shared/utils/sliderValuesToImageValues.d.ts +6 -3
|
@@ -43,7 +43,7 @@ var SliderRow = function SliderRow(_ref) {
|
|
|
43
43
|
className: "axis-slider-container"
|
|
44
44
|
}, /*#__PURE__*/React.createElement("span", {
|
|
45
45
|
className: "slider-name"
|
|
46
|
-
}, label), max
|
|
46
|
+
}, label), max <= min ? /*#__PURE__*/React.createElement("i", null, "No values to adjust") : /*#__PURE__*/React.createElement("span", {
|
|
47
47
|
className: "axis-slider"
|
|
48
48
|
}, /*#__PURE__*/React.createElement(SmarterSlider, {
|
|
49
49
|
className: isRange ? "" : "slider-single-handle",
|
|
@@ -77,7 +77,7 @@ var SliderRow = function SliderRow(_ref) {
|
|
|
77
77
|
onChange: wrappedOnChange,
|
|
78
78
|
onStart: onStart,
|
|
79
79
|
onEnd: onEnd
|
|
80
|
-
})), max >
|
|
80
|
+
})), max > min && /*#__PURE__*/React.createElement("span", {
|
|
81
81
|
className: "slider-values"
|
|
82
82
|
}, /*#__PURE__*/React.createElement(NumericInput, {
|
|
83
83
|
min: min,
|
|
@@ -0,0 +1,327 @@
|
|
|
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 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; }
|
|
6
|
+
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; }
|
|
7
|
+
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; }
|
|
8
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
9
|
+
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); }
|
|
10
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
11
|
+
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."); }
|
|
12
|
+
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; } }
|
|
13
|
+
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; }
|
|
14
|
+
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; } }
|
|
15
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
16
|
+
import { LoadSpec, VolumeLoaderContext } from "@aics/vole-core";
|
|
17
|
+
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
18
|
+
import { Box3, Vector3 } from "three";
|
|
19
|
+
import { AXIS_TO_LOADER_PRIORITY, CACHE_MAX_SIZE, getDefaultChannelColor, QUEUE_MAX_LOW_PRIORITY_SIZE, QUEUE_MAX_SIZE } from "../shared/constants";
|
|
20
|
+
import { ViewMode } from "../shared/enums";
|
|
21
|
+
import { useConstructor, useRefWithSetter } from "../shared/utils/hooks";
|
|
22
|
+
import PlayControls from "../shared/utils/playControls";
|
|
23
|
+
import SceneStore from "../shared/utils/sceneStore";
|
|
24
|
+
import { getDisplayName, makeChannelIndexGrouping } from "../shared/utils/viewerChannelSettings";
|
|
25
|
+
import { initializeOneChannelSetting } from "../shared/utils/viewerState";
|
|
26
|
+
import { ViewerStateContext } from "./ViewerStateProvider";
|
|
27
|
+
export var ImageLoadStatus = /*#__PURE__*/function (ImageLoadStatus) {
|
|
28
|
+
ImageLoadStatus[ImageLoadStatus["REQUESTED"] = 0] = "REQUESTED";
|
|
29
|
+
ImageLoadStatus[ImageLoadStatus["LOADING"] = 1] = "LOADING";
|
|
30
|
+
ImageLoadStatus[ImageLoadStatus["LOADED"] = 2] = "LOADED";
|
|
31
|
+
ImageLoadStatus[ImageLoadStatus["ERROR"] = 3] = "ERROR";
|
|
32
|
+
return ImageLoadStatus;
|
|
33
|
+
}({});
|
|
34
|
+
|
|
35
|
+
// Used by `channelVersions` (see below)
|
|
36
|
+
var CHANNEL_INITIAL_LOAD = -1;
|
|
37
|
+
var CHANNEL_RELOAD = 0;
|
|
38
|
+
/**
|
|
39
|
+
* Temporary hack while `useEffectEvent` is still experimental.
|
|
40
|
+
*
|
|
41
|
+
* Some functions play the role of event handlers (called to notify that something has happened) _within_ an effect.
|
|
42
|
+
* Without any intervention, the linter will insist the function needs to be in the effect's dependencies, even though
|
|
43
|
+
* it would make no sense to re-run the effect when the handler changes. So we hide the function behind a stable ref.
|
|
44
|
+
*
|
|
45
|
+
* See https://react.dev/learn/separating-events-from-effects#declaring-an-effect-event
|
|
46
|
+
*/
|
|
47
|
+
var useEffectEventRef = function useEffectEventRef(callback) {
|
|
48
|
+
var callbackRef = useRef(callback);
|
|
49
|
+
useEffect(function () {
|
|
50
|
+
callbackRef.current = callback;
|
|
51
|
+
}, [callback]);
|
|
52
|
+
return callbackRef;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Hook to open a volume from one or more sources (URLs or raw data) and provide controls for (re)loading and playback.
|
|
57
|
+
*
|
|
58
|
+
* @param scenePaths An array of volume data sources, one per scene. These can be:
|
|
59
|
+
* - a string URL to a single source, or
|
|
60
|
+
* - an array of strings to load multiple sources as a single volume, or
|
|
61
|
+
* - a `RawArrayLoaderOptions` object to load raw data directly.
|
|
62
|
+
* @param options An optional object with callbacks and other info. See docs for `UseVolumeOptions`.
|
|
63
|
+
* @returns An object with the current image, its load status, and controls for playback and loading.
|
|
64
|
+
*/
|
|
65
|
+
var useVolume = function useVolume(scenePaths, options) {
|
|
66
|
+
var viewerStateRef = useContext(ViewerStateContext).ref;
|
|
67
|
+
var onErrorRef = useEffectEventRef(options === null || options === void 0 ? void 0 : options.onError);
|
|
68
|
+
var onChannelLoadedRef = useEffectEventRef(options === null || options === void 0 ? void 0 : options.onChannelLoaded);
|
|
69
|
+
var maskChannelName = options === null || options === void 0 ? void 0 : options.maskChannelName;
|
|
70
|
+
|
|
71
|
+
// set up our big objects: the image, its loading infrastructure, and controls for playback
|
|
72
|
+
var _useState = useState(null),
|
|
73
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
74
|
+
image = _useState2[0],
|
|
75
|
+
setImage = _useState2[1];
|
|
76
|
+
var loadContext = useConstructor(function () {
|
|
77
|
+
return new VolumeLoaderContext(CACHE_MAX_SIZE, QUEUE_MAX_SIZE, QUEUE_MAX_LOW_PRIORITY_SIZE);
|
|
78
|
+
});
|
|
79
|
+
var sceneLoader = useMemo(function () {
|
|
80
|
+
return new SceneStore(loadContext, scenePaths);
|
|
81
|
+
}, [loadContext, scenePaths]);
|
|
82
|
+
var playControls = useConstructor(function () {
|
|
83
|
+
return new PlayControls();
|
|
84
|
+
});
|
|
85
|
+
var _useState3 = useState(null),
|
|
86
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
87
|
+
playingAxis = _useState4[0],
|
|
88
|
+
setPlayingAxis = _useState4[1];
|
|
89
|
+
useEffect(function () {
|
|
90
|
+
playControls.onPlayingAxisChanged = function (axis) {
|
|
91
|
+
var isPlaying = axis !== null;
|
|
92
|
+
setPlayingAxis(axis);
|
|
93
|
+
// prioritize prefetching along the playing axis
|
|
94
|
+
sceneLoader.setPrefetchPriority(axis ? [AXIS_TO_LOADER_PRIORITY[axis]] : []);
|
|
95
|
+
// sync multichannel loading so we don't show loaded channels one at a time
|
|
96
|
+
sceneLoader.syncMultichannelLoading(isPlaying);
|
|
97
|
+
if (image) {
|
|
98
|
+
// If we're playing and entire axis is not in memory (T always, Z likely), downlevel to speed things up
|
|
99
|
+
var _image$imageInfo = image.imageInfo,
|
|
100
|
+
volumeSize = _image$imageInfo.volumeSize,
|
|
101
|
+
subregionSize = _image$imageInfo.subregionSize;
|
|
102
|
+
var shouldDownlevel = isPlaying && (axis === "t" || volumeSize[axis] !== subregionSize[axis]);
|
|
103
|
+
image.updateRequiredData({
|
|
104
|
+
scaleLevelBias: shouldDownlevel ? 1 : 0
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}, [sceneLoader, playControls, image]);
|
|
109
|
+
|
|
110
|
+
// track which channels have been loaded
|
|
111
|
+
var _useState5 = useState([]),
|
|
112
|
+
_useState6 = _slicedToArray(_useState5, 2),
|
|
113
|
+
channelVersions = _useState6[0],
|
|
114
|
+
_setChannelVersions = _useState6[1];
|
|
115
|
+
var _useRefWithSetter = useRefWithSetter(_setChannelVersions, channelVersions),
|
|
116
|
+
_useRefWithSetter2 = _slicedToArray(_useRefWithSetter, 2),
|
|
117
|
+
channelVersionsRef = _useRefWithSetter2[0],
|
|
118
|
+
setChannelVersions = _useRefWithSetter2[1];
|
|
119
|
+
var channelSettings = viewerStateRef.current.channelSettings;
|
|
120
|
+
// Some extra items for tracking load status
|
|
121
|
+
var _useState7 = useState(false),
|
|
122
|
+
_useState8 = _slicedToArray(_useState7, 2),
|
|
123
|
+
loadThrewError = _useState8[0],
|
|
124
|
+
setLoadThrewError = _useState8[1];
|
|
125
|
+
var inInitialLoadRef = useRef(true);
|
|
126
|
+
|
|
127
|
+
// derive whether the image is loaded from whether any and/or all channels are loaded
|
|
128
|
+
var imageLoadStatus = useMemo(function () {
|
|
129
|
+
if (loadThrewError) {
|
|
130
|
+
return ImageLoadStatus.ERROR;
|
|
131
|
+
}
|
|
132
|
+
var _channelVersions$redu = channelVersions.reduce(function (_ref, version, idx) {
|
|
133
|
+
var _ref2 = _slicedToArray(_ref, 2),
|
|
134
|
+
allLoaded = _ref2[0],
|
|
135
|
+
noneLoaded = _ref2[1];
|
|
136
|
+
var setting = channelSettings[idx];
|
|
137
|
+
if (setting && (setting.volumeEnabled || setting.isosurfaceEnabled || maskChannelName === setting.name)) {
|
|
138
|
+
var loaded = version > 0;
|
|
139
|
+
return [allLoaded && loaded, noneLoaded && !loaded];
|
|
140
|
+
}
|
|
141
|
+
return [allLoaded, noneLoaded];
|
|
142
|
+
}, [true, true]),
|
|
143
|
+
_channelVersions$redu2 = _slicedToArray(_channelVersions$redu, 2),
|
|
144
|
+
allLoaded = _channelVersions$redu2[0],
|
|
145
|
+
noneLoaded = _channelVersions$redu2[1];
|
|
146
|
+
if (allLoaded && inInitialLoadRef.current) {
|
|
147
|
+
inInitialLoadRef.current = false;
|
|
148
|
+
}
|
|
149
|
+
return noneLoaded ? ImageLoadStatus.REQUESTED : allLoaded ? ImageLoadStatus.LOADED : ImageLoadStatus.LOADING;
|
|
150
|
+
}, [channelVersions, channelSettings, maskChannelName, loadThrewError]);
|
|
151
|
+
var setIsLoading = useCallback(function () {
|
|
152
|
+
setLoadThrewError(false);
|
|
153
|
+
setChannelVersions(channelVersionsRef.current.map(function (version) {
|
|
154
|
+
return Math.min(version, CHANNEL_RELOAD);
|
|
155
|
+
}));
|
|
156
|
+
}, [channelVersionsRef, setChannelVersions]);
|
|
157
|
+
var onError = useCallback(function (e) {
|
|
158
|
+
var _onErrorRef$current;
|
|
159
|
+
setLoadThrewError(true);
|
|
160
|
+
(_onErrorRef$current = onErrorRef.current) === null || _onErrorRef$current === void 0 || _onErrorRef$current.call(onErrorRef, e);
|
|
161
|
+
throw e;
|
|
162
|
+
}, [onErrorRef]);
|
|
163
|
+
|
|
164
|
+
// channel indexes, sorted by category
|
|
165
|
+
var _useState9 = useState({}),
|
|
166
|
+
_useState10 = _slicedToArray(_useState9, 2),
|
|
167
|
+
channelGroupedByType = _useState10[0],
|
|
168
|
+
setChannelGroupedByType = _useState10[1];
|
|
169
|
+
var onChannelDataLoaded = useCallback(function (aimg, channelIndex) {
|
|
170
|
+
var _onChannelLoadedRef$c;
|
|
171
|
+
// let the hook caller know that this channel has loaded
|
|
172
|
+
var isInitialLoad = channelVersionsRef.current[channelIndex] === CHANNEL_INITIAL_LOAD;
|
|
173
|
+
(_onChannelLoadedRef$c = onChannelLoadedRef.current) === null || _onChannelLoadedRef$c === void 0 || _onChannelLoadedRef$c.call(onChannelLoadedRef, aimg, channelIndex, isInitialLoad);
|
|
174
|
+
|
|
175
|
+
// set this channel as loaded
|
|
176
|
+
var newVersions = channelVersionsRef.current.slice();
|
|
177
|
+
newVersions[channelIndex] = Math.max(newVersions[channelIndex], CHANNEL_RELOAD) + 1;
|
|
178
|
+
setChannelVersions(newVersions);
|
|
179
|
+
|
|
180
|
+
// if the whole image has loaded, let `playControls` know (if we're playing, it may want to go to the next frame)
|
|
181
|
+
if (aimg.isLoaded()) {
|
|
182
|
+
playControls.onImageLoaded();
|
|
183
|
+
}
|
|
184
|
+
}, [channelVersionsRef, onChannelLoadedRef, playControls, setChannelVersions]);
|
|
185
|
+
|
|
186
|
+
// effect to start the initial load of the image
|
|
187
|
+
useEffect(function () {
|
|
188
|
+
var _viewerStateRef$curre = viewerStateRef.current,
|
|
189
|
+
changeViewerSetting = _viewerStateRef$curre.changeViewerSetting,
|
|
190
|
+
channelSettings = _viewerStateRef$curre.channelSettings,
|
|
191
|
+
getCurrentViewerChannelSettings = _viewerStateRef$curre.getCurrentViewerChannelSettings,
|
|
192
|
+
setChannelSettings = _viewerStateRef$curre.setChannelSettings;
|
|
193
|
+
setChannelVersions(new Array(channelVersionsRef.current.length).fill(CHANNEL_INITIAL_LOAD));
|
|
194
|
+
setLoadThrewError(false);
|
|
195
|
+
inInitialLoadRef.current = true;
|
|
196
|
+
var setChannelStateForNewImage = function setChannelStateForNewImage(channelNames) {
|
|
197
|
+
var viewerChannelSettings = getCurrentViewerChannelSettings();
|
|
198
|
+
var grouping = makeChannelIndexGrouping(channelNames, viewerChannelSettings);
|
|
199
|
+
setChannelGroupedByType(grouping);
|
|
200
|
+
|
|
201
|
+
// compare each channel's new displayName to the old displayNames currently in state:
|
|
202
|
+
// same number of channels, and each channel has same displayName
|
|
203
|
+
var allNamesAreEqual = channelNames.every(function (name, idx) {
|
|
204
|
+
var _channelSettings$idx;
|
|
205
|
+
var displayName = getDisplayName(name, idx, viewerChannelSettings);
|
|
206
|
+
return displayName === ((_channelSettings$idx = channelSettings[idx]) === null || _channelSettings$idx === void 0 ? void 0 : _channelSettings$idx.displayName);
|
|
207
|
+
});
|
|
208
|
+
if (allNamesAreEqual) {
|
|
209
|
+
var _newChannelSettings = channelNames.map(function (channel, index) {
|
|
210
|
+
return _objectSpread(_objectSpread({}, channelSettings[index]), {}, {
|
|
211
|
+
name: channel
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
setChannelSettings(_newChannelSettings);
|
|
215
|
+
return _newChannelSettings;
|
|
216
|
+
}
|
|
217
|
+
var newChannelSettings = channelNames.map(function (channel, index) {
|
|
218
|
+
var color = getDefaultChannelColor(index);
|
|
219
|
+
return initializeOneChannelSetting(channel, index, color, viewerChannelSettings);
|
|
220
|
+
});
|
|
221
|
+
setChannelSettings(newChannelSettings);
|
|
222
|
+
return newChannelSettings;
|
|
223
|
+
};
|
|
224
|
+
var openImage = /*#__PURE__*/function () {
|
|
225
|
+
var _ref3 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
|
|
226
|
+
var _getCurrentViewerChan;
|
|
227
|
+
var _viewerStateRef$curre2, scene, time, loadSpec, aimg, channelNames, newChannelSettings, requiredLoadSpec, requiredChannelsToLoad, maskChannelName, maskChannelIndex, slice;
|
|
228
|
+
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
229
|
+
while (1) switch (_context.prev = _context.next) {
|
|
230
|
+
case 0:
|
|
231
|
+
_viewerStateRef$curre2 = viewerStateRef.current, scene = _viewerStateRef$curre2.scene, time = _viewerStateRef$curre2.time;
|
|
232
|
+
loadSpec = new LoadSpec();
|
|
233
|
+
loadSpec.time = time;
|
|
234
|
+
_context.next = 5;
|
|
235
|
+
return sceneLoader.createVolume(scene, loadSpec, onChannelDataLoaded)["catch"](onError);
|
|
236
|
+
case 5:
|
|
237
|
+
aimg = _context.sent;
|
|
238
|
+
channelNames = aimg.imageInfo.channelNames;
|
|
239
|
+
newChannelSettings = setChannelStateForNewImage(channelNames);
|
|
240
|
+
setChannelVersions(new Array(channelNames.length).fill(CHANNEL_INITIAL_LOAD));
|
|
241
|
+
setImage(aimg);
|
|
242
|
+
playControls.stepAxis = function (axis) {
|
|
243
|
+
var _viewerStateRef$curre3 = viewerStateRef.current,
|
|
244
|
+
time = _viewerStateRef$curre3.time,
|
|
245
|
+
slice = _viewerStateRef$curre3.slice;
|
|
246
|
+
if (axis === "t") {
|
|
247
|
+
changeViewerSetting("time", (time + 1) % aimg.imageInfo.times);
|
|
248
|
+
} else {
|
|
249
|
+
var max = aimg.imageInfo.volumeSize[axis];
|
|
250
|
+
var current = slice[axis] * max;
|
|
251
|
+
changeViewerSetting("slice", _objectSpread(_objectSpread({}, slice), {}, _defineProperty({}, axis, (current + 1) % max / max)));
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
playControls.getVolumeIsLoaded = aimg.isLoaded.bind(aimg);
|
|
255
|
+
requiredLoadSpec = new LoadSpec();
|
|
256
|
+
requiredLoadSpec.time = time;
|
|
257
|
+
|
|
258
|
+
// make the currently enabled channels "required":
|
|
259
|
+
// find all enabled indices in newChannelSettings:
|
|
260
|
+
requiredChannelsToLoad = newChannelSettings ? newChannelSettings.map(function (channel, index) {
|
|
261
|
+
return channel.volumeEnabled ? index : -1;
|
|
262
|
+
}).filter(function (index) {
|
|
263
|
+
return index >= 0;
|
|
264
|
+
}) : []; // add mask channel to required channels, if specified
|
|
265
|
+
maskChannelName = (_getCurrentViewerChan = getCurrentViewerChannelSettings()) === null || _getCurrentViewerChan === void 0 ? void 0 : _getCurrentViewerChan.maskChannelName;
|
|
266
|
+
if (maskChannelName) {
|
|
267
|
+
maskChannelIndex = channelNames.indexOf(maskChannelName);
|
|
268
|
+
if (maskChannelIndex >= 0 && !requiredChannelsToLoad.includes(maskChannelIndex)) {
|
|
269
|
+
requiredChannelsToLoad.push(maskChannelIndex);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
requiredLoadSpec.channels = requiredChannelsToLoad;
|
|
273
|
+
|
|
274
|
+
// When in 2D Z-axis view mode, we restrict the subregion to only the current slice. This is
|
|
275
|
+
// to match an optimization that volume viewer does by loading Z-slices at a higher resolution,
|
|
276
|
+
// and ensures the very first volume that is loaded is the same as the one that
|
|
277
|
+
// will be shown whenever we switch back to the same viewer settings (2D Z-axis view mode).
|
|
278
|
+
// (We don't do this for ZX and YZ modes because we assume that the data won't be chunked along the
|
|
279
|
+
// X or Y axes in ways that would improve loading resolution, and we load the full 3D volume instead.)
|
|
280
|
+
if (viewerStateRef.current.viewMode === ViewMode.xy) {
|
|
281
|
+
slice = viewerStateRef.current.slice;
|
|
282
|
+
requiredLoadSpec.subregion = new Box3(new Vector3(0, 0, slice.z), new Vector3(1, 1, slice.z));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// initiate loading only after setting up new channel settings,
|
|
286
|
+
// in case the loader callback fires before the state is set
|
|
287
|
+
sceneLoader.loadScene(scene, aimg, requiredLoadSpec)["catch"](onError);
|
|
288
|
+
case 20:
|
|
289
|
+
case "end":
|
|
290
|
+
return _context.stop();
|
|
291
|
+
}
|
|
292
|
+
}, _callee);
|
|
293
|
+
}));
|
|
294
|
+
return function openImage() {
|
|
295
|
+
return _ref3.apply(this, arguments);
|
|
296
|
+
};
|
|
297
|
+
}();
|
|
298
|
+
openImage();
|
|
299
|
+
}, [sceneLoader, onError, onChannelLoadedRef, viewerStateRef, channelVersionsRef, setChannelVersions, playControls, setIsLoading, onChannelDataLoaded]);
|
|
300
|
+
// of the above dependencies, we expect only `sceneLoader` to change.
|
|
301
|
+
|
|
302
|
+
var setTime = useCallback(function (view3d, time) {
|
|
303
|
+
if (image && !inInitialLoadRef.current) {
|
|
304
|
+
view3d.setTime(image, time, onChannelDataLoaded)["catch"](onError);
|
|
305
|
+
setIsLoading();
|
|
306
|
+
}
|
|
307
|
+
}, [image, onError, setIsLoading, inInitialLoadRef, onChannelDataLoaded]);
|
|
308
|
+
var setScene = useCallback(function (scene) {
|
|
309
|
+
if (image && !inInitialLoadRef.current) {
|
|
310
|
+
sceneLoader.loadScene(scene, image, undefined, onChannelDataLoaded)["catch"](onError);
|
|
311
|
+
setIsLoading();
|
|
312
|
+
}
|
|
313
|
+
}, [image, onError, sceneLoader, setIsLoading, inInitialLoadRef, onChannelDataLoaded]);
|
|
314
|
+
return useMemo(function () {
|
|
315
|
+
return {
|
|
316
|
+
image: image,
|
|
317
|
+
channelVersions: channelVersions,
|
|
318
|
+
imageLoadStatus: imageLoadStatus,
|
|
319
|
+
setTime: setTime,
|
|
320
|
+
setScene: setScene,
|
|
321
|
+
playControls: playControls,
|
|
322
|
+
playingAxis: playingAxis,
|
|
323
|
+
channelGroupedByType: channelGroupedByType
|
|
324
|
+
};
|
|
325
|
+
}, [image, channelVersions, imageLoadStatus, setTime, setScene, playControls, playingAxis, channelGroupedByType]);
|
|
326
|
+
};
|
|
327
|
+
export default useVolume;
|
|
@@ -2,6 +2,7 @@ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" ==
|
|
|
2
2
|
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; }
|
|
3
3
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
4
4
|
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); }
|
|
5
|
+
import { PrefetchDirection } from "@aics/vole-core";
|
|
5
6
|
import { ImageType, RenderMode, ViewMode } from "./enums";
|
|
6
7
|
// Add all exported constants here to prevent circular dependencies
|
|
7
8
|
export var
|
|
@@ -32,6 +33,14 @@ export var CACHE_MAX_SIZE = 1000000000;
|
|
|
32
33
|
export var QUEUE_MAX_SIZE = 10;
|
|
33
34
|
export var QUEUE_MAX_LOW_PRIORITY_SIZE = 4;
|
|
34
35
|
|
|
36
|
+
/** Maps an axis the user can "play" through to the direction to prefetch while playing that axis */
|
|
37
|
+
export var AXIS_TO_LOADER_PRIORITY = {
|
|
38
|
+
t: PrefetchDirection.T_PLUS,
|
|
39
|
+
z: PrefetchDirection.Z_PLUS,
|
|
40
|
+
y: PrefetchDirection.Y_PLUS,
|
|
41
|
+
x: PrefetchDirection.X_PLUS
|
|
42
|
+
};
|
|
43
|
+
|
|
35
44
|
/** Maps channel data types to their minimum and maximum values */
|
|
36
45
|
export var DTYPE_RANGE = {
|
|
37
46
|
int8: {
|
|
@@ -1,27 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
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."); }
|
|
3
|
-
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; } }
|
|
4
|
-
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; }
|
|
5
|
-
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; } }
|
|
6
|
-
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
7
|
-
import { useCallback, useRef, useState } from "react";
|
|
8
|
-
|
|
9
|
-
/** A `useState` that also creates a getter function for breaking through closures. */
|
|
10
|
-
export function useStateWithGetter(initialState) {
|
|
11
|
-
var _useState = useState(initialState),
|
|
12
|
-
_useState2 = _slicedToArray(_useState, 2),
|
|
13
|
-
state = _useState2[0],
|
|
14
|
-
setState = _useState2[1];
|
|
15
|
-
var stateRef = useRef(state);
|
|
16
|
-
var wrappedSetState = useCallback(function (value) {
|
|
17
|
-
stateRef.current = value;
|
|
18
|
-
setState(value);
|
|
19
|
-
}, []);
|
|
20
|
-
var getState = useCallback(function () {
|
|
21
|
-
return stateRef.current;
|
|
22
|
-
}, []);
|
|
23
|
-
return [state, wrappedSetState, getState];
|
|
24
|
-
}
|
|
1
|
+
import { useCallback, useRef } from "react";
|
|
25
2
|
|
|
26
3
|
/**
|
|
27
4
|
* Wraps a setter function and keeps a ref updated to follow the set value. Useful for making the most up-to-date value
|
|
@@ -22,15 +22,21 @@ export var gammaSliderToImageValues = function gammaSliderToImageValues(sliderVa
|
|
|
22
22
|
};
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
// Density and brightness
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
// Density and brightness were once overloaded for the two rendering modes in the volume viewer (raymarch & pathtrace).
|
|
26
|
+
// Now both work the same, and the mapping from slider values to numbers in vole-core is relatively straightforward.
|
|
27
|
+
// But the mapping can still be tweaked here in the future, should we want to.
|
|
28
|
+
|
|
29
|
+
/** vole-core expects a value from 0..1 for density. */
|
|
30
|
+
export var densitySliderToImageValue = function densitySliderToImageValue(sliderValue) {
|
|
31
|
+
return sliderValue / 100.0;
|
|
30
32
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
|
|
34
|
+
/** vole-core expects a value from 0..1 for brigness. */
|
|
35
|
+
export var brightnessSliderToImageValue = function brightnessSliderToImageValue(sliderValue) {
|
|
36
|
+
return sliderValue / 100.0;
|
|
33
37
|
};
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
|
|
39
|
+
/** vole-core expects a value from 0..1 for alpha, and its scale is inverted from the app's slider. */
|
|
40
|
+
export var alphaSliderToImageValue = function alphaSliderToImageValue(sliderValue) {
|
|
41
|
+
return 1 - sliderValue / 100.0;
|
|
36
42
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aics/vole-app",
|
|
3
|
-
"version": "2.13.
|
|
3
|
+
"version": "2.13.2",
|
|
4
4
|
"description": "web view of cell volume images",
|
|
5
5
|
"repository": "github:allen-cell-animated/vole-app",
|
|
6
6
|
"main": "es/index.js",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"author": "Megan Riel-Mehan",
|
|
42
42
|
"license": "ISC",
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@aics/vole-core": "^3.15.
|
|
44
|
+
"@aics/vole-core": "^3.15.7",
|
|
45
45
|
"@ant-design/icons": "^5.2.5",
|
|
46
46
|
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
|
47
47
|
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { RawArrayLoaderOptions, View3d, Volume } from "@aics/vole-core";
|
|
2
|
+
import { AxisName } from "../shared/types";
|
|
3
|
+
import PlayControls from "../shared/utils/playControls";
|
|
4
|
+
import { ChannelGrouping } from "../shared/utils/viewerChannelSettings";
|
|
5
|
+
export type UseVolumeOptions = {
|
|
6
|
+
/** Callback for when a single channel of the volume has loaded. */
|
|
7
|
+
onChannelLoaded?: (image: Volume, channelIndex: number, isInitialLoad: boolean) => void;
|
|
8
|
+
/** Callback for when image loading encounters an error. */
|
|
9
|
+
onError?: (error: unknown) => void;
|
|
10
|
+
/** The name of a channel which should be treated as a mask rather than as viewable data. */
|
|
11
|
+
maskChannelName?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare const enum ImageLoadStatus {
|
|
14
|
+
REQUESTED = 0,
|
|
15
|
+
LOADING = 1,
|
|
16
|
+
LOADED = 2,
|
|
17
|
+
ERROR = 3
|
|
18
|
+
}
|
|
19
|
+
export type ReactiveVolume = {
|
|
20
|
+
image: Volume | null;
|
|
21
|
+
/**
|
|
22
|
+
* Indicates the load status of each channel:
|
|
23
|
+
*
|
|
24
|
+
* - `-1` indicates the channel has not yet loaded and will get some extra initialization (e.g. LUTs) when it loads.
|
|
25
|
+
* - `0` indicates the channel has been loaded once, but is currently waiting for new data.
|
|
26
|
+
* - `1` or greater indicates the channel is loaded. Note this is not exactly `1` to handle multiple simultaneous
|
|
27
|
+
* loads; if multiple loads are issued before the first one completes, _incrementing_ rather than _setting_ the
|
|
28
|
+
* version number means we react to each load when it completes, rather than just the first.
|
|
29
|
+
*/
|
|
30
|
+
channelVersions: number[];
|
|
31
|
+
imageLoadStatus: ImageLoadStatus;
|
|
32
|
+
setTime: (view3d: View3d, time: number) => void;
|
|
33
|
+
setScene: (scene: number) => void;
|
|
34
|
+
playControls: PlayControls;
|
|
35
|
+
playingAxis: AxisName | "t" | null;
|
|
36
|
+
channelGroupedByType: ChannelGrouping;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Hook to open a volume from one or more sources (URLs or raw data) and provide controls for (re)loading and playback.
|
|
40
|
+
*
|
|
41
|
+
* @param scenePaths An array of volume data sources, one per scene. These can be:
|
|
42
|
+
* - a string URL to a single source, or
|
|
43
|
+
* - an array of strings to load multiple sources as a single volume, or
|
|
44
|
+
* - a `RawArrayLoaderOptions` object to load raw data directly.
|
|
45
|
+
* @param options An optional object with callbacks and other info. See docs for `UseVolumeOptions`.
|
|
46
|
+
* @returns An object with the current image, its load status, and controls for playback and loading.
|
|
47
|
+
*/
|
|
48
|
+
declare const useVolume: (scenePaths: (string | string[] | RawArrayLoaderOptions)[], options?: UseVolumeOptions) => ReactiveVolume;
|
|
49
|
+
export default useVolume;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type CameraState, type Channel, PrefetchDirection } from "@aics/vole-core";
|
|
2
2
|
import { ChannelState, ViewerState } from "../components/ViewerStateProvider/types";
|
|
3
3
|
import { ViewMode } from "./enums";
|
|
4
|
+
import { AxisName } from "./types";
|
|
4
5
|
import { ColorArray } from "./utils/colorRepresentations";
|
|
5
6
|
import { ViewerChannelSettings } from "./utils/viewerChannelSettings";
|
|
6
7
|
export declare const // Control panel will automatically close if viewport is less than this width
|
|
@@ -10,6 +11,8 @@ export declare const TFEDITOR_MAX_BIN = 255;
|
|
|
10
11
|
export declare const CACHE_MAX_SIZE = 1000000000;
|
|
11
12
|
export declare const QUEUE_MAX_SIZE = 10;
|
|
12
13
|
export declare const QUEUE_MAX_LOW_PRIORITY_SIZE = 4;
|
|
14
|
+
/** Maps an axis the user can "play" through to the direction to prefetch while playing that axis */
|
|
15
|
+
export declare const AXIS_TO_LOADER_PRIORITY: Record<AxisName | "t", PrefetchDirection>;
|
|
13
16
|
/** Maps channel data types to their minimum and maximum values */
|
|
14
17
|
export declare const DTYPE_RANGE: {
|
|
15
18
|
[T in Channel["dtype"]]: {
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { MutableRefObject } from "react";
|
|
2
|
-
/** A `useState` that also creates a getter function for breaking through closures. */
|
|
3
|
-
export declare function useStateWithGetter<T>(initialState: T | (() => T)): [T, (value: T) => void, () => T];
|
|
4
2
|
/**
|
|
5
3
|
* Wraps a setter function and keeps a ref updated to follow the set value. Useful for making the most up-to-date value
|
|
6
4
|
* of some state accessible to a closure that might be called after the value is updated.
|
|
@@ -4,7 +4,10 @@ type ImageValues = {
|
|
|
4
4
|
scale: number;
|
|
5
5
|
};
|
|
6
6
|
export declare const gammaSliderToImageValues: (sliderValues: [number, number, number]) => ImageValues;
|
|
7
|
-
|
|
8
|
-
export declare const
|
|
9
|
-
|
|
7
|
+
/** vole-core expects a value from 0..1 for density. */
|
|
8
|
+
export declare const densitySliderToImageValue: (sliderValue: number) => number;
|
|
9
|
+
/** vole-core expects a value from 0..1 for brigness. */
|
|
10
|
+
export declare const brightnessSliderToImageValue: (sliderValue: number) => number;
|
|
11
|
+
/** vole-core expects a value from 0..1 for alpha, and its scale is inverted from the app's slider. */
|
|
12
|
+
export declare const alphaSliderToImageValue: (sliderValue: number) => number;
|
|
10
13
|
export {};
|