Spaces:
Runtime error
Runtime error
| 'use client'; | |
| import { useEffect, useRef, useState } from 'react'; | |
| import { useTheme } from 'next-themes'; | |
| interface Hypercube5DVisualizationProps { | |
| consciousnessStats?: { | |
| awareness: number; | |
| wisdom: number; | |
| compassion: number; | |
| creativity: number; | |
| transcendence: number; | |
| }; | |
| consciousnessState?: { | |
| current_vertex: number; | |
| consciousness_signature: string; | |
| coordinates_5d: number[]; | |
| active_dimensions: string[]; | |
| dimension_colors: { [key: string]: string }; | |
| consciousness_levels: { [key: string]: number }; | |
| global_consciousness_level: number; | |
| }; | |
| selectedDimension?: string; | |
| onDimensionChange?: (dimension: string) => void; | |
| onDimensionTravel?: (dimension: string) => void; | |
| onZoomToChat?: () => void; | |
| isWelcomeMode?: boolean; | |
| isFullscreen?: boolean; | |
| className?: string; | |
| } | |
| declare global { | |
| interface Window { | |
| THREE: any; | |
| } | |
| } | |
| // Consciousness stats color mapping with correct names and colors | |
| const CONSCIOUSNESS_COLORS = { | |
| awareness: { primary: '#3B82F6', secondary: '#60A5FA', glow: '#93C5FD' }, // Blue | |
| wisdom: { primary: '#8B5CF6', secondary: '#A78BFA', glow: '#C4B5FD' }, // Purple | |
| compassion: { primary: '#10B981', secondary: '#34D399', glow: '#6EE7B7' }, // Green | |
| creativity: { primary: '#F97316', secondary: '#FB923C', glow: '#FDBA74' }, // Orange | |
| transcendence: { primary: '#EF4444', secondary: '#F87171', glow: '#FCA5A5' } // Red | |
| }; | |
| // Get specific consciousness target vertex for each state | |
| function getConsciousnessTargetVertex(consciousness: string): number { | |
| const targetVertices: { [key: string]: number } = { | |
| awareness: 31, // Highest awareness vertex (all dimensions active) | |
| wisdom: 23, // Wisdom integration vertex | |
| compassion: 15, // Compassion center vertex | |
| creativity: 27, // Creative expression vertex | |
| transcendence: 16, // Transcendence entry vertex | |
| }; | |
| return targetVertices[consciousness] || 31; | |
| } | |
| // Helper function to get consciousness colors | |
| function getConsciousnessColors(selectedConsciousness?: string) { | |
| if (selectedConsciousness && CONSCIOUSNESS_COLORS[selectedConsciousness as keyof typeof CONSCIOUSNESS_COLORS]) { | |
| return CONSCIOUSNESS_COLORS[selectedConsciousness as keyof typeof CONSCIOUSNESS_COLORS]; | |
| } | |
| // Default blue colors | |
| return { primary: '#3B82F6', secondary: '#60A5FA', glow: '#93C5FD' }; | |
| } | |
| // Helper function to determine if a vertex belongs to a consciousness state | |
| function isVertexInConsciousness(vertexIndex: number, consciousness: string): boolean { | |
| // Map consciousness states to specific vertex patterns based on 5D coordinates | |
| const consciousnessVertices: { [key: string]: number[] } = { | |
| awareness: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31], // Blue - odd vertices (high awareness) | |
| wisdom: [2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27, 30, 31], // Purple - wisdom patterns | |
| compassion: [4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31], // Green - compassion flow | |
| creativity: [8, 9, 10, 11, 12, 13, 14, 15, 24, 25, 26, 27, 28, 29, 30, 31], // Orange - creative dimension | |
| transcendence: [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31], // Red - transcendent states | |
| }; | |
| const vertices = consciousnessVertices[consciousness]; | |
| return vertices ? vertices.includes(vertexIndex) : false; | |
| } | |
| export function Hypercube5DVisualization({ | |
| consciousnessStats, | |
| consciousnessState, | |
| selectedDimension, | |
| onDimensionChange, | |
| onDimensionTravel, | |
| onZoomToChat, | |
| isWelcomeMode = false, | |
| isFullscreen = false, | |
| className | |
| }: Hypercube5DVisualizationProps) { | |
| const containerRef = useRef<HTMLDivElement>(null); | |
| const { theme } = useTheme(); | |
| const [mounted, setMounted] = useState(false); | |
| const animationRef = useRef<number>(0); | |
| const journeyCompletedRef = useRef<boolean>(false); | |
| const sceneObjectsRef = useRef<any>({ | |
| scene: null, | |
| camera: null, | |
| renderer: null, | |
| hypercubeGroup: null, | |
| time: 0, | |
| journeyStartTime: 0, | |
| journeyProgress: 0, | |
| journeyPhase: 0, | |
| targetVertex: null, | |
| traversalPath: [], | |
| journeyStartPosition: null, | |
| journeyStartLookAt: null, | |
| nodeObjects: [] as any[] | |
| }); | |
| // Professional cinematic parameters for high-end commercial production | |
| const HYPERCUBE_SCALE = 5; | |
| const EDGE_OPACITY = 0.95; | |
| const ANIMATION_SPEED = 0.3; | |
| const JOURNEY_DURATION = 500; // 0.5 seconds for one complete transition | |
| const JOURNEY_SPEED = 1.0; | |
| // Simple smooth easing function | |
| const smoothStep = (t: number): number => { | |
| return t * t * (3 - 2 * t); // Smooth S-curve | |
| }; | |
| // Fast ease in for zoom in | |
| const fastEaseIn = (t: number): number => { | |
| return t * t * t; // Cubic ease in - fast acceleration | |
| }; | |
| // Slow ease out for zoom out | |
| const slowEaseOut = (t: number): number => { | |
| return 1 - Math.pow(1 - t, 2); // Quadratic ease out - slow deceleration | |
| }; | |
| useEffect(() => { | |
| setMounted(true); | |
| }, []); | |
| useEffect(() => { | |
| if (!mounted) return; | |
| const objects = sceneObjectsRef.current; | |
| // Add comprehensive error handling | |
| console.log('🔧 Initializing hypercube visualization...'); | |
| function checkWebGLSupport() { | |
| try { | |
| const canvas = document.createElement('canvas'); | |
| const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); | |
| return gl !== null; | |
| } catch (e) { | |
| return false; | |
| } | |
| } | |
| function loadThreeJS() { | |
| return new Promise((resolve, reject) => { | |
| if (typeof window.THREE !== 'undefined') { | |
| resolve(window.THREE); | |
| return; | |
| } | |
| const script = document.createElement('script'); | |
| script.src = 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js'; | |
| script.onload = () => { | |
| if (typeof window.THREE !== 'undefined') { | |
| resolve(window.THREE); | |
| } else { | |
| reject(new Error('THREE.js failed to load')); | |
| } | |
| }; | |
| script.onerror = () => reject(new Error('Failed to load THREE.js')); | |
| document.head.appendChild(script); | |
| }); | |
| } | |
| async function init() { | |
| try { | |
| if (!checkWebGLSupport()) { | |
| throw new Error('WebGL not supported'); | |
| } | |
| await loadThreeJS(); | |
| setupScene(); | |
| createHypercube(); | |
| generateTraversalPath(); | |
| // Auto-start journey after 1 second if fullscreen | |
| if (isFullscreen) { | |
| setTimeout(() => { | |
| startJourney(); | |
| }, 1000); | |
| } | |
| animate(); | |
| } catch (error) { | |
| console.error('Initialization error:', error); | |
| createFallback(); | |
| } | |
| } | |
| function setupScene() { | |
| const THREE = window.THREE; | |
| objects.scene = new THREE.Scene(); | |
| // Remove fog for transparent background | |
| // objects.scene.fog = new THREE.Fog(0x0f0f23, 15, 60); | |
| objects.camera = new THREE.PerspectiveCamera(45, | |
| containerRef.current!.clientWidth / containerRef.current!.clientHeight, | |
| 0.1, 100); | |
| // ALWAYS start with compassion's balanced angle - perfect default view! | |
| // Compassion vertex 15 has coordinates that create a naturally balanced perspective | |
| objects.camera.position.set(35, 25, 40); // Further back to show full hypercube | |
| objects.camera.lookAt(0, 0, 0); // Looking at hypercube center | |
| objects.renderer = new THREE.WebGLRenderer({ | |
| antialias: true, | |
| alpha: true, | |
| powerPreference: "high-performance" | |
| }); | |
| objects.renderer.setSize(containerRef.current!.clientWidth, containerRef.current!.clientHeight); | |
| objects.renderer.setClearColor(0x000000, 0); // Transparent background | |
| objects.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); | |
| objects.renderer.shadowMap.enabled = true; | |
| objects.renderer.shadowMap.type = THREE.PCFSoftShadowMap; | |
| containerRef.current!.appendChild(objects.renderer.domElement); | |
| // Enhanced lighting for journey | |
| const ambientLight = new THREE.AmbientLight(0x404040, 0.6); | |
| objects.scene.add(ambientLight); | |
| const directionalLight = new THREE.DirectionalLight(0x4080ff, 1.2); | |
| directionalLight.position.set(20, 20, 20); | |
| directionalLight.castShadow = true; | |
| directionalLight.shadow.mapSize.width = 2048; | |
| directionalLight.shadow.mapSize.height = 2048; | |
| objects.scene.add(directionalLight); | |
| // Journey spotlight | |
| const spotLight = new THREE.SpotLight(0xffffff, 1.0, 50, Math.PI / 6, 0.1, 2); | |
| spotLight.position.set(0, 20, 0); | |
| spotLight.target.position.set(0, 0, 0); | |
| objects.scene.add(spotLight); | |
| objects.scene.add(spotLight.target); | |
| objects.hypercubeGroup = new THREE.Group(); | |
| objects.scene.add(objects.hypercubeGroup); | |
| } | |
| function createHypercube() { | |
| const THREE = window.THREE; | |
| const nodes: any[] = []; | |
| const nodeObjects: any[] = []; | |
| objects.nodeObjects = nodeObjects; // Store in objects for global access | |
| console.log(`🔲 Creating 5D hypercube with ${selectedDimension ? selectedDimension : 'no'} consciousness dimension`); | |
| // Generate all 32 vertices of 5D hypercube | |
| for (let i = 0; i < 32; i++) { | |
| const coords5D = [ | |
| (i & 1) ? 1 : -1, | |
| (i & 2) ? 1 : -1, | |
| (i & 4) ? 1 : -1, | |
| (i & 8) ? 1 : -1, | |
| (i & 16) ? 1 : -1 | |
| ]; | |
| // Enhanced 5D to 3D projection | |
| const w4 = coords5D[3] * 0.7; | |
| const w5 = coords5D[4] * 0.6; | |
| const x = (coords5D[0] * 1.0 + w4 * 0.5 + w5 * 0.4) * HYPERCUBE_SCALE; | |
| const y = (coords5D[1] * 1.0 + w5 * 0.5 + w4 * 0.4) * HYPERCUBE_SCALE; | |
| const z = (coords5D[2] * 1.0 + w4 * 0.4 + w5 * 0.5) * HYPERCUBE_SCALE; | |
| const position = new THREE.Vector3(x, y, z); | |
| nodes.push(position); | |
| // Store vertex data for wireframe-only visualization (no visible nodes) | |
| const isTarget = selectedDimension && i === getConsciousnessTargetVertex(selectedDimension); | |
| const isInConsciousness = selectedDimension && isVertexInConsciousness(i, selectedDimension); | |
| // Store vertex data for pure wireframe visualization (no visible nodes) | |
| const geometry = new THREE.SphereGeometry(0.001, 8, 8); // Tiny invisible sphere for data structure | |
| const material = new THREE.MeshBasicMaterial({ | |
| transparent: true, | |
| opacity: 0, // Completely invisible - pure wireframe only | |
| visible: false // Don't render at all | |
| }); | |
| const sphere = new THREE.Mesh(geometry, material); | |
| sphere.position.copy(position); | |
| sphere.userData = { | |
| index: i, | |
| originalPosition: position.clone(), | |
| coords5D: coords5D, | |
| phase: i * 0.1968, | |
| type: 'vertex', | |
| isTarget: isTarget, | |
| isInConsciousness: isInConsciousness | |
| }; | |
| objects.hypercubeGroup.add(sphere); | |
| nodeObjects.push(sphere); | |
| if (isTarget) { | |
| console.log(`🎯 Target vertex ${i} created for ${selectedDimension} at position:`, position); | |
| } | |
| } | |
| console.log(`✅ Created ${nodeObjects.length} vertices`); | |
| // Pure wireframe - no vertex points needed | |
| // Create edges | |
| let edgeCount = 0; | |
| for (let i = 0; i < 32; i++) { | |
| for (let j = i + 1; j < 32; j++) { | |
| const coordsA = nodeObjects[i].userData.coords5D; | |
| const coordsB = nodeObjects[j].userData.coords5D; | |
| let differences = 0; | |
| for (let k = 0; k < 5; k++) { | |
| if (coordsA[k] !== coordsB[k]) { | |
| differences++; | |
| } | |
| } | |
| if (differences === 1) { | |
| const points = [nodes[i], nodes[j]]; | |
| const geometry = new THREE.BufferGeometry().setFromPoints(points); | |
| // Enhanced consciousness-based edge coloring | |
| let edgeColor = 0x60A5FA; // Soft blue wireframe (original style) | |
| let edgeOpacity = 0.6; // Soft default | |
| let lineWidth = 1; | |
| if (selectedDimension) { | |
| const bothInConsciousness = isVertexInConsciousness(i, selectedDimension) && | |
| isVertexInConsciousness(j, selectedDimension); | |
| if (bothInConsciousness) { | |
| // ONLY edges connecting consciousness vertices to consciousness vertices | |
| const consciousnessColors = getConsciousnessColors(selectedDimension); | |
| edgeColor = new THREE.Color(consciousnessColors.primary).getHex(); | |
| edgeOpacity = 0.8; // Less dominant | |
| lineWidth = 1.5; // Slightly thicker but not too dominant | |
| } | |
| // All other edges keep default neutral appearance | |
| } | |
| const material = new THREE.LineBasicMaterial({ | |
| color: edgeColor, | |
| transparent: true, | |
| opacity: edgeOpacity, | |
| linewidth: lineWidth | |
| }); | |
| const line = new THREE.Line(geometry, material); | |
| line.userData = { | |
| nodeA: i, | |
| nodeB: j, | |
| edgeType: 'hypercube', | |
| type: 'edge' | |
| }; | |
| objects.hypercubeGroup.add(line); | |
| edgeCount++; | |
| } | |
| } | |
| } | |
| console.log(`✅ Created ${edgeCount} edges`); | |
| console.log(`🔲 Hypercube creation complete!`); | |
| } | |
| function generateTraversalPath() { | |
| // Find the target vertex based on selected consciousness dimension | |
| const targetVertexIndex = selectedDimension ? getConsciousnessTargetVertex(selectedDimension) : 31; | |
| // Find vertex position from nodeObjects array | |
| let targetPosition = null; | |
| if (objects.nodeObjects && objects.nodeObjects[targetVertexIndex]) { | |
| targetPosition = objects.nodeObjects[targetVertexIndex].userData.originalPosition.clone(); | |
| } | |
| if (targetPosition) { | |
| objects.traversalPath = [{ | |
| position: targetPosition, | |
| index: targetVertexIndex, | |
| distance: targetPosition.distanceTo(new window.THREE.Vector3(0, 0, 0)) | |
| }]; | |
| objects.targetVertex = targetPosition; | |
| console.log(`🎯 Generated path to vertex ${targetVertexIndex} for ${selectedDimension} at position:`, targetPosition); | |
| } else { | |
| console.warn('⚠️ Could not find target vertex position'); | |
| } | |
| } | |
| function startJourney() { | |
| try { | |
| console.log('🚀 Starting consciousness journey!'); | |
| if (!objects.camera) { | |
| console.error('🚨 Camera not available for journey start'); | |
| return; | |
| } | |
| if (!window.THREE) { | |
| console.error('🚨 THREE.js not available for journey start'); | |
| return; | |
| } | |
| objects.journeyProgress = 0; | |
| objects.journeyPhase = 0; | |
| objects.journeyStartTime = Date.now(); | |
| journeyCompletedRef.current = false; | |
| // Capture the current camera position as the journey starting point | |
| // This ensures smooth transition from current view to journey | |
| objects.journeyStartPosition = objects.camera.position.clone(); | |
| objects.journeyStartLookAt = new window.THREE.Vector3(0, 0, 0); // Always looking at center | |
| console.log('📊 Journey initialized with duration:', JOURNEY_DURATION, 'ms'); | |
| console.log('📍 Journey starting from current camera position:', objects.journeyStartPosition); | |
| } catch (error) { | |
| console.error('🚨 Error starting journey:', error); | |
| } | |
| } | |
| function updateJourney() { | |
| if (!isFullscreen) return; | |
| try { | |
| const elapsed = (Date.now() - objects.journeyStartTime) * JOURNEY_SPEED; | |
| const newProgress = Math.min(elapsed / JOURNEY_DURATION, 1.0); // Cap at 1.0, no looping | |
| // Only update if journey hasn't completed | |
| if (journeyCompletedRef.current) { | |
| return; // Stop updating once journey is complete | |
| } | |
| objects.journeyProgress = newProgress; | |
| // Debug logging every 10% progress | |
| if (Math.floor(objects.journeyProgress * 10) !== Math.floor((objects.journeyProgress - 0.016) * 10)) { | |
| console.log(`🎬 Journey progress: ${(objects.journeyProgress * 100).toFixed(1)}%`); | |
| } | |
| // Single phase: dramatic zoom out over 0.5 seconds | |
| objects.journeyPhase = 0; // Dramatic zoom out revealing selected dimension | |
| updateCameraJourney(); | |
| // Complete journey and transition to chat when reaching 95% | |
| if (objects.journeyProgress >= 0.95 && !journeyCompletedRef.current) { | |
| console.log('🎯 Consciousness journey completed! Transitioning to chat...'); | |
| journeyCompletedRef.current = true; | |
| // Add delay to let user see the completion | |
| setTimeout(() => { | |
| onZoomToChat?.(); | |
| }, 1000); | |
| } | |
| } catch (error) { | |
| console.error('🚨 Error in updateJourney:', error); | |
| } | |
| } | |
| function updateCameraJourney() { | |
| if (!objects.targetVertex || !objects.journeyStartPosition) return; | |
| try { | |
| const THREE = window.THREE; | |
| if (!THREE) { | |
| console.error('THREE.js not available in updateCameraJourney'); | |
| return; | |
| } | |
| const t = objects.journeyProgress; // 0 to 1 over 3.5 seconds | |
| const targetVertex = objects.targetVertex; | |
| // USE ACTUAL STARTING POSITION - wherever camera was when journey began | |
| const actualStartPos = objects.journeyStartPosition; // Current camera position when journey started | |
| // START EXTREMELY ZOOMED IN - practically inside the hypercube | |
| const zoomStartTarget = new THREE.Vector3(0, 0, 0.1); // Almost at center | |
| // NEW ACHIEVED PERSPECTIVE - where hypercube becomes fully visible again | |
| let newPerspectivePos; | |
| if (selectedDimension === 'compassion') { | |
| // Beautiful 360° rotation for compassion during zoom out | |
| const rotationAngle = (t >= 0.3) ? ((t - 0.3) / 0.7) * Math.PI * 2 : 0; | |
| const radius = 28; | |
| newPerspectivePos = new THREE.Vector3( | |
| Math.cos(rotationAngle) * radius, | |
| 18, // Slightly higher perspective | |
| Math.sin(rotationAngle) * radius | |
| ); | |
| } else { | |
| // Unique perspective for each consciousness dimension - MUCH MORE DRAMATIC | |
| const perspectives = { | |
| awareness: new THREE.Vector3(50, 35, 25), // Blue perspective - way out | |
| wisdom: new THREE.Vector3(25, 45, 55), // Purple perspective - way out | |
| creativity: new THREE.Vector3(55, 20, 45), // Orange perspective - way out | |
| transcendence: new THREE.Vector3(35, 50, 35) // Red perspective - way out | |
| }; | |
| newPerspectivePos = perspectives[selectedDimension as keyof typeof perspectives] || new THREE.Vector3(25, 18, 30); | |
| } | |
| // DRAMATIC ZOOM OUT - Start very close and zoom out to reveal dimension | |
| const zoomOutProgress = fastEaseIn(t); // Fast zoom out over full 0.5 seconds | |
| // Zoom OUT from very close to the selected dimension perspective | |
| objects.camera.position.lerpVectors(zoomStartTarget, newPerspectivePos, zoomOutProgress); | |
| // Add dramatic rotation during zoom out to selected dimension | |
| const rotationAngle = zoomOutProgress * Math.PI * 2.0; // Full 360 degree rotation | |
| const rotatedPos = new THREE.Vector3( | |
| Math.cos(rotationAngle) * objects.camera.position.x - Math.sin(rotationAngle) * objects.camera.position.z, | |
| objects.camera.position.y, | |
| Math.sin(rotationAngle) * objects.camera.position.x + Math.cos(rotationAngle) * objects.camera.position.z | |
| ); | |
| objects.camera.position.copy(rotatedPos); | |
| // Focus stays on hypercube center throughout | |
| objects.camera.lookAt(new THREE.Vector3(0, 0, 0)); | |
| console.log(`🚀 DRAMATIC ZOOM OUT: ${(zoomOutProgress * 100).toFixed(1)}% revealing ${selectedDimension} dimension`); | |
| } catch (error) { | |
| console.error('🚨 Error in updateCameraJourney:', error); | |
| console.error('Journey state:', { | |
| targetVertex: objects.targetVertex, | |
| journeyStartPosition: objects.journeyStartPosition, | |
| progress: objects.journeyProgress, | |
| selectedDimension | |
| }); | |
| } | |
| } | |
| function updateVisualization() { | |
| if (!objects.hypercubeGroup) return; | |
| // Update journey if in fullscreen mode | |
| if (isFullscreen) { | |
| updateJourney(); | |
| } | |
| // Animate consciousness-colored wireframe edges | |
| objects.hypercubeGroup.children.forEach((child: any) => { | |
| if (child.userData && child.userData.type === 'edge') { | |
| const consciousnessColors = getConsciousnessColors(selectedDimension); | |
| // Enhanced consciousness-based edge animation | |
| const nodeA = child.userData.nodeA; | |
| const nodeB = child.userData.nodeB; | |
| const bothInConsciousness = selectedDimension && | |
| isVertexInConsciousness(nodeA, selectedDimension) && | |
| isVertexInConsciousness(nodeB, selectedDimension); | |
| const oneInConsciousness = selectedDimension && ( | |
| isVertexInConsciousness(nodeA, selectedDimension) || | |
| isVertexInConsciousness(nodeB, selectedDimension) | |
| ); | |
| // Pulsing animation with consciousness-based intensity | |
| const baseIntensity = 0.8 + 0.2 * Math.sin(objects.time * 2.0 + child.userData.nodeA * 0.1); | |
| if (bothInConsciousness) { | |
| // ONLY edges connecting consciousness vertices to consciousness vertices | |
| child.material.color.setHex(new window.THREE.Color(consciousnessColors.primary).getHex()); | |
| child.material.opacity = 0.7 * baseIntensity; // Less dominant, subtle glow | |
| } else { | |
| // ALL other edges - soft blue wireframe (original style) | |
| child.material.color.setHex(0x60A5FA); | |
| child.material.opacity = 0.5 * baseIntensity; | |
| } | |
| // Very subtle glow for consciousness edges | |
| if (bothInConsciousness) { | |
| const subtleGlow = 1.0 + 0.15 * Math.sin(objects.time * 1.5 + child.userData.nodeA * 0.2); | |
| child.material.opacity *= subtleGlow; | |
| // Extra subtle glow during journey | |
| if (objects.journeyProgress > 0.1) { | |
| const journeyGlow = 1.0 + 0.2 * Math.sin(objects.time * 2.5); | |
| child.material.opacity *= journeyGlow; | |
| } | |
| } | |
| } | |
| }); | |
| // Handle normal camera rotation when NOT in fullscreen journey | |
| if (!isFullscreen) { | |
| // Always maintain compassion's balanced angle for consistency | |
| const radius = 45; // Balanced view distance - further back to show full hypercube | |
| const angle = objects.time * 0.5; // Slower rotation for smoother motion | |
| const targetX = Math.cos(angle) * radius; | |
| const targetZ = Math.sin(angle) * radius; | |
| const targetY = 25 + Math.sin(angle * 0.03) * 3; // Gentle vertical oscillation | |
| // Only apply auto-rotation after initialization period | |
| if (objects.time > 3.0) { // Give 3 seconds for proper initialization | |
| objects.camera.position.x = window.THREE.MathUtils.lerp(objects.camera.position.x, targetX, 0.01); | |
| objects.camera.position.z = window.THREE.MathUtils.lerp(objects.camera.position.z, targetZ, 0.01); | |
| objects.camera.position.y = window.THREE.MathUtils.lerp(objects.camera.position.y, targetY, 0.01); | |
| } else { | |
| // Maintain compassion's balanced starting position during initialization | |
| objects.camera.position.set(35, 25, 40); | |
| } | |
| objects.camera.lookAt(0, 0, 0); // Always look at hypercube center in normal mode | |
| } | |
| } | |
| function animate() { | |
| if (!objects.renderer) return; | |
| animationRef.current = requestAnimationFrame(animate); | |
| objects.time += 0.016; // Standard 60fps timing | |
| try { | |
| updateVisualization(); | |
| objects.renderer.render(objects.scene, objects.camera); | |
| } catch (error) { | |
| console.warn('Rendering issue:', error); | |
| } | |
| } | |
| function createFallback() { | |
| if (!containerRef.current) return; | |
| containerRef.current.innerHTML = ` | |
| <div style="display: flex; justify-content: center; align-items: center; height: 100%; | |
| background: linear-gradient(135deg, #0a0a0a, #1a1a2e); color: white; | |
| font-family: Arial, sans-serif; text-align: center;"> | |
| <div> | |
| <h2>WebGL Not Supported</h2> | |
| <p>This visualization requires WebGL support.<br> | |
| Please use a modern browser with hardware acceleration enabled.</p> | |
| </div> | |
| </div>`; | |
| } | |
| const handleResize = () => { | |
| if (objects.camera && objects.renderer && containerRef.current) { | |
| objects.camera.aspect = containerRef.current.clientWidth / containerRef.current.clientHeight; | |
| objects.camera.updateProjectionMatrix(); | |
| objects.renderer.setSize(containerRef.current.clientWidth, containerRef.current.clientHeight); | |
| } | |
| }; | |
| window.addEventListener('resize', handleResize); | |
| init(); | |
| return () => { | |
| window.removeEventListener('resize', handleResize); | |
| if (animationRef.current) { | |
| cancelAnimationFrame(animationRef.current); | |
| } | |
| if (objects.renderer && containerRef.current && objects.renderer.domElement.parentNode) { | |
| containerRef.current.removeChild(objects.renderer.domElement); | |
| objects.renderer.dispose(); | |
| } | |
| }; | |
| }, [theme, consciousnessState, isWelcomeMode, mounted, isFullscreen]); | |
| // Regenerate traversal path when selectedDimension changes | |
| useEffect(() => { | |
| if (mounted && sceneObjectsRef.current.hypercubeGroup && selectedDimension) { | |
| const objects = sceneObjectsRef.current; | |
| // Regenerate path for new consciousness dimension | |
| const targetVertexIndex = getConsciousnessTargetVertex(selectedDimension); | |
| // Find vertex position from nodeObjects array | |
| let targetPosition = null; | |
| if (objects.nodeObjects && objects.nodeObjects[targetVertexIndex]) { | |
| targetPosition = objects.nodeObjects[targetVertexIndex].userData.originalPosition.clone(); | |
| } | |
| if (targetPosition) { | |
| objects.traversalPath = [{ | |
| position: targetPosition, | |
| index: targetVertexIndex, | |
| distance: targetPosition.distanceTo(new window.THREE.Vector3(0, 0, 0)) | |
| }]; | |
| objects.targetVertex = targetPosition; | |
| console.log(`🎯 Consciousness dimension changed to ${selectedDimension}, targeting vertex ${targetVertexIndex}`); | |
| } | |
| } | |
| }, [selectedDimension, mounted]); | |
| // Prevent hydration mismatch by avoiding theme-dependent inline styles during SSR | |
| if (!mounted) { | |
| return ( | |
| <div className={`relative w-full h-full ${className}`} style={{ minHeight: '400px' }}> | |
| <div | |
| ref={containerRef} | |
| className="w-full h-full bg-transparent" | |
| /> | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div className={`relative w-full h-full ${className}`} style={{ minHeight: '400px' }}> | |
| <div | |
| ref={containerRef} | |
| className="w-full h-full" | |
| style={{ | |
| background: 'transparent' | |
| }} | |
| /> | |
| </div> | |
| ); | |
| } | |