implement remaining keyboard shortcuts and improve line update interactions with other tools

This commit is contained in:
Yeicor
2024-03-01 20:20:25 +01:00
parent f1776d8436
commit e42ccd795e
3 changed files with 48 additions and 28 deletions

View File

@@ -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;
}

View File

@@ -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') {

View File

@@ -27,6 +27,7 @@ const SelectionComponent = defineAsyncComponent({
loadingComponent: () => "Loading...",
delay: 0,
});
let selectionComp = ref<InstanceType<typeof SelectionComponent> | 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();
});
</script>
<template>
<orientation-gizmo :scene="props.viewer.scene" v-if="props.viewer?.scene"/>
<orientation-gizmo :scene="props.viewer.scene" :elem="props.viewer?.elem" v-if="props.viewer?.scene"/>
<v-divider/>
<h5>Camera</h5>
<v-btn icon @click="toggleProjection"><span class="icon-detail">{{ toggleProjectionText }}</span>
<v-tooltip activator="parent">Toggle Projection<br/>(currently
<v-tooltip activator="parent">Toggle (P)rojection<br/>(currently
{{ toggleProjectionText === 'PERSP' ? 'perspective' : 'orthographic' }})
</v-tooltip>
<svg-icon type="mdi" :path="mdiProjector"></svg-icon>
</v-btn>
<v-btn icon @click="centerCamera">
<v-tooltip activator="parent">Recenter Camera</v-tooltip>
<v-tooltip activator="parent">Re(c)enter Camera</v-tooltip>
<svg-icon type="mdi" :path="mdiCrosshairsGps"/>
</v-btn>
<v-divider/>
<h5>Selection ({{ selectionFaceCount() }}F {{ selectionEdgeCount() }}E {{ selectionVertexCount() }}V)</h5>
<selection-component :viewer="props.viewer" v-model="selection" @findModel="(name) => emit('findModel', name)"/>
<selection-component :ref="selectionComp" :viewer="props.viewer" v-model="selection"
@findModel="(name) => emit('findModel', name)"/>
<v-divider/>
<v-spacer></v-spacer>
<h5>Extras</h5>
<v-btn icon @click="downloadSceneGlb">
<v-tooltip activator="parent">Download Scene</v-tooltip>
<v-tooltip activator="parent">(D)ownload Scene</v-tooltip>
<svg-icon type="mdi" :path="mdiDownload"/>
</v-btn>
<v-dialog id="licenses-dialog" fullscreen>
@@ -148,7 +162,7 @@ async function openGithub() {
</template>
</v-dialog>
<v-btn icon @click="openGithub">
<v-tooltip activator="parent">Open GitHub</v-tooltip>
<v-tooltip activator="parent">Open (G)itHub</v-tooltip>
<svg-icon type="mdi" :path="mdiGithub"/>
</v-btn>
<div ref="statsHolder"></div>