@luxonis/visualizer-protobuf 2.22.0 → 2.23.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/dist/{WorkerImageDecoder.worker-tkX9-IYo.js → WorkerImageDecoder.worker-C3ZBQ2Wk.js} +1 -1
- package/dist/{decodeImage-C8kB6T3V.js → decodeImage-CxUhz2gE.js} +14278 -2893
- package/dist/{index-P-f_cKZS.js → index-B9Zf3rrb.js} +2 -2
- package/dist/{index-BQ24Upp_.js → index-BJOK4X3d.js} +2 -2
- package/dist/{index-DMvr0-pP.js → index-BTO4og7t.js} +2 -2
- package/dist/{index-DTCT-lVn.js → index-BqTw2FSJ.js} +4 -4
- package/dist/{index-CH1TUS48.js → index-Bw0fCcF0.js} +2 -2
- package/dist/{index-DDVf76z9.js → index-CCWfhL1j.js} +2 -2
- package/dist/{index-BHXfMPMv.js → index-CFz07x1R.js} +2 -2
- package/dist/{index-Bvet1xE9.js → index-CM0J0Tip.js} +2 -2
- package/dist/{index-DtzTeqB7.js → index-D3by772J.js} +2 -2
- package/dist/{index-DzyYicoH.js → index-DMmaMUCD.js} +2813 -1608
- package/dist/{index-C-cGIa0r.js → index-DQ_hdLpb.js} +2 -2
- package/dist/{index-yfiGMPtK.js → index-DRmoIUFd.js} +2 -2
- package/dist/{index-Dcus_L6F.js → index-DWgnF3_o.js} +156 -57
- package/dist/{index-DYpNYj7G.js → index-Db42Qzy_.js} +2 -2
- package/dist/{index-C_ioBAtk.js → index-DgisSKDf.js} +2 -2
- package/dist/{index-CV57d9Tz.js → index-DjOkSXUO.js} +2 -2
- package/dist/{index-D5F-PpU5.js → index-DqqFhpKC.js} +2 -2
- package/dist/{index-RKZ-F77P.js → index-Wr3SUBO9.js} +2 -2
- package/dist/{index-DHgo3Ne_.js → index-oTzD1_p-.js} +2 -2
- package/dist/index.js +2 -2
- package/dist/lib/src/connection/foxglove-connection.d.ts +3 -1
- package/dist/lib/src/connection/foxglove-connection.d.ts.map +1 -1
- package/dist/lib/src/connection/foxglove-connection.js +16 -32
- package/dist/lib/src/connection/foxglove-connection.js.map +1 -1
- package/dist/lib/src/messaging/deserialization/pointcloud/pointcloudFromDepth.worker.js +373 -247
- package/dist/lib/src/messaging/deserialization/pointcloud/pointcloudFromDepth.worker.js.map +1 -1
- package/dist/lib/src/messaging/deserialization/pointcloud/poitcloudPoolManager.d.ts +30 -0
- package/dist/lib/src/messaging/deserialization/pointcloud/poitcloudPoolManager.d.ts.map +1 -0
- package/dist/lib/src/messaging/deserialization/pointcloud/poitcloudPoolManager.js +106 -0
- package/dist/lib/src/messaging/deserialization/pointcloud/poitcloudPoolManager.js.map +1 -0
- package/dist/lib/src/messaging/deserialization/pointcloud/utils.d.ts +0 -9
- package/dist/lib/src/messaging/deserialization/pointcloud/utils.d.ts.map +1 -1
- package/dist/lib/src/messaging/deserialization/pointcloud/utils.js +0 -16
- package/dist/lib/src/messaging/deserialization/pointcloud/utils.js.map +1 -1
- package/dist/lib/src/panels/PointCloudPanel.js +3 -3
- package/dist/lib/src/panels/PointCloudPanel.js.map +1 -1
- package/dist/lib/src/utils/poitcloud-sync.js +1 -1
- package/dist/lib/src/utils/poitcloud-sync.js.map +1 -1
- package/dist/packages/studio-base/src/panels/ThreeDeeRender/renderables/CameraStateSettings.d.ts +1 -0
- package/dist/packages/studio-base/src/panels/ThreeDeeRender/renderables/CameraStateSettings.d.ts.map +1 -1
- package/dist/packages/studio-base/src/panels/ThreeDeeRender/renderables/CameraStateSettings.js +243 -154
- package/dist/packages/studio-base/src/panels/ThreeDeeRender/renderables/CameraStateSettings.js.map +1 -1
- package/dist/pointcloudFromDepth.worker-qotYPy_e.js +450 -0
- package/dist/{utils-Cmsz3FxA.js → utils-Hzt3wxhG.js} +2 -20
- package/package.json +2 -1
- package/dist/pointcloudFromDepth.worker-CNKyMUU-.js +0 -326
package/dist/packages/studio-base/src/panels/ThreeDeeRender/renderables/CameraStateSettings.js
CHANGED
|
@@ -4,45 +4,43 @@
|
|
|
4
4
|
import { t } from "i18next";
|
|
5
5
|
import * as _ from "lodash-es";
|
|
6
6
|
import * as THREE from "three";
|
|
7
|
-
import
|
|
7
|
+
import CameraControls from "camera-controls";
|
|
8
8
|
import { CoordinateFrame, makePose, } from "@foxglove/studio-base/panels/ThreeDeeRender/transforms";
|
|
9
9
|
import { SceneExtension } from "../SceneExtension";
|
|
10
10
|
import { DEFAULT_CAMERA_STATE } from "../camera";
|
|
11
11
|
import { PRECISION_DEGREES, PRECISION_DISTANCE } from "../settings";
|
|
12
12
|
const DISPLAY_FRAME_NOT_FOUND = "DISPLAY_FRAME_NOT_FOUND";
|
|
13
|
-
const UNIT_X = new THREE.Vector3(1, 0, 0);
|
|
14
|
-
const UNIT_Z = new THREE.Vector3(0, 0, 1);
|
|
15
|
-
const PI_2 = Math.PI / 2;
|
|
16
|
-
// used for holding unfollowPoseSnapshot in render frame every new frame
|
|
17
13
|
const snapshotInRenderFrame = makePose();
|
|
18
14
|
const tempVec3 = new THREE.Vector3();
|
|
19
15
|
const tempSpherical = new THREE.Spherical();
|
|
20
|
-
const tempEuler = new THREE.Euler();
|
|
21
16
|
const FOLLOW_TF_PATH = ["general", "followTf"];
|
|
17
|
+
const AZIMUTH_SENSITIVITY = 0.25;
|
|
18
|
+
const POLAR_SENSITIVITY = 0.25;
|
|
19
|
+
const MIN_POLAR_DEG = 1;
|
|
20
|
+
const MAX_POLAR_DEG = 179;
|
|
22
21
|
export class CameraStateSettings extends SceneExtension {
|
|
23
|
-
|
|
22
|
+
renderer;
|
|
24
23
|
#unfollowSnapshotFrameIds;
|
|
25
|
-
// The pose of the render frame in the fixed frame when following was disabled
|
|
26
|
-
// This is used to position and orient the camera from the fixed frame in the render frame
|
|
27
24
|
unfollowPoseSnapshot;
|
|
28
25
|
#controls;
|
|
29
26
|
#isUpdatingCameraState = false;
|
|
30
27
|
#canvas;
|
|
31
|
-
// This group is used to transform the cameras based on the Frame follow mode
|
|
32
|
-
// quaternion is affected in stationary and position-only follow modes
|
|
33
|
-
// both position and quaternion of the group are affected in stationary mode
|
|
34
28
|
#cameraGroup;
|
|
35
29
|
#perspectiveCamera;
|
|
36
30
|
#orthographicCamera;
|
|
37
31
|
#aspect;
|
|
32
|
+
#lastTargetOffset = new THREE.Vector3();
|
|
33
|
+
#isDraggingForRotation = false;
|
|
34
|
+
#lastMouseX = 1;
|
|
35
|
+
#lastMouseY = 1;
|
|
38
36
|
constructor(renderer, canvas, aspect) {
|
|
39
37
|
super("foxglove.CameraStateSettings", renderer);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
renderer.on("
|
|
43
|
-
renderer.settings.errors.on("update", this.#handleErrorChange);
|
|
44
|
-
renderer.settings.errors.on("clear", this.#handleErrorChange);
|
|
45
|
-
renderer.settings.errors.on("remove", this.#handleErrorChange);
|
|
38
|
+
this.renderer = renderer;
|
|
39
|
+
CameraControls.install({ THREE: THREE });
|
|
40
|
+
this.renderer.on("transformTreeUpdated", this.#handleTransformTreeUpdated);
|
|
41
|
+
this.renderer.settings.errors.on("update", this.#handleErrorChange);
|
|
42
|
+
this.renderer.settings.errors.on("clear", this.#handleErrorChange);
|
|
43
|
+
this.renderer.settings.errors.on("remove", this.#handleErrorChange);
|
|
46
44
|
this.#canvas = canvas;
|
|
47
45
|
this.#perspectiveCamera = new THREE.PerspectiveCamera();
|
|
48
46
|
this.#orthographicCamera = new THREE.OrthographicCamera();
|
|
@@ -50,31 +48,69 @@ export class CameraStateSettings extends SceneExtension {
|
|
|
50
48
|
this.#cameraGroup.add(this.#perspectiveCamera);
|
|
51
49
|
this.#cameraGroup.add(this.#orthographicCamera);
|
|
52
50
|
this.add(this.#cameraGroup);
|
|
53
|
-
this.#controls = new
|
|
54
|
-
this.#controls.
|
|
55
|
-
this.#controls.mouseButtons.
|
|
56
|
-
this.#controls.mouseButtons.
|
|
57
|
-
this.#controls.
|
|
58
|
-
this.#controls.touches.
|
|
59
|
-
this.#controls.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
51
|
+
this.#controls = new CameraControls(this.#perspectiveCamera, this.#canvas);
|
|
52
|
+
this.#controls.dampingFactor = 0.05;
|
|
53
|
+
this.#controls.mouseButtons.left = CameraControls.ACTION.NONE;
|
|
54
|
+
this.#controls.mouseButtons.right = CameraControls.ACTION.TRUCK;
|
|
55
|
+
this.#controls.mouseButtons.wheel = CameraControls.ACTION.ZOOM;
|
|
56
|
+
this.#controls.touches.one = CameraControls.ACTION.TOUCH_SCREEN_PAN;
|
|
57
|
+
this.#controls.infinityDolly = true;
|
|
58
|
+
this.#perspectiveCamera.rotation.set(0, Math.PI, Math.PI);
|
|
59
|
+
this.#perspectiveCamera.up.set(0, -1, 0);
|
|
60
|
+
this.#controls.update(0);
|
|
61
|
+
const euler = new THREE.Euler().setFromQuaternion(this.#perspectiveCamera.quaternion, "ZYX");
|
|
62
|
+
this.#perspectiveCamera.quaternion.setFromEuler(euler);
|
|
63
|
+
this.#controls.addEventListener("update", this.#handleControlsUpdate);
|
|
64
|
+
const controls = this.#controls;
|
|
65
|
+
const animate = () => {
|
|
66
|
+
controls.update(1.0);
|
|
67
|
+
requestAnimationFrame(animate);
|
|
68
|
+
};
|
|
69
|
+
animate();
|
|
65
70
|
canvas.tabIndex = 1000;
|
|
66
71
|
this.#aspect = aspect;
|
|
67
|
-
|
|
68
|
-
this.#
|
|
72
|
+
canvas.addEventListener("mousedown", this.#handleMouseDown);
|
|
73
|
+
canvas.addEventListener("mousemove", this.#handleMouseMove);
|
|
74
|
+
window.addEventListener("mouseup", this.#handleMouseUp);
|
|
75
|
+
canvas.addEventListener("mouseleave", this.#handleMouseLeave);
|
|
76
|
+
canvas.addEventListener("contextmenu", (event) => event.preventDefault());
|
|
77
|
+
// Set camera state to default
|
|
78
|
+
setTimeout(() => {
|
|
79
|
+
const activeCamera = this.getActiveCamera();
|
|
80
|
+
activeCamera.updateMatrixWorld(true);
|
|
81
|
+
this.renderer.queueAnimationFrame();
|
|
82
|
+
this.#applyCameraStateToControls(this.renderer.config.cameraState, undefined);
|
|
83
|
+
this.#controls.update(0);
|
|
84
|
+
}, 10);
|
|
85
|
+
// Rotates the camera to the default position
|
|
86
|
+
setTimeout(() => {
|
|
87
|
+
const currentTarget = new THREE.Vector3();
|
|
88
|
+
this.#controls.getTarget(currentTarget);
|
|
89
|
+
const currentPosition = new THREE.Vector3();
|
|
90
|
+
this.#perspectiveCamera.getWorldPosition(currentPosition);
|
|
91
|
+
const offset = currentPosition.clone().sub(currentTarget);
|
|
92
|
+
const spherical = new THREE.Spherical();
|
|
93
|
+
spherical.setFromVector3(offset);
|
|
94
|
+
spherical.theta = Math.max(0.001, Math.min(Math.PI - 0.001, spherical.theta + Math.PI));
|
|
95
|
+
const newOffset = new THREE.Vector3().setFromSpherical(spherical);
|
|
96
|
+
const newCameraPosition = currentTarget.clone().add(newOffset);
|
|
97
|
+
this.#controls.setLookAt(newCameraPosition.x, newCameraPosition.y, newCameraPosition.z, currentTarget.x, currentTarget.y, currentTarget.z, false);
|
|
98
|
+
this.#controls.update(0);
|
|
99
|
+
this.renderer.queueAnimationFrame();
|
|
100
|
+
}, 100);
|
|
69
101
|
}
|
|
70
102
|
dispose() {
|
|
71
|
-
// for camera settings
|
|
72
|
-
this.renderer.off("cameraMove", this.#handleCameraMove);
|
|
73
|
-
// for frame settings
|
|
74
103
|
this.renderer.off("transformTreeUpdated", this.#handleTransformTreeUpdated);
|
|
75
104
|
this.renderer.settings.errors.off("update", this.#handleErrorChange);
|
|
76
105
|
this.renderer.settings.errors.off("clear", this.#handleErrorChange);
|
|
77
106
|
this.renderer.settings.errors.off("remove", this.#handleErrorChange);
|
|
107
|
+
this.#controls.removeEventListener("update", this.#handleControlsUpdate);
|
|
108
|
+
this.#canvas.removeEventListener("mousedown", this.#handleMouseDown);
|
|
109
|
+
this.#canvas.removeEventListener("mousemove", this.#handleMouseMove);
|
|
110
|
+
window.removeEventListener("mouseup", this.#handleMouseUp);
|
|
111
|
+
this.#canvas.removeEventListener("mouseleave", this.#handleMouseLeave);
|
|
112
|
+
this.#canvas.removeEventListener("contextmenu", (event) => event.preventDefault());
|
|
113
|
+
this.#controls.dispose();
|
|
78
114
|
super.dispose();
|
|
79
115
|
}
|
|
80
116
|
settingsNodes() {
|
|
@@ -82,13 +118,15 @@ export class CameraStateSettings extends SceneExtension {
|
|
|
82
118
|
}
|
|
83
119
|
#cameraSettingsNode() {
|
|
84
120
|
const config = this.renderer.config;
|
|
85
|
-
const
|
|
121
|
+
const camera = config.cameraState;
|
|
86
122
|
const handler = this.handleSettingsAction;
|
|
87
123
|
return {
|
|
88
124
|
path: ["cameraState"],
|
|
89
125
|
node: {
|
|
90
126
|
label: t("threeDee:view"),
|
|
91
|
-
actions: [
|
|
127
|
+
actions: [
|
|
128
|
+
{ type: "action", id: "reset-camera", label: t("threeDee:reset") },
|
|
129
|
+
],
|
|
92
130
|
handler,
|
|
93
131
|
fields: {
|
|
94
132
|
syncCamera: {
|
|
@@ -103,7 +141,7 @@ export class CameraStateSettings extends SceneExtension {
|
|
|
103
141
|
input: "number",
|
|
104
142
|
step: 1,
|
|
105
143
|
precision: PRECISION_DISTANCE,
|
|
106
|
-
value: camera.distance,
|
|
144
|
+
value: camera.distance ?? config.cameraState.distance,
|
|
107
145
|
},
|
|
108
146
|
perspective: {
|
|
109
147
|
label: t("threeDee:perspective"),
|
|
@@ -115,23 +153,25 @@ export class CameraStateSettings extends SceneExtension {
|
|
|
115
153
|
input: "vec3",
|
|
116
154
|
labels: ["X", "Y", "Z"],
|
|
117
155
|
precision: PRECISION_DISTANCE,
|
|
118
|
-
value: [
|
|
156
|
+
value: [
|
|
157
|
+
...(camera.targetOffset ?? config.cameraState.targetOffset),
|
|
158
|
+
],
|
|
119
159
|
},
|
|
120
160
|
thetaOffset: {
|
|
121
161
|
label: t("threeDee:theta"),
|
|
122
162
|
input: "number",
|
|
123
163
|
step: 1,
|
|
124
164
|
precision: PRECISION_DEGREES,
|
|
125
|
-
value: camera.thetaOffset,
|
|
165
|
+
value: camera.thetaOffset ?? config.cameraState.thetaOffset,
|
|
166
|
+
},
|
|
167
|
+
phi: {
|
|
168
|
+
label: t("threeDee:phi"),
|
|
169
|
+
input: "number",
|
|
170
|
+
step: 1,
|
|
171
|
+
precision: PRECISION_DEGREES,
|
|
172
|
+
value: camera.phi ?? config.cameraState.phi,
|
|
126
173
|
},
|
|
127
174
|
...(camera.perspective && {
|
|
128
|
-
phi: {
|
|
129
|
-
label: t("threeDee:phi"),
|
|
130
|
-
input: "number",
|
|
131
|
-
step: 1,
|
|
132
|
-
precision: PRECISION_DEGREES,
|
|
133
|
-
value: camera.phi,
|
|
134
|
-
},
|
|
135
175
|
fovy: {
|
|
136
176
|
label: t("threeDee:fovy"),
|
|
137
177
|
input: "number",
|
|
@@ -152,7 +192,7 @@ export class CameraStateSettings extends SceneExtension {
|
|
|
152
192
|
input: "number",
|
|
153
193
|
step: 1,
|
|
154
194
|
precision: PRECISION_DISTANCE,
|
|
155
|
-
value: camera.far,
|
|
195
|
+
value: camera.far ?? config.cameraState.far,
|
|
156
196
|
},
|
|
157
197
|
},
|
|
158
198
|
},
|
|
@@ -161,16 +201,17 @@ export class CameraStateSettings extends SceneExtension {
|
|
|
161
201
|
#frameSettingsNode() {
|
|
162
202
|
const config = this.renderer.config;
|
|
163
203
|
const handler = this.handleSettingsAction;
|
|
164
|
-
// If the user-selected frame does not exist, show it in the dropdown
|
|
165
|
-
// anyways. A settings node error will be displayed
|
|
166
204
|
let followTfOptions = this.renderer.coordinateFrameList;
|
|
167
205
|
const followFrameId = this.renderer.followFrameId;
|
|
168
206
|
this.#updateFollowTfError();
|
|
169
|
-
// always show current config value if it exists
|
|
170
207
|
const followTfValue = config.followTf ?? followFrameId;
|
|
171
|
-
if (followTfValue != undefined &&
|
|
208
|
+
if (followTfValue != undefined &&
|
|
209
|
+
!this.renderer.transformTree.hasFrame(followTfValue)) {
|
|
172
210
|
followTfOptions = [
|
|
173
|
-
{
|
|
211
|
+
{
|
|
212
|
+
label: CoordinateFrame.DisplayName(followTfValue),
|
|
213
|
+
value: followTfValue,
|
|
214
|
+
},
|
|
174
215
|
...followTfOptions,
|
|
175
216
|
];
|
|
176
217
|
}
|
|
@@ -180,7 +221,7 @@ export class CameraStateSettings extends SceneExtension {
|
|
|
180
221
|
{ label: t("threeDee:position"), value: "follow-position" },
|
|
181
222
|
{ label: t("threeDee:fixed"), value: "follow-none" },
|
|
182
223
|
];
|
|
183
|
-
const followModeValue =
|
|
224
|
+
const followModeValue = config.followMode;
|
|
184
225
|
return {
|
|
185
226
|
path: ["general"],
|
|
186
227
|
node: {
|
|
@@ -208,67 +249,73 @@ export class CameraStateSettings extends SceneExtension {
|
|
|
208
249
|
};
|
|
209
250
|
}
|
|
210
251
|
handleSettingsAction = (action) => {
|
|
211
|
-
if (action.action === "perform-node-action" &&
|
|
252
|
+
if (action.action === "perform-node-action" &&
|
|
253
|
+
action.payload.id === "reset-camera") {
|
|
254
|
+
const previousState = _.cloneDeep(this.renderer.config.cameraState);
|
|
212
255
|
this.renderer.updateConfig((draft) => {
|
|
213
256
|
draft.cameraState = _.cloneDeep(DEFAULT_CAMERA_STATE);
|
|
214
257
|
});
|
|
215
|
-
this.
|
|
258
|
+
this.#applyCameraStateToControls(this.renderer.config.cameraState, previousState);
|
|
259
|
+
this.renderer.emit("cameraMove", this.renderer);
|
|
216
260
|
return;
|
|
217
261
|
}
|
|
218
262
|
if (action.action !== "update" || action.payload.path.length === 0) {
|
|
219
263
|
return;
|
|
220
264
|
}
|
|
221
|
-
const { path
|
|
222
|
-
|
|
265
|
+
const { path, value } = action.payload;
|
|
266
|
+
const category = path[0];
|
|
223
267
|
if (category === "cameraState") {
|
|
268
|
+
const previousState = _.cloneDeep(this.renderer.config.cameraState);
|
|
224
269
|
if (path[1] === "syncCamera") {
|
|
225
|
-
// Update the configuration. This is done manually since syncCamera is under `scene`, not `cameraState`
|
|
226
270
|
this.renderer.updateConfig((draft) => {
|
|
227
271
|
draft.scene.syncCamera = value;
|
|
228
272
|
});
|
|
229
273
|
}
|
|
230
274
|
else {
|
|
231
|
-
this.renderer.updateConfig((draft) =>
|
|
275
|
+
this.renderer.updateConfig((draft) => {
|
|
276
|
+
_.set(draft, path, value);
|
|
277
|
+
});
|
|
278
|
+
this.#applyCameraStateToControls(this.renderer.config.cameraState, previousState);
|
|
279
|
+
this.renderer.emit("cameraMove", this.renderer);
|
|
232
280
|
}
|
|
233
|
-
this.updateSettingsTree();
|
|
234
281
|
}
|
|
235
|
-
// frame settings
|
|
236
282
|
if (category === "general") {
|
|
283
|
+
const previousState = _.cloneDeep(this.renderer.config.cameraState);
|
|
237
284
|
if (path[1] === "followTf") {
|
|
238
285
|
const followTf = value;
|
|
239
|
-
// Update the configuration. This is done manually since followTf is at the top level of
|
|
240
|
-
// config, not under `general`
|
|
241
286
|
this.renderer.updateConfig((draft) => {
|
|
242
287
|
draft.followTf = followTf;
|
|
243
288
|
});
|
|
244
289
|
this.#updateFollowFrameId();
|
|
245
|
-
this.renderer.settings.errors.clearPath(
|
|
290
|
+
this.renderer.settings.errors.clearPath(FOLLOW_TF_PATH);
|
|
246
291
|
}
|
|
247
292
|
else if (path[1] === "followMode") {
|
|
248
293
|
const followMode = value;
|
|
249
|
-
// Update the configuration. This is done manually since followMode is at the top level of
|
|
250
|
-
// config, not under `general`
|
|
251
294
|
this.renderer.updateConfig((draft) => {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
295
|
+
if (draft.followMode === "follow-none" ||
|
|
296
|
+
followMode === "follow-pose") {
|
|
297
|
+
draft.cameraState.targetOffset = [
|
|
298
|
+
...DEFAULT_CAMERA_STATE.targetOffset,
|
|
299
|
+
];
|
|
257
300
|
}
|
|
258
|
-
|
|
301
|
+
if (followMode === "follow-pose") {
|
|
259
302
|
draft.cameraState.thetaOffset = DEFAULT_CAMERA_STATE.thetaOffset;
|
|
260
303
|
}
|
|
261
304
|
draft.followMode = followMode;
|
|
262
305
|
});
|
|
306
|
+
this.#applyCameraStateToControls(this.renderer.config.cameraState, previousState);
|
|
307
|
+
this.renderer.emit("cameraMove", this.renderer);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
this.renderer.updateConfig((draft) => {
|
|
311
|
+
_.set(draft, path, value);
|
|
312
|
+
});
|
|
263
313
|
}
|
|
264
|
-
this.updateSettingsTree();
|
|
265
314
|
}
|
|
266
315
|
};
|
|
267
|
-
// this extension has NO RENDERABLES so the parent startFrame would do nothing
|
|
268
316
|
startFrame(currentTime, renderFrameId, fixedFrameId) {
|
|
269
317
|
const followMode = this.renderer.config.followMode;
|
|
270
318
|
if (followMode === "follow-pose" ||
|
|
271
|
-
// we don't need the unfollow pose snapshot when there are no transforms
|
|
272
319
|
fixedFrameId === CoordinateFrame.FALLBACK_FRAME_ID ||
|
|
273
320
|
renderFrameId === CoordinateFrame.FALLBACK_FRAME_ID) {
|
|
274
321
|
this.#unfollowSnapshotFrameIds = undefined;
|
|
@@ -280,35 +327,89 @@ export class CameraStateSettings extends SceneExtension {
|
|
|
280
327
|
const poseSnapshot = this.#getUnfollowPoseSnapshot(fixedFrameId, renderFrameId, currentTime);
|
|
281
328
|
const transformTree = this.renderer.transformTree;
|
|
282
329
|
if (poseSnapshot) {
|
|
283
|
-
// transform position of snapshot in fixed frame to the render frame
|
|
284
330
|
const appliedTransform = Boolean(transformTree.apply(snapshotInRenderFrame, poseSnapshot, renderFrameId, fixedFrameId, fixedFrameId, currentTime, currentTime));
|
|
285
331
|
if (!appliedTransform) {
|
|
286
332
|
return;
|
|
287
333
|
}
|
|
288
|
-
/**
|
|
289
|
-
* the application of the unfollowPoseSnapshot position and orientation
|
|
290
|
-
* components makes the camera position and rotation static relative to the fixed frame.
|
|
291
|
-
* So when the display frame changes the angle of the camera relative
|
|
292
|
-
* to the scene will not change because only the snapshotPose orientation is applied
|
|
293
|
-
*/
|
|
294
334
|
if (followMode === "follow-position") {
|
|
295
|
-
// only make orientation static/stationary in this mode
|
|
296
|
-
// the position still follows the frame
|
|
297
335
|
this.#cameraGroup.position.set(0, 0, 0);
|
|
298
336
|
}
|
|
299
337
|
else {
|
|
300
338
|
this.#cameraGroup.position.set(snapshotInRenderFrame.position.x, snapshotInRenderFrame.position.y, snapshotInRenderFrame.position.z);
|
|
301
339
|
}
|
|
302
|
-
// this negates the rotation of the changes in renderFrame
|
|
303
340
|
this.#cameraGroup.quaternion.set(snapshotInRenderFrame.orientation.x, snapshotInRenderFrame.orientation.y, snapshotInRenderFrame.orientation.z, snapshotInRenderFrame.orientation.w);
|
|
304
341
|
}
|
|
305
342
|
}
|
|
306
|
-
#
|
|
307
|
-
this
|
|
343
|
+
#handleControlsUpdate = () => {
|
|
344
|
+
if (!this.#isUpdatingCameraState && !this.#isDraggingForRotation) {
|
|
345
|
+
const newDistance = this.#controls.distance;
|
|
346
|
+
this.#controls.getTarget(tempVec3);
|
|
347
|
+
const state = this.renderer.config.cameraState;
|
|
348
|
+
const distChanged = Math.abs(state.distance - newDistance) > 1e-6;
|
|
349
|
+
const targetChanged = !_.isEqual(state.targetOffset, [
|
|
350
|
+
tempVec3.x,
|
|
351
|
+
tempVec3.y,
|
|
352
|
+
tempVec3.z,
|
|
353
|
+
]);
|
|
354
|
+
if (distChanged || targetChanged) {
|
|
355
|
+
this.renderer.updateConfig((draft) => {
|
|
356
|
+
if (distChanged)
|
|
357
|
+
draft.cameraState.distance = newDistance;
|
|
358
|
+
if (targetChanged)
|
|
359
|
+
draft.cameraState.targetOffset = [
|
|
360
|
+
tempVec3.x,
|
|
361
|
+
tempVec3.y,
|
|
362
|
+
tempVec3.z,
|
|
363
|
+
];
|
|
364
|
+
});
|
|
365
|
+
this.#lastTargetOffset.copy(tempVec3);
|
|
366
|
+
this.renderer.emit("cameraMove", this.renderer);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
#handleMouseDown = (event) => {
|
|
371
|
+
if (event.button === 0) {
|
|
372
|
+
event.preventDefault();
|
|
373
|
+
this.#isDraggingForRotation = true;
|
|
374
|
+
this.#lastMouseX = event.clientX;
|
|
375
|
+
this.#lastMouseY = event.clientY;
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
#handleMouseMove = (event) => {
|
|
379
|
+
if (!this.#isDraggingForRotation) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const deltaX = event.clientX - this.#lastMouseX;
|
|
383
|
+
const deltaY = event.clientY - this.#lastMouseY;
|
|
384
|
+
this.#lastMouseX = event.clientX;
|
|
385
|
+
this.#lastMouseY = event.clientY;
|
|
386
|
+
if (deltaX === 0 && deltaY === 0) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const deltaAzimuth = -deltaX * AZIMUTH_SENSITIVITY;
|
|
390
|
+
const deltaPolar = deltaY * POLAR_SENSITIVITY;
|
|
391
|
+
const previousState = _.cloneDeep(this.renderer.config.cameraState);
|
|
392
|
+
this.renderer.updateConfig((draft) => {
|
|
393
|
+
let currentPhi = draft.cameraState.phi + deltaPolar;
|
|
394
|
+
currentPhi = Math.max(MIN_POLAR_DEG, Math.min(MAX_POLAR_DEG, currentPhi));
|
|
395
|
+
draft.cameraState.phi = currentPhi;
|
|
396
|
+
draft.cameraState.thetaOffset += deltaAzimuth;
|
|
397
|
+
});
|
|
398
|
+
this.#applyCameraStateToControls(this.renderer.config.cameraState, previousState);
|
|
399
|
+
this.renderer.emit("cameraMove", this.renderer);
|
|
400
|
+
};
|
|
401
|
+
#handleMouseUp = (event) => {
|
|
402
|
+
if (event.button === 0 && this.#isDraggingForRotation) {
|
|
403
|
+
this.#isDraggingForRotation = false;
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
#handleMouseLeave = () => {
|
|
407
|
+
if (this.#isDraggingForRotation) {
|
|
408
|
+
this.#isDraggingForRotation = false;
|
|
409
|
+
}
|
|
308
410
|
};
|
|
309
411
|
#handleTransformTreeUpdated = () => {
|
|
310
412
|
this.#updateFollowFrameId();
|
|
311
|
-
this.updateSettingsTree();
|
|
312
413
|
};
|
|
313
414
|
#updateFollowFrameId() {
|
|
314
415
|
const { followTf } = this.renderer.config;
|
|
@@ -319,37 +420,31 @@ export class CameraStateSettings extends SceneExtension {
|
|
|
319
420
|
this.renderer.setFollowFrameId(followTf);
|
|
320
421
|
return;
|
|
321
422
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const followFrameId = transformTree.getDefaultFollowFrameId();
|
|
325
|
-
this.renderer.setFollowFrameId(followFrameId);
|
|
423
|
+
const defaultFollowFrameId = transformTree.getDefaultFollowFrameId();
|
|
424
|
+
this.renderer.setFollowFrameId(defaultFollowFrameId);
|
|
326
425
|
}
|
|
327
426
|
#updateFollowTfError = () => {
|
|
328
427
|
const { followTf } = this.renderer.config;
|
|
329
428
|
const { transformTree } = this.renderer;
|
|
330
429
|
if (followTf != undefined) {
|
|
331
|
-
|
|
332
|
-
if (followTfFrameExists) {
|
|
430
|
+
if (transformTree.hasFrame(followTf)) {
|
|
333
431
|
this.renderer.settings.errors.remove(FOLLOW_TF_PATH, DISPLAY_FRAME_NOT_FOUND);
|
|
334
432
|
}
|
|
335
433
|
else {
|
|
336
|
-
this.renderer.settings.errors.add(FOLLOW_TF_PATH, DISPLAY_FRAME_NOT_FOUND, t("threeDee:frameNotFound", {
|
|
337
|
-
frameId: followTf,
|
|
338
|
-
}));
|
|
434
|
+
this.renderer.settings.errors.add(FOLLOW_TF_PATH, DISPLAY_FRAME_NOT_FOUND, t("threeDee:frameNotFound", { frameId: followTf }));
|
|
339
435
|
}
|
|
340
436
|
}
|
|
437
|
+
else {
|
|
438
|
+
this.renderer.settings.errors.remove(FOLLOW_TF_PATH, DISPLAY_FRAME_NOT_FOUND);
|
|
439
|
+
}
|
|
341
440
|
};
|
|
342
|
-
#handleErrorChange = () => {
|
|
343
|
-
this.updateSettingsTree();
|
|
344
|
-
};
|
|
345
|
-
// Redefine follow pose snapshot whenever renderFrame or fixedFrame changes
|
|
441
|
+
#handleErrorChange = () => { };
|
|
346
442
|
#getUnfollowPoseSnapshot(fixedFrameId, renderFrameId, currentTime) {
|
|
347
443
|
const transformTree = this.renderer.transformTree;
|
|
348
444
|
if (this.#unfollowSnapshotFrameIds?.fixed !== fixedFrameId ||
|
|
349
|
-
this.#unfollowSnapshotFrameIds
|
|
445
|
+
this.#unfollowSnapshotFrameIds?.render !== renderFrameId) {
|
|
350
446
|
this.unfollowPoseSnapshot = makePose();
|
|
351
|
-
|
|
352
|
-
transformTree.apply(this.unfollowPoseSnapshot, this.unfollowPoseSnapshot, fixedFrameId, fixedFrameId, renderFrameId, currentTime, currentTime);
|
|
447
|
+
transformTree.apply(this.unfollowPoseSnapshot, makePose(), fixedFrameId, renderFrameId, fixedFrameId, currentTime, currentTime);
|
|
353
448
|
this.#unfollowSnapshotFrameIds = {
|
|
354
449
|
fixed: fixedFrameId,
|
|
355
450
|
render: renderFrameId,
|
|
@@ -367,71 +462,65 @@ export class CameraStateSettings extends SceneExtension {
|
|
|
367
462
|
this.setCameraState(this.renderer.config.cameraState);
|
|
368
463
|
}
|
|
369
464
|
getCameraState() {
|
|
370
|
-
|
|
371
|
-
return {
|
|
372
|
-
perspective: config.cameraState.perspective,
|
|
373
|
-
distance: this.#controls.getDistance(),
|
|
374
|
-
phi: THREE.MathUtils.radToDeg(this.#controls.getPolarAngle()),
|
|
375
|
-
thetaOffset: THREE.MathUtils.radToDeg(-this.#controls.getAzimuthalAngle()),
|
|
376
|
-
targetOffset: [this.#controls.target.x, this.#controls.target.y, this.#controls.target.z],
|
|
377
|
-
target: config.cameraState.target,
|
|
378
|
-
targetOrientation: config.cameraState.targetOrientation,
|
|
379
|
-
fovy: config.cameraState.fovy,
|
|
380
|
-
near: config.cameraState.near,
|
|
381
|
-
far: config.cameraState.far,
|
|
382
|
-
};
|
|
465
|
+
return _.cloneDeep(this.renderer.config.cameraState);
|
|
383
466
|
}
|
|
384
467
|
setCameraState(cameraState) {
|
|
468
|
+
const previousState = _.cloneDeep(this.renderer.config.cameraState);
|
|
385
469
|
this.#isUpdatingCameraState = true;
|
|
386
|
-
this.#
|
|
387
|
-
|
|
388
|
-
// due to the fact that they are manipulating the camera after update with the `cameraGroup`
|
|
389
|
-
if (this.renderer.config.followMode === "follow-pose") {
|
|
390
|
-
this.#controls.update();
|
|
391
|
-
}
|
|
470
|
+
this.#updateRawCameras(cameraState);
|
|
471
|
+
this.#applyCameraStateToControls(cameraState, previousState);
|
|
392
472
|
this.#isUpdatingCameraState = false;
|
|
393
473
|
}
|
|
394
|
-
|
|
395
|
-
#updateCameras(cameraState) {
|
|
396
|
-
const targetOffset = tempVec3;
|
|
397
|
-
const config = this.renderer.config;
|
|
398
|
-
targetOffset.fromArray(cameraState.targetOffset);
|
|
399
|
-
const phi = THREE.MathUtils.degToRad(cameraState.phi);
|
|
400
|
-
const theta = -THREE.MathUtils.degToRad(cameraState.thetaOffset);
|
|
401
|
-
// Always update the perspective camera even if the current mode is orthographic. This is needed
|
|
402
|
-
// to make the OrbitControls work properly since they track the perspective camera.
|
|
403
|
-
// https://github.com/foxglove/studio/issues/4138
|
|
404
|
-
// Convert the camera spherical coordinates (radius, phi, theta) to Cartesian (X, Y, Z)
|
|
405
|
-
tempSpherical.set(cameraState.distance, phi, theta);
|
|
406
|
-
this.#perspectiveCamera.position.setFromSpherical(tempSpherical).applyAxisAngle(UNIT_X, PI_2);
|
|
407
|
-
this.#perspectiveCamera.position.add(targetOffset);
|
|
408
|
-
// Convert the camera spherical coordinates (phi, theta) to a quaternion rotation
|
|
409
|
-
this.#perspectiveCamera.quaternion.setFromEuler(tempEuler.set(phi, 0, theta, "ZYX"));
|
|
474
|
+
#updateRawCameras(cameraState) {
|
|
410
475
|
this.#perspectiveCamera.fov = cameraState.fovy;
|
|
411
476
|
this.#perspectiveCamera.near = cameraState.near;
|
|
412
477
|
this.#perspectiveCamera.far = cameraState.far;
|
|
413
478
|
this.#perspectiveCamera.aspect = this.#aspect;
|
|
414
479
|
this.#perspectiveCamera.updateProjectionMatrix();
|
|
415
|
-
|
|
480
|
+
if (!cameraState.perspective) {
|
|
481
|
+
const orthoHeight = cameraState.distance *
|
|
482
|
+
Math.tan(THREE.MathUtils.degToRad(cameraState.fovy * 0.5)) *
|
|
483
|
+
2;
|
|
484
|
+
const orthoWidth = orthoHeight * this.#aspect;
|
|
485
|
+
this.#orthographicCamera.left = -orthoWidth / 2;
|
|
486
|
+
this.#orthographicCamera.right = orthoWidth / 2;
|
|
487
|
+
this.#orthographicCamera.top = orthoHeight / 2;
|
|
488
|
+
this.#orthographicCamera.bottom = -orthoHeight / 2;
|
|
489
|
+
this.#orthographicCamera.near = cameraState.near;
|
|
490
|
+
this.#orthographicCamera.far = cameraState.far;
|
|
491
|
+
this.#orthographicCamera.updateProjectionMatrix();
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
#applyCameraStateToControls(cameraState, previousState) {
|
|
495
|
+
const targetOffset = tempVec3.fromArray(cameraState.targetOffset);
|
|
496
|
+
const distance = cameraState.distance;
|
|
497
|
+
const phiRad = THREE.MathUtils.degToRad(cameraState.phi);
|
|
498
|
+
const azimuthRad = -THREE.MathUtils.degToRad(cameraState.thetaOffset);
|
|
499
|
+
const propsChanged = !previousState ||
|
|
500
|
+
Math.abs(previousState.distance - distance) > 1e-6 ||
|
|
501
|
+
Math.abs(previousState.phi - cameraState.phi) > 1e-6 ||
|
|
502
|
+
Math.abs(previousState.thetaOffset - cameraState.thetaOffset) > 1e-6 ||
|
|
503
|
+
!_.isEqual(previousState.targetOffset, cameraState.targetOffset);
|
|
504
|
+
if (propsChanged) {
|
|
505
|
+
tempSpherical.set(distance, phiRad, azimuthRad);
|
|
506
|
+
const cameraPosition = new THREE.Vector3().setFromSpherical(tempSpherical);
|
|
507
|
+
cameraPosition.add(targetOffset);
|
|
508
|
+
this.#controls.setLookAt(cameraPosition.x, cameraPosition.y, cameraPosition.z, targetOffset.x, targetOffset.y, targetOffset.z, false);
|
|
509
|
+
this.#lastTargetOffset.copy(targetOffset);
|
|
510
|
+
}
|
|
416
511
|
if (cameraState.perspective) {
|
|
417
|
-
// Unlock the polar angle (pitch axis)
|
|
418
512
|
this.#controls.minPolarAngle = 0;
|
|
419
513
|
this.#controls.maxPolarAngle = Math.PI;
|
|
420
514
|
}
|
|
421
515
|
else {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
this.#controls.
|
|
425
|
-
this.#
|
|
426
|
-
this.#orthographicCamera.
|
|
427
|
-
this.#orthographicCamera.
|
|
428
|
-
this.#orthographicCamera.right = (cameraState.distance / 2) * this.#aspect;
|
|
429
|
-
this.#orthographicCamera.top = cameraState.distance / 2;
|
|
430
|
-
this.#orthographicCamera.bottom = -cameraState.distance / 2;
|
|
431
|
-
this.#orthographicCamera.near = cameraState.near;
|
|
432
|
-
this.#orthographicCamera.far = cameraState.far;
|
|
433
|
-
this.#orthographicCamera.updateProjectionMatrix();
|
|
516
|
+
this.#controls.minPolarAngle = 0;
|
|
517
|
+
this.#controls.maxPolarAngle = Math.PI;
|
|
518
|
+
this.#controls.getPosition(this.#orthographicCamera.position);
|
|
519
|
+
this.#controls.getTarget(tempVec3);
|
|
520
|
+
this.#orthographicCamera.lookAt(tempVec3);
|
|
521
|
+
this.#orthographicCamera.updateMatrixWorld();
|
|
434
522
|
}
|
|
523
|
+
this.#controls.update(0);
|
|
435
524
|
}
|
|
436
525
|
}
|
|
437
526
|
//# sourceMappingURL=CameraStateSettings.js.map
|