Spaces:
Runtime error
Runtime error
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Enhanced 3D Solar System</title> | |
| <style> | |
| body { margin: 0; overflow: hidden; } | |
| #info { position: absolute; top: 10px; left: 10px; color: white; background: rgba(0,0,0,0.5); padding: 10px; font-family: Arial, sans-serif; border-radius: 5px; z-index: 100; } | |
| #panel { position: absolute; top: 10px; right: 10px; background: rgba(0,0,0,0.8); color: #fff; padding: 15px; border-radius: 10px; min-width: 220px; z-index: 200; display: none; } | |
| #panel h2 { margin-top: 0; } | |
| #closePanel { float: right; cursor: pointer; color: #fff; font-size: 18px; } | |
| #ai-chat { position: absolute; bottom: 10px; right: 10px; color: white; background: rgba(0,0,0,0.7); padding: 10px; font-family: Arial, sans-serif; border-radius: 5px; max-width: 300px; z-index: 100; } | |
| #loading { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; background: rgba(0,0,0,0.7); padding: 20px; border-radius: 10px; font-family: Arial, sans-serif; display: none; z-index: 1000; } | |
| button { background: #444; color: white; border: none; padding: 5px 10px; margin: 5px 0; cursor: pointer; border-radius: 3px; } | |
| button:hover { background: #666; } | |
| input { padding: 5px; margin: 5px 0; width: 100%; } | |
| #ai-response { margin-top: 10px; max-height: 200px; overflow-y: auto; } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="info">Click a planet to learn more!</div> | |
| <div id="panel"><span id="closePanel">×</span><h2 id="panel-title"></h2><div id="panel-content"></div></div> | |
| <div id="ai-chat"> | |
| <h3>Ask the AI about the Solar System</h3> | |
| <input id="ai-question" type="text" placeholder="Your question..."> | |
| <button id="ai-submit">Ask</button> | |
| <div id="ai-response"></div> | |
| </div> | |
| <div id="loading">Loading...</div> | |
| <script src="https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/three@0.152.2/examples/js/controls/OrbitControls.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/three@0.152.2/examples/js/renderers/CSS2DRenderer.js"></script> | |
| <script> | |
| // --- Three.js Solar System with enhancements --- | |
| const scene = new THREE.Scene(); | |
| scene.background = new THREE.Color(0x000010); | |
| const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); | |
| camera.position.set(0, 30, 70); | |
| const renderer = new THREE.WebGLRenderer({ antialias: true }); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| document.body.appendChild(renderer.domElement); | |
| // CSS2DRenderer for labels | |
| const labelRenderer = new THREE.CSS2DRenderer(); | |
| labelRenderer.setSize(window.innerWidth, window.innerHeight); | |
| labelRenderer.domElement.style.position = 'absolute'; | |
| labelRenderer.domElement.style.top = '0px'; | |
| labelRenderer.domElement.style.pointerEvents = 'none'; | |
| document.body.appendChild(labelRenderer.domElement); | |
| // Controls | |
| const controls = new THREE.OrbitControls(camera, renderer.domElement); | |
| // Lighting | |
| scene.add(new THREE.AmbientLight(0x404040)); | |
| const sunLight = new THREE.PointLight(0xffffaa, 2, 200); | |
| scene.add(sunLight); | |
| // Textures (remote URLs) | |
| const textures = { | |
| "Sun": "https://www.solarsystemscope.com/textures/download/2k_sun.jpg", | |
| "Mercury": "https://www.solarsystemscope.com/textures/download/2k_mercury.jpg", | |
| "Venus": "https://www.solarsystemscope.com/textures/download/2k_venus_surface.jpg", | |
| "Earth": "https://www.solarsystemscope.com/textures/download/2k_earth_daymap.jpg", | |
| "Moon": "https://www.solarsystemscope.com/textures/download/2k_moon.jpg", | |
| "Mars": "https://www.solarsystemscope.com/textures/download/2k_mars.jpg", | |
| "Jupiter": "https://www.solarsystemscope.com/textures/download/2k_jupiter.jpg", | |
| "Saturn": "https://www.solarsystemscope.com/textures/download/2k_saturn.jpg", | |
| "Uranus": "https://www.solarsystemscope.com/textures/download/2k_uranus.jpg", | |
| "Neptune": "https://www.solarsystemscope.com/textures/download/2k_neptune.jpg" | |
| }; | |
| // Planets and moons | |
| const planets = [ | |
| { name: "Mercury", radius: 0.8, distance: 10, speed: 0.04, moons: [] }, | |
| { name: "Venus", radius: 1.2, distance: 15, speed: 0.03, moons: [] }, | |
| { | |
| name: "Earth", radius: 1.2, distance: 20, speed: 0.02, | |
| moons: [{ name: "Moon", radius: 0.3, distance: 2, speed: 0.05 }] | |
| }, | |
| { name: "Mars", radius: 0.9, distance: 25, speed: 0.015, moons: [] }, | |
| { | |
| name: "Jupiter", radius: 2.5, distance: 35, speed: 0.01, | |
| moons: [{ name: "Europa", radius: 0.2, distance: 4, speed: 0.04 }] | |
| }, | |
| { | |
| name: "Saturn", radius: 2.2, distance: 45, speed: 0.008, rings: true, moons: [] | |
| }, | |
| { name: "Uranus", radius: 1.8, distance: 55, speed: 0.005, moons: [] }, | |
| { name: "Neptune", radius: 1.8, distance: 65, speed: 0.003, moons: [] } | |
| ]; | |
| // Asteroid belt | |
| for (let i = 0; i < 200; i++) { | |
| const beltGeometry = new THREE.SphereGeometry(0.1, 8, 8); | |
| const beltMaterial = new THREE.MeshPhongMaterial({ color: 0x888888 }); | |
| const beltMesh = new THREE.Mesh(beltGeometry, beltMaterial); | |
| const angle = Math.random() * Math.PI * 2; | |
| const distance = 28 + Math.random() * 4; | |
| beltMesh.position.x = Math.cos(angle) * distance; | |
| beltMesh.position.z = Math.sin(angle) * distance; | |
| beltMesh.position.y = (Math.random() - 0.5) * 0.5; | |
| scene.add(beltMesh); | |
| } | |
| // Sun | |
| const sunTexture = new THREE.TextureLoader().load(textures["Sun"]); | |
| const sunGeometry = new THREE.SphereGeometry(4, 64, 64); | |
| const sunMaterial = new THREE.MeshBasicMaterial({ map: sunTexture }); | |
| const sun = new THREE.Mesh(sunGeometry, sunMaterial); | |
| scene.add(sun); | |
| sunLight.position.copy(sun.position); | |
| addLabel(sun, "Sun"); | |
| // Create planets, moons, rings, orbits | |
| const planetObjects = []; | |
| const clickableObjects = []; | |
| planets.forEach((planet) => { | |
| const texture = textures[planet.name] ? new THREE.TextureLoader().load(textures[planet.name]) : null; | |
| const geometry = new THREE.SphereGeometry(planet.radius, 32, 32); | |
| const material = texture ? new THREE.MeshPhongMaterial({ map: texture }) : new THREE.MeshPhongMaterial({ color: 0xffffff }); | |
| const mesh = new THREE.Mesh(geometry, material); | |
| mesh.position.x = planet.distance; | |
| mesh.userData = { type: 'planet', name: planet.name }; | |
| scene.add(mesh); | |
| clickableObjects.push(mesh); | |
| addLabel(mesh, planet.name); | |
| // Saturn's rings | |
| if (planet.rings) { | |
| const ringGeometry = new THREE.RingGeometry(planet.radius * 1.5, planet.radius * 2.2, 64); | |
| const ringMaterial = new THREE.MeshPhongMaterial({ color: 0xe9e5c6, side: THREE.DoubleSide, transparent: true, opacity: 0.7 }); | |
| const ring = new THREE.Mesh(ringGeometry, ringMaterial); | |
| ring.rotation.x = Math.PI / 2; | |
| mesh.add(ring); | |
| } | |
| // Moons | |
| const planetMoons = []; | |
| if (planet.moons && planet.moons.length > 0) { | |
| planet.moons.forEach(moon => { | |
| const moonGeometry = new THREE.SphereGeometry(moon.radius, 16, 16); | |
| const moonMaterial = new THREE.MeshPhongMaterial({ color: 0xcccccc }); | |
| const moonMesh = new THREE.Mesh(moonGeometry, moonMaterial); | |
| moonMesh.position.x = moon.distance; | |
| moonMesh.userData = { type: 'moon', name: moon.name, parent: planet.name }; | |
| mesh.add(moonMesh); | |
| planetMoons.push({ mesh: moonMesh, ...moon }); | |
| addLabel(moonMesh, moon.name); | |
| clickableObjects.push(moonMesh); | |
| }); | |
| } | |
| // Orbit path | |
| const orbitGeometry = new THREE.BufferGeometry(); | |
| const points = []; | |
| const segments = 64; | |
| for (let i = 0; i <= segments; i++) { | |
| const theta = (i / segments) * Math.PI * 2; | |
| points.push(new THREE.Vector3( | |
| Math.cos(theta) * planet.distance, | |
| 0, | |
| Math.sin(theta) * planet.distance | |
| )); | |
| } | |
| orbitGeometry.setFromPoints(points); | |
| const orbitMaterial = new THREE.LineBasicMaterial({ color: 0xaaaaaa, transparent: true, opacity: 0.4 }); | |
| const orbit = new THREE.Line(orbitGeometry, orbitMaterial); | |
| scene.add(orbit); | |
| planetObjects.push({ mesh, ...planet, moons: planetMoons }); | |
| }); | |
| // Add labels | |
| function addLabel(object, name) { | |
| const div = document.createElement('div'); | |
| div.className = 'label'; | |
| div.textContent = name; | |
| div.style.color = '#fff'; | |
| div.style.fontSize = '14px'; | |
| div.style.textShadow = '0 0 5px #000'; | |
| const label = new THREE.CSS2DObject(div); | |
| label.position.set(0, object.geometry.parameters.radius + 0.6, 0); | |
| object.add(label); | |
| } | |
| // Raycaster for click events | |
| const raycaster = new THREE.Raycaster(); | |
| const mouse = new THREE.Vector2(); | |
| window.addEventListener('click', (event) => { | |
| mouse.x = (event.clientX / window.innerWidth) * 2 - 1; | |
| mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; | |
| raycaster.setFromCamera(mouse, camera); | |
| const intersects = raycaster.intersectObjects(clickableObjects, true); | |
| if (intersects.length > 0) { | |
| const obj = intersects[0].object; | |
| showPanel(obj.userData); | |
| } | |
| }); | |
| // Info panel logic | |
| function showPanel(data) { | |
| const panel = document.getElementById('panel'); | |
| const title = document.getElementById('panel-title'); | |
| const content = document.getElementById('panel-content'); | |
| panel.style.display = 'block'; | |
| title.textContent = data.name; | |
| content.innerHTML = "Loading info..."; | |
| // Example: fetch Wikipedia summary (or use your AI backend) | |
| fetch(`https://en.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(data.name)}`) | |
| .then(res => res.json()) | |
| .then(info => { | |
| content.innerHTML = info.extract_html || "No info found."; | |
| }) | |
| .catch(() => { | |
| content.innerHTML = "No info found."; | |
| }); | |
| } | |
| document.getElementById('closePanel').onclick = () => { | |
| document.getElementById('panel').style.display = 'none'; | |
| }; | |
| // Animation loop | |
| let angle = 0; | |
| function animate() { | |
| requestAnimationFrame(animate); | |
| controls.update(); | |
| angle += 0.005; | |
| planetObjects.forEach((planet) => { | |
| planet.mesh.position.x = Math.cos(angle * planet.speed) * planet.distance; | |
| planet.mesh.position.z = Math.sin(angle * planet.speed) * planet.distance; | |
| if (planet.moons) { | |
| planet.moons.forEach((moon) => { | |
| moon.mesh.position.x = Math.cos(angle * moon.speed) * moon.distance; | |
| moon.mesh.position.z = Math.sin(angle * moon.speed) * moon.distance; | |
| }); | |
| } | |
| }); | |
| renderer.render(scene, camera); | |
| labelRenderer.render(scene, camera); | |
| } | |
| animate(); | |
| // Responsive resize | |
| window.addEventListener('resize', () => { | |
| camera.aspect = window.innerWidth / window.innerHeight; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| labelRenderer.setSize(window.innerWidth, window.innerHeight); | |
| }); | |
| // --- AI Chat Integration --- | |
| document.getElementById('ai-submit').addEventListener('click', async () => { | |
| const question = document.getElementById('ai-question').value; | |
| const responseDiv = document.getElementById('ai-response'); | |
| const loadingDiv = document.getElementById('loading'); | |
| responseDiv.textContent = ""; | |
| loadingDiv.style.display = "block"; | |
| try { | |
| const resp = await fetch("/api/ask", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ question }) | |
| }); | |
| const data = await resp.json(); | |
| if (data.answer) { | |
| responseDiv.textContent = "AI: " + data.answer; | |
| } else { | |
| responseDiv.textContent = "Error: " + (data.error || "Unknown error."); | |
| } | |
| } catch (error) { | |
| responseDiv.textContent = "Error: Could not connect to AI."; | |
| } | |
| loadingDiv.style.display = "none"; | |
| }); | |
| </script> | |
| </body> | |
| </html> | |