diff --git a/camera-config.json b/camera-config.json index a9ed5df..c126b03 100644 --- a/camera-config.json +++ b/camera-config.json @@ -1,20 +1,20 @@ { "camera": { "position": { - "x": 450, - "y": 154, - "z": -130 + "x": 514, + "y": 228, + "z": -403 }, "target": { "x": 50, "y": 85, "z": -140 }, - "distance": 406, + "distance": 552, "spherical": { - "radius": 406, - "theta": 1.55, - "phi": 1.4 + "radius": 552, + "theta": 2.09, + "phi": 1.31 } }, "description": "Default camera configuration for 3D model viewer. Position values can be copied from console logs and pasted here to set new defaults." diff --git a/src/index.js b/src/index.js index 371a6d7..1130426 100644 --- a/src/index.js +++ b/src/index.js @@ -120,88 +120,6 @@ async function init() { function setupCustomTouchControls() { const canvas = renderer.domElement; - let touchStartDistance = 0; - let lastTouchMoveTime = 0; - - // Variables for touch tracking - let touches = []; - let isZooming = false; - - // Helper function to get touch distance (for pinch zoom) - function getTouchDistance(touch1, touch2) { - const dx = touch1.clientX - touch2.clientX; - const dy = touch1.clientY - touch2.clientY; - return Math.sqrt(dx * dx + dy * dy); - } - - // Touch start handler - only handle two-finger gestures, let OrbitControls handle single finger - canvas.addEventListener('touchstart', function(event) { - touches = Array.from(event.touches); - - if (touches.length === 2) { - // Two finger pinch/zoom start - prevent default for custom handling - event.preventDefault(); - touchStartDistance = getTouchDistance(touches[0], touches[1]); - isZooming = true; - } else { - // Single finger - let OrbitControls handle it natively - isZooming = false; - } - - lastTouchMoveTime = Date.now(); - }, { passive: false }); - - // Touch move handler - only custom handle pinch zoom - canvas.addEventListener('touchmove', function(event) { - const currentTime = Date.now(); - const timeDelta = currentTime - lastTouchMoveTime; - - // Throttle touch move events for performance (max 60fps) - if (timeDelta < 16) return; - - touches = Array.from(event.touches); - - if (touches.length === 2 && isZooming) { - // Prevent default only for two-finger gestures - event.preventDefault(); - - // Handle pinch zoom - const currentDistance = getTouchDistance(touches[0], touches[1]); - const zoomDelta = (currentDistance - touchStartDistance) * 0.01; - - // Apply zoom with smooth scaling - const zoomFactor = Math.exp(-zoomDelta); - const newDistance = controls.object.position.distanceTo(controls.target) * zoomFactor; - - // Clamp zoom within limits - const clampedDistance = Math.max(controls.minDistance, - Math.min(controls.maxDistance, newDistance)); - - // Update camera position - const direction = controls.object.position.clone().sub(controls.target).normalize(); - controls.object.position.copy(controls.target).add(direction.multiplyScalar(clampedDistance)); - - touchStartDistance = currentDistance; - controls.update(); - } - // Single finger rotation handled natively by OrbitControls - - lastTouchMoveTime = currentTime; - }, { passive: false }); - - // Touch end handler - canvas.addEventListener('touchend', function(event) { - touches = Array.from(event.touches); - - if (touches.length < 2) { - isZooming = false; - } - }, { passive: false }); - - // Prevent context menu on long press - canvas.addEventListener('contextmenu', function(event) { - event.preventDefault(); - }, { passive: false }); // Add touch-friendly CSS for better interaction canvas.style.touchAction = 'none'; @@ -210,7 +128,12 @@ function setupCustomTouchControls() { canvas.style.mozUserSelect = 'none'; canvas.style.msUserSelect = 'none'; - console.log('Custom touch controls initialized - OrbitControls handles single finger rotation'); + // Prevent context menu on long press + canvas.addEventListener('contextmenu', function(event) { + event.preventDefault(); + }, { passive: false }); + + console.log('Touch controls enabled - OrbitControls handles all touch gestures natively'); } function setupLighting() { @@ -544,37 +467,49 @@ function centerModelAndControls(object) { } function setupRotationLogging() { - // Log rotation and zoom changes whenever the camera moves + let debounceTimer = null; + + // Log rotation and zoom changes with 1-second debounce controls.addEventListener('change', function() { - const distance = camera.position.distanceTo(controls.target); - const position = camera.position.clone(); - const target = controls.target.clone(); + // Clear existing timer + if (debounceTimer) { + clearTimeout(debounceTimer); + } - // Calculate spherical coordinates for rotation - const spherical = new THREE.Spherical().setFromVector3( - position.clone().sub(target) - ); - - console.log('=== CAMERA STATE ==='); - console.log('Position:', { - x: Math.round(position.x * 100) / 100, - y: Math.round(position.y * 100) / 100, - z: Math.round(position.z * 100) / 100 - }); - console.log('Target:', { - x: Math.round(target.x * 100) / 100, - y: Math.round(target.y * 100) / 100, - z: Math.round(target.z * 100) / 100 - }); - console.log('Distance (Zoom):', Math.round(distance * 100) / 100); - console.log('Spherical Rotation:'); - console.log(' - Radius:', Math.round(spherical.radius * 100) / 100); - console.log(' - Theta (horizontal):', Math.round(spherical.theta * 100) / 100, 'radians'); - console.log(' - Phi (vertical):', Math.round(spherical.phi * 100) / 100, 'radians'); - console.log('=================='); + // Set new timer for 1 second delay + debounceTimer = setTimeout(() => { + const distance = camera.position.distanceTo(controls.target); + const position = camera.position.clone(); + const target = controls.target.clone(); + + // Calculate spherical coordinates for rotation + const spherical = new THREE.Spherical().setFromVector3( + position.clone().sub(target) + ); + + console.log('=== CAMERA STATE ==='); + console.log('Position:', { + x: Math.round(position.x * 100) / 100, + y: Math.round(position.y * 100) / 100, + z: Math.round(position.z * 100) / 100 + }); + console.log('Target:', { + x: Math.round(target.x * 100) / 100, + y: Math.round(target.y * 100) / 100, + z: Math.round(target.z * 100) / 100 + }); + console.log('Distance (Zoom):', Math.round(distance * 100) / 100); + console.log('Spherical Rotation:'); + console.log(' - Radius:', Math.round(spherical.radius * 100) / 100); + console.log(' - Theta (horizontal):', Math.round(spherical.theta * 100) / 100, 'radians'); + console.log(' - Phi (vertical):', Math.round(spherical.phi * 100) / 100, 'radians'); + console.log('=================='); + + debounceTimer = null; + }, 1000); }); - console.log('Rotation logging enabled - camera state will be logged on every change'); + console.log('Rotation logging enabled - camera state will be logged 1 second after movement stops'); } function animate() { diff --git a/src/styles.css b/src/styles.css index da7d734..c1d081b 100644 --- a/src/styles.css +++ b/src/styles.css @@ -23,6 +23,46 @@ body { border-radius: 10px; -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); + max-width: 300px; +} + +/* Mobile and tablet responsive styles - covers both portrait and landscape */ +@media (max-width: 1024px) { + #info { + top: 5px; + left: 5px; + padding: 4px 6px; + font-size: 10px; + max-width: 140px; + border-radius: 4px; + } + + #info h2 { + font-size: 11px; + margin: 0 0 3px 0; + } + + #info p { + margin: 1px 0; + line-height: 1.1; + } + + #info hr { + margin: 3px 0 !important; + border: 0.5px solid #555; + } +} + +/* Very small screens (phones in portrait) */ +@media (max-width: 480px) { + #info { + max-width: 120px; + font-size: 9px; + } + + #info h2 { + font-size: 10px; + } } #loading {