diff --git a/TODO.md b/TODO.md index 48e129217..58d584db2 100644 --- a/TODO.md +++ b/TODO.md @@ -43,7 +43,7 @@ A step-by-step checklist for porting MatterControl's design features to a Vue + ### Camera Controls - [x] Install and configure OrbitControls (zoom, pan, orbit) -- [ ] Add "fit to selection" camera function +- [x] Add "fit to selection" camera function - [ ] Add "reset view" function (home position) - [ ] Add view presets (front, back, top, bottom, left, right) - [ ] Add orthographic/perspective toggle diff --git a/src/components/Viewport.vue b/src/components/Viewport.vue index bb2c09bda..93ff2cc37 100644 --- a/src/components/Viewport.vue +++ b/src/components/Viewport.vue @@ -140,6 +140,26 @@ function animate() { renderer.render(scene, camera) } +function fitToSelection(objects: THREE.Object3D[]) { + if (objects.length === 0) return + + const box = new THREE.Box3() + for (const obj of objects) { + box.expandByObject(obj) + } + + const center = box.getCenter(new THREE.Vector3()) + const size = box.getSize(new THREE.Vector3()) + const maxDim = Math.max(size.x, size.y, size.z) + const fov = camera.fov * (Math.PI / 180) + const distance = maxDim / (2 * Math.tan(fov / 2)) * 1.5 + + const direction = camera.position.clone().sub(controls.target).normalize() + camera.position.copy(center).add(direction.multiplyScalar(distance)) + controls.target.copy(center) + controls.update() +} + function cleanup() { if (animationFrameId) { cancelAnimationFrame(animationFrameId) @@ -166,6 +186,10 @@ onMounted(() => { onBeforeUnmount(() => { cleanup() }) + +defineExpose({ + fitToSelection, +})