mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-21 23:14:27 +01:00
implement remaining keyboard shortcuts and improve line update interactions with other tools
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user