@aics/vole-app 3.3.0 → 3.4.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.
- package/LICENSE +21 -25
- package/es/aics-image-viewer/components/App/index.js +11 -9
- package/es/aics-image-viewer/components/BottomPanel/index.js +48 -19
- package/es/aics-image-viewer/components/BottomPanel/styles.css +16 -7
- package/es/aics-image-viewer/components/CellViewerCanvasWrapper/index.js +39 -19
- package/es/aics-image-viewer/components/ChannelsWidget.js +2 -2
- package/es/aics-image-viewer/components/ControlPanel/index.js +17 -11
- package/es/aics-image-viewer/components/ControlPanel/styles.css +5 -5
- package/es/aics-image-viewer/components/ErrorAlert/index.js +91 -64
- package/es/aics-image-viewer/components/ErrorAlert/styles.css +33 -4
- package/es/aics-image-viewer/components/StyleProvider/index.js +42 -22
- package/es/aics-image-viewer/components/TfEditor/index.js +8 -1
- package/es/aics-image-viewer/components/Toolbar/index.js +33 -12
- package/es/aics-image-viewer/components/Toolbar/styles.css +21 -130
- package/es/aics-image-viewer/components/dimension_sliders/AxisClipSliders.js +148 -0
- package/es/aics-image-viewer/components/dimension_sliders/RotationSliders.js +165 -0
- package/es/aics-image-viewer/components/dimension_sliders/SliderRows.js +167 -0
- package/es/aics-image-viewer/components/{AxisClipSliders → dimension_sliders}/styles.css +7 -1
- package/es/aics-image-viewer/components/shared/ControlPanelRow/styles.css +1 -1
- package/es/aics-image-viewer/components/shared/NumericInput/index.js +4 -2
- package/es/aics-image-viewer/components/shared/SliderRow/index.js +2 -0
- package/es/aics-image-viewer/components/shared/SliderRow/styles.css +2 -1
- package/es/aics-image-viewer/components/useVolume.js +3 -0
- package/es/aics-image-viewer/shared/constants.js +2 -2
- package/es/aics-image-viewer/shared/utils/camera.js +122 -0
- package/es/aics-image-viewer/shared/utils/playControls.js +12 -1
- package/es/aics-image-viewer/shared/utils/sceneStore.js +8 -0
- package/es/aics-image-viewer/shared/utils/test/camera.test.js +127 -0
- package/es/aics-image-viewer/shared/utils/test/urlParsing.test.js +23 -702
- package/es/aics-image-viewer/shared/utils/urlParsing.js +14 -828
- package/es/aics-image-viewer/shared/utils/viewerChannelSettings.js +3 -4
- package/es/aics-image-viewer/state/deserialize.js +437 -0
- package/es/aics-image-viewer/state/serialize.js +145 -0
- package/es/aics-image-viewer/state/test/deserialize.test.js +432 -0
- package/es/aics-image-viewer/state/test/serialize.test.js +97 -0
- package/es/aics-image-viewer/state/test/test_data.js +168 -0
- package/es/aics-image-viewer/state/types.js +265 -1
- package/package.json +6 -5
- package/type-declarations/aics-image-viewer/components/App/types.d.ts +2 -2
- package/type-declarations/aics-image-viewer/components/BottomPanel/index.d.ts +6 -3
- package/type-declarations/aics-image-viewer/components/CellViewerCanvasWrapper/index.d.ts +1 -0
- package/type-declarations/aics-image-viewer/components/ControlPanel/index.d.ts +4 -5
- package/type-declarations/aics-image-viewer/components/ErrorAlert/index.d.ts +8 -5
- package/type-declarations/aics-image-viewer/components/Toolbar/index.d.ts +2 -1
- package/type-declarations/aics-image-viewer/components/{AxisClipSliders/index.d.ts → dimension_sliders/AxisClipSliders.d.ts} +2 -3
- package/type-declarations/aics-image-viewer/components/dimension_sliders/RotationSliders.d.ts +6 -0
- package/type-declarations/aics-image-viewer/components/dimension_sliders/SliderRows.d.ts +31 -0
- package/type-declarations/aics-image-viewer/components/shared/NumericInput/index.d.ts +1 -0
- package/type-declarations/aics-image-viewer/components/shared/SliderRow/index.d.ts +3 -1
- package/type-declarations/aics-image-viewer/components/useVolume.d.ts +1 -1
- package/type-declarations/aics-image-viewer/shared/constants.d.ts +1 -1
- package/type-declarations/aics-image-viewer/shared/utils/camera.d.ts +19 -0
- package/type-declarations/aics-image-viewer/shared/utils/playControls.d.ts +1 -0
- package/type-declarations/aics-image-viewer/shared/utils/sceneStore.d.ts +2 -1
- package/type-declarations/aics-image-viewer/shared/utils/urlParsing.d.ts +1 -307
- package/type-declarations/aics-image-viewer/state/deserialize.d.ts +70 -0
- package/type-declarations/aics-image-viewer/state/serialize.d.ts +20 -0
- package/type-declarations/aics-image-viewer/state/types.d.ts +216 -0
- package/LICENSE.txt +0 -26
- package/es/aics-image-viewer/components/AxisClipSliders/index.js +0 -295
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
.clip-sliders{
|
|
2
|
-
--w-group-title:
|
|
2
|
+
--w-group-title:60px;
|
|
3
3
|
--w-axis-slider:480px;
|
|
4
4
|
--w-name:10px;
|
|
5
5
|
--w-values-3d:160px;
|
|
@@ -93,8 +93,14 @@
|
|
|
93
93
|
.clip-sliders .slider-group .slider-group-title{
|
|
94
94
|
flex:0 0 var(--w-group-title);
|
|
95
95
|
margin:0;
|
|
96
|
+
font-family:var(--font-family);
|
|
97
|
+
color:var(--color-text-header);
|
|
96
98
|
}
|
|
97
99
|
|
|
100
|
+
.clip-sliders .slider-group .slider-group-title.group-title-extra{
|
|
101
|
+
flex-basis:calc(var(--w-group-title) + var(--w-name) + 14px);
|
|
102
|
+
}
|
|
103
|
+
|
|
98
104
|
.clip-sliders .slider-group .slider-group-rows{
|
|
99
105
|
flex:1 1 auto;
|
|
100
106
|
}
|
|
@@ -4,8 +4,8 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
|
|
|
4
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
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
6
|
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
7
|
-
import React from "react";
|
|
8
7
|
import { CaretDownOutlined, CaretUpOutlined } from "@ant-design/icons";
|
|
8
|
+
import React from "react";
|
|
9
9
|
import { useRefWithSetter } from "../../../shared/utils/hooks";
|
|
10
10
|
import "./styles.css";
|
|
11
11
|
/**
|
|
@@ -27,6 +27,8 @@ var NumericInput = function NumericInput(_ref) {
|
|
|
27
27
|
disabled = _ref$disabled === void 0 ? false : _ref$disabled,
|
|
28
28
|
_ref$className = _ref.className,
|
|
29
29
|
className = _ref$className === void 0 ? "" : _ref$className,
|
|
30
|
+
_ref$unitSymbol = _ref.unitSymbol,
|
|
31
|
+
unitSymbol = _ref$unitSymbol === void 0 ? "" : _ref$unitSymbol,
|
|
30
32
|
onChange = _ref.onChange;
|
|
31
33
|
// While the input has focus, allow invalid input, just don't call `onChange` with it
|
|
32
34
|
var _React$useState = React.useState(false),
|
|
@@ -100,7 +102,7 @@ var NumericInput = function NumericInput(_ref) {
|
|
|
100
102
|
className: fullClassName,
|
|
101
103
|
onKeyDown: onKeyDown
|
|
102
104
|
}, /*#__PURE__*/React.createElement("input", {
|
|
103
|
-
value: hasFocus ? textContent : displayedValue,
|
|
105
|
+
value: hasFocus ? textContent : displayedValue + unitSymbol,
|
|
104
106
|
step: step,
|
|
105
107
|
min: min,
|
|
106
108
|
max: max,
|
|
@@ -23,10 +23,12 @@ var SliderRow = function SliderRow(props) {
|
|
|
23
23
|
max: props.max
|
|
24
24
|
},
|
|
25
25
|
start: props.start,
|
|
26
|
+
disabled: props.disabled,
|
|
26
27
|
connect: true,
|
|
27
28
|
tooltips: true,
|
|
28
29
|
behaviour: "drag",
|
|
29
30
|
format: props.formatInteger ? INTEGER_FORMATTER : undefined,
|
|
31
|
+
onSlide: props.onSlide,
|
|
30
32
|
onUpdate: props.onUpdate,
|
|
31
33
|
onChange: props.onChange
|
|
32
34
|
})), props.children && /*#__PURE__*/React.createElement("div", {
|
|
@@ -110,6 +110,9 @@ var useVolume = function useVolume(scenePaths, options) {
|
|
|
110
110
|
setPlayingAxis(axis);
|
|
111
111
|
// prioritize prefetching along the playing axis
|
|
112
112
|
sceneLoader.setPrefetchPriority(axis ? [AXIS_TO_LOADER_PRIORITY[axis]] : []);
|
|
113
|
+
sceneLoader.updateFetchOptions({
|
|
114
|
+
onlyPriorityDirections: isPlaying
|
|
115
|
+
});
|
|
113
116
|
// sync multichannel loading so we don't show loaded channels one at a time
|
|
114
117
|
sceneLoader.syncMultichannelLoading(isPlaying);
|
|
115
118
|
if (image) {
|
|
@@ -13,7 +13,7 @@ export var
|
|
|
13
13
|
BACKGROUND_COLOR_DEFAULT = [0, 0, 0],
|
|
14
14
|
BOUNDING_BOX_COLOR_DEFAULT = [255, 255, 255],
|
|
15
15
|
AXIS_MARGIN_DEFAULT = [16, 16],
|
|
16
|
-
SCALE_BAR_MARGIN_DEFAULT = [
|
|
16
|
+
SCALE_BAR_MARGIN_DEFAULT = [220, 12],
|
|
17
17
|
// These settings were chosen to work well with most AICS microscopy pipeline images.
|
|
18
18
|
// These numbers mean: remap the bottom LUT_MIN_PERCENTILE fraction of pixels to zero intensity,
|
|
19
19
|
// and linearly increase intensity up to the LUT_MAX_PERCENTILE fraction of pixels.
|
|
@@ -31,7 +31,7 @@ export var TFEDITOR_DEFAULT_COLOR = [255, 255, 255];
|
|
|
31
31
|
export var TFEDITOR_MAX_BIN = 255;
|
|
32
32
|
export var CACHE_MAX_SIZE = 1000000000;
|
|
33
33
|
export var QUEUE_MAX_SIZE = 10;
|
|
34
|
-
export var QUEUE_MAX_LOW_PRIORITY_SIZE =
|
|
34
|
+
export var QUEUE_MAX_LOW_PRIORITY_SIZE = 6;
|
|
35
35
|
|
|
36
36
|
/** Maps an axis the user can "play" through to the direction to prefetch while playing that axis */
|
|
37
37
|
export var AXIS_TO_LOADER_PRIORITY = {
|
|
@@ -0,0 +1,122 @@
|
|
|
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 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; }
|
|
3
|
+
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; }
|
|
4
|
+
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; }
|
|
5
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
6
|
+
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); }
|
|
7
|
+
import { mat3, quat, vec3 } from "gl-matrix";
|
|
8
|
+
import React from "react";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Converts a `vec3` to a `[number, number, number]`.
|
|
12
|
+
*
|
|
13
|
+
* Technically, these are already equivalent types for all the math we do here, but the `vec3` type also includes any
|
|
14
|
+
* other indexable collection of `number`s, so this keeps the type checker happy.
|
|
15
|
+
*/
|
|
16
|
+
var toArray = function toArray(v) {
|
|
17
|
+
return [v[0], v[1], v[2]];
|
|
18
|
+
};
|
|
19
|
+
var vecToTarget = function vecToTarget(state) {
|
|
20
|
+
return vec3.subtract(vec3.create(), state.target, state.position);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** Transpose a 3x3 matrix */
|
|
24
|
+
var transpose = function transpose(matrix) {
|
|
25
|
+
var out = mat3.create();
|
|
26
|
+
mat3.transpose(out, matrix);
|
|
27
|
+
return out;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Multiply every vector in `state` by `matrix` */
|
|
31
|
+
export var applyMatrix = function applyMatrix(state, matrix) {
|
|
32
|
+
var position = toArray(vec3.transformMat3(vec3.create(), state.position, matrix));
|
|
33
|
+
var up = toArray(vec3.transformMat3(vec3.create(), state.up, matrix));
|
|
34
|
+
var target = toArray(vec3.transformMat3(vec3.create(), state.target, matrix));
|
|
35
|
+
return {
|
|
36
|
+
position: position,
|
|
37
|
+
up: up,
|
|
38
|
+
target: target
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/** Orthonormal camera basis derived from `state`. */
|
|
42
|
+
var getBasis = function getBasis(state) {
|
|
43
|
+
var toTarget = vecToTarget(state);
|
|
44
|
+
var distance = vec3.length(toTarget);
|
|
45
|
+
var forward = vec3.scale(vec3.create(), toTarget, 1 / distance);
|
|
46
|
+
var right = vec3.cross(vec3.create(), forward, state.up);
|
|
47
|
+
vec3.normalize(right, right);
|
|
48
|
+
var up = vec3.cross(vec3.create(), right, forward);
|
|
49
|
+
return {
|
|
50
|
+
forward: forward,
|
|
51
|
+
right: right,
|
|
52
|
+
up: up,
|
|
53
|
+
distance: distance
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** Creates a XYZ rotation matrix with the given angles */
|
|
58
|
+
export var rotationMatrix = function rotationMatrix(x, y, z) {
|
|
59
|
+
var rotationQuat = quat.create();
|
|
60
|
+
quat.rotateX(rotationQuat, rotationQuat, x);
|
|
61
|
+
quat.rotateY(rotationQuat, rotationQuat, y);
|
|
62
|
+
quat.rotateZ(rotationQuat, rotationQuat, z);
|
|
63
|
+
var rotation = mat3.fromQuat(mat3.create(), rotationQuat);
|
|
64
|
+
return rotation;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/** Extract Tait-Bryan angles from a `CameraState` */
|
|
68
|
+
export var getRotationAngles = function getRotationAngles(state) {
|
|
69
|
+
var _getBasis = getBasis(state),
|
|
70
|
+
forward = _getBasis.forward,
|
|
71
|
+
right = _getBasis.right,
|
|
72
|
+
up = _getBasis.up;
|
|
73
|
+
var sy = Math.max(-1, Math.min(1, -forward[0]));
|
|
74
|
+
var y = Math.asin(sy);
|
|
75
|
+
if (Math.abs(sy) < 1 - 1e-6) {
|
|
76
|
+
return {
|
|
77
|
+
x: Math.atan2(forward[1], -forward[2]),
|
|
78
|
+
y: y,
|
|
79
|
+
z: Math.atan2(-up[0], right[0])
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
// Gimbal lock: y = ±90°, fold combined rotation into x.
|
|
83
|
+
return {
|
|
84
|
+
x: Math.atan2(up[2], up[1]),
|
|
85
|
+
y: y,
|
|
86
|
+
z: 0
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Constructs a new `CameraState` equivalent to rotating `state` about the origin such that the camera returns to its
|
|
92
|
+
* default *orientation* (looking towards -Z, +Y up), though not necessarily its default *position*.
|
|
93
|
+
*/
|
|
94
|
+
export var defaultOrientedCamera = function defaultOrientedCamera(state) {
|
|
95
|
+
var distance = vec3.length(vecToTarget(state));
|
|
96
|
+
// Rotate `target` about the origin, then derive `position` from `target`
|
|
97
|
+
var _getRotationAngles = getRotationAngles(state),
|
|
98
|
+
x = _getRotationAngles.x,
|
|
99
|
+
y = _getRotationAngles.y,
|
|
100
|
+
z = _getRotationAngles.z;
|
|
101
|
+
var matrix = transpose(rotationMatrix(x, y, z));
|
|
102
|
+
var target = toArray(vec3.transformMat3(vec3.create(), state.target, matrix));
|
|
103
|
+
var position = toArray(vec3.add(vec3.create(), target, vec3.fromValues(0, 0, distance)));
|
|
104
|
+
return {
|
|
105
|
+
target: target,
|
|
106
|
+
position: position,
|
|
107
|
+
up: [0, 1, 0]
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/** Creates a callback that performs some action on the camera, by applying `transform` to the current camera state. */
|
|
112
|
+
export var useCameraCallback = function useCameraCallback(transform, view3d) {
|
|
113
|
+
return React.useCallback(function () {
|
|
114
|
+
var state = view3d.getCameraState();
|
|
115
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
116
|
+
args[_key] = arguments[_key];
|
|
117
|
+
}
|
|
118
|
+
var nextState = transform.apply(void 0, [state].concat(args));
|
|
119
|
+
view3d.setCameraState(nextState);
|
|
120
|
+
return _objectSpread(_objectSpread({}, state), nextState);
|
|
121
|
+
}, [view3d, transform]);
|
|
122
|
+
};
|
|
@@ -13,6 +13,7 @@ var PlayControls = /*#__PURE__*/function () {
|
|
|
13
13
|
_defineProperty(this, "playWaitingForLoad", false);
|
|
14
14
|
_defineProperty(this, "playHolding", false);
|
|
15
15
|
_defineProperty(this, "playTimeoutId", 0);
|
|
16
|
+
_defineProperty(this, "lastStepTime", 0);
|
|
16
17
|
}
|
|
17
18
|
return _createClass(PlayControls, [{
|
|
18
19
|
key: "setPlayingAxis",
|
|
@@ -28,11 +29,18 @@ var PlayControls = /*#__PURE__*/function () {
|
|
|
28
29
|
if (!this.playingAxis || this.playHolding || !this.stepAxis) {
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
|
-
// If the volume is not loaded, wait for it to load before continuing
|
|
32
32
|
if (!((_this$getVolumeIsLoad = this.getVolumeIsLoaded) !== null && _this$getVolumeIsLoad !== void 0 && _this$getVolumeIsLoad.call(this))) {
|
|
33
33
|
this.playWaitingForLoad = true;
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
// Enforce minimum interval between frame *presentations* (not requests).
|
|
38
|
+
// lastStepTime is set in onImageLoaded when a frame's data arrives.
|
|
39
|
+
var delay = PLAY_STEP_INTERVAL_MS - (performance.now() - this.lastStepTime);
|
|
40
|
+
if (delay > 0) {
|
|
41
|
+
this.playTimeoutId = window.setTimeout(this.playStep.bind(this), delay);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
36
44
|
this.stepAxis(this.playingAxis);
|
|
37
45
|
this.playTimeoutId = window.setTimeout(this.playStep.bind(this), PLAY_STEP_INTERVAL_MS);
|
|
38
46
|
}
|
|
@@ -41,6 +49,9 @@ var PlayControls = /*#__PURE__*/function () {
|
|
|
41
49
|
}, {
|
|
42
50
|
key: "onImageLoaded",
|
|
43
51
|
value: function onImageLoaded() {
|
|
52
|
+
// Record when the frame was presented, so scheduleNextStep enforces
|
|
53
|
+
// PLAY_STEP_INTERVAL_MS between presentations (not between requests).
|
|
54
|
+
this.lastStepTime = performance.now();
|
|
44
55
|
if (this.playWaitingForLoad) {
|
|
45
56
|
this.playWaitingForLoad = false;
|
|
46
57
|
this.playStep();
|
|
@@ -158,6 +158,14 @@ var SceneStore = /*#__PURE__*/function () {
|
|
|
158
158
|
currentLoader.setPrefetchPriority(priority);
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
|
+
}, {
|
|
162
|
+
key: "updateFetchOptions",
|
|
163
|
+
value: function updateFetchOptions(fetchOptions) {
|
|
164
|
+
var currentLoader = this.loaders[this.currentScene];
|
|
165
|
+
if (currentLoader) {
|
|
166
|
+
currentLoader.updateFetchOptions(fetchOptions);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
161
169
|
}]);
|
|
162
170
|
}();
|
|
163
171
|
export { SceneStore as default };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
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 { describe, expect, it } from "@jest/globals";
|
|
8
|
+
import { mat3 } from "gl-matrix";
|
|
9
|
+
import { applyMatrix, defaultOrientedCamera, getRotationAngles, rotationMatrix } from "../camera";
|
|
10
|
+
var expectCameraStateToBe = function expectCameraStateToBe(actual, expected, precision) {
|
|
11
|
+
console.log(actual.up, expected.up);
|
|
12
|
+
var _actual$position = _slicedToArray(actual.position, 3),
|
|
13
|
+
apx = _actual$position[0],
|
|
14
|
+
apy = _actual$position[1],
|
|
15
|
+
apz = _actual$position[2];
|
|
16
|
+
var _expected$position = _slicedToArray(expected.position, 3),
|
|
17
|
+
epx = _expected$position[0],
|
|
18
|
+
epy = _expected$position[1],
|
|
19
|
+
epz = _expected$position[2];
|
|
20
|
+
expect(apx).toBeCloseTo(epx, precision);
|
|
21
|
+
expect(apy).toBeCloseTo(epy, precision);
|
|
22
|
+
expect(apz).toBeCloseTo(epz, precision);
|
|
23
|
+
var _actual$target = _slicedToArray(actual.target, 3),
|
|
24
|
+
atx = _actual$target[0],
|
|
25
|
+
aty = _actual$target[1],
|
|
26
|
+
atz = _actual$target[2];
|
|
27
|
+
var _expected$target = _slicedToArray(expected.target, 3),
|
|
28
|
+
etx = _expected$target[0],
|
|
29
|
+
ety = _expected$target[1],
|
|
30
|
+
etz = _expected$target[2];
|
|
31
|
+
expect(atx).toBeCloseTo(etx, precision);
|
|
32
|
+
expect(aty).toBeCloseTo(ety, precision);
|
|
33
|
+
expect(atz).toBeCloseTo(etz, precision);
|
|
34
|
+
var _actual$up = _slicedToArray(actual.up, 3),
|
|
35
|
+
aux = _actual$up[0],
|
|
36
|
+
auy = _actual$up[1],
|
|
37
|
+
auz = _actual$up[2];
|
|
38
|
+
var _expected$up = _slicedToArray(expected.up, 3),
|
|
39
|
+
eux = _expected$up[0],
|
|
40
|
+
euy = _expected$up[1],
|
|
41
|
+
euz = _expected$up[2];
|
|
42
|
+
expect(aux).toBeCloseTo(eux, precision);
|
|
43
|
+
expect(auy).toBeCloseTo(euy, precision);
|
|
44
|
+
expect(auz).toBeCloseTo(euz, precision);
|
|
45
|
+
};
|
|
46
|
+
describe("applyMatrix", function () {
|
|
47
|
+
it("multiplies every vector in a `CameraState` by a matrix", function () {
|
|
48
|
+
var INPUT = {
|
|
49
|
+
position: [3, 0, 0],
|
|
50
|
+
up: [0, 1, 0],
|
|
51
|
+
target: [0, 0, 1]
|
|
52
|
+
};
|
|
53
|
+
var EXPECTED = {
|
|
54
|
+
position: [0, 0, 3],
|
|
55
|
+
up: [0, 2, 0],
|
|
56
|
+
target: [3, 0, 0]
|
|
57
|
+
};
|
|
58
|
+
var actual = applyMatrix(INPUT, mat3.fromValues(0, 0, 1, 0, 2, 0, 3, 0, 0));
|
|
59
|
+
expectCameraStateToBe(actual, EXPECTED);
|
|
60
|
+
});
|
|
61
|
+
it("properly handles the matrix in column-major order", function () {
|
|
62
|
+
var INPUT = {
|
|
63
|
+
position: [7, 11, 3],
|
|
64
|
+
up: [0, 0, 1],
|
|
65
|
+
target: [2, 3, 5]
|
|
66
|
+
};
|
|
67
|
+
var EXPECTED = {
|
|
68
|
+
position: [38, 31, 57],
|
|
69
|
+
up: [3, 2, 1],
|
|
70
|
+
target: [23, 17, 20]
|
|
71
|
+
};
|
|
72
|
+
var actual = applyMatrix(INPUT, mat3.fromValues(1, 2, 3, 2, 1, 3, 3, 2, 1));
|
|
73
|
+
expectCameraStateToBe(actual, EXPECTED);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe("defaultOrientedCamera", function () {
|
|
77
|
+
var testDefaultOrientedCamera = function testDefaultOrientedCamera(input, position, target) {
|
|
78
|
+
var result = defaultOrientedCamera(input);
|
|
79
|
+
expectCameraStateToBe(result, {
|
|
80
|
+
position: position,
|
|
81
|
+
target: target,
|
|
82
|
+
up: [0, 1, 0]
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
it("returns the camera to its default orientation (towards -Z, with +Y up)", function () {
|
|
86
|
+
testDefaultOrientedCamera({
|
|
87
|
+
position: [Math.SQRT2 / 2, 0, Math.SQRT2 / 2],
|
|
88
|
+
target: [0, 0, 0],
|
|
89
|
+
up: [-1, 0, 0]
|
|
90
|
+
}, [0, 0, 1], [0, 0, 0]);
|
|
91
|
+
});
|
|
92
|
+
it("preserves the distance from `position` to `target`", function () {
|
|
93
|
+
testDefaultOrientedCamera({
|
|
94
|
+
position: [0, 1, 1],
|
|
95
|
+
target: [0, 0, 0],
|
|
96
|
+
up: [-1, 0, 0]
|
|
97
|
+
}, [0, 0, Math.SQRT2], [0, 0, 0]);
|
|
98
|
+
});
|
|
99
|
+
it("rotates both `target` and `position` about the origin", function () {
|
|
100
|
+
testDefaultOrientedCamera({
|
|
101
|
+
position: [-1, -2, 1],
|
|
102
|
+
target: [-1, -1, 1],
|
|
103
|
+
up: [-1, 0, 0]
|
|
104
|
+
}, [1, 1, 2], [1, 1, 1]);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
describe("getRotationAngles", function () {
|
|
108
|
+
it("is the inverse of applying a rotation matrix to a default-oriented camera when -PI/2 < Y < PI/2", function () {
|
|
109
|
+
var testRotationAnglesInverse = function testRotationAnglesInverse(x, y, z, target) {
|
|
110
|
+
var state = {
|
|
111
|
+
position: [target[0], target[1], target[2] - 1],
|
|
112
|
+
target: target,
|
|
113
|
+
up: [0, 1, 0]
|
|
114
|
+
};
|
|
115
|
+
var rotated = applyMatrix(defaultOrientedCamera(state), rotationMatrix(x, y, z));
|
|
116
|
+
var result = getRotationAngles(rotated);
|
|
117
|
+
expect(result.x).toBeCloseTo(x);
|
|
118
|
+
expect(result.y).toBeCloseTo(y);
|
|
119
|
+
expect(result.z).toBeCloseTo(z);
|
|
120
|
+
};
|
|
121
|
+
testRotationAnglesInverse(Math.PI / 2, 0, 0, [0, 0, 0]);
|
|
122
|
+
testRotationAnglesInverse(Math.PI / 2, 0, 0, [1, 2, 3]);
|
|
123
|
+
testRotationAnglesInverse(Math.PI / 3, Math.PI / 3, Math.PI / 3, [3, 3, 3]);
|
|
124
|
+
testRotationAnglesInverse(Math.PI, Math.PI / 4, -Math.PI, [3, 3, 3]);
|
|
125
|
+
testRotationAnglesInverse(1, -0.8, 3, [0.5, 0.8, 1.2]);
|
|
126
|
+
});
|
|
127
|
+
});
|