456 lines
12 KiB
TypeScript
456 lines
12 KiB
TypeScript
// === 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();
|