// === SISTEMA SOLARE - Simulazione TypeScript === interface CorpoCeleste { nome: string; raggio: number; asseMaggiore: number; eccentricita: number; velocita: number; colore: string; angoli?: number[]; raggiMondi?: number[]; coloriMondi?: string[]; nomiMondi?: string[]; velocitaMondi?: number[]; anelli?: boolean; } const canvas = document.getElementById("sistema") as HTMLCanvasElement; const ctx = canvas.getContext("2d")!; canvas.width = window.innerWidth; canvas.height = window.innerHeight; // === COMETA DI HALLEY === // Periodo: ~75.3 anni | Eccentricità: 0.967 | Inclinazione: 162° (retrograda) // Perielio: 0.586 UA | Afelio: 35.1 UA | Semiasse maggiore: 17.8 UA interface ParticellaCoda { distanza: number; larghezza: number; angoloOffset: number; opacita: number; } interface Cometa { nome: string; asseMaggiore: number; eccentricita: number; velocita: number; angoloIniziale: number; angoloOrbita: number; coda: ParticellaCoda[]; } const cometaHalley: Cometa = { nome: "Halley", asseMaggiore: 267, // ~17.8 UA in scala eccentricita: 0.967, velocita: 0.00084, // P=75.3 anni → scala angoloIniziale: Math.PI * 0.45, // posizione iniziale angoloOrbita: Math.PI * 0.85, // rotazione ellisse (simula inclinazione) coda: [], // particelle coda }; // Inizializza particelle della coda for (let i = 0; i < 60; i++) { cometaHalley.coda.push({ distanza: i * 3, larghezza: (60 - i) * 0.08, angoloOffset: (Math.random() - 0.5) * 0.3, opacita: 1 - i / 60, }); } const corpo: CorpoCeleste[] = [ { nome: "Mercurio", raggio: 4, asseMaggiore: 60, eccentricita: 0.206, velocita: 4.15, colore: "#b5b5b5", }, { nome: "Venere", raggio: 7, asseMaggiore: 90, eccentricita: 0.007, velocita: 1.62, colore: "#e8cda0", }, { nome: "Terra", raggio: 8, asseMaggiore: 130, eccentricita: 0.017, velocita: 1.0, colore: "#4a90d9", angoli: [0], raggiMondi: [18], coloriMondi: ["#cccccc"], nomiMondi: ["Luna"], }, { nome: "Marte", raggio: 6, asseMaggiore: 170, eccentricita: 0.093, velocita: 0.53, colore: "#c1440e", angoli: [0, 0.5], raggiMondi: [12, 16], coloriMondi: ["#aaaaaa", "#999999"], nomiMondi: ["Fobos", "Deimos"], }, { nome: "Giove", raggio: 20, asseMaggiore: 240, eccentricita: 0.049, velocita: 0.084, colore: "#c88b3a", angoli: [0, 0.8, 1.6, 2.4], raggiMondi: [30, 36, 42, 48], coloriMondi: ["#dddddd", "#bbbbbb", "#aaaaaa", "#999999"], nomiMondi: ["Io", "Europa", "Ganimede", "Callisto"], }, { nome: "Saturno", raggio: 17, asseMaggiore: 320, eccentricita: 0.057, velocita: 0.034, colore: "#e8d191", angoli: [0, 1.2, 2.4], raggiMondi: [30, 38, 46], coloriMondi: ["#cccccc", "#bbbbbb", "#aaaaaa"], nomiMondi: ["Encelado", "Mimante", "Titano"], anelli: true, }, { nome: "Urano", raggio: 12, asseMaggiore: 390, eccentricita: 0.046, velocita: 0.011, colore: "#72b5c4", angoli: [0, 1.5, 3.0], raggiMondi: [20, 26, 32], coloriMondi: ["#cccccc", "#bbbbbb", "#aaaaaa"], nomiMondi: ["Miranda", "Ariel", "Titania"], }, { nome: "Nettuno", raggio: 11, asseMaggiore: 450, eccentricita: 0.011, velocita: 0.006, colore: "#3f54ba", angoli: [0, 1.0], raggiMondi: [20, 26], coloriMondi: ["#cccccc", "#bbbbbb"], nomiMondi: ["Tritone", "Nereide"], }, { nome: "Plutone", raggio: 5, asseMaggiore: 590, eccentricita: 0.244, velocita: 0.004, colore: "#a89f91", angoli: [0, 1.2, 2.4, 3.6, 4.8], raggiMondi: [25, 45, 65, 42, 52], coloriMondi: ["#cccccc", "#bbbbbb", "#aaaaaa", "#999999", "#888888"], nomiMondi: ["Caronte", "Nix", "Idra", "Cerbero", "Stige"], velocitaMondi: [10, 2.5, 1.7, 3.2, 5.3], }, ]; let tempo = 0; function disegnaSole(cx: number, cy: number) { // Glow esterno const grad = ctx.createRadialGradient(cx, cy, 10, cx, cy, 60); grad.addColorStop(0, "#fff700"); grad.addColorStop(0.3, "#ff8c00"); grad.addColorStop(1, "rgba(255,140,0,0)"); ctx.fillStyle = grad; ctx.beginPath(); ctx.arc(cx, cy, 60, 0, Math.PI * 2); ctx.fill(); // Sole ctx.fillStyle = "#ffcc00"; ctx.shadowColor = "#ff8800"; ctx.shadowBlur = 30; ctx.beginPath(); ctx.arc(cx, cy, 25, 0, Math.PI * 2); ctx.fill(); ctx.shadowBlur = 0; } function disegnaAnelliSaturno(cx: number, cy: number, raggio: number) { ctx.strokeStyle = "rgba(210, 180, 140, 0.5)"; ctx.lineWidth = 4; ctx.beginPath(); ctx.ellipse(cx, cy, raggio * 2.2, raggio * 0.6, 0, 0, Math.PI * 2); ctx.stroke(); ctx.strokeStyle = "rgba(190, 160, 120, 0.3)"; ctx.lineWidth = 3; ctx.beginPath(); ctx.ellipse(cx, cy, raggio * 2.6, raggio * 0.7, 0, 0, Math.PI * 2); ctx.stroke(); } function disegnaOrbita(cx: number, cy: number, a: number, b: number) { ctx.strokeStyle = "rgba(255,255,255,0.08)"; ctx.lineWidth = 1; ctx.beginPath(); ctx.ellipse(cx, cy, a, b, 0, 0, Math.PI * 2); ctx.stroke(); } function disegnaCorpo(c: CorpoCeleste, cx: number, cy: number, angolo: number) { const a = c.asseMaggiore; const b = a * Math.sqrt(1 - c.eccentricita * c.eccentricita); const x = cx + a * Math.cos(angolo); const y = cy + b * Math.sin(angolo); // Disegna orbita ellittica disegnaOrbita(cx, cy, a, b); // Anelli di Saturno (dietro il pianeta) if (c.anelli) { disegnaAnelliSaturno(x, y, c.raggio); } // Pianeta ctx.fillStyle = c.colore; ctx.beginPath(); ctx.arc(x, y, c.raggio, 0, Math.PI * 2); ctx.fill(); // Dettaglio Giove (fasce) if (c.nome === "Giove") { ctx.strokeStyle = "rgba(160, 100, 30, 0.4)"; ctx.lineWidth = 2; for (let i = -2; i <= 2; i++) { ctx.beginPath(); ctx.arc(x, y, c.raggio * 0.6, 0, Math.PI); ctx.stroke(); } // Grande macchia rossa ctx.fillStyle = "rgba(200, 80, 50, 0.6)"; ctx.beginPath(); ctx.ellipse(x + 5, y + 3, 5, 3, 0, 0, Math.PI * 2); ctx.fill(); } // Dettaglio Terra (continenti) if (c.nome === "Terra") { ctx.fillStyle = "rgba(50, 150, 50, 0.5)"; ctx.beginPath(); ctx.arc(x - 2, y - 2, 3, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = "rgba(50, 150, 50, 0.4)"; ctx.beginPath(); ctx.arc(x + 3, y + 2, 2, 0, Math.PI * 2); ctx.fill(); } // Disegna lune if (c.raggiMondi && c.coloriMondi) { const coloriM = c.coloriMondi; c.raggiMondi.forEach((raggioM, i) => { const velocitaM = c.velocitaMondi?.[i] ?? 3; const angoloM = angolo + (c.angoli?.[i] || 0) + tempo * velocitaM; const mx = x + Math.cos(angoloM) * raggioM; const my = y + Math.sin(angoloM) * raggioM; // Orbita luna (circolare per semplicità) ctx.strokeStyle = "rgba(255,255,255,0.05)"; ctx.lineWidth = 0.5; ctx.beginPath(); ctx.arc(x, y, raggioM, 0, Math.PI * 2); ctx.stroke(); // Luna ctx.fillStyle = coloriM[i]; ctx.beginPath(); ctx.arc(mx, my, 2, 0, Math.PI * 2); ctx.fill(); // Etichetta ctx.fillStyle = "rgba(255,255,255,0.4)"; ctx.font = "8px monospace"; ctx.fillText(c.nomiMondi?.[i] || "", mx + 4, my - 4); }); } // Etichetta pianeta ctx.fillStyle = "rgba(255,255,255,0.6)"; ctx.font = "11px monospace"; ctx.fillText(c.nome, x + c.raggio + 5, y - 5); } function animazione() { // Sfondo ctx.fillStyle = "#000010"; ctx.fillRect(0, 0, canvas.width, canvas.height); // Stelle disegnaStelle(); const cx = canvas.width / 2; const cy = canvas.height / 2; // Sole disegnaSole(cx, cy); // Pianeti corpo.forEach((c) => { const angolo = tempo * c.velocita; disegnaCorpo(c, cx, cy, angolo); }); // === COMETA DI HALLEY === disegnaCometaHalley(cx, cy); tempo += 0.005; requestAnimationFrame(animazione); } // Stelle di sfondo (statiche) const stelle: { x: number; y: number; r: number; a: number }[] = []; for (let i = 0; i < 300; i++) { stelle.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, r: Math.random() * 1.2, a: Math.random() * 0.5 + 0.2, }); } function disegnaCometaHalley(cx: number, cy: number) { const c = cometaHalley; const a = c.asseMaggiore; const b = a * Math.sqrt(1 - c.eccentricita * c.eccentricita); const angolo = tempo * c.velocita + c.angoloIniziale; // Posizione cometa sull'orbita ellittica const xOrbita = a * Math.cos(angolo); const yOrbita = b * Math.sin(angolo); // Ruota l'orbita per simulare inclinazione/retrograda const cosA = Math.cos(c.angoloOrbita); const sinA = Math.sin(c.angoloOrbita); const x = cx + xOrbita * cosA - yOrbita * sinA; const y = cy + xOrbita * sinA + yOrbita * cosA; // Disegna orbita (ellisse ruotata) ctx.save(); ctx.translate(cx, cy); ctx.rotate(c.angoloOrbita); ctx.strokeStyle = "rgba(150,200,255,0.12)"; ctx.lineWidth = 1; ctx.setLineDash([4, 6]); ctx.beginPath(); ctx.ellipse(0, 0, a, b, 0, Math.PI * 2); ctx.stroke(); ctx.setLineDash([]); ctx.restore(); // Distanza dal Sole per direzione coda const dxSole = cx - x; const dySole = cy - y; const distSole = Math.sqrt(dxSole * dxSole + dySole * dySole); const direzioneX = dxSole / distSole; const direzioneY = dySole / distSole; // Perielio: la cometa è più luminosa quando è vicina al Sole const perielioDist = a * (1 - c.eccentricita); // ~9px in scala const luminosita = Math.min(1, perielioDist / distSole); // Coda di polvere (più larga, curva leggermente) for (let i = 0; i < cometaHalley.coda.length; i++) { const p = cometaHalley.coda[i]; const tailLen = p.distanza * (0.5 + luminosita * 1.5); const tailW = p.larghezza * (0.5 + luminosita); const angoloCoda = Math.atan2(direzioneY, direzioneX) + p.angoloOffset; const px = x - Math.cos(angoloCoda) * tailLen; const py = y - Math.sin(angoloCoda) * tailLen; const alpha = p.opacita * luminosita * 0.6; if (alpha > 0.01) { ctx.fillStyle = `rgba(180,220,255,${alpha})`; ctx.beginPath(); ctx.arc(px, py, tailW, 0, Math.PI * 2); ctx.fill(); } } // Coda ionica (più sottile, dritta, blu) const codaIonicaLunghezza = 40 + luminosita * 80; const angoloIonico = Math.atan2(direzioneY, direzioneX); for (let i = 0; i < 30; i++) { const t = i / 30; const ix = x - Math.cos(angoloIonico) * codaIonicaLunghezza * t; const iy = y - Math.sin(angoloIonico) * codaIonicaLunghezza * t; const alpha = (1 - t) * luminosita * 0.5; if (alpha > 0.01) { ctx.fillStyle = `rgba(100,150,255,${alpha})`; ctx.beginPath(); ctx.arc(ix, iy, 1.5 * (1 - t), 0, Math.PI * 2); ctx.fill(); } } // Coma (chioma) intorno al nucleo if (luminosita > 0.1) { const comaRaggio = 6 + luminosita * 12; const comaGrad = ctx.createRadialGradient(x, y, 2, x, y, comaRaggio); comaGrad.addColorStop(0, `rgba(200,230,255,${luminosita * 0.8})`); comaGrad.addColorStop(0.3, `rgba(150,200,255,${luminosita * 0.3})`); comaGrad.addColorStop(1, "rgba(100,150,255,0)"); ctx.fillStyle = comaGrad; ctx.beginPath(); ctx.arc(x, y, comaRaggio, 0, Math.PI * 2); ctx.fill(); } // Nucleo della cometa const nucleoRaggio = 2.5 + luminosita * 1.5; ctx.fillStyle = `rgba(220,240,255,${0.5 + luminosita * 0.5})`; ctx.shadowColor = "rgba(150,200,255,0.8)"; ctx.shadowBlur = 8 * luminosita; ctx.beginPath(); ctx.arc(x, y, nucleoRaggio, 0, Math.PI * 2); ctx.fill(); ctx.shadowBlur = 0; // Etichetta ctx.fillStyle = `rgba(180,220,255,${0.4 + luminosita * 0.4})`; ctx.font = "10px monospace"; ctx.fillText(c.nome, x + 10, y - 8); } function disegnaStelle() { stelle.forEach((s) => { ctx.fillStyle = `rgba(255,255,255,${s.a})`; ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2); ctx.fill(); }); } // Resize window.addEventListener("resize", () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }); // Avvia animazione();