Spaces:
Runtime error
Runtime error
Create index.html
Browse files- index.html +281 -0
index.html
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
+
<title>Enhanced 3D Solar System</title>
|
| 6 |
+
<style>
|
| 7 |
+
body { margin: 0; overflow: hidden; }
|
| 8 |
+
#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; }
|
| 9 |
+
#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; }
|
| 10 |
+
#panel h2 { margin-top: 0; }
|
| 11 |
+
#closePanel { float: right; cursor: pointer; color: #fff; font-size: 18px; }
|
| 12 |
+
#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; }
|
| 13 |
+
#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; }
|
| 14 |
+
button { background: #444; color: white; border: none; padding: 5px 10px; margin: 5px 0; cursor: pointer; border-radius: 3px; }
|
| 15 |
+
button:hover { background: #666; }
|
| 16 |
+
input { padding: 5px; margin: 5px 0; width: 100%; }
|
| 17 |
+
#ai-response { margin-top: 10px; max-height: 200px; overflow-y: auto; }
|
| 18 |
+
</style>
|
| 19 |
+
</head>
|
| 20 |
+
<body>
|
| 21 |
+
<div id="info">Click a planet to learn more!</div>
|
| 22 |
+
<div id="panel"><span id="closePanel">×</span><h2 id="panel-title"></h2><div id="panel-content"></div></div>
|
| 23 |
+
<div id="ai-chat">
|
| 24 |
+
<h3>Ask the AI about the Solar System</h3>
|
| 25 |
+
<input id="ai-question" type="text" placeholder="Your question...">
|
| 26 |
+
<button id="ai-submit">Ask</button>
|
| 27 |
+
<div id="ai-response"></div>
|
| 28 |
+
</div>
|
| 29 |
+
<div id="loading">Loading...</div>
|
| 30 |
+
<script src="https://cdn.jsdelivr.net/npm/three@0.152.2/build/three.min.js"></script>
|
| 31 |
+
<script src="https://cdn.jsdelivr.net/npm/three@0.152.2/examples/js/controls/OrbitControls.js"></script>
|
| 32 |
+
<script src="https://cdn.jsdelivr.net/npm/three@0.152.2/examples/js/renderers/CSS2DRenderer.js"></script>
|
| 33 |
+
<script>
|
| 34 |
+
// --- Three.js Solar System with enhancements ---
|
| 35 |
+
const scene = new THREE.Scene();
|
| 36 |
+
scene.background = new THREE.Color(0x000010);
|
| 37 |
+
|
| 38 |
+
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
| 39 |
+
camera.position.set(0, 30, 70);
|
| 40 |
+
|
| 41 |
+
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
| 42 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 43 |
+
document.body.appendChild(renderer.domElement);
|
| 44 |
+
|
| 45 |
+
// CSS2DRenderer for labels
|
| 46 |
+
const labelRenderer = new THREE.CSS2DRenderer();
|
| 47 |
+
labelRenderer.setSize(window.innerWidth, window.innerHeight);
|
| 48 |
+
labelRenderer.domElement.style.position = 'absolute';
|
| 49 |
+
labelRenderer.domElement.style.top = '0px';
|
| 50 |
+
labelRenderer.domElement.style.pointerEvents = 'none';
|
| 51 |
+
document.body.appendChild(labelRenderer.domElement);
|
| 52 |
+
|
| 53 |
+
// Controls
|
| 54 |
+
const controls = new THREE.OrbitControls(camera, renderer.domElement);
|
| 55 |
+
|
| 56 |
+
// Lighting
|
| 57 |
+
scene.add(new THREE.AmbientLight(0x404040));
|
| 58 |
+
const sunLight = new THREE.PointLight(0xffffaa, 2, 200);
|
| 59 |
+
scene.add(sunLight);
|
| 60 |
+
|
| 61 |
+
// Textures (remote URLs)
|
| 62 |
+
const textures = {
|
| 63 |
+
"Sun": "https://www.solarsystemscope.com/textures/download/2k_sun.jpg",
|
| 64 |
+
"Mercury": "https://www.solarsystemscope.com/textures/download/2k_mercury.jpg",
|
| 65 |
+
"Venus": "https://www.solarsystemscope.com/textures/download/2k_venus_surface.jpg",
|
| 66 |
+
"Earth": "https://www.solarsystemscope.com/textures/download/2k_earth_daymap.jpg",
|
| 67 |
+
"Moon": "https://www.solarsystemscope.com/textures/download/2k_moon.jpg",
|
| 68 |
+
"Mars": "https://www.solarsystemscope.com/textures/download/2k_mars.jpg",
|
| 69 |
+
"Jupiter": "https://www.solarsystemscope.com/textures/download/2k_jupiter.jpg",
|
| 70 |
+
"Saturn": "https://www.solarsystemscope.com/textures/download/2k_saturn.jpg",
|
| 71 |
+
"Uranus": "https://www.solarsystemscope.com/textures/download/2k_uranus.jpg",
|
| 72 |
+
"Neptune": "https://www.solarsystemscope.com/textures/download/2k_neptune.jpg"
|
| 73 |
+
};
|
| 74 |
+
|
| 75 |
+
// Planets and moons
|
| 76 |
+
const planets = [
|
| 77 |
+
{ name: "Mercury", radius: 0.8, distance: 10, speed: 0.04, moons: [] },
|
| 78 |
+
{ name: "Venus", radius: 1.2, distance: 15, speed: 0.03, moons: [] },
|
| 79 |
+
{
|
| 80 |
+
name: "Earth", radius: 1.2, distance: 20, speed: 0.02,
|
| 81 |
+
moons: [{ name: "Moon", radius: 0.3, distance: 2, speed: 0.05 }]
|
| 82 |
+
},
|
| 83 |
+
{ name: "Mars", radius: 0.9, distance: 25, speed: 0.015, moons: [] },
|
| 84 |
+
{
|
| 85 |
+
name: "Jupiter", radius: 2.5, distance: 35, speed: 0.01,
|
| 86 |
+
moons: [{ name: "Europa", radius: 0.2, distance: 4, speed: 0.04 }]
|
| 87 |
+
},
|
| 88 |
+
{
|
| 89 |
+
name: "Saturn", radius: 2.2, distance: 45, speed: 0.008, rings: true, moons: []
|
| 90 |
+
},
|
| 91 |
+
{ name: "Uranus", radius: 1.8, distance: 55, speed: 0.005, moons: [] },
|
| 92 |
+
{ name: "Neptune", radius: 1.8, distance: 65, speed: 0.003, moons: [] }
|
| 93 |
+
];
|
| 94 |
+
|
| 95 |
+
// Asteroid belt
|
| 96 |
+
for (let i = 0; i < 200; i++) {
|
| 97 |
+
const beltGeometry = new THREE.SphereGeometry(0.1, 8, 8);
|
| 98 |
+
const beltMaterial = new THREE.MeshPhongMaterial({ color: 0x888888 });
|
| 99 |
+
const beltMesh = new THREE.Mesh(beltGeometry, beltMaterial);
|
| 100 |
+
const angle = Math.random() * Math.PI * 2;
|
| 101 |
+
const distance = 28 + Math.random() * 4;
|
| 102 |
+
beltMesh.position.x = Math.cos(angle) * distance;
|
| 103 |
+
beltMesh.position.z = Math.sin(angle) * distance;
|
| 104 |
+
beltMesh.position.y = (Math.random() - 0.5) * 0.5;
|
| 105 |
+
scene.add(beltMesh);
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
// Sun
|
| 109 |
+
const sunTexture = new THREE.TextureLoader().load(textures["Sun"]);
|
| 110 |
+
const sunGeometry = new THREE.SphereGeometry(4, 64, 64);
|
| 111 |
+
const sunMaterial = new THREE.MeshBasicMaterial({ map: sunTexture });
|
| 112 |
+
const sun = new THREE.Mesh(sunGeometry, sunMaterial);
|
| 113 |
+
scene.add(sun);
|
| 114 |
+
sunLight.position.copy(sun.position);
|
| 115 |
+
addLabel(sun, "Sun");
|
| 116 |
+
|
| 117 |
+
// Create planets, moons, rings, orbits
|
| 118 |
+
const planetObjects = [];
|
| 119 |
+
const clickableObjects = [];
|
| 120 |
+
planets.forEach((planet) => {
|
| 121 |
+
const texture = textures[planet.name] ? new THREE.TextureLoader().load(textures[planet.name]) : null;
|
| 122 |
+
const geometry = new THREE.SphereGeometry(planet.radius, 32, 32);
|
| 123 |
+
const material = texture ? new THREE.MeshPhongMaterial({ map: texture }) : new THREE.MeshPhongMaterial({ color: 0xffffff });
|
| 124 |
+
const mesh = new THREE.Mesh(geometry, material);
|
| 125 |
+
mesh.position.x = planet.distance;
|
| 126 |
+
mesh.userData = { type: 'planet', name: planet.name };
|
| 127 |
+
scene.add(mesh);
|
| 128 |
+
clickableObjects.push(mesh);
|
| 129 |
+
addLabel(mesh, planet.name);
|
| 130 |
+
|
| 131 |
+
// Saturn's rings
|
| 132 |
+
if (planet.rings) {
|
| 133 |
+
const ringGeometry = new THREE.RingGeometry(planet.radius * 1.5, planet.radius * 2.2, 64);
|
| 134 |
+
const ringMaterial = new THREE.MeshPhongMaterial({ color: 0xe9e5c6, side: THREE.DoubleSide, transparent: true, opacity: 0.7 });
|
| 135 |
+
const ring = new THREE.Mesh(ringGeometry, ringMaterial);
|
| 136 |
+
ring.rotation.x = Math.PI / 2;
|
| 137 |
+
mesh.add(ring);
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
// Moons
|
| 141 |
+
const planetMoons = [];
|
| 142 |
+
if (planet.moons && planet.moons.length > 0) {
|
| 143 |
+
planet.moons.forEach(moon => {
|
| 144 |
+
const moonGeometry = new THREE.SphereGeometry(moon.radius, 16, 16);
|
| 145 |
+
const moonMaterial = new THREE.MeshPhongMaterial({ color: 0xcccccc });
|
| 146 |
+
const moonMesh = new THREE.Mesh(moonGeometry, moonMaterial);
|
| 147 |
+
moonMesh.position.x = moon.distance;
|
| 148 |
+
moonMesh.userData = { type: 'moon', name: moon.name, parent: planet.name };
|
| 149 |
+
mesh.add(moonMesh);
|
| 150 |
+
planetMoons.push({ mesh: moonMesh, ...moon });
|
| 151 |
+
addLabel(moonMesh, moon.name);
|
| 152 |
+
clickableObjects.push(moonMesh);
|
| 153 |
+
});
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
// Orbit path
|
| 157 |
+
const orbitGeometry = new THREE.BufferGeometry();
|
| 158 |
+
const points = [];
|
| 159 |
+
const segments = 64;
|
| 160 |
+
for (let i = 0; i <= segments; i++) {
|
| 161 |
+
const theta = (i / segments) * Math.PI * 2;
|
| 162 |
+
points.push(new THREE.Vector3(
|
| 163 |
+
Math.cos(theta) * planet.distance,
|
| 164 |
+
0,
|
| 165 |
+
Math.sin(theta) * planet.distance
|
| 166 |
+
));
|
| 167 |
+
}
|
| 168 |
+
orbitGeometry.setFromPoints(points);
|
| 169 |
+
const orbitMaterial = new THREE.LineBasicMaterial({ color: 0xaaaaaa, transparent: true, opacity: 0.4 });
|
| 170 |
+
const orbit = new THREE.Line(orbitGeometry, orbitMaterial);
|
| 171 |
+
scene.add(orbit);
|
| 172 |
+
|
| 173 |
+
planetObjects.push({ mesh, ...planet, moons: planetMoons });
|
| 174 |
+
});
|
| 175 |
+
|
| 176 |
+
// Add labels
|
| 177 |
+
function addLabel(object, name) {
|
| 178 |
+
const div = document.createElement('div');
|
| 179 |
+
div.className = 'label';
|
| 180 |
+
div.textContent = name;
|
| 181 |
+
div.style.color = '#fff';
|
| 182 |
+
div.style.fontSize = '14px';
|
| 183 |
+
div.style.textShadow = '0 0 5px #000';
|
| 184 |
+
const label = new THREE.CSS2DObject(div);
|
| 185 |
+
label.position.set(0, object.geometry.parameters.radius + 0.6, 0);
|
| 186 |
+
object.add(label);
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
// Raycaster for click events
|
| 190 |
+
const raycaster = new THREE.Raycaster();
|
| 191 |
+
const mouse = new THREE.Vector2();
|
| 192 |
+
|
| 193 |
+
window.addEventListener('click', (event) => {
|
| 194 |
+
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
| 195 |
+
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
|
| 196 |
+
raycaster.setFromCamera(mouse, camera);
|
| 197 |
+
const intersects = raycaster.intersectObjects(clickableObjects, true);
|
| 198 |
+
if (intersects.length > 0) {
|
| 199 |
+
const obj = intersects[0].object;
|
| 200 |
+
showPanel(obj.userData);
|
| 201 |
+
}
|
| 202 |
+
});
|
| 203 |
+
|
| 204 |
+
// Info panel logic
|
| 205 |
+
function showPanel(data) {
|
| 206 |
+
const panel = document.getElementById('panel');
|
| 207 |
+
const title = document.getElementById('panel-title');
|
| 208 |
+
const content = document.getElementById('panel-content');
|
| 209 |
+
panel.style.display = 'block';
|
| 210 |
+
title.textContent = data.name;
|
| 211 |
+
content.innerHTML = "Loading info...";
|
| 212 |
+
// Example: fetch Wikipedia summary (or use your AI backend)
|
| 213 |
+
fetch(`https://en.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(data.name)}`)
|
| 214 |
+
.then(res => res.json())
|
| 215 |
+
.then(info => {
|
| 216 |
+
content.innerHTML = info.extract_html || "No info found.";
|
| 217 |
+
})
|
| 218 |
+
.catch(() => {
|
| 219 |
+
content.innerHTML = "No info found.";
|
| 220 |
+
});
|
| 221 |
+
}
|
| 222 |
+
document.getElementById('closePanel').onclick = () => {
|
| 223 |
+
document.getElementById('panel').style.display = 'none';
|
| 224 |
+
};
|
| 225 |
+
|
| 226 |
+
// Animation loop
|
| 227 |
+
let angle = 0;
|
| 228 |
+
function animate() {
|
| 229 |
+
requestAnimationFrame(animate);
|
| 230 |
+
controls.update();
|
| 231 |
+
angle += 0.005;
|
| 232 |
+
planetObjects.forEach((planet) => {
|
| 233 |
+
planet.mesh.position.x = Math.cos(angle * planet.speed) * planet.distance;
|
| 234 |
+
planet.mesh.position.z = Math.sin(angle * planet.speed) * planet.distance;
|
| 235 |
+
if (planet.moons) {
|
| 236 |
+
planet.moons.forEach((moon) => {
|
| 237 |
+
moon.mesh.position.x = Math.cos(angle * moon.speed) * moon.distance;
|
| 238 |
+
moon.mesh.position.z = Math.sin(angle * moon.speed) * moon.distance;
|
| 239 |
+
});
|
| 240 |
+
}
|
| 241 |
+
});
|
| 242 |
+
renderer.render(scene, camera);
|
| 243 |
+
labelRenderer.render(scene, camera);
|
| 244 |
+
}
|
| 245 |
+
animate();
|
| 246 |
+
|
| 247 |
+
// Responsive resize
|
| 248 |
+
window.addEventListener('resize', () => {
|
| 249 |
+
camera.aspect = window.innerWidth / window.innerHeight;
|
| 250 |
+
camera.updateProjectionMatrix();
|
| 251 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
| 252 |
+
labelRenderer.setSize(window.innerWidth, window.innerHeight);
|
| 253 |
+
});
|
| 254 |
+
|
| 255 |
+
// --- AI Chat Integration ---
|
| 256 |
+
document.getElementById('ai-submit').addEventListener('click', async () => {
|
| 257 |
+
const question = document.getElementById('ai-question').value;
|
| 258 |
+
const responseDiv = document.getElementById('ai-response');
|
| 259 |
+
const loadingDiv = document.getElementById('loading');
|
| 260 |
+
responseDiv.textContent = "";
|
| 261 |
+
loadingDiv.style.display = "block";
|
| 262 |
+
try {
|
| 263 |
+
const resp = await fetch("/api/ask", {
|
| 264 |
+
method: "POST",
|
| 265 |
+
headers: { "Content-Type": "application/json" },
|
| 266 |
+
body: JSON.stringify({ question })
|
| 267 |
+
});
|
| 268 |
+
const data = await resp.json();
|
| 269 |
+
if (data.answer) {
|
| 270 |
+
responseDiv.textContent = "AI: " + data.answer;
|
| 271 |
+
} else {
|
| 272 |
+
responseDiv.textContent = "Error: " + (data.error || "Unknown error.");
|
| 273 |
+
}
|
| 274 |
+
} catch (error) {
|
| 275 |
+
responseDiv.textContent = "Error: Could not connect to AI.";
|
| 276 |
+
}
|
| 277 |
+
loadingDiv.style.display = "none";
|
| 278 |
+
});
|
| 279 |
+
</script>
|
| 280 |
+
</body>
|
| 281 |
+
</html>
|