From e42ccd795e68f35773301745b0046b5f6d1ebd73 Mon Sep 17 00:00:00 2001 From: Yeicor <4929005+Yeicor@users.noreply.github.com> Date: Fri, 1 Mar 2024 20:20:25 +0100 Subject: [PATCH] implement remaining keyboard shortcuts and improve line update interactions with other tools --- src/tools/OrientationGizmo.vue | 5 +++- src/tools/Selection.vue | 43 ++++++++++++++++++---------------- src/tools/Tools.vue | 28 ++++++++++++++++------ 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/tools/OrientationGizmo.vue b/src/tools/OrientationGizmo.vue index 70beb9e..b6f4889 100644 --- a/src/tools/OrientationGizmo.vue +++ b/src/tools/OrientationGizmo.vue @@ -2,6 +2,7 @@ import {onMounted, onUpdated, ref} from "vue"; import type {ModelScene} from "@google/model-viewer/lib/three-components/ModelScene"; import * as OrientationGizmoRaw from "three-orientation-gizmo/src/OrientationGizmo"; +import type {ModelViewerElement} from '@google/model-viewer'; // Optimized minimal dependencies from three import {Vector3} from "three/src/math/Vector3.js"; @@ -9,7 +10,7 @@ import {Matrix4} from "three/src/math/Matrix4.js"; globalThis.THREE = {Vector3, Matrix4} as any // HACK: Required for the gizmo to work -const props = defineProps<{ scene: ModelScene }>(); +const props = defineProps<{ elem: ModelViewerElement | null, scene: ModelScene }>(); function createGizmo(expectedParent: HTMLElement, scene: ModelScene): HTMLElement { // noinspection SpellCheckingInspection @@ -43,6 +44,8 @@ function createGizmo(expectedParent: HTMLElement, scene: ModelScene): HTMLElemen (scene as any).__perspectiveCamera.lookAt(lookAt); } scene.queueRender(); + requestIdleCallback(() => props.elem?.dispatchEvent( + new CustomEvent('camera-change', {detail: {source: 'none'}}))) } return gizmo; } diff --git a/src/tools/Selection.vue b/src/tools/Selection.vue index 6e8f92e..e151168 100644 --- a/src/tools/Selection.vue +++ b/src/tools/Selection.vue @@ -185,6 +185,26 @@ function toggleShowBoundingBox() { let viewerFound = false let firstLoad = true; +let cameraChangeWaiting = false; +let cameraChangeLast = 0 +let onCameraChange = () => { + // Avoid updates while dragging (slow operation) + cameraChangeLast = performance.now(); + if (cameraChangeWaiting) return; + cameraChangeWaiting = true; + let waitingHandler: () => void; + waitingHandler = () => { + // Ignore also inertia + if (performance.now() - cameraChangeLast > 250) { + updateBoundingBox(); + cameraChangeWaiting = false; + } else { + // If the camera is still moving, wait a bit more + setTimeout(waitingHandler, 100); + } + }; + setTimeout(waitingHandler, 100); // Wait for the camera to stop moving +}; watch(() => props.viewer, (viewer) => { if (!viewer) return; if (viewerFound) return; @@ -204,26 +224,7 @@ watch(() => props.viewer, (viewer) => { updateBoundingBox(); } }); - let isWaiting = false; - let lastCameraChange = 0 - elem.addEventListener('camera-change', () => { - // Avoid updates while dragging (slow operation) - lastCameraChange = performance.now(); - if (isWaiting) return; - isWaiting = true; - let waitingHandler: () => void; - waitingHandler = () => { - // Ignore also inertia - if (performance.now() - lastCameraChange > 250) { - updateBoundingBox(); - isWaiting = false; - } else { - // If the camera is still moving, wait a bit more - setTimeout(waitingHandler, 100); - } - }; - setTimeout(waitingHandler, 100); // Wait for the camera to stop moving - }); + elem.addEventListener('camera-change', onCameraChange); }); }); @@ -360,6 +361,8 @@ function updateDistances() { return; } +defineExpose({onCameraChange}) + // Add keyboard shortcuts window.addEventListener('keydown', (event) => { if (event.key === 's') { diff --git a/src/tools/Tools.vue b/src/tools/Tools.vue index 22b6f2c..4768619 100644 --- a/src/tools/Tools.vue +++ b/src/tools/Tools.vue @@ -27,6 +27,7 @@ const SelectionComponent = defineAsyncComponent({ loadingComponent: () => "Loading...", delay: 0, }); +let selectionComp = ref | null>(null); const LicensesDialogContent = defineAsyncComponent({ loader: () => import("./LicensesDialogContent.vue"), @@ -69,13 +70,16 @@ function toggleProjection() { if (wasPerspectiveCamera) { (scene as any).__perspectiveCamera = prevCam; // Save the default perspective camera // This hack also needs to sync the camera position and target - requestAnimationFrame(() => syncOrthoCamera(true)); + syncOrthoCamera(true); } else { // Restore the default perspective camera scene.camera = (scene as any).__perspectiveCamera; props.viewer.scene.queueRender() // Force rerender of model-viewer } toggleProjectionText.value = wasPerspectiveCamera ? 'ORTHO' : 'PERSP'; + // The camera change may take a few frames to take effect, dispatch the event after a delay + requestIdleCallback(() => props.viewer?.elem?.dispatchEvent( + new CustomEvent('camera-change', {detail: {source: 'none'}}))) } async function centerCamera() { @@ -98,30 +102,40 @@ async function downloadSceneGlb() { async function openGithub() { window.open('https://github.com/yeicor-3d/yet-another-cad-viewer', '_blank') } + + +// Add keyboard shortcuts +window.addEventListener('keydown', (event) => { + if (event.key === 'p') toggleProjection(); + else if (event.key === 'c') centerCamera(); + else if (event.key === 'd') downloadSceneGlb(); + else if (event.key === 'g') openGithub(); +}); - Open GitHub + Open (G)itHub