Spaces:
Runtime error
Runtime error
| from flask import Flask, render_template_string | |
| app = Flask(__name__) | |
| html_content = """ | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>AstroLearn</title> | |
| <style> | |
| body { margin:0; overflow:hidden; background:black; font-family:Arial,sans-serif; } | |
| canvas { display:block; } | |
| /* Header Branding */ | |
| #header { | |
| position:absolute; | |
| top:0; | |
| left:50%; | |
| transform:translateX(-50%); | |
| color:white; | |
| font-size:32px; | |
| font-weight:bold; | |
| padding:10px; | |
| z-index:10; | |
| pointer-events:none; | |
| } | |
| /* Info Box */ | |
| #infoBox { | |
| position:absolute; | |
| top:60px; | |
| left:20px; | |
| padding:15px; | |
| background: rgba(0,0,0,0.7); | |
| color:white; | |
| border-radius:8px; | |
| max-width:350px; | |
| line-height:1.5; | |
| pointer-events:none; | |
| transition:all 0.3s ease; | |
| } | |
| /* Popup */ | |
| .popup { | |
| position:absolute; | |
| top:50%; | |
| left:50%; | |
| transform:translate(-50%,-50%); | |
| background: rgba(0,0,0,0.9); | |
| color:white; | |
| padding:20px; | |
| border-radius:10px; | |
| max-width:400px; | |
| display:none; | |
| z-index:10; | |
| text-align:center; | |
| } | |
| .popup img { | |
| width:100%; | |
| border-radius:8px; | |
| margin-bottom:10px; | |
| } | |
| .popup button { | |
| margin-top:10px; | |
| padding:5px 15px; | |
| border:none; | |
| border-radius:5px; | |
| background:#2196F3; | |
| color:white; | |
| cursor:pointer; | |
| } | |
| /* Footer Branding */ | |
| #footer { | |
| position:absolute; | |
| bottom:10px; | |
| right:10px; | |
| color:#888; | |
| font-size:14px; | |
| pointer-events:none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="header">AstroLearn</div> | |
| <div id="infoBox">Hover over a planet to learn more!</div> | |
| <div id="popup" class="popup"> | |
| <h2 id="popupTitle"></h2> | |
| <img id="popupImg" src="" alt=""> | |
| <p id="popupDesc"></p> | |
| <button onclick="closePopup()">Close</button> | |
| </div> | |
| <div id="footer">Powered by AstroLearn</div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script> | |
| <script> | |
| // === Scene Setup === | |
| const scene = new THREE.Scene(); | |
| const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 5000); | |
| const renderer = new THREE.WebGLRenderer({ antialias:true }); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| document.body.appendChild(renderer.domElement); | |
| const controls = new THREE.OrbitControls(camera, renderer.domElement); | |
| controls.enableDamping = true; controls.dampingFactor = 0.1; | |
| const light = new THREE.PointLight(0xffffff,2); light.position.set(0,0,0); | |
| scene.add(light); scene.add(new THREE.AmbientLight(0x404040,0.3)); | |
| // === Planets Data === | |
| const planets = [ | |
| { name:"Mercury", radius:0.38, distance:10, color:0xb2b2b2, info:"Mercury: Diameter ~4,879 km, distance from Sun ~57.9M km. Very thin atmosphere.", image:"https://upload.wikimedia.org/wikipedia/commons/4/4a/Mercury_in_true_color.jpg" }, | |
| { name:"Venus", radius:0.95, distance:15, color:0xeeddaa, clouds:"https://threejs.org/examples/textures/earth_clouds_1024.png", info:"Venus: Diameter ~12,104 km, distance ~108M km. Dense CO2 atmosphere, scorching ~465°C.", image:"https://upload.wikimedia.org/wikipedia/commons/e/e5/Venus-real_color.jpg" }, | |
| { name:"Earth", radius:1, distance:20, color:0x2266ff, clouds:"https://threejs.org/examples/textures/earth_clouds_1024.png", info:"Earth: Diameter ~12,742 km, distance 149.6M km. Only planet known to support life.", image:"https://upload.wikimedia.org/wikipedia/commons/9/97/The_Earth_seen_from_Apollo_17.jpg" }, | |
| { name:"Mars", radius:0.53, distance:28, color:0xff5533, info:"Mars: Diameter ~6,779 km, distance 227.9M km. Known for Olympus Mons.", image:"https://mars.nasa.gov/system/news_items/main_images/9323_PIA25679-FigureA-web.jpg" }, | |
| { name:"Jupiter", radius:11.2, distance:40, color:0xffaa55, info:"Jupiter: Diameter ~139,820 km, distance 778M km. Gas giant, Great Red Spot, 79 moons.", image:"https://upload.wikimedia.org/wikipedia/commons/e/e2/Jupiter.jpg" }, | |
| { name:"Saturn", radius:9.45, distance:55, color:0xffddaa, info:"Saturn: Diameter ~116,460 km, distance 1.43B km. Famous for rings.", image:"https://upload.wikimedia.org/wikipedia/commons/c/c7/Saturn_during_Equinox.jpg" }, | |
| { name:"Uranus", radius:4, distance:65, color:0x66ccff, info:"Uranus: Diameter ~50,724 km, distance 2.87B km. Rotates on its side.", image:"https://upload.wikimedia.org/wikipedia/commons/3/3d/Uranus2.jpg" }, | |
| { name:"Neptune", radius:3.88, distance:75, color:0x3366ff, info:"Neptune: Diameter ~49,244 km, distance 4.5B km. Strongest winds ~2,100 km/h.", image:"https://upload.wikimedia.org/wikipedia/commons/5/56/Neptune_Full.jpg" } | |
| ]; | |
| const planetMeshes = [], cloudMeshes = []; | |
| planets.forEach(p=>{ | |
| const geo = new THREE.SphereGeometry(p.radius,64,64); | |
| const mat = new THREE.MeshStandardMaterial({ color:p.color }); | |
| const mesh = new THREE.Mesh(geo,mat); | |
| mesh.position.x = p.distance; mesh.userData=p; scene.add(mesh); planetMeshes.push(mesh); | |
| if(p.clouds){ | |
| const cloudGeo = new THREE.SphereGeometry(p.radius+0.03,64,64); | |
| const cloudMat = new THREE.MeshPhongMaterial({ map:new THREE.TextureLoader().load(p.clouds), transparent:true, opacity:0.4 }); | |
| const cloudMesh = new THREE.Mesh(cloudGeo,cloudMat); | |
| cloudMesh.position.copy(mesh.position); scene.add(cloudMesh); cloudMeshes.push(cloudMesh); | |
| } | |
| const ringGeo = new THREE.RingGeometry(p.distance-0.02,p.distance+0.02,128); | |
| const ringMat = new THREE.MeshBasicMaterial({color:0x888888, side:THREE.DoubleSide, transparent:true, opacity:0.2}); | |
| const ring = new THREE.Mesh(ringGeo, ringMat); ring.rotation.x=Math.PI/2; scene.add(ring); | |
| }); | |
| // Starfield | |
| const starGeo = new THREE.BufferGeometry(); const starCount=4000; const pos=[]; | |
| for(let i=0;i<starCount;i++){ pos.push((Math.random()-0.5)*2000); pos.push((Math.random()-0.5)*2000); pos.push((Math.random()-0.5)*2000); } | |
| starGeo.setAttribute('position', new THREE.Float32BufferAttribute(pos,3)); | |
| scene.add(new THREE.Points(starGeo,new THREE.PointsMaterial({color:0xffffff,size:0.5}))); | |
| camera.position.set(0,10,120); | |
| // Interaction | |
| const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); | |
| const infoBox = document.getElementById('infoBox'); const popup = document.getElementById('popup'); | |
| const popupTitle = document.getElementById('popupTitle'); const popupImg = document.getElementById('popupImg'); | |
| const popupDesc = document.getElementById('popupDesc'); | |
| window.addEventListener('mousemove', e => { mouse.x = (e.clientX/window.innerWidth)*2-1; mouse.y=-(e.clientY/window.innerHeight)*2+1; }); | |
| window.addEventListener('click', ()=>{ | |
| raycaster.setFromCamera(mouse,camera); | |
| const intersects = raycaster.intersectObjects(planetMeshes); | |
| if(intersects.length>0){ | |
| const planet=intersects[0].object.userData; | |
| popupTitle.innerText=planet.name; popupImg.src=planet.image; popupDesc.innerText=planet.info; popup.style.display="block"; | |
| const targetPos = intersects[0].object.position.clone(); | |
| gsap.to(camera.position,{x:targetPos.x+planet.radius*2, y:targetPos.y+planet.radius*1.5, z:targetPos.z+planet.radius*4, duration:2, ease:"power2.inOut"}); | |
| gsap.to(controls.target,{x:targetPos.x, y:targetPos.y, z:targetPos.z, duration:2, ease:"power2.inOut"}); | |
| } | |
| }); | |
| function closePopup(){ popup.style.display="none"; } | |
| function animate(){ | |
| requestAnimationFrame(animate); | |
| planetMeshes.forEach((mesh,i)=>{ mesh.rotation.y+=0.001+i*0.0005; if(cloudMeshes[i]) cloudMeshes[i].rotation.y+=0.002; }); | |
| raycaster.setFromCamera(mouse,camera); | |
| const intersects = raycaster.intersectObjects(planetMeshes); | |
| infoBox.innerHTML = intersects.length>0 ? intersects[0].object.userData.info : "Hover over a planet to learn more!"; | |
| controls.update(); renderer.render(scene,camera); | |
| } | |
| animate(); | |
| window.addEventListener("resize", ()=>{ | |
| camera.aspect=window.innerWidth/window.innerHeight; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(window.innerWidth, window.innerHeight); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| def index(): | |
| return render_template_string(html_content) | |
| if __name__ == "__main__": | |
| app.run(debug=True) | |