@luxonis/depthai-viewer-common 3.9.7 → 4.0.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/THIRD_PARTY_NOTICES.md +36 -304
- package/benchmark-cli.mjs +166 -11
- package/benchmark-lib.sh +40 -5
- package/benchmark-root.sh +79 -13
- package/benchmark.sh +112 -5
- package/dist/components/Streams.d.ts +6 -0
- package/dist/components/Streams.d.ts.map +1 -1
- package/dist/components/Streams.js +12 -4
- package/dist/components/Streams.js.map +1 -1
- package/dist/hooks/benchmark-automation.d.ts.map +1 -1
- package/dist/hooks/benchmark-automation.js +288 -63
- package/dist/hooks/benchmark-automation.js.map +1 -1
- package/dist/index.css +7 -0
- package/dist/utils/vite-dev-fix.d.ts.map +1 -1
- package/dist/utils/vite-dev-fix.js +0 -15
- package/dist/utils/vite-dev-fix.js.map +1 -1
- package/dist/visualizer/components/Panel.d.ts.map +1 -1
- package/dist/visualizer/components/Panel.js +6 -3
- package/dist/visualizer/components/Panel.js.map +1 -1
- package/dist/visualizer/components/shared-interval.d.ts +1 -1
- package/dist/visualizer/components/shared-interval.d.ts.map +1 -1
- package/dist/visualizer/components/shared-interval.js +6 -2
- package/dist/visualizer/components/shared-interval.js.map +1 -1
- package/dist/visualizer/connection/communicator.d.ts +1 -0
- package/dist/visualizer/connection/communicator.d.ts.map +1 -1
- package/dist/visualizer/connection/communicator.js.map +1 -1
- package/dist/visualizer/connection/connection.d.ts.map +1 -1
- package/dist/visualizer/connection/connection.js +8 -7
- package/dist/visualizer/connection/connection.js.map +1 -1
- package/dist/visualizer/connection/foxglove-connection.d.ts +4 -2
- package/dist/visualizer/connection/foxglove-connection.d.ts.map +1 -1
- package/dist/visualizer/connection/foxglove-connection.js +88 -40
- package/dist/visualizer/connection/foxglove-connection.js.map +1 -1
- package/dist/visualizer/context/MessagePipelineProvider.d.ts +1 -1
- package/dist/visualizer/context/MessagePipelineProvider.d.ts.map +1 -1
- package/dist/visualizer/context/MessagePipelineProvider.js +24 -6
- package/dist/visualizer/context/MessagePipelineProvider.js.map +1 -1
- package/dist/visualizer/context/VisualizerContext.d.ts.map +1 -1
- package/dist/visualizer/context/VisualizerContext.js +1 -32
- package/dist/visualizer/context/VisualizerContext.js.map +1 -1
- package/dist/visualizer/index.d.ts +1 -0
- package/dist/visualizer/index.d.ts.map +1 -1
- package/dist/visualizer/index.js +1 -0
- package/dist/visualizer/index.js.map +1 -1
- package/dist/visualizer/messaging/browser-decoder-fixtures.d.ts.map +1 -1
- package/dist/visualizer/messaging/browser-decoder-fixtures.js +187 -24
- package/dist/visualizer/messaging/browser-decoder-fixtures.js.map +1 -1
- package/dist/visualizer/messaging/decode-core.d.ts +9 -1
- package/dist/visualizer/messaging/decode-core.d.ts.map +1 -1
- package/dist/visualizer/messaging/decode-core.js +26 -16
- package/dist/visualizer/messaging/decode-core.js.map +1 -1
- package/dist/visualizer/messaging/decoder-fixtures.d.ts.map +1 -1
- package/dist/visualizer/messaging/decoder-fixtures.js +128 -1
- package/dist/visualizer/messaging/decoder-fixtures.js.map +1 -1
- package/dist/visualizer/messaging/decoder-harness.d.ts +2 -1
- package/dist/visualizer/messaging/decoder-harness.d.ts.map +1 -1
- package/dist/visualizer/messaging/decoder-harness.js.map +1 -1
- package/dist/visualizer/messaging/deserialization/pointcloud/pointcloudFromDepth.worker.d.ts +6 -1
- package/dist/visualizer/messaging/deserialization/pointcloud/pointcloudFromDepth.worker.d.ts.map +1 -1
- package/dist/visualizer/messaging/deserialization/pointcloud/pointcloudFromDepth.worker.js +5 -1
- package/dist/visualizer/messaging/deserialization/pointcloud/pointcloudFromDepth.worker.js.map +1 -1
- package/dist/visualizer/messaging/deserialization/pointcloud/poitcloudPoolManager.d.ts +10 -14
- package/dist/visualizer/messaging/deserialization/pointcloud/poitcloudPoolManager.d.ts.map +1 -1
- package/dist/visualizer/messaging/deserialization/pointcloud/poitcloudPoolManager.js +56 -288
- package/dist/visualizer/messaging/deserialization/pointcloud/poitcloudPoolManager.js.map +1 -1
- package/dist/visualizer/messaging/deserialization/pointcloud/utils.d.ts.map +1 -1
- package/dist/visualizer/messaging/deserialization/pointcloud/utils.js +2 -7
- package/dist/visualizer/messaging/deserialization/pointcloud/utils.js.map +1 -1
- package/dist/visualizer/messaging/deserialization/video/depth.d.ts +2 -1
- package/dist/visualizer/messaging/deserialization/video/depth.d.ts.map +1 -1
- package/dist/visualizer/messaging/deserialization/video/depth.js +9 -65
- package/dist/visualizer/messaging/deserialization/video/depth.js.map +1 -1
- package/dist/visualizer/messaging/deserialization/video/h264.d.ts +3 -3
- package/dist/visualizer/messaging/deserialization/video/h264.d.ts.map +1 -1
- package/dist/visualizer/messaging/deserialization/video/h264.js +80 -25
- package/dist/visualizer/messaging/deserialization/video/h264.js.map +1 -1
- package/dist/visualizer/messaging/deserialization/video/h265.d.ts +1 -1
- package/dist/visualizer/messaging/deserialization/video/h265.d.ts.map +1 -1
- package/dist/visualizer/messaging/deserialization/video/h265.js +62 -22
- package/dist/visualizer/messaging/deserialization/video/h265.js.map +1 -1
- package/dist/visualizer/messaging/deserialization/video/mjpeg.d.ts +1 -0
- package/dist/visualizer/messaging/deserialization/video/mjpeg.d.ts.map +1 -1
- package/dist/visualizer/messaging/deserialization/video/mjpeg.js +14 -2
- package/dist/visualizer/messaging/deserialization/video/mjpeg.js.map +1 -1
- package/dist/visualizer/messaging/deserialization/video/rgbd-to-pointcloud.d.ts +3 -1
- package/dist/visualizer/messaging/deserialization/video/rgbd-to-pointcloud.d.ts.map +1 -1
- package/dist/visualizer/messaging/deserialization/video/rgbd-to-pointcloud.js +152 -250
- package/dist/visualizer/messaging/deserialization/video/rgbd-to-pointcloud.js.map +1 -1
- package/dist/visualizer/messaging/deserialization.worker.d.ts +5 -3
- package/dist/visualizer/messaging/deserialization.worker.d.ts.map +1 -1
- package/dist/visualizer/messaging/deserialization.worker.js +16 -2
- package/dist/visualizer/messaging/deserialization.worker.js.map +1 -1
- package/dist/visualizer/messaging/message-event.d.ts +3 -0
- package/dist/visualizer/messaging/message-event.d.ts.map +1 -1
- package/dist/visualizer/messaging/message-event.js +18 -4
- package/dist/visualizer/messaging/message-event.js.map +1 -1
- package/dist/visualizer/messaging/message-handler.d.ts +3 -1
- package/dist/visualizer/messaging/message-handler.d.ts.map +1 -1
- package/dist/visualizer/messaging/message-handler.js +120 -49
- package/dist/visualizer/messaging/message-handler.js.map +1 -1
- package/dist/visualizer/output.css +7 -0
- package/dist/visualizer/panels/PointCloudPanel.d.ts.map +1 -1
- package/dist/visualizer/panels/PointCloudPanel.js +2 -0
- package/dist/visualizer/panels/PointCloudPanel.js.map +1 -1
- package/dist/visualizer/panels/image/ImageCanvasRenderer.d.ts.map +1 -1
- package/dist/visualizer/panels/image/ImageCanvasRenderer.js +246 -69
- package/dist/visualizer/panels/image/ImageCanvasRenderer.js.map +1 -1
- package/dist/visualizer/panels/image/annotation-overlay.d.ts.map +1 -1
- package/dist/visualizer/panels/image/annotation-overlay.js +16 -2
- package/dist/visualizer/panels/image/annotation-overlay.js.map +1 -1
- package/dist/visualizer/panels/image/raw-image-decoder.d.ts.map +1 -1
- package/dist/visualizer/panels/image/raw-image-decoder.js +1 -0
- package/dist/visualizer/panels/image/raw-image-decoder.js.map +1 -1
- package/dist/visualizer/panels/image/webgl-image-renderer.d.ts +11 -2
- package/dist/visualizer/panels/image/webgl-image-renderer.d.ts.map +1 -1
- package/dist/visualizer/panels/image/webgl-image-renderer.js +236 -41
- package/dist/visualizer/panels/image/webgl-image-renderer.js.map +1 -1
- package/dist/visualizer/panels/image/webgl-segmentation-mask-renderer.d.ts +16 -0
- package/dist/visualizer/panels/image/webgl-segmentation-mask-renderer.d.ts.map +1 -0
- package/dist/visualizer/panels/image/webgl-segmentation-mask-renderer.js +184 -0
- package/dist/visualizer/panels/image/webgl-segmentation-mask-renderer.js.map +1 -0
- package/dist/visualizer/panels/image/worker-image-decoder.d.ts.map +1 -1
- package/dist/visualizer/panels/image/worker-image-decoder.js +8 -3
- package/dist/visualizer/panels/image/worker-image-decoder.js.map +1 -1
- package/dist/visualizer/panels/pointcloud/PointCloudCanvasRenderer.d.ts.map +1 -1
- package/dist/visualizer/panels/pointcloud/PointCloudCanvasRenderer.js +256 -73
- package/dist/visualizer/panels/pointcloud/PointCloudCanvasRenderer.js.map +1 -1
- package/dist/visualizer/panels/pointcloud/point-cloud-materials.d.ts +5 -0
- package/dist/visualizer/panels/pointcloud/point-cloud-materials.d.ts.map +1 -0
- package/dist/visualizer/panels/pointcloud/point-cloud-materials.js +123 -0
- package/dist/visualizer/panels/pointcloud/point-cloud-materials.js.map +1 -0
- package/dist/visualizer/protobuf.generated/NNData.d.ts +92 -0
- package/dist/visualizer/protobuf.generated/NNData.d.ts.map +1 -0
- package/dist/visualizer/protobuf.generated/NNData.js +620 -0
- package/dist/visualizer/protobuf.generated/NNData.js.map +1 -0
- package/dist/visualizer/utils/benchmark-metrics.d.ts +27 -0
- package/dist/visualizer/utils/benchmark-metrics.d.ts.map +1 -0
- package/dist/visualizer/utils/benchmark-metrics.js +139 -0
- package/dist/visualizer/utils/benchmark-metrics.js.map +1 -0
- package/dist/visualizer/utils/comlink-wrap.d.ts.map +1 -1
- package/dist/visualizer/utils/comlink-wrap.js +5 -0
- package/dist/visualizer/utils/comlink-wrap.js.map +1 -1
- package/dist/visualizer/utils/poitcloud-sync.d.ts.map +1 -1
- package/dist/visualizer/utils/poitcloud-sync.js +5 -2
- package/dist/visualizer/utils/poitcloud-sync.js.map +1 -1
- package/package.json +3 -4
- package/dist/visualizer/messaging/deserialization/pointcloud/i420ToRgbaToPoitcloud.worker.d.ts +0 -2
- package/dist/visualizer/messaging/deserialization/pointcloud/i420ToRgbaToPoitcloud.worker.d.ts.map +0 -1
- package/dist/visualizer/messaging/deserialization/pointcloud/i420ToRgbaToPoitcloud.worker.js +0 -45
- package/dist/visualizer/messaging/deserialization/pointcloud/i420ToRgbaToPoitcloud.worker.js.map +0 -1
- package/dist/visualizer/messaging/deserialization/video/depth-coloring.worker.d.ts +0 -5
- package/dist/visualizer/messaging/deserialization/video/depth-coloring.worker.d.ts.map +0 -1
- package/dist/visualizer/messaging/deserialization/video/depth-coloring.worker.js +0 -124
- package/dist/visualizer/messaging/deserialization/video/depth-coloring.worker.js.map +0 -1
|
@@ -6,7 +6,8 @@ import { useLiveMessageSubscriptionController, useMessagePipeline, } from "../..
|
|
|
6
6
|
import { useMessageSubscription } from "../../messaging/utils.js";
|
|
7
7
|
import { readCanvasColor, useThemeMode } from "../../utils/use-theme-mode.js";
|
|
8
8
|
import { DEFAULT_POINT_CLOUD_CAMERA_STATE } from "../pointcloud-camera.js";
|
|
9
|
-
import {
|
|
9
|
+
import { createPackedPointCloudAttribute, createPackedPointCloudMaterial, createPointCloudMaterial, } from "./point-cloud-materials.js";
|
|
10
|
+
import { getPointCloudRenderScale, hasPointCloudColor } from "./point-cloud-rendering.js";
|
|
10
11
|
const PANEL_STYLE = {
|
|
11
12
|
width: "100%",
|
|
12
13
|
height: "100%",
|
|
@@ -31,11 +32,11 @@ const DEFAULT_ORBIT_SPEED_RAD_PER_SECOND = 0.6;
|
|
|
31
32
|
const ORBIT_SPEED_STEP_MULTIPLIER = 1.25;
|
|
32
33
|
const MIN_ORBIT_SPEED_RAD_PER_SECOND = 0.05;
|
|
33
34
|
const MAX_ORBIT_SPEED_RAD_PER_SECOND = 6;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
const FAST_PATH_BOUNDS_REFRESH_INTERVAL = 30;
|
|
36
|
+
const HOVER_PICK_INTERVAL_MS = 100;
|
|
37
|
+
const MAX_HOVER_PICK_POINT_COUNT = 100_000;
|
|
38
|
+
const MAX_CLICK_PICK_POINT_COUNT = 500_000;
|
|
39
|
+
const MAX_FAST_PATH_BOUNDS_SAMPLES = 16_384;
|
|
39
40
|
function getTopicPointSettings(topic, initialConfig) {
|
|
40
41
|
return {
|
|
41
42
|
minValue: initialConfig.topics?.[topic]?.minValue ?? 0,
|
|
@@ -68,6 +69,9 @@ function isPointCloudMessage(message) {
|
|
|
68
69
|
Array.isArray(candidate.fields) &&
|
|
69
70
|
candidate.data instanceof Uint8Array);
|
|
70
71
|
}
|
|
72
|
+
function extractRenderablePointCloudFrame(event) {
|
|
73
|
+
return extractPointCloudMessage(event);
|
|
74
|
+
}
|
|
71
75
|
function extractPointCloudMessage(event) {
|
|
72
76
|
if (!POINT_CLOUD_RENDER_SCHEMAS.has(event.schemaName)) {
|
|
73
77
|
return undefined;
|
|
@@ -209,6 +213,7 @@ class StandalonePointCloudRenderer {
|
|
|
209
213
|
#resizeObserver;
|
|
210
214
|
#geometry;
|
|
211
215
|
#material;
|
|
216
|
+
#packedMaterial;
|
|
212
217
|
#points;
|
|
213
218
|
#cameraReticle = createCameraReticle();
|
|
214
219
|
#raycaster = new THREE.Raycaster();
|
|
@@ -222,6 +227,11 @@ class StandalonePointCloudRenderer {
|
|
|
222
227
|
#whiteColorsArray;
|
|
223
228
|
#positionsAttribute;
|
|
224
229
|
#colorsAttribute;
|
|
230
|
+
#packedPointDataAttribute;
|
|
231
|
+
#positionInterleavedBuffer;
|
|
232
|
+
#usesPackedPointData = false;
|
|
233
|
+
#boundsPointCount = 0;
|
|
234
|
+
#framesSinceBoundsRefresh = FAST_PATH_BOUNDS_REFRESH_INTERVAL;
|
|
225
235
|
#currentBackground = "";
|
|
226
236
|
#pointSize;
|
|
227
237
|
#pointSettings;
|
|
@@ -258,20 +268,8 @@ class StandalonePointCloudRenderer {
|
|
|
258
268
|
this.#controls.addEventListener("change", this.#requestRender);
|
|
259
269
|
this.#canvas.addEventListener("keydown", this.#handleKeyDown);
|
|
260
270
|
this.#geometry = new THREE.BufferGeometry();
|
|
261
|
-
this.#material =
|
|
262
|
-
|
|
263
|
-
fragmentShader: POINT_CLOUD_FRAGMENT_SHADER,
|
|
264
|
-
uniforms: {
|
|
265
|
-
uPointSize: { value: this.#pointSize },
|
|
266
|
-
uUseDepthColor: { value: false },
|
|
267
|
-
uMinDepth: { value: 0 },
|
|
268
|
-
uMaxDepth: { value: 1 },
|
|
269
|
-
},
|
|
270
|
-
depthTest: true,
|
|
271
|
-
depthWrite: true,
|
|
272
|
-
transparent: false,
|
|
273
|
-
toneMapped: false,
|
|
274
|
-
});
|
|
271
|
+
this.#material = createPointCloudMaterial(this.#pointSize, this.#pointSettings.minValue, this.#pointSettings.maxValue);
|
|
272
|
+
this.#packedMaterial = createPackedPointCloudMaterial(this.#pointSize, this.#pointSettings.minValue, this.#pointSettings.maxValue);
|
|
275
273
|
this.#points = new THREE.Points(this.#geometry, this.#material);
|
|
276
274
|
this.#scene.add(this.#points);
|
|
277
275
|
this.#scene.add(this.#cameraReticle);
|
|
@@ -298,6 +296,7 @@ class StandalonePointCloudRenderer {
|
|
|
298
296
|
this.#controls.dispose();
|
|
299
297
|
this.#geometry.dispose();
|
|
300
298
|
this.#material.dispose();
|
|
299
|
+
this.#packedMaterial.dispose();
|
|
301
300
|
this.#cameraReticle.geometry.dispose();
|
|
302
301
|
if (Array.isArray(this.#cameraReticle.material)) {
|
|
303
302
|
for (const material of this.#cameraReticle.material) {
|
|
@@ -322,6 +321,7 @@ class StandalonePointCloudRenderer {
|
|
|
322
321
|
this.#pointCloud = pointCloud;
|
|
323
322
|
const useDepthColor = !hasPointCloudColor(pointCloud.fields);
|
|
324
323
|
this.#material.uniforms.uUseDepthColor.value = useDepthColor;
|
|
324
|
+
this.#packedMaterial.uniforms.uUseDepthColor.value = useDepthColor;
|
|
325
325
|
this.#updateGeometry(pointCloud);
|
|
326
326
|
this.#updateDepthColorRange();
|
|
327
327
|
this.#updatePointsPose(pointCloud);
|
|
@@ -366,8 +366,10 @@ class StandalonePointCloudRenderer {
|
|
|
366
366
|
}
|
|
367
367
|
this.#requestRender();
|
|
368
368
|
}
|
|
369
|
-
pick(clientX, clientY) {
|
|
370
|
-
if (this.#pointCloud == undefined ||
|
|
369
|
+
pick(clientX, clientY, maxPointCount = Number.POSITIVE_INFINITY) {
|
|
370
|
+
if (this.#pointCloud == undefined ||
|
|
371
|
+
this.#geometry.drawRange.count === 0 ||
|
|
372
|
+
this.#pointCloud.pointCount > maxPointCount) {
|
|
371
373
|
return undefined;
|
|
372
374
|
}
|
|
373
375
|
const rect = this.#canvas.getBoundingClientRect();
|
|
@@ -376,6 +378,9 @@ class StandalonePointCloudRenderer {
|
|
|
376
378
|
}
|
|
377
379
|
const pointer = new THREE.Vector2(((clientX - rect.left) / rect.width) * 2 - 1, -((clientY - rect.top) / rect.height) * 2 + 1);
|
|
378
380
|
this.#raycaster.setFromCamera(pointer, this.#camera);
|
|
381
|
+
if (this.#usesPackedPointData) {
|
|
382
|
+
return this.#pickPackedPointCloud();
|
|
383
|
+
}
|
|
379
384
|
const hit = this.#raycaster.intersectObject(this.#points, false)[0];
|
|
380
385
|
if (!hit || hit.index == undefined) {
|
|
381
386
|
return undefined;
|
|
@@ -576,12 +581,118 @@ class StandalonePointCloudRenderer {
|
|
|
576
581
|
this.#cameraReticle.quaternion.copy(this.#points.quaternion);
|
|
577
582
|
this.#points.updateMatrixWorld(true);
|
|
578
583
|
}
|
|
579
|
-
#
|
|
580
|
-
this.#
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
+
#pickPackedPointCloud() {
|
|
585
|
+
const pointCloud = this.#pointCloud;
|
|
586
|
+
if (!pointCloud) {
|
|
587
|
+
return undefined;
|
|
588
|
+
}
|
|
589
|
+
const localRay = this.#raycaster.ray
|
|
590
|
+
.clone()
|
|
591
|
+
.applyMatrix4(new THREE.Matrix4().copy(this.#points.matrixWorld).invert());
|
|
592
|
+
const threshold = this.#raycaster.params.Points?.threshold ?? 1;
|
|
593
|
+
const averageScale = (this.#points.scale.x + this.#points.scale.y + this.#points.scale.z) / 3;
|
|
594
|
+
const localThresholdSq = (threshold / averageScale) ** 2;
|
|
595
|
+
const positions = new Float32Array(pointCloud.data.buffer, pointCloud.data.byteOffset, pointCloud.pointCount * 4);
|
|
596
|
+
const point = new THREE.Vector3();
|
|
597
|
+
const worldPoint = new THREE.Vector3();
|
|
598
|
+
let closestDistance = Number.POSITIVE_INFINITY;
|
|
599
|
+
let closestIndex;
|
|
600
|
+
for (let index = 0; index < pointCloud.pointCount; index += 1) {
|
|
601
|
+
const offset = index * 4;
|
|
602
|
+
point.set(positions[offset], positions[offset + 1], positions[offset + 2]);
|
|
603
|
+
if (!Number.isFinite(point.x) || !Number.isFinite(point.y) || !Number.isFinite(point.z)) {
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (localRay.distanceSqToPoint(point) > localThresholdSq) {
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
worldPoint.copy(point).applyMatrix4(this.#points.matrixWorld);
|
|
610
|
+
const distance = this.#raycaster.ray.origin.distanceTo(worldPoint);
|
|
611
|
+
if (distance < closestDistance) {
|
|
612
|
+
closestDistance = distance;
|
|
613
|
+
closestIndex = index;
|
|
614
|
+
}
|
|
584
615
|
}
|
|
616
|
+
if (closestIndex == undefined) {
|
|
617
|
+
return undefined;
|
|
618
|
+
}
|
|
619
|
+
return {
|
|
620
|
+
index: closestIndex,
|
|
621
|
+
offset: getHitOffset(pointCloud, closestIndex),
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
#refreshFastPathBounds(pointCount, positions, pointStride) {
|
|
625
|
+
const shouldRefresh = this.#geometry.boundingSphere == undefined ||
|
|
626
|
+
this.#boundsPointCount !== pointCount ||
|
|
627
|
+
this.#framesSinceBoundsRefresh >= FAST_PATH_BOUNDS_REFRESH_INTERVAL;
|
|
628
|
+
if (!shouldRefresh) {
|
|
629
|
+
this.#framesSinceBoundsRefresh += 1;
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
633
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
634
|
+
let minZ = Number.POSITIVE_INFINITY;
|
|
635
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
636
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
637
|
+
let maxZ = Number.NEGATIVE_INFINITY;
|
|
638
|
+
const sampleStride = Math.max(1, Math.ceil(pointCount / MAX_FAST_PATH_BOUNDS_SAMPLES));
|
|
639
|
+
// Bounds only drive camera fitting and coarse frustum culling. Sampling uniformly keeps
|
|
640
|
+
// their refresh cost bounded instead of periodically scanning millions of points on the
|
|
641
|
+
// main thread.
|
|
642
|
+
for (let index = 0; index < pointCount; index += sampleStride) {
|
|
643
|
+
const offset = index * pointStride;
|
|
644
|
+
const x = positions[offset];
|
|
645
|
+
const y = positions[offset + 1];
|
|
646
|
+
const z = positions[offset + 2];
|
|
647
|
+
if (x == undefined || y == undefined || z == undefined) {
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) {
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
if (x < minX)
|
|
654
|
+
minX = x;
|
|
655
|
+
if (y < minY)
|
|
656
|
+
minY = y;
|
|
657
|
+
if (z < minZ)
|
|
658
|
+
minZ = z;
|
|
659
|
+
if (x > maxX)
|
|
660
|
+
maxX = x;
|
|
661
|
+
if (y > maxY)
|
|
662
|
+
maxY = y;
|
|
663
|
+
if (z > maxZ)
|
|
664
|
+
maxZ = z;
|
|
665
|
+
}
|
|
666
|
+
if (minX === Number.POSITIVE_INFINITY) {
|
|
667
|
+
this.#geometry.boundingBox = new THREE.Box3();
|
|
668
|
+
this.#geometry.boundingSphere = new THREE.Sphere(new THREE.Vector3(), 0);
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
const boundingBox = new THREE.Box3(new THREE.Vector3(minX, minY, minZ), new THREE.Vector3(maxX, maxY, maxZ));
|
|
672
|
+
const padding = Math.max(1, boundingBox.getSize(new THREE.Vector3()).length() * 0.02);
|
|
673
|
+
boundingBox.expandByScalar(padding);
|
|
674
|
+
this.#geometry.boundingBox = boundingBox;
|
|
675
|
+
this.#geometry.boundingSphere = boundingBox.getBoundingSphere(new THREE.Sphere());
|
|
676
|
+
}
|
|
677
|
+
this.#boundsSphere.copy(this.#geometry.boundingSphere);
|
|
678
|
+
this.#boundsPointCount = pointCount;
|
|
679
|
+
this.#framesSinceBoundsRefresh = 0;
|
|
680
|
+
}
|
|
681
|
+
#disposeGeometryAttributes() {
|
|
682
|
+
// Three.js keys GPU buffers by attribute identity. Disposing before replacing an
|
|
683
|
+
// attribute ensures a resolution/layout change cannot strand the old GPU buffer.
|
|
684
|
+
this.#geometry.dispose();
|
|
685
|
+
for (const attributeName of Object.keys(this.#geometry.attributes)) {
|
|
686
|
+
this.#geometry.deleteAttribute(attributeName);
|
|
687
|
+
}
|
|
688
|
+
this.#geometry.boundingBox = null;
|
|
689
|
+
this.#geometry.boundingSphere = null;
|
|
690
|
+
this.#positionsAttribute = undefined;
|
|
691
|
+
this.#colorsAttribute = undefined;
|
|
692
|
+
this.#packedPointDataAttribute = undefined;
|
|
693
|
+
this.#positionInterleavedBuffer = undefined;
|
|
694
|
+
this.#boundsPointCount = 0;
|
|
695
|
+
this.#framesSinceBoundsRefresh = FAST_PATH_BOUNDS_REFRESH_INTERVAL;
|
|
585
696
|
}
|
|
586
697
|
#updateDepthColorRange() {
|
|
587
698
|
const minDepth = Number.isFinite(this.#pointSettings.minValue)
|
|
@@ -594,31 +705,37 @@ class StandalonePointCloudRenderer {
|
|
|
594
705
|
: typeof boundsMaxDepth === "number" && Number.isFinite(boundsMaxDepth)
|
|
595
706
|
? boundsMaxDepth
|
|
596
707
|
: minDepth + 1;
|
|
708
|
+
const normalizedMaxDepth = maxDepth > minDepth ? maxDepth : minDepth + 1;
|
|
597
709
|
this.#material.uniforms.uMinDepth.value = minDepth;
|
|
598
|
-
this.#material.uniforms.uMaxDepth.value =
|
|
710
|
+
this.#material.uniforms.uMaxDepth.value = normalizedMaxDepth;
|
|
711
|
+
this.#packedMaterial.uniforms.uMinDepth.value = minDepth;
|
|
712
|
+
this.#packedMaterial.uniforms.uMaxDepth.value = normalizedMaxDepth;
|
|
599
713
|
}
|
|
600
714
|
#applyFastPathGeometry(pointCloud, layout) {
|
|
601
|
-
if (this.#pointSettings.minValue > 0 || Number.isFinite(this.#pointSettings.maxValue)) {
|
|
602
|
-
return false;
|
|
603
|
-
}
|
|
604
715
|
if (isStandardXYZRGBALayout(pointCloud, layout)) {
|
|
605
716
|
const pointCount = pointCloud.pointCount;
|
|
606
717
|
const positionsView = new Float32Array(pointCloud.data.buffer, pointCloud.data.byteOffset, pointCount * 4);
|
|
607
|
-
const
|
|
608
|
-
const
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
718
|
+
const packedPointData = new Uint32Array(pointCloud.data.buffer, pointCloud.data.byteOffset, pointCount * 4);
|
|
719
|
+
const canReuseAttributes = this.#packedPointDataAttribute != undefined &&
|
|
720
|
+
this.#packedPointDataAttribute.array.byteLength === packedPointData.byteLength;
|
|
721
|
+
if (canReuseAttributes) {
|
|
722
|
+
this.#packedPointDataAttribute.array = packedPointData;
|
|
723
|
+
this.#packedPointDataAttribute.needsUpdate = true;
|
|
724
|
+
}
|
|
725
|
+
else {
|
|
726
|
+
this.#disposeGeometryAttributes();
|
|
727
|
+
this.#packedPointDataAttribute = createPackedPointCloudAttribute(packedPointData);
|
|
728
|
+
this.#geometry.setAttribute("aPointData", this.#packedPointDataAttribute);
|
|
729
|
+
}
|
|
730
|
+
this.#usesPackedPointData = true;
|
|
731
|
+
this.#points.material = this.#packedMaterial;
|
|
614
732
|
this.#geometry.setDrawRange(0, pointCount);
|
|
615
|
-
this.#
|
|
733
|
+
this.#refreshFastPathBounds(pointCount, positionsView, 4);
|
|
616
734
|
return true;
|
|
617
735
|
}
|
|
618
736
|
if (isStandardXYZLayout(pointCloud, layout)) {
|
|
619
737
|
const pointCount = pointCloud.pointCount;
|
|
620
738
|
const positionsView = new Float32Array(pointCloud.data.buffer, pointCloud.data.byteOffset, pointCount * 3);
|
|
621
|
-
const position = new THREE.BufferAttribute(positionsView, 3);
|
|
622
739
|
const colorLength = pointCount * 4;
|
|
623
740
|
const colors = this.#whiteColorsArray && this.#whiteColorsArray.length === colorLength
|
|
624
741
|
? this.#whiteColorsArray
|
|
@@ -627,13 +744,29 @@ class StandalonePointCloudRenderer {
|
|
|
627
744
|
colors.fill(255);
|
|
628
745
|
this.#whiteColorsArray = colors;
|
|
629
746
|
}
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
747
|
+
const canReuseAttributes = this.#positionsAttribute instanceof THREE.InterleavedBufferAttribute &&
|
|
748
|
+
this.#colorsAttribute instanceof THREE.Uint8BufferAttribute &&
|
|
749
|
+
this.#positionInterleavedBuffer != undefined &&
|
|
750
|
+
this.#positionInterleavedBuffer.stride === 3 &&
|
|
751
|
+
this.#positionInterleavedBuffer.array.byteLength === positionsView.byteLength &&
|
|
752
|
+
this.#colorsAttribute.array === colors;
|
|
753
|
+
if (canReuseAttributes) {
|
|
754
|
+
this.#positionInterleavedBuffer.array = positionsView;
|
|
755
|
+
this.#positionInterleavedBuffer.needsUpdate = true;
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
this.#disposeGeometryAttributes();
|
|
759
|
+
const positionBuffer = new THREE.InterleavedBuffer(positionsView, 3).setUsage(THREE.StreamDrawUsage);
|
|
760
|
+
this.#positionInterleavedBuffer = positionBuffer;
|
|
761
|
+
this.#positionsAttribute = new THREE.InterleavedBufferAttribute(positionBuffer, 3, 0, false);
|
|
762
|
+
this.#colorsAttribute = new THREE.Uint8BufferAttribute(colors, 4, true);
|
|
763
|
+
this.#geometry.setAttribute("position", this.#positionsAttribute);
|
|
764
|
+
this.#geometry.setAttribute("aColor", this.#colorsAttribute);
|
|
765
|
+
}
|
|
766
|
+
this.#usesPackedPointData = false;
|
|
767
|
+
this.#points.material = this.#material;
|
|
635
768
|
this.#geometry.setDrawRange(0, pointCount);
|
|
636
|
-
this.#
|
|
769
|
+
this.#refreshFastPathBounds(pointCount, positionsView, 3);
|
|
637
770
|
return true;
|
|
638
771
|
}
|
|
639
772
|
return false;
|
|
@@ -697,19 +830,25 @@ class StandalonePointCloudRenderer {
|
|
|
697
830
|
const position = this.#positionsAttribute instanceof THREE.BufferAttribute &&
|
|
698
831
|
this.#positionsAttribute.array === positions
|
|
699
832
|
? this.#positionsAttribute
|
|
700
|
-
: new THREE.BufferAttribute(positions, 3);
|
|
833
|
+
: new THREE.BufferAttribute(positions, 3).setUsage(THREE.StreamDrawUsage);
|
|
701
834
|
if (position === this.#positionsAttribute) {
|
|
702
835
|
position.needsUpdate = true;
|
|
703
836
|
}
|
|
704
837
|
const color = this.#colorsAttribute instanceof THREE.Uint8BufferAttribute &&
|
|
705
838
|
this.#colorsAttribute.array === colors
|
|
706
839
|
? this.#colorsAttribute
|
|
707
|
-
: new THREE.Uint8BufferAttribute(colors, 4, true);
|
|
840
|
+
: new THREE.Uint8BufferAttribute(colors, 4, true).setUsage(THREE.StreamDrawUsage);
|
|
708
841
|
if (color === this.#colorsAttribute) {
|
|
709
842
|
color.needsUpdate = true;
|
|
710
843
|
}
|
|
844
|
+
if (position !== this.#positionsAttribute || color !== this.#colorsAttribute) {
|
|
845
|
+
this.#disposeGeometryAttributes();
|
|
846
|
+
}
|
|
847
|
+
this.#positionInterleavedBuffer = undefined;
|
|
711
848
|
this.#positionsAttribute = position;
|
|
712
849
|
this.#colorsAttribute = color;
|
|
850
|
+
this.#usesPackedPointData = false;
|
|
851
|
+
this.#points.material = this.#material;
|
|
713
852
|
this.#geometry.setAttribute("position", position);
|
|
714
853
|
this.#geometry.setAttribute("aColor", color);
|
|
715
854
|
this.#geometry.setDrawRange(0, visiblePointCount);
|
|
@@ -725,19 +864,29 @@ class StandalonePointCloudRenderer {
|
|
|
725
864
|
this.#boundsSphere.copy(this.#geometry.boundingSphere);
|
|
726
865
|
}
|
|
727
866
|
}
|
|
867
|
+
function applyFrameToRenderer(renderer, frame, onRendered) {
|
|
868
|
+
renderer.applyPointCloud(frame);
|
|
869
|
+
onRendered?.();
|
|
870
|
+
return true;
|
|
871
|
+
}
|
|
728
872
|
export function PointCloudCanvasRenderer({ topic, initialConfig, frameRenderedEvent, handleOnClick, handleCursorPositionUpdate, registerRendered, unregisterRendered, customOverlay, }) {
|
|
729
873
|
const canvasRef = React.useRef(null);
|
|
730
874
|
const rendererRef = React.useRef(undefined);
|
|
731
875
|
const handledSeekTimeRef = React.useRef(undefined);
|
|
732
876
|
const hoverFrameRef = React.useRef(undefined);
|
|
877
|
+
const pointCloudApplyFrameRef = React.useRef(undefined);
|
|
878
|
+
const pendingPointCloudRef = React.useRef(undefined);
|
|
733
879
|
const latestPointerPositionRef = React.useRef(undefined);
|
|
880
|
+
const lastHoverPickAtRef = React.useRef(0);
|
|
734
881
|
const pointerInteractionRef = React.useRef(false);
|
|
735
882
|
const draggedDuringPointerRef = React.useRef(false);
|
|
736
|
-
const subscriptionIdRef = React.useRef(createSubscriptionId());
|
|
737
883
|
const liveMessageSubscriptions = useLiveMessageSubscriptionController();
|
|
738
|
-
const setSubscriptions = useMessagePipeline((ctx) => ctx.setSubscriptions);
|
|
739
884
|
const lastSeekTime = useMessagePipeline((ctx) => ctx.playerState.activeData?.lastSeekTime);
|
|
740
885
|
const themeMode = useThemeMode();
|
|
886
|
+
const themeModeRef = React.useRef(themeMode);
|
|
887
|
+
const frameRenderedEventRef = React.useRef(frameRenderedEvent);
|
|
888
|
+
themeModeRef.current = themeMode;
|
|
889
|
+
frameRenderedEventRef.current = frameRenderedEvent;
|
|
741
890
|
const cameraState = React.useMemo(() => getInitialCameraState(initialConfig.cameraState), [initialConfig.cameraState]);
|
|
742
891
|
const pointSettings = React.useMemo(() => getTopicPointSettings(topic, initialConfig), [initialConfig, topic]);
|
|
743
892
|
React.useEffect(() => {
|
|
@@ -745,29 +894,39 @@ export function PointCloudCanvasRenderer({ topic, initialConfig, frameRenderedEv
|
|
|
745
894
|
if (!canvas) {
|
|
746
895
|
return;
|
|
747
896
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
pointSettings,
|
|
751
|
-
|
|
752
|
-
|
|
897
|
+
let renderer;
|
|
898
|
+
try {
|
|
899
|
+
renderer = new StandalonePointCloudRenderer({ canvas, pointSettings, cameraState });
|
|
900
|
+
}
|
|
901
|
+
catch (error) {
|
|
902
|
+
console.error("Point-cloud renderer initialization failed.", error);
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
753
905
|
rendererRef.current = renderer;
|
|
906
|
+
renderer.setThemeMode(themeModeRef.current);
|
|
754
907
|
registerRendered?.(renderer);
|
|
908
|
+
const pendingFrame = pendingPointCloudRef.current;
|
|
909
|
+
if (pendingFrame) {
|
|
910
|
+
applyFrameToRenderer(renderer, pendingFrame, () => frameRenderedEventRef.current?.(0));
|
|
911
|
+
pendingPointCloudRef.current = undefined;
|
|
912
|
+
}
|
|
755
913
|
return () => {
|
|
756
914
|
if (hoverFrameRef.current != undefined) {
|
|
757
915
|
cancelAnimationFrame(hoverFrameRef.current);
|
|
758
916
|
hoverFrameRef.current = undefined;
|
|
759
917
|
}
|
|
918
|
+
if (pointCloudApplyFrameRef.current != undefined) {
|
|
919
|
+
cancelAnimationFrame(pointCloudApplyFrameRef.current);
|
|
920
|
+
pointCloudApplyFrameRef.current = undefined;
|
|
921
|
+
}
|
|
922
|
+
pendingPointCloudRef.current = undefined;
|
|
760
923
|
renderer.dispose();
|
|
761
|
-
rendererRef.current
|
|
924
|
+
if (rendererRef.current === renderer) {
|
|
925
|
+
rendererRef.current = undefined;
|
|
926
|
+
}
|
|
762
927
|
unregisterRendered?.();
|
|
763
928
|
};
|
|
764
929
|
}, [cameraState, pointSettings, registerRendered, unregisterRendered]);
|
|
765
|
-
React.useEffect(() => {
|
|
766
|
-
setSubscriptions(subscriptionIdRef.current, [{ topic, preloadType: "partial" }]);
|
|
767
|
-
return () => {
|
|
768
|
-
setSubscriptions(subscriptionIdRef.current, []);
|
|
769
|
-
};
|
|
770
|
-
}, [setSubscriptions, topic]);
|
|
771
930
|
React.useEffect(() => {
|
|
772
931
|
const renderer = rendererRef.current;
|
|
773
932
|
if (!renderer) {
|
|
@@ -784,25 +943,44 @@ export function PointCloudCanvasRenderer({ topic, initialConfig, frameRenderedEv
|
|
|
784
943
|
if (!renderer) {
|
|
785
944
|
return;
|
|
786
945
|
}
|
|
787
|
-
|
|
946
|
+
if (pointCloudApplyFrameRef.current != undefined) {
|
|
947
|
+
cancelAnimationFrame(pointCloudApplyFrameRef.current);
|
|
948
|
+
pointCloudApplyFrameRef.current = undefined;
|
|
949
|
+
}
|
|
950
|
+
pendingPointCloudRef.current = undefined;
|
|
951
|
+
const cachedMessages = liveMessageSubscriptions.getCachedMessages([topic]);
|
|
952
|
+
for (let index = cachedMessages.length - 1; index >= 0; index -= 1) {
|
|
953
|
+
const frame = extractRenderablePointCloudFrame(cachedMessages[index]);
|
|
954
|
+
if (frame && applyFrameToRenderer(renderer, frame)) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
renderer.clear();
|
|
788
959
|
}, [lastSeekTime, liveMessageSubscriptions, topic]);
|
|
789
960
|
useMessageSubscription({
|
|
790
961
|
topics: [topic],
|
|
791
962
|
onMessages: (messages, incomingLastSeekTime) => {
|
|
792
|
-
const renderer = rendererRef.current;
|
|
793
|
-
if (!renderer) {
|
|
794
|
-
return;
|
|
795
|
-
}
|
|
796
963
|
if (incomingLastSeekTime != undefined) {
|
|
797
964
|
handledSeekTimeRef.current = incomingLastSeekTime;
|
|
798
965
|
}
|
|
799
966
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
800
|
-
const
|
|
801
|
-
if (!
|
|
967
|
+
const frame = extractRenderablePointCloudFrame(messages[index]);
|
|
968
|
+
if (!frame) {
|
|
802
969
|
continue;
|
|
803
970
|
}
|
|
804
|
-
|
|
805
|
-
|
|
971
|
+
pendingPointCloudRef.current = frame;
|
|
972
|
+
if (pointCloudApplyFrameRef.current == undefined && rendererRef.current) {
|
|
973
|
+
pointCloudApplyFrameRef.current = requestAnimationFrame(() => {
|
|
974
|
+
pointCloudApplyFrameRef.current = undefined;
|
|
975
|
+
const pendingPointCloud = pendingPointCloudRef.current;
|
|
976
|
+
pendingPointCloudRef.current = undefined;
|
|
977
|
+
const activeRenderer = rendererRef.current;
|
|
978
|
+
if (!pendingPointCloud || !activeRenderer) {
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
applyFrameToRenderer(activeRenderer, pendingPointCloud, () => frameRenderedEventRef.current?.(0));
|
|
982
|
+
});
|
|
983
|
+
}
|
|
806
984
|
return;
|
|
807
985
|
}
|
|
808
986
|
},
|
|
@@ -817,7 +995,12 @@ export function PointCloudCanvasRenderer({ topic, initialConfig, frameRenderedEv
|
|
|
817
995
|
if (!renderer || !position || !handleCursorPositionUpdate) {
|
|
818
996
|
return;
|
|
819
997
|
}
|
|
820
|
-
const
|
|
998
|
+
const now = performance.now();
|
|
999
|
+
if (now - lastHoverPickAtRef.current < HOVER_PICK_INTERVAL_MS) {
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
lastHoverPickAtRef.current = now;
|
|
1003
|
+
const hit = renderer.pick(position.x, position.y, MAX_HOVER_PICK_POINT_COUNT);
|
|
821
1004
|
handleCursorPositionUpdate(position, hit?.offset);
|
|
822
1005
|
}, [handleCursorPositionUpdate]);
|
|
823
1006
|
const handleCanvasPointerMove = React.useCallback((event) => {
|
|
@@ -852,7 +1035,7 @@ export function PointCloudCanvasRenderer({ topic, initialConfig, frameRenderedEv
|
|
|
852
1035
|
handleOnClick(event, undefined);
|
|
853
1036
|
return;
|
|
854
1037
|
}
|
|
855
|
-
const hit = renderer.pick(event.clientX, event.clientY);
|
|
1038
|
+
const hit = renderer.pick(event.clientX, event.clientY, MAX_CLICK_PICK_POINT_COUNT);
|
|
856
1039
|
handleOnClick(event, hit?.offset);
|
|
857
1040
|
}, [handleOnClick]);
|
|
858
1041
|
const handleCanvasPointerDown = React.useCallback((event) => {
|