UNPKG

cube-parameters

Version:

A sophisticated 3D model viewer built with React, TypeScript, and Three.js, featuring advanced visualization tools, measurement capabilities, and lighting controls.

173 lines (135 loc) 5.68 kB
import { useCallback, useEffect } from 'react'; import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; interface StandardViewsHook { viewTop: () => void; viewFront: () => void; viewBack: () => void; viewBottom: () => void; viewRight: () => void; viewLeft: () => void; viewIsometric: () => void; } export const useStandardViews = ( sceneRef: React.RefObject<THREE.Scene | null>, cameraRef: React.RefObject<THREE.Camera | null>, controlsRef: React.RefObject<OrbitControls | null>, rendererRef?: React.RefObject<THREE.WebGLRenderer | null> ): StandardViewsHook => { const animateToView = useCallback((targetPosition: THREE.Vector3, targetLookAt: THREE.Vector3) => { if (!cameraRef.current || !controlsRef.current) return; const startPosition = cameraRef.current.position.clone(); const startTarget = controlsRef.current.target.clone(); const startTime = Date.now(); const duration = 800; const animate = () => { const elapsed = Date.now() - startTime; const progress = Math.min(elapsed / duration, 1); const easedProgress = 1 - Math.pow(1 - progress, 3); cameraRef.current!.position.lerpVectors(startPosition, targetPosition, easedProgress); controlsRef.current!.target.lerpVectors(startTarget, targetLookAt, easedProgress); controlsRef.current!.update(); if (rendererRef?.current) { rendererRef.current.render(sceneRef.current!, cameraRef.current!); } if (progress < 1) { requestAnimationFrame(animate); } }; animate(); }, [sceneRef, cameraRef, controlsRef, rendererRef]); const getBoundingBox = useCallback(() => { if (!sceneRef.current) return new THREE.Box3(); const box = new THREE.Box3(); sceneRef.current.traverse((object) => { if (object instanceof THREE.Mesh && object.visible) { const objBox = new THREE.Box3().setFromObject(object); box.union(objBox); } }); if (box.isEmpty()) { return new THREE.Box3(new THREE.Vector3(-5, -5, -5), new THREE.Vector3(5, 5, 5)); } return box; }, [sceneRef]); const viewTop = useCallback(() => { const box = getBoundingBox(); const center = box.getCenter(new THREE.Vector3()); const size = box.getSize(new THREE.Vector3()); const maxDim = Math.max(size.x, size.z); const distance = maxDim * 1.5; const targetPosition = center.clone().add(new THREE.Vector3(0, distance, 0)); animateToView(targetPosition, center); }, [getBoundingBox, animateToView]); const viewFront = useCallback(() => { const box = getBoundingBox(); const center = box.getCenter(new THREE.Vector3()); const size = box.getSize(new THREE.Vector3()); const maxDim = Math.max(size.x, size.y); const distance = maxDim * 1.5; const targetPosition = center.clone().add(new THREE.Vector3(0, 0, distance)); animateToView(targetPosition, center); }, [getBoundingBox, animateToView]); const viewBack = useCallback(() => { const box = getBoundingBox(); const center = box.getCenter(new THREE.Vector3()); const size = box.getSize(new THREE.Vector3()); const maxDim = Math.max(size.x, size.y); const distance = maxDim * 1.5; const targetPosition = center.clone().add(new THREE.Vector3(0, 0, -distance)); animateToView(targetPosition, center); }, [getBoundingBox, animateToView]); const viewBottom = useCallback(() => { const box = getBoundingBox(); const center = box.getCenter(new THREE.Vector3()); const size = box.getSize(new THREE.Vector3()); const maxDim = Math.max(size.x, size.z); const distance = maxDim * 1.5; const targetPosition = center.clone().add(new THREE.Vector3(0, -distance, 0)); animateToView(targetPosition, center); }, [getBoundingBox, animateToView]); const viewRight = useCallback(() => { const box = getBoundingBox(); const center = box.getCenter(new THREE.Vector3()); const size = box.getSize(new THREE.Vector3()); const maxDim = Math.max(size.y, size.z); const distance = maxDim * 1.5; const targetPosition = center.clone().add(new THREE.Vector3(distance, 0, 0)); animateToView(targetPosition, center); }, [getBoundingBox, animateToView]); const viewLeft = useCallback(() => { const box = getBoundingBox(); const center = box.getCenter(new THREE.Vector3()); const size = box.getSize(new THREE.Vector3()); const maxDim = Math.max(size.y, size.z); const distance = maxDim * 1.5; const targetPosition = center.clone().add(new THREE.Vector3(-distance, 0, 0)); animateToView(targetPosition, center); }, [getBoundingBox, animateToView]); const viewIsometric = useCallback(() => { const box = getBoundingBox(); 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 distance = maxDim * 1.5; const targetPosition = center.clone().add(new THREE.Vector3(1, 1, 1).normalize().multiplyScalar(distance)); animateToView(targetPosition, center); }, [getBoundingBox, animateToView]); const standardViews = { viewTop, viewFront, viewBack, viewBottom, viewRight, viewLeft, viewIsometric }; // Expose standard views globally for external access useEffect(() => { (window as any).__standardViews = standardViews; return () => { delete (window as any).__standardViews; }; }, [standardViews]); return standardViews; };