primo commit
This commit is contained in:
@@ -1,3 +1,119 @@
|
|||||||
# webpaper
|
# Webpaper
|
||||||
|
|
||||||
Pagine web come sfondi animati
|
Pagina web come sfondo del desktop. Ispirato a
|
||||||
|
[Hidamari](https://github.com/jeffshee/hidamari), dal quale è stato estratto e
|
||||||
|
ridotto all'essenziale il meccanismo della pagina web come wallpaper: niente
|
||||||
|
server DBus, niente video, niente GUI di controllo — solo lo sfondo.
|
||||||
|
|
||||||
|
## Come funziona
|
||||||
|
|
||||||
|
1. Una `Gtk.ApplicationWindow` piena di una `WebKit2.WebView` (WebKitGTK 4.1)
|
||||||
|
carica l'URI richiesto;
|
||||||
|
2. la finestra viene marcata con `Gdk.WindowTypeHint.DESKTOP`: il window
|
||||||
|
manager la tiene in fondo allo stack, sotto tutte le altre finestre,
|
||||||
|
esattamente come lo sfondo nativo;
|
||||||
|
3. viene creata una finestra per ogni monitor, dimensionata e posizionata
|
||||||
|
sulla geometria del monitor (`monitor.get_geometry()`), con aggiornamento
|
||||||
|
automatico su eventi `monitor-added` / `monitor-removed` / `size-changed`;
|
||||||
|
4. il backend è forzato a **X11** (`GDK_BACKEND=x11`): le finestre DESKTOP non
|
||||||
|
funzionano su Wayland nativo, quindi su una sessione Wayland GDK ricade su
|
||||||
|
XWayland;
|
||||||
|
5. workaround NVIDIA: se è presente una GPU NVIDIA, WebKit viene puntato su un
|
||||||
|
render node Mesa o su buffer SHM (il renderer DMA-BUF di WebKitGTK fallisce
|
||||||
|
sui driver NVIDIA e la pagina resterebbe bianca).
|
||||||
|
|
||||||
|
## Requisiti
|
||||||
|
|
||||||
|
- Python 3 con PyGObject
|
||||||
|
- GTK 3
|
||||||
|
- WebKitGTK 4.1
|
||||||
|
|
||||||
|
Debian/Ubuntu:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt install python3-gi gir1.2-gtk-3.0 gir1.2-webkit2-4.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Arch:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo pacman -S python-gobject gtk3 webkit2gtk-4.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Uso
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# da questa cartella, un file HTML locale:
|
||||||
|
python3 -m webpaper /percorso/pagina.html
|
||||||
|
|
||||||
|
# oppure un URL:
|
||||||
|
python3 -m webpaper https://esempio.com
|
||||||
|
|
||||||
|
# DUAL MONITOR: una pagina per monitor (prima sorgente = primo monitor,
|
||||||
|
# seconda = secondo monitor):
|
||||||
|
python3 -m webpaper /percorso/pagina1.html /percorso/pagina2.html
|
||||||
|
|
||||||
|
# log di debug:
|
||||||
|
python3 -m webpaper -d /percorso/pagina.html
|
||||||
|
|
||||||
|
# non sistemare le icone desktop (le icone restano sotto il wallpaper):
|
||||||
|
python3 -m webpaper --no-fix-icons /percorso/pagina.html
|
||||||
|
```
|
||||||
|
|
||||||
|
Con **una sola** sorgente, viene mostrata su tutti i monitor (comportamento
|
||||||
|
originale). Con **più sorgenti** la mappatura è 1:1 nell'ordine dei monitor;
|
||||||
|
se i monitor sono più delle sorgenti, gli extra ripetono l'ultima sorgente, se
|
||||||
|
le sorgenti superano i monitor quelle in eccesso vengono ignorate (con un
|
||||||
|
warning nel log).
|
||||||
|
|
||||||
|
In alternativa, installabile come comando:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip install -e .
|
||||||
|
webpaper /percorso/pagina.html
|
||||||
|
```
|
||||||
|
|
||||||
|
Per uscire: click destro sulla pagina → **Esci** (oppure `Ctrl+C`).
|
||||||
|
|
||||||
|
## Autostart al login
|
||||||
|
|
||||||
|
Crea il file `~/.config/autostart/webpaper.desktop` con:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=Webpaper
|
||||||
|
Comment=Pagina web come sfondo del desktop
|
||||||
|
Exec=sh -c "cd /percorso/del/progetto && sleep 3 && exec python3 -m webpaper /percorso/pagina.html"
|
||||||
|
Terminal=false
|
||||||
|
X-GNOME-Autostart-enabled=true
|
||||||
|
X-GNOME-Autostart-Delay=0
|
||||||
|
```
|
||||||
|
|
||||||
|
Nota: il `sleep 3` è importante — dà tempo a `nemo-desktop` (le icone della
|
||||||
|
scrivania) di partire per primo. Se webpaper parte prima, nemo-desktop si
|
||||||
|
rifiuta di avviarsi ("Desktop already managed") e le icone spariscono del
|
||||||
|
tutto.
|
||||||
|
|
||||||
|
Per disattivare l'autostart: elimina il file oppure toglilo da *Impostazioni
|
||||||
|
di sistema → Applicazioni all'avvio*.
|
||||||
|
|
||||||
|
## Note
|
||||||
|
|
||||||
|
- Il meccanismo funziona su **X11 e XWayland** (Wayland puro non è supportato,
|
||||||
|
come in Hidamari).
|
||||||
|
- **Icone della scrivania**: le estensioni che disegnano le icone desktop
|
||||||
|
(nemo-desktop su Cinnamon, DING su GNOME) usano a loro volta finestre di
|
||||||
|
tipo DESKTOP; il nostro wallpaper, essendo mappato dopo, le copre. All'avvio
|
||||||
|
Webpaper le porta automaticamente sopra il wallpaper (attivandole una a una,
|
||||||
|
via `xdotool`). Requisito: `xdotool` installato (`sudo apt install xdotool`
|
||||||
|
oppure `sudo pacman -S xdotool`). Se non è presente, o con
|
||||||
|
`--no-fix-icons`, le icone restano sotto il wallpaper.
|
||||||
|
- La pagina è un vero browser (WebKitGTK): funzionano animazioni CSS,
|
||||||
|
canvas/WebGL, iframe, ecc. L'audio della pagina viene riprodotto; per
|
||||||
|
mutarlo non c'è un'API GTK semplice (in Hidamari è un TODO).
|
||||||
|
|
||||||
|
## Licenza
|
||||||
|
|
||||||
|
GPL-3.0-or-later, coerente con il progetto originale
|
||||||
|
[Hidamari](https://github.com/jeffshee/hidamari) dal quale deriva.
|
||||||
|
|||||||
Executable
+202
@@ -0,0 +1,202 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# install.sh - Script di installazione per Webpaper
|
||||||
|
# Requisiti: bash, pip3, xrandr (solitamente preinstallato)
|
||||||
|
|
||||||
|
# --- Colori ---
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# --- Variabili ---
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
SOLE_HTML="$SCRIPT_DIR/sistema_stellare/sole.html"
|
||||||
|
ALPHA_HTML="$SCRIPT_DIR/sistema_stellare/alpha-centauri.html"
|
||||||
|
AUTOSTART_DIR="$HOME/.config/autostart"
|
||||||
|
|
||||||
|
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||||
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||||
|
|
||||||
|
# --- 1. Rilevamento Distribuzione ---
|
||||||
|
detect_distro() {
|
||||||
|
if [ -f /etc/os-release ]; then
|
||||||
|
. /etc/os-release
|
||||||
|
echo "$ID"
|
||||||
|
else
|
||||||
|
echo "unknown"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
DISTRO=$(detect_distro)
|
||||||
|
|
||||||
|
# --- 2. Installazione Dipendenze (corretta per tutte le distro) ---
|
||||||
|
install_deps() {
|
||||||
|
log_info "Rilevata distribuzione: $DISTRO"
|
||||||
|
|
||||||
|
local sudo_cmd=""
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
sudo_cmd="sudo"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Definizione dei pacchetti per ogni famiglia
|
||||||
|
declare -A pkg_map
|
||||||
|
pkg_map[ubuntu]="python3-gi gir1.2-gtk-3.0 gir1.2-webkit2-4.1 xdotool wmctrl"
|
||||||
|
pkg_map[debian]="python3-gi gir1.2-gtk-3.0 gir1.2-webkit2-4.1 xdotool wmctrl"
|
||||||
|
pkg_map[linuxmint]="python3-gi gir1.2-gtk-3.0 gir1.2-webkit2-4.1 xdotool wmctrl"
|
||||||
|
pkg_map[pop]="python3-gi gir1.2-gtk-3.0 gir1.2-webkit2-4.1 xdotool wmctrl"
|
||||||
|
pkg_map[arch]="python-gobject gtk3 webkit2gtk-4.1 xdotool wmctrl"
|
||||||
|
pkg_map[manjaro]="python-gobject gtk3 webkit2gtk-4.1 xdotool wmctrl"
|
||||||
|
pkg_map[endeavouros]="python-gobject gtk3 webkit2gtk-4.1 xdotool wmctrl"
|
||||||
|
pkg_map[fedora]="python3-gobject gtk3 webkit2gtk4.1 xdotool wmctrl"
|
||||||
|
pkg_map[rhel]="python3-gobject gtk3 webkit2gtk4.1 xdotool wmctrl"
|
||||||
|
|
||||||
|
local packages=(${pkg_map[$DISTRO]})
|
||||||
|
if [ ${#packages[@]} -eq 0 ]; then
|
||||||
|
log_warn "Distribuzione sconosciuta ($DISTRO). Installa manualmente:"
|
||||||
|
log_warn "- python3-gi (PyGObject)"
|
||||||
|
log_warn "- GTK3"
|
||||||
|
log_warn "- WebKit2GTK 4.1"
|
||||||
|
log_warn "- xdotool"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verifica pacchetti installati
|
||||||
|
local missing=()
|
||||||
|
for pkg in "${packages[@]}"; do
|
||||||
|
local installed=false
|
||||||
|
case "$DISTRO" in
|
||||||
|
arch|manjaro|endeavouros)
|
||||||
|
if pacman -Q "$pkg" &>/dev/null; then installed=true; fi
|
||||||
|
;;
|
||||||
|
fedora|rhel)
|
||||||
|
if rpm -q "$pkg" &>/dev/null; then installed=true; fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if dpkg -s "$pkg" &>/dev/null; then installed=true; fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if [ "$installed" = false ]; then
|
||||||
|
missing+=("$pkg")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ${#missing[@]} -eq 0 ]; then
|
||||||
|
log_info "Tutte le dipendenze di sistema sono installate."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_warn "Pacchetti mancanti: ${missing[*]}"
|
||||||
|
log_info "Installazione in corso..."
|
||||||
|
|
||||||
|
case "$DISTRO" in
|
||||||
|
arch|manjaro|endeavouros)
|
||||||
|
$sudo_cmd pacman -S --noconfirm "${missing[@]}"
|
||||||
|
;;
|
||||||
|
fedora|rhel)
|
||||||
|
$sudo_cmd dnf install -y "${missing[@]}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
$sudo_cmd apt-get update && $sudo_cmd apt-get install -y "${missing[@]}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
log_error "Errore durante l'installazione delle dipendenze."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- 3. Installazione Python ---
|
||||||
|
install_python_pkg() {
|
||||||
|
log_info "Installazione pacchetto Python..."
|
||||||
|
|
||||||
|
if ! command -v pip3 &>/dev/null && ! command -v pip &>/dev/null; then
|
||||||
|
log_error "pip non trovato. Installalo prima."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Usa pip3 se disponibile, altrimenti pip
|
||||||
|
local pip_cmd="pip3"
|
||||||
|
if ! command -v pip3 &>/dev/null; then
|
||||||
|
pip_cmd="pip"
|
||||||
|
fi
|
||||||
|
|
||||||
|
$pip_cmd install -e "$SCRIPT_DIR" --user
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
log_error "Errore installazione pacchetto Python."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_info "Pacchetto Python installato."
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- 4. Rilevamento Monitor ---
|
||||||
|
detect_monitors() {
|
||||||
|
local count
|
||||||
|
count=$(xrandr --query 2>/dev/null | grep " connected" | wc -l)
|
||||||
|
if [ "$count" -eq 0 ]; then
|
||||||
|
log_warn "Impossibile rilevare monitor con xrandr. Imposto 1 monitor."
|
||||||
|
echo 1
|
||||||
|
else
|
||||||
|
echo "$count"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- 5. Configurazione Autostart (CORRETTA) ---
|
||||||
|
setup_autostart() {
|
||||||
|
read -p "Vuoi aggiungere Webpaper all'avvio automatico? (y/N): " answer
|
||||||
|
if [[ ! "$answer" =~ ^[Yy]$ ]]; then
|
||||||
|
log_info "Autostart saltato."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$AUTOSTART_DIR"
|
||||||
|
|
||||||
|
local monitor_count=$(detect_monitors)
|
||||||
|
local exec_cmd=""
|
||||||
|
if [ "$monitor_count" -ge 2 ]; then
|
||||||
|
exec_cmd="python3 -m webpaper \"$SOLE_HTML\" \"$ALPHA_HTML\""
|
||||||
|
log_info "Rilevati $monitor_count monitor. Configurerò Webpaper per mostrare:"
|
||||||
|
log_info " - Monitor 1: Sole.html"
|
||||||
|
log_info " - Monitor 2: Alpha-Centauri.html"
|
||||||
|
else
|
||||||
|
exec_cmd="python3 -m webpaper \"$SOLE_HTML\""
|
||||||
|
log_info "Rilevato $monitor_count monitor. Configurerò Webpaper per mostrare:"
|
||||||
|
log_info " - Monitor 1: Sole.html"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# IMPORTANTE: qui correggiamo l'here-document e le virgolette
|
||||||
|
cat > "$AUTOSTART_DIR/webpaper.desktop" <<-EOF
|
||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=Webpaper
|
||||||
|
Comment=Pagina web come sfondo del desktop
|
||||||
|
Exec=sh -c "cd \"$SCRIPT_DIR\" && sleep 3 && exec $exec_cmd"
|
||||||
|
Terminal=false
|
||||||
|
X-GNOME-Autostart-enabled=true
|
||||||
|
X-GNOME-Autostart-Delay=0
|
||||||
|
Hidden=false
|
||||||
|
NoDisplay=false
|
||||||
|
EOF
|
||||||
|
|
||||||
|
log_info "File creato in: $AUTOSTART_DIR/webpaper.desktop"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Esecuzione ---
|
||||||
|
main() {
|
||||||
|
echo "========================================"
|
||||||
|
echo " Webpaper Installer"
|
||||||
|
echo "========================================"
|
||||||
|
|
||||||
|
install_deps
|
||||||
|
install_python_pkg
|
||||||
|
setup_autostart
|
||||||
|
|
||||||
|
echo "========================================"
|
||||||
|
log_info "Installazione completata!"
|
||||||
|
echo "========================================"
|
||||||
|
echo "Per avviare manualmente: webpaper"
|
||||||
|
echo "Oppure: python3 -m webpaper"
|
||||||
|
}
|
||||||
|
|
||||||
|
main
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=61"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "webpaper"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Pagina web come sfondo del desktop (ispirato a Hidamari)"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
license = { text = "GPL-3.0-or-later" }
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
webpaper = "webpaper.__main__:main"
|
||||||
|
|
||||||
|
[tool.setuptools]
|
||||||
|
packages = ["webpaper"]
|
||||||
@@ -0,0 +1,380 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Alpha Centauri</title>
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body { overflow: hidden; background: #000; }
|
||||||
|
canvas { display: block; }
|
||||||
|
#info {
|
||||||
|
position: fixed;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
color: rgba(255,255,255,0.5);
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="alpha"></canvas>
|
||||||
|
<div id="info">Alpha Centauri — Simulazione</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// === SISTEMA ALPHA CENTAURI — JavaScript ===
|
||||||
|
|
||||||
|
let tempo = 0;
|
||||||
|
|
||||||
|
// Stelle del sistema triplo
|
||||||
|
const stelle = [
|
||||||
|
{
|
||||||
|
nome: "Alpha Cen A",
|
||||||
|
tipo: "G2V",
|
||||||
|
colore: "#fff4e0",
|
||||||
|
raggio: 18,
|
||||||
|
asseMaggiore: 175,
|
||||||
|
eccentricita: 0.517,
|
||||||
|
velocita: 0.0125,
|
||||||
|
angoloIniziale: 0,
|
||||||
|
massa: "1.1 M☉",
|
||||||
|
coloreTesto: "rgba(255,244,224,0.8)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: "Alpha Cen B",
|
||||||
|
tipo: "K1V",
|
||||||
|
colore: "#ffcc66",
|
||||||
|
raggio: 14,
|
||||||
|
asseMaggiore: 215,
|
||||||
|
eccentricita: 0.517,
|
||||||
|
velocita: 0.0125,
|
||||||
|
angoloIniziale: Math.PI,
|
||||||
|
massa: "0.907 M☉",
|
||||||
|
coloreTesto: "rgba(255,204,102,0.8)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: "Proxima Cen",
|
||||||
|
tipo: "M5.5Ve",
|
||||||
|
colore: "#cc4422",
|
||||||
|
raggio: 10,
|
||||||
|
asseMaggiore: 650,
|
||||||
|
eccentricita: 0.509,
|
||||||
|
velocita: 0.00091,
|
||||||
|
angoloIniziale: Math.PI * 0.7,
|
||||||
|
massa: "0.122 M☉",
|
||||||
|
coloreTesto: "rgba(255,150,100,0.8)",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Pianeti: [stella_ospite, dati_pianeta]
|
||||||
|
const pianeti = [
|
||||||
|
// === SU ALPHA CENTAURI B ===
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
nome: "Alpha Cen Ab",
|
||||||
|
raggio: 7,
|
||||||
|
asseMaggiore: 55,
|
||||||
|
eccentricita: 0.02,
|
||||||
|
velocita: 1.63,
|
||||||
|
colore: "#5a8ab5",
|
||||||
|
angoli: [0],
|
||||||
|
dettagli: [
|
||||||
|
{ tipo: "oceano", colore: "#3a6a9a", x: -2, y: -1, r: 3 },
|
||||||
|
{ tipo: "continente", colore: "#4a8a4a", x: 2, y: 1, r: 2.5 },
|
||||||
|
{ tipo: "nuvola", colore: "rgba(255,255,255,0.25)", x: 0, y: -2, r: 1.5 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
// === SU PROXIMA CENTAURI ===
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
nome: "Proxima d",
|
||||||
|
raggio: 3,
|
||||||
|
asseMaggiore: 30,
|
||||||
|
eccentricita: 0.04,
|
||||||
|
velocita: 2.094,
|
||||||
|
colore: "#6b7b8d",
|
||||||
|
angoli: [0],
|
||||||
|
dettagli: [
|
||||||
|
{ tipo: "superficie", colore: "#5a6a7a", x: -1, y: -1, r: 1.5 },
|
||||||
|
{ tipo: "superficie", colore: "#7a8a9a", x: 1, y: 1, r: 1 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
nome: "Proxima b",
|
||||||
|
raggio: 8,
|
||||||
|
asseMaggiore: 50,
|
||||||
|
eccentricita: 0.11,
|
||||||
|
velocita: 0.959,
|
||||||
|
colore: "#4a90d9",
|
||||||
|
angoli: [0],
|
||||||
|
dettagli: [
|
||||||
|
{ tipo: "oceano", colore: "#2a5a8a", x: -3, y: -2, r: 4 },
|
||||||
|
{ tipo: "continente", colore: "#3a7a3a", x: 2, y: 1, r: 3 },
|
||||||
|
{ tipo: "continente", colore: "#4a8a4a", x: -1, y: 3, r: 2.5 },
|
||||||
|
{ tipo: "nuvola", colore: "rgba(255,255,255,0.3)", x: 0, y: -3, r: 2 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
nome: "Proxima c",
|
||||||
|
raggio: 14,
|
||||||
|
asseMaggiore: 200,
|
||||||
|
eccentricita: 0.04,
|
||||||
|
velocita: 0.00559,
|
||||||
|
colore: "#c8944a",
|
||||||
|
angoli: [0],
|
||||||
|
raggiMondi: [35, 55, 75],
|
||||||
|
coloriMondi: ["#d4c4a4", "#b4a484", "#948464"],
|
||||||
|
nomiMondi: ["Candidate-m1", "Candidate-m2", "Candidate-m3"],
|
||||||
|
velocitaMondi: [8, 4, 2.5],
|
||||||
|
anelli: true,
|
||||||
|
dettagli: [
|
||||||
|
{ tipo: "banda", colore: "#a8743a", y: -4 },
|
||||||
|
{ tipo: "banda", colore: "#b8844a", y: 0 },
|
||||||
|
{ tipo: "banda", colore: "#98642a", y: 4 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const canvas = document.getElementById("alpha");
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
|
||||||
|
// Stelle di sfondo
|
||||||
|
const stelleSfondo = [];
|
||||||
|
for (let i = 0; i < 300; i++) {
|
||||||
|
stelleSfondo.push({
|
||||||
|
x: Math.random() * canvas.width,
|
||||||
|
y: Math.random() * canvas.height,
|
||||||
|
r: Math.random() * 1.2,
|
||||||
|
a: Math.random() * 0.5 + 0.2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaStelle() {
|
||||||
|
stelleSfondo.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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaStella(stella, x, y) {
|
||||||
|
// Glow differenziato per tipo
|
||||||
|
if (stella.tipo === "G2V") {
|
||||||
|
const g = ctx.createRadialGradient(x, y, stella.raggio * 0.5, x, y, stella.raggio * 5);
|
||||||
|
g.addColorStop(0, "#fff8e0");
|
||||||
|
g.addColorStop(0.2, "#ffdd44");
|
||||||
|
g.addColorStop(0.5, "rgba(255,221,68,0.2)");
|
||||||
|
g.addColorStop(1, "rgba(255,200,0,0)");
|
||||||
|
ctx.fillStyle = g;
|
||||||
|
} else if (stella.tipo === "K1V") {
|
||||||
|
const g = ctx.createRadialGradient(x, y, stella.raggio * 0.5, x, y, stella.raggio * 4);
|
||||||
|
g.addColorStop(0, "#ffe0a0");
|
||||||
|
g.addColorStop(0.2, "#ffaa44");
|
||||||
|
g.addColorStop(0.5, "rgba(255,170,68,0.2)");
|
||||||
|
g.addColorStop(1, "rgba(255,150,0,0)");
|
||||||
|
ctx.fillStyle = g;
|
||||||
|
} else {
|
||||||
|
const g = ctx.createRadialGradient(x, y, stella.raggio * 0.3, x, y, stella.raggio * 4);
|
||||||
|
g.addColorStop(0, "rgba(255,100,50,0.8)");
|
||||||
|
g.addColorStop(0.2, "rgba(200,50,20,0.4)");
|
||||||
|
g.addColorStop(0.5, "rgba(150,30,10,0.1)");
|
||||||
|
g.addColorStop(1, "rgba(100,20,5,0)");
|
||||||
|
ctx.fillStyle = g;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, stella.raggio * 5, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.fillStyle = stella.colore;
|
||||||
|
ctx.shadowColor = stella.colore;
|
||||||
|
ctx.shadowBlur = stella.raggio * 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, stella.raggio, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.shadowBlur = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaAnelli(cx, cy, raggio) {
|
||||||
|
ctx.strokeStyle = "rgba(210, 180, 140, 0.4)";
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.ellipse(cx, cy, raggio * 2.0, raggio * 0.5, 0, 0, Math.PI * 2);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.strokeStyle = "rgba(190, 160, 120, 0.25)";
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.ellipse(cx, cy, raggio * 2.4, raggio * 0.6, 0, 0, Math.PI * 2);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaOrbita(cx, cy, a, b) {
|
||||||
|
ctx.strokeStyle = "rgba(255,255,255,0.06)";
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.setLineDash([3, 3]);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.ellipse(cx, cy, a, b, 0, 0, Math.PI * 2);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaDettaglioPianeta(dettagli, x, y, raggio) {
|
||||||
|
dettagli.forEach((d) => {
|
||||||
|
ctx.fillStyle = d.colore;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x + (d.x || 0), y + (d.y || 0), d.r || 1, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaCorpo(c, stellaX, stellaY, angolo) {
|
||||||
|
const a = c.asseMaggiore;
|
||||||
|
const b = a * Math.sqrt(1 - c.eccentricita * c.eccentricita);
|
||||||
|
const x = stellaX + a * Math.cos(angolo);
|
||||||
|
const y = stellaY + b * Math.sin(angolo);
|
||||||
|
|
||||||
|
disegnaOrbita(stellaX, stellaY, a, b);
|
||||||
|
|
||||||
|
if (c.anelli) {
|
||||||
|
disegnaAnelli(x, y, c.raggio);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillStyle = c.colore;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, c.raggio, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
if (c.dettagli) {
|
||||||
|
disegnaDettaglioPianeta(c.dettagli, x, y, c.raggio);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = stellaX + Math.cos(angoloM) * raggioM;
|
||||||
|
const my = stellaY + Math.sin(angoloM) * raggioM;
|
||||||
|
|
||||||
|
ctx.strokeStyle = "rgba(255,255,255,0.04)";
|
||||||
|
ctx.lineWidth = 0.5;
|
||||||
|
ctx.setLineDash([2, 2]);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(stellaX, stellaY, raggioM, 0, Math.PI * 2);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
|
||||||
|
ctx.fillStyle = coloriM[i];
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(mx, my, 2, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.fillStyle = "rgba(255,255,255,0.3)";
|
||||||
|
ctx.font = "7px monospace";
|
||||||
|
ctx.fillText(c.nomiMondi?.[i] || "", mx + 3, my - 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillStyle = "rgba(255,255,255,0.6)";
|
||||||
|
ctx.font = "11px monospace";
|
||||||
|
ctx.fillText(c.nome, x + c.raggio + 5, y - 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaLineaTraStelle(x1, y1, x2, y2) {
|
||||||
|
ctx.strokeStyle = "rgba(255,255,255,0.03)";
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.setLineDash([5, 10]);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x1, y1);
|
||||||
|
ctx.lineTo(x2, y2);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function animazione() {
|
||||||
|
ctx.fillStyle = "#000010";
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
disegnaStelle();
|
||||||
|
|
||||||
|
const cx = canvas.width / 2;
|
||||||
|
const cy = canvas.height / 2;
|
||||||
|
|
||||||
|
// Calcola posizioni stelle relative al baricentro
|
||||||
|
const posizioniStelle = [];
|
||||||
|
|
||||||
|
stelle.forEach((s) => {
|
||||||
|
const a = s.asseMaggiore;
|
||||||
|
const b = a * Math.sqrt(1 - s.eccentricita * s.eccentricita);
|
||||||
|
const angolo = tempo * s.velocita + s.angoloIniziale;
|
||||||
|
const x = cx + a * Math.cos(angolo);
|
||||||
|
const y = cy + b * Math.sin(angolo);
|
||||||
|
posizioniStelle.push({ x, y });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calcola baricentro del triangolo formato dalle tre stelle
|
||||||
|
const baricentroX = (posizioniStelle[0].x + posizioniStelle[1].x + posizioniStelle[2].x) / 3;
|
||||||
|
const baricentroY = (posizioniStelle[0].y + posizioniStelle[1].y + posizioniStelle[2].y) / 3;
|
||||||
|
|
||||||
|
// Offset per centrare tutto il sistema sul canvas
|
||||||
|
const offsetX = baricentroX - cx;
|
||||||
|
const offsetY = baricentroY - cy;
|
||||||
|
|
||||||
|
// Linea tra Alpha Cen A e B
|
||||||
|
disegnaLineaTraStelle(
|
||||||
|
posizioniStelle[0].x - offsetX,
|
||||||
|
posizioniStelle[0].y - offsetY,
|
||||||
|
posizioniStelle[1].x - offsetX,
|
||||||
|
posizioniStelle[1].y - offsetY
|
||||||
|
);
|
||||||
|
|
||||||
|
// Disegna stelle
|
||||||
|
stelle.forEach((s, i) => {
|
||||||
|
disegnaStella(s, posizioniStelle[i].x - offsetX, posizioniStelle[i].y - offsetY);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pianeti
|
||||||
|
pianeti.forEach(([stellaIdx, p]) => {
|
||||||
|
const angolo = tempo * p.velocita;
|
||||||
|
disegnaCorpo(p, posizioniStelle[stellaIdx].x - offsetX, posizioniStelle[stellaIdx].y - offsetY, angolo);
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.fillStyle = "rgba(255,255,255,0.4)";
|
||||||
|
ctx.font = "10px monospace";
|
||||||
|
ctx.fillText(
|
||||||
|
"Alpha Centauri — Sistema triplo (4.37 anni luce) | A+B orbita: 79.9 anni | Proxima orbita: 547 anni",
|
||||||
|
10,
|
||||||
|
canvas.height - 10
|
||||||
|
);
|
||||||
|
|
||||||
|
tempo += 0.005;
|
||||||
|
requestAnimationFrame(animazione);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", () => {
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
animazione();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,445 @@
|
|||||||
|
// === SISTEMA ALPHA CENTAURI — Simulazione TypeScript ===
|
||||||
|
// Sistema triplo: Alpha Cen A + Alpha Cen B + Proxima Centauri
|
||||||
|
// Distanza totale: 4.37 anni luce dalla Terra
|
||||||
|
|
||||||
|
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;
|
||||||
|
dettagli?: Dettaglio[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Dettaglio {
|
||||||
|
tipo: string;
|
||||||
|
colore: string;
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
|
r?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const canvas = document.getElementById("alpha") as HTMLCanvasElement;
|
||||||
|
const ctx = canvas.getContext("2d")!;
|
||||||
|
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
|
||||||
|
// === DATI ASTROFISICI ===
|
||||||
|
// Alpha Centauri A (Rigil Kentaurus): G2V, 1.1 M☉, 1.221 R☉
|
||||||
|
// Alpha Centauri B (Toliman): K1V, 0.907 M☉, 0.863 R☉
|
||||||
|
// Orbita A-B: a=23.4 UA, e=0.517, P=79.91 anni
|
||||||
|
// Proxima Centauri: M5.5Ve, 0.122 M☉
|
||||||
|
// Orbita Proxima: a~125 UA, e~0.509, P~547 anni
|
||||||
|
|
||||||
|
// === PIANETI CONFERMATI ===
|
||||||
|
// Alpha Cen Bb: 0.011 M⊕, 3.236 giorni, 0.064 UA (controverso)
|
||||||
|
// Alpha Cen Ab: 1.1 M⊕, 224 giorni, ~1.25 UA (confermato HARPS 2012)
|
||||||
|
// Proxima d: 0.26 M⊕, 5.122 giorni, 0.029 UA (2022)
|
||||||
|
// Proxima b: 1.07 M⊕, 11.186 giorni, 0.0485 UA (2016)
|
||||||
|
// Proxima c: ~7 M⊕, ~5.28 anni, ~1.489 UA (2019)
|
||||||
|
|
||||||
|
let tempo = 0;
|
||||||
|
|
||||||
|
// Stelle del sistema triplo
|
||||||
|
interface Stella {
|
||||||
|
nome: string;
|
||||||
|
tipo: string;
|
||||||
|
colore: string;
|
||||||
|
raggio: number;
|
||||||
|
asseMaggiore: number;
|
||||||
|
eccentricita: number;
|
||||||
|
velocita: number;
|
||||||
|
angoloIniziale: number;
|
||||||
|
massa: string;
|
||||||
|
coloreTesto: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stelle: Stella[] = [
|
||||||
|
{
|
||||||
|
nome: "Alpha Cen A",
|
||||||
|
tipo: "G2V",
|
||||||
|
colore: "#fff4e0",
|
||||||
|
raggio: 18,
|
||||||
|
asseMaggiore: 175,
|
||||||
|
eccentricita: 0.517,
|
||||||
|
velocita: 0.0125, // P=79.91 anni → scala
|
||||||
|
angoloIniziale: 0,
|
||||||
|
massa: "1.1 M☉",
|
||||||
|
coloreTesto: "rgba(255,244,224,0.8)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: "Alpha Cen B",
|
||||||
|
tipo: "K1V",
|
||||||
|
colore: "#ffcc66",
|
||||||
|
raggio: 14,
|
||||||
|
asseMaggiore: 215,
|
||||||
|
eccentricita: 0.517,
|
||||||
|
velocita: 0.0125,
|
||||||
|
angoloIniziale: Math.PI, // opposto ad A
|
||||||
|
massa: "0.907 M☉",
|
||||||
|
coloreTesto: "rgba(255,204,102,0.8)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nome: "Proxima Cen",
|
||||||
|
tipo: "M5.5Ve",
|
||||||
|
colore: "#cc4422",
|
||||||
|
raggio: 10,
|
||||||
|
asseMaggiore: 650,
|
||||||
|
eccentricita: 0.509,
|
||||||
|
velocita: 0.00091, // P=547 anni → scala
|
||||||
|
angoloIniziale: Math.PI * 0.7,
|
||||||
|
massa: "0.122 M☉",
|
||||||
|
coloreTesto: "rgba(255,150,100,0.8)",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Pianeti: [stella_ospite, dati_pianeta]
|
||||||
|
type Pianeta = [number, CorpoCeleste];
|
||||||
|
|
||||||
|
const pianeti: Pianeta[] = [
|
||||||
|
// === SU ALPHA CENTAURI B ===
|
||||||
|
[
|
||||||
|
1, // Alpha Cen B
|
||||||
|
{
|
||||||
|
nome: "Alpha Cen Ab",
|
||||||
|
raggio: 7,
|
||||||
|
asseMaggiore: 55,
|
||||||
|
eccentricita: 0.02,
|
||||||
|
velocita: 1.63, // 224 giorni → scala: 10s orbita
|
||||||
|
colore: "#5a8ab5",
|
||||||
|
angoli: [0],
|
||||||
|
dettagli: [
|
||||||
|
{ tipo: "oceano", colore: "#3a6a9a", x: -2, y: -1, r: 3 },
|
||||||
|
{ tipo: "continente", colore: "#4a8a4a", x: 2, y: 1, r: 2.5 },
|
||||||
|
{ tipo: "nuvola", colore: "rgba(255,255,255,0.25)", x: 0, y: -2, r: 1.5 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
// === SU PROXIMA CENTAURI ===
|
||||||
|
[
|
||||||
|
2, // Proxima Centauri
|
||||||
|
{
|
||||||
|
nome: "Proxima d",
|
||||||
|
raggio: 3,
|
||||||
|
asseMaggiore: 30,
|
||||||
|
eccentricita: 0.04,
|
||||||
|
velocita: 2.094, // 10 secondi orbita
|
||||||
|
colore: "#6b7b8d",
|
||||||
|
angoli: [0],
|
||||||
|
dettagli: [
|
||||||
|
{ tipo: "superficie", colore: "#5a6a7a", x: -1, y: -1, r: 1.5 },
|
||||||
|
{ tipo: "superficie", colore: "#7a8a9a", x: 1, y: 1, r: 1 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
nome: "Proxima b",
|
||||||
|
raggio: 8,
|
||||||
|
asseMaggiore: 50,
|
||||||
|
eccentricita: 0.11,
|
||||||
|
velocita: 0.959, // ~22 secondi orbita
|
||||||
|
colore: "#4a90d9",
|
||||||
|
angoli: [0],
|
||||||
|
dettagli: [
|
||||||
|
{ tipo: "oceano", colore: "#2a5a8a", x: -3, y: -2, r: 4 },
|
||||||
|
{ tipo: "continente", colore: "#3a7a3a", x: 2, y: 1, r: 3 },
|
||||||
|
{ tipo: "continente", colore: "#4a8a4a", x: -1, y: 3, r: 2.5 },
|
||||||
|
{ tipo: "nuvola", colore: "rgba(255,255,255,0.3)", x: 0, y: -3, r: 2 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
nome: "Proxima c",
|
||||||
|
raggio: 14,
|
||||||
|
asseMaggiore: 200,
|
||||||
|
eccentricita: 0.04,
|
||||||
|
velocita: 0.00559, // ~62 minuti orbita
|
||||||
|
colore: "#c8944a",
|
||||||
|
angoli: [0],
|
||||||
|
raggiMondi: [35, 55, 75],
|
||||||
|
coloriMondi: ["#d4c4a4", "#b4a484", "#948464"],
|
||||||
|
nomiMondi: ["Candidate-m1", "Candidate-m2", "Candidate-m3"],
|
||||||
|
velocitaMondi: [8, 4, 2.5],
|
||||||
|
anelli: true,
|
||||||
|
dettagli: [
|
||||||
|
{ tipo: "banda", colore: "#a8743a", y: -4 },
|
||||||
|
{ tipo: "banda", colore: "#b8844a", y: 0 },
|
||||||
|
{ tipo: "banda", colore: "#98642a", y: 4 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// === FUNZIONI DI DISEGNO ===
|
||||||
|
|
||||||
|
function disegnaStelle() {
|
||||||
|
stelleSfondo.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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaStella(stella: Stella, x: number, y: number) {
|
||||||
|
// Glow
|
||||||
|
const grad = ctx.createRadialGradient(x, y, stella.raggio * 0.3, x, y, stella.raggio * 4);
|
||||||
|
grad.addColorStop(0, stella.colore);
|
||||||
|
grad.addColorStop(0.3, stella.colore.replace(")", ",0.5)").replace("#", "rgba(").replace(/(..)(..)(..)/, (m, r, g, b) => {
|
||||||
|
const red = parseInt(r, 16);
|
||||||
|
const green = parseInt(g, 16);
|
||||||
|
const blue = parseInt(b, 16);
|
||||||
|
return `rgba(${red},${green},${blue}`;
|
||||||
|
}) + ",0.3)");
|
||||||
|
grad.addColorStop(1, "rgba(0,0,0,0)");
|
||||||
|
|
||||||
|
// Glow semplificato per tipo di stella
|
||||||
|
if (stella.tipo === "G2V") {
|
||||||
|
// Gialla come il Sole
|
||||||
|
const g = ctx.createRadialGradient(x, y, stella.raggio * 0.5, x, y, stella.raggio * 5);
|
||||||
|
g.addColorStop(0, "#fff8e0");
|
||||||
|
g.addColorStop(0.2, "#ffdd44");
|
||||||
|
g.addColorStop(0.5, "rgba(255,221,68,0.2)");
|
||||||
|
g.addColorStop(1, "rgba(255,200,0,0)");
|
||||||
|
ctx.fillStyle = g;
|
||||||
|
} else if (stella.tipo === "K1V") {
|
||||||
|
// Arancione
|
||||||
|
const g = ctx.createRadialGradient(x, y, stella.raggio * 0.5, x, y, stella.raggio * 4);
|
||||||
|
g.addColorStop(0, "#ffe0a0");
|
||||||
|
g.addColorStop(0.2, "#ffaa44");
|
||||||
|
g.addColorStop(0.5, "rgba(255,170,68,0.2)");
|
||||||
|
g.addColorStop(1, "rgba(255,150,0,0)");
|
||||||
|
ctx.fillStyle = g;
|
||||||
|
} else {
|
||||||
|
// Rossa (Proxima)
|
||||||
|
const g = ctx.createRadialGradient(x, y, stella.raggio * 0.3, x, y, stella.raggio * 4);
|
||||||
|
g.addColorStop(0, "rgba(255,100,50,0.8)");
|
||||||
|
g.addColorStop(0.2, "rgba(200,50,20,0.4)");
|
||||||
|
g.addColorStop(0.5, "rgba(150,30,10,0.1)");
|
||||||
|
g.addColorStop(1, "rgba(100,20,5,0)");
|
||||||
|
ctx.fillStyle = g;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, stella.raggio * 5, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Stella
|
||||||
|
ctx.fillStyle = stella.colore;
|
||||||
|
ctx.shadowColor = stella.colore;
|
||||||
|
ctx.shadowBlur = stella.raggio * 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, stella.raggio, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.shadowBlur = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaAnelli(cx: number, cy: number, raggio: number) {
|
||||||
|
ctx.strokeStyle = "rgba(210, 180, 140, 0.4)";
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.ellipse(cx, cy, raggio * 2.0, raggio * 0.5, 0, 0, Math.PI * 2);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.strokeStyle = "rgba(190, 160, 120, 0.25)";
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.ellipse(cx, cy, raggio * 2.4, raggio * 0.6, 0, 0, Math.PI * 2);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaOrbita(cx: number, cy: number, a: number, b: number) {
|
||||||
|
ctx.strokeStyle = "rgba(255,255,255,0.06)";
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.setLineDash([3, 3]);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.ellipse(cx, cy, a, b, 0, 0, Math.PI * 2);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaDettaglioPianeta(
|
||||||
|
dettagli: Dettaglio[],
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
raggio: number
|
||||||
|
) {
|
||||||
|
dettagli.forEach((d) => {
|
||||||
|
ctx.fillStyle = d.colore;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x + (d.x || 0), y + (d.y || 0), d.r || 1, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaCorpo(
|
||||||
|
c: CorpoCeleste,
|
||||||
|
stellaX: number,
|
||||||
|
stellaY: number,
|
||||||
|
angolo: number
|
||||||
|
) {
|
||||||
|
const a = c.asseMaggiore;
|
||||||
|
const b = a * Math.sqrt(1 - c.eccentricita * c.eccentricita);
|
||||||
|
const x = stellaX + a * Math.cos(angolo);
|
||||||
|
const y = stellaY + b * Math.sin(angolo);
|
||||||
|
|
||||||
|
// Orbita
|
||||||
|
disegnaOrbita(stellaX, stellaY, a, b);
|
||||||
|
|
||||||
|
// Anelli
|
||||||
|
if (c.anelli) {
|
||||||
|
disegnaAnelli(x, y, c.raggio);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pianeta
|
||||||
|
ctx.fillStyle = c.colore;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, c.raggio, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Dettagli
|
||||||
|
if (c.dettagli) {
|
||||||
|
disegnaDettaglioPianeta(c.dettagli, x, y, c.raggio);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = stellaX + Math.cos(angoloM) * raggioM;
|
||||||
|
const my = stellaY + Math.sin(angoloM) * raggioM;
|
||||||
|
|
||||||
|
ctx.strokeStyle = "rgba(255,255,255,0.04)";
|
||||||
|
ctx.lineWidth = 0.5;
|
||||||
|
ctx.setLineDash([2, 2]);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(stellaX, stellaY, raggioM, 0, Math.PI * 2);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
|
||||||
|
ctx.fillStyle = coloriM[i];
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(mx, my, 2, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.fillStyle = "rgba(255,255,255,0.3)";
|
||||||
|
ctx.font = "7px monospace";
|
||||||
|
ctx.fillText(c.nomiMondi?.[i] || "", mx + 3, my - 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Etichetta
|
||||||
|
ctx.fillStyle = "rgba(255,255,255,0.6)";
|
||||||
|
ctx.font = "11px monospace";
|
||||||
|
ctx.fillText(c.nome, x + c.raggio + 5, y - 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaLineaTraStelle(x1: number, y1: number, x2: number, y2: number) {
|
||||||
|
ctx.strokeStyle = "rgba(255,255,255,0.03)";
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.setLineDash([5, 10]);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x1, y1);
|
||||||
|
ctx.lineTo(x2, y2);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function animazione() {
|
||||||
|
// Sfondo
|
||||||
|
ctx.fillStyle = "#000010";
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Stelle
|
||||||
|
disegnaStelle();
|
||||||
|
|
||||||
|
// Centro del sistema (baricentro A-B)
|
||||||
|
const cx = canvas.width / 2;
|
||||||
|
const cy = canvas.height / 2;
|
||||||
|
|
||||||
|
// Calcola posizioni stelle relative al baricentro
|
||||||
|
const posizioniStelle: { x: number; y: number }[] = [];
|
||||||
|
|
||||||
|
stelle.forEach((s) => {
|
||||||
|
const a = s.asseMaggiore;
|
||||||
|
const b = a * Math.sqrt(1 - s.eccentricita * s.eccentricita);
|
||||||
|
const angolo = tempo * s.velocita + s.angoloIniziale;
|
||||||
|
const x = cx + a * Math.cos(angolo);
|
||||||
|
const y = cy + b * Math.sin(angolo);
|
||||||
|
posizioniStelle.push({ x, y });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calcola baricentro del triangolo formato dalle tre stelle
|
||||||
|
const baricentroX = (posizioniStelle[0].x + posizioniStelle[1].x + posizioniStelle[2].x) / 3;
|
||||||
|
const baricentroY = (posizioniStelle[0].y + posizioniStelle[1].y + posizioniStelle[2].y) / 3;
|
||||||
|
|
||||||
|
// Offset per centrare tutto il sistema sul canvas
|
||||||
|
const offsetX = baricentroX - cx;
|
||||||
|
const offsetY = baricentroY - cy;
|
||||||
|
|
||||||
|
// Linea tra Alpha Cen A e B
|
||||||
|
disegnaLineaTraStelle(
|
||||||
|
posizioniStelle[0].x - offsetX,
|
||||||
|
posizioniStelle[0].y - offsetY,
|
||||||
|
posizioniStelle[1].x - offsetX,
|
||||||
|
posizioniStelle[1].y - offsetY
|
||||||
|
);
|
||||||
|
|
||||||
|
// Disegna stelle
|
||||||
|
stelle.forEach((s, i) => {
|
||||||
|
disegnaStella(s, posizioniStelle[i].x - offsetX, posizioniStelle[i].y - offsetY);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pianeti
|
||||||
|
pianeti.forEach(([stellaIdx, p]) => {
|
||||||
|
const angolo = tempo * p.velocita;
|
||||||
|
disegnaCorpo(p, posizioniStella[stellaIdx].x - offsetX, posizioniStelle[stellaIdx].y - offsetY, angolo);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Info
|
||||||
|
ctx.fillStyle = "rgba(255,255,255,0.4)";
|
||||||
|
ctx.font = "10px monospace";
|
||||||
|
ctx.fillText(
|
||||||
|
"Alpha Centauri — Sistema triplo (4.37 anni luce) | A+B orbita: 79.9 anni | Proxima orbita: 547 anni",
|
||||||
|
10,
|
||||||
|
canvas.height - 10
|
||||||
|
);
|
||||||
|
|
||||||
|
tempo += 0.005;
|
||||||
|
requestAnimationFrame(animazione);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stelle di sfondo
|
||||||
|
const stelleSfondo: { x: number; y: number; r: number; a: number }[] = [];
|
||||||
|
for (let i = 0; i < 300; i++) {
|
||||||
|
stelleSfondo.push({
|
||||||
|
x: Math.random() * canvas.width,
|
||||||
|
y: Math.random() * canvas.height,
|
||||||
|
r: Math.random() * 1.2,
|
||||||
|
a: Math.random() * 0.5 + 0.2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize
|
||||||
|
window.addEventListener("resize", () => {
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Avvia
|
||||||
|
animazione();
|
||||||
Executable
+24
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 1. Chromium in modalità app, senza vero fullscreen, dimensionato allo schermo
|
||||||
|
chromium index.html \
|
||||||
|
--window-position=0,0 --window-size=1920,1080 \
|
||||||
|
--disable-infobars --noerrdialogs --disable-session-crashed-bubble &
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# 2. Trova l'ID finestra
|
||||||
|
WID=$(xdotool search --sync --onlyvisible --class "chromium" | head -1)
|
||||||
|
|
||||||
|
# 3. Riposiziona/ridimensiona per sicurezza
|
||||||
|
xdotool windowmove "$WID" 0 0
|
||||||
|
xdotool windowsize "$WID" 1920 1080
|
||||||
|
|
||||||
|
# 4. Marcala come finestra "desktop" e mandala in fondo allo stack
|
||||||
|
wmctrl -i -r "$WID" -b add,below
|
||||||
|
wmctrl -i -r "$WID" -b add,sticky,skip_taskbar,skip_pager
|
||||||
|
xprop -id "$WID" -f _NET_WM_WINDOW_TYPE 32a \
|
||||||
|
-set _NET_WM_WINDOW_TYPE _NET_WM_WINDOW_TYPE_DESKTOP
|
||||||
|
|
||||||
|
# 5. Forza nemo-desktop a ridisegnarsi sopra (necessario spesso su Mint)
|
||||||
|
killall nemo-desktop 2>/dev/null
|
||||||
|
nemo-desktop &
|
||||||
@@ -0,0 +1,455 @@
|
|||||||
|
// === 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();
|
||||||
@@ -0,0 +1,353 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="it">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Sistema Solare</title>
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body { overflow: hidden; background: #000; }
|
||||||
|
canvas { display: block; }
|
||||||
|
#info {
|
||||||
|
position: fixed;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
color: rgba(255,255,255,0.5);
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="sistema"></canvas>
|
||||||
|
<div id="info">Sistema Solare — Simulazione</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// === SISTEMA SOLARE — JavaScript (da sistema-solare.ts) ===
|
||||||
|
|
||||||
|
let tempo = 0;
|
||||||
|
|
||||||
|
// === 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
|
||||||
|
const cometaHalley = {
|
||||||
|
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 = [
|
||||||
|
{ 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] },
|
||||||
|
];
|
||||||
|
|
||||||
|
const canvas = document.getElementById("sistema");
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
|
||||||
|
// Stelle
|
||||||
|
const stelle = [];
|
||||||
|
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 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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaSole(cx, cy) {
|
||||||
|
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();
|
||||||
|
|
||||||
|
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, cy, raggio) {
|
||||||
|
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, cy, a, b) {
|
||||||
|
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, cx, cy, angolo) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
disegnaOrbita(cx, cy, a, b);
|
||||||
|
|
||||||
|
if (c.anelli) {
|
||||||
|
disegnaAnelliSaturno(x, y, c.raggio);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillStyle = c.colore;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, c.raggio, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Dettaglio Giove
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
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
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lune
|
||||||
|
if (c.raggiMondi && 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;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
ctx.fillStyle = c.coloriMondi[i];
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(mx, my, 2, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.fillStyle = "rgba(255,255,255,0.4)";
|
||||||
|
ctx.font = "8px monospace";
|
||||||
|
ctx.fillText(c.nomiMondi?.[i] || "", mx + 4, my - 4);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Etichetta
|
||||||
|
ctx.fillStyle = "rgba(255,255,255,0.6)";
|
||||||
|
ctx.font = "11px monospace";
|
||||||
|
ctx.fillText(c.nome, x + c.raggio + 5, y - 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
function animazione() {
|
||||||
|
ctx.fillStyle = "#000010";
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
disegnaStelle();
|
||||||
|
|
||||||
|
const cx = canvas.width / 2;
|
||||||
|
const cy = canvas.height / 2;
|
||||||
|
|
||||||
|
disegnaSole(cx, cy);
|
||||||
|
|
||||||
|
corpo.forEach((c) => {
|
||||||
|
const angolo = tempo * c.velocita;
|
||||||
|
disegnaCorpo(c, cx, cy, angolo);
|
||||||
|
});
|
||||||
|
|
||||||
|
// === COMETA DI HALLEY ===
|
||||||
|
disegnaCometaHalley(cx, cy);
|
||||||
|
|
||||||
|
tempo += 0.005;
|
||||||
|
requestAnimationFrame(animazione);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disegnaCometaHalley(cx, cy) {
|
||||||
|
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, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", () => {
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
animazione();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"""webpaper — pagina web come sfondo del desktop."""
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
@@ -0,0 +1,311 @@
|
|||||||
|
"""webpaper — pagina web come sfondo del desktop.
|
||||||
|
|
||||||
|
Ispirato a Hidamari (https://github.com/jeffshee/hidamari), video wallpaper
|
||||||
|
per Linux, dal quale è stato estratto il meccanismo della pagina web:
|
||||||
|
|
||||||
|
una normale Gtk.ApplicationWindow piena di una WebKit2.WebView viene marcata
|
||||||
|
come finestra DESKTOP (Gdk.WindowTypeHint.DESKTOP): il window manager la
|
||||||
|
tiene in fondo allo stack, sotto ogni altra finestra, esattamente come lo
|
||||||
|
sfondo nativo. Viene creata una finestra per ogni monitor, dimensionata e
|
||||||
|
posizionata sulla geometria del monitor stesso.
|
||||||
|
|
||||||
|
Uso:
|
||||||
|
python3 -m webpaper /percorso/pagina.html
|
||||||
|
python3 -m webpaper https://esempio.com
|
||||||
|
python3 -m webpaper pagina1.html pagina2.html # dual monitor: una pagina per monitor
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import glob
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Le finestre con WindowTypeHint.DESKTOP funzionano solo su X11.
|
||||||
|
# Su una sessione Wayland, GDK ricade automaticamente su XWayland.
|
||||||
|
os.environ["GDK_BACKEND"] = "x11"
|
||||||
|
|
||||||
|
import gi # noqa: E402
|
||||||
|
|
||||||
|
gi.require_version("Gtk", "3.0")
|
||||||
|
gi.require_version("Gdk", "3.0")
|
||||||
|
gi.require_version("WebKit2", "4.1")
|
||||||
|
from gi.repository import Gdk, GLib, Gtk, WebKit2 # noqa: E402
|
||||||
|
|
||||||
|
logger = logging.getLogger("webpaper")
|
||||||
|
|
||||||
|
# Disattivabile con --no-fix-icons
|
||||||
|
_fix_icons_enabled = True
|
||||||
|
|
||||||
|
# Al login le icone desktop (nemo-desktop/DING) potrebbero non esserci ancora:
|
||||||
|
# riprova alcune volte prima di rinunciare.
|
||||||
|
_fix_attempts = 0
|
||||||
|
MAX_FIX_ATTEMPTS = 4
|
||||||
|
|
||||||
|
# Classi delle finestre che disegnano le icone desktop, per DE:
|
||||||
|
# Cinnamon -> nemo-desktop
|
||||||
|
# GNOME (DING/desktop icons) -> org.gnome.NautilusDesktopWindow
|
||||||
|
DESKTOP_ICONS_CLASSES = ("nemo-desktop", "org.gnome.NautilusDesktopWindow")
|
||||||
|
|
||||||
|
|
||||||
|
def fix_desktop_icons():
|
||||||
|
"""Porta sopra le icone della scrivania, sotto il wallpaper.
|
||||||
|
|
||||||
|
Le icone desktop (es. nemo-desktop su Cinnamon, DING su GNOME) sono a loro
|
||||||
|
volta finestre di tipo DESKTOP: quando il nostro wallpaper viene mappato
|
||||||
|
dopo di loro, il window manager lo impila sopra e le icone spariscono.
|
||||||
|
Su Muffin (Cinnamon) funziona attivarle (windowactivate), non alzarle
|
||||||
|
(windowraise). Richiede xdotool: se non è installato o non ci sono
|
||||||
|
finestre corrispondenti, non fa nulla.
|
||||||
|
"""
|
||||||
|
if not _fix_icons_enabled:
|
||||||
|
return False
|
||||||
|
global _fix_attempts
|
||||||
|
try:
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
if shutil.which("xdotool") is None:
|
||||||
|
logger.debug("xdotool non disponibile: icone desktop non sistemate")
|
||||||
|
return False
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
found = False
|
||||||
|
for cls in DESKTOP_ICONS_CLASSES:
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["xdotool", "search", "--class", cls],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
continue
|
||||||
|
window_ids = result.stdout.split()
|
||||||
|
if not window_ids:
|
||||||
|
continue
|
||||||
|
found = True
|
||||||
|
# Nota: attivare le finestre una alla volta (non la forma
|
||||||
|
# concatenata `search ... windowactivate`): su Muffin solo cosi'
|
||||||
|
# le finestre DESKTOP vengono rimesse sopra il wallpaper.
|
||||||
|
for window_id in window_ids:
|
||||||
|
subprocess.run(
|
||||||
|
["xdotool", "windowactivate", window_id],
|
||||||
|
capture_output=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
logger.info("Icone desktop (%s) portate sopra il wallpaper", cls)
|
||||||
|
except (subprocess.TimeoutExpired, OSError):
|
||||||
|
logger.debug("xdotool non riuscito per %s", cls)
|
||||||
|
_fix_attempts += 1
|
||||||
|
if not found and _fix_attempts < MAX_FIX_ATTEMPTS:
|
||||||
|
return True # per GLib.timeout_add: riprova tra 2 s
|
||||||
|
_fix_attempts = 0
|
||||||
|
return False # per GLib.timeout_add: esegui una sola volta
|
||||||
|
|
||||||
|
|
||||||
|
def setup_render_device():
|
||||||
|
"""Workaround NVIDIA.
|
||||||
|
|
||||||
|
Il renderer DMA-BUF di WebKitGTK non riesce ad allocare buffer GBM sui
|
||||||
|
driver NVIDIA ("Failed to create GBM buffer: Invalid argument"), e la
|
||||||
|
pagina resterebbe bianca. Se è presente una GPU NVIDIA, si punta WebKit su
|
||||||
|
un render node Mesa oppure, su sistemi solo-NVIDIA, si ripiega su buffer
|
||||||
|
condivisi (SHM). Va chiamato prima della creazione della prima WebView.
|
||||||
|
"""
|
||||||
|
overrides = (
|
||||||
|
"WEBKIT_WEB_RENDER_DEVICE_FILE",
|
||||||
|
"WEBKIT_DMABUF_RENDERER_FORCE_SHM",
|
||||||
|
"WEBKIT_DISABLE_DMABUF_RENDERER",
|
||||||
|
)
|
||||||
|
if any(var in os.environ for var in overrides):
|
||||||
|
return
|
||||||
|
drivers = {}
|
||||||
|
for node in sorted(glob.glob("/dev/dri/renderD*")):
|
||||||
|
driver = os.path.realpath(
|
||||||
|
f"/sys/class/drm/{os.path.basename(node)}/device/driver"
|
||||||
|
)
|
||||||
|
drivers[node] = os.path.basename(driver)
|
||||||
|
if "nvidia" not in drivers.values():
|
||||||
|
return
|
||||||
|
for node, driver in drivers.items():
|
||||||
|
if driver != "nvidia":
|
||||||
|
logger.info("Usando render device %s (%s)", node, driver)
|
||||||
|
os.environ["WEBKIT_WEB_RENDER_DEVICE_FILE"] = node
|
||||||
|
return
|
||||||
|
logger.info("Sistema solo-NVIDIA: buffer SHM per WebKit")
|
||||||
|
os.environ["WEBKIT_DMABUF_RENDERER_FORCE_SHM"] = "1"
|
||||||
|
|
||||||
|
|
||||||
|
def to_uri(source):
|
||||||
|
"""Converte un percorso locale in un URI file://.
|
||||||
|
|
||||||
|
Gli URL (http/https) e gli URI file:// già completi vengono lasciati
|
||||||
|
invariati. Solleva FileNotFoundError se un percorso locale non esiste.
|
||||||
|
"""
|
||||||
|
if source.startswith(("http://", "https://", "file://")):
|
||||||
|
return source
|
||||||
|
path = pathlib.Path(source).expanduser()
|
||||||
|
if not path.is_file():
|
||||||
|
raise FileNotFoundError(f"File non trovato: {source}")
|
||||||
|
return path.resolve().as_uri()
|
||||||
|
|
||||||
|
|
||||||
|
class WebWindow(Gtk.ApplicationWindow):
|
||||||
|
"""Finestra DESKTOP che contiene la pagina web (una WebView a tutto schermo)."""
|
||||||
|
|
||||||
|
def __init__(self, uri, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.uri = uri
|
||||||
|
self.webview = WebKit2.WebView()
|
||||||
|
self.add(self.webview)
|
||||||
|
self.webview.show()
|
||||||
|
# Click destro: menu con Ricarica / Esci
|
||||||
|
self.webview.connect("button-press-event", self._on_button_press_event)
|
||||||
|
self._menu = None
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
self.webview.load_uri(self.uri)
|
||||||
|
|
||||||
|
def _on_button_press_event(self, widget, event):
|
||||||
|
if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
|
||||||
|
if self._menu is None:
|
||||||
|
self._menu = Gtk.Menu()
|
||||||
|
reload_item = Gtk.MenuItem(label="Ricarica")
|
||||||
|
reload_item.connect("activate", lambda *_: self.webview.reload())
|
||||||
|
self._menu.append(reload_item)
|
||||||
|
quit_item = Gtk.MenuItem(label="Esci")
|
||||||
|
quit_item.connect("activate", lambda *_: self.get_application().quit())
|
||||||
|
self._menu.append(quit_item)
|
||||||
|
self._menu.show_all()
|
||||||
|
self._menu.popup_at_pointer()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class WebWallpaper(Gtk.Application):
|
||||||
|
"""Applicazione: una finestra DESKTOP per ogni monitor."""
|
||||||
|
|
||||||
|
def __init__(self, uris):
|
||||||
|
super().__init__(application_id="org.webpaper.wallpaper")
|
||||||
|
self.uris = uris
|
||||||
|
self.windows = {} # Gdk.Monitor -> WebWindow
|
||||||
|
self._monitor_detect()
|
||||||
|
if len(self.uris) > len(self.windows):
|
||||||
|
logger.warning(
|
||||||
|
"%d sorgenti ma solo %d monitor: le sorgenti in eccesso verranno ignorate",
|
||||||
|
len(self.uris),
|
||||||
|
len(self.windows),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _uri_for_index(self, i):
|
||||||
|
"""Sorgente per l'i-esimo monitor.
|
||||||
|
|
||||||
|
Una sola sorgente => mostrata su tutti i monitor (compatibilità col
|
||||||
|
comportamento originale). Con N sorgenti => mappatura 1:1 nell'ordine
|
||||||
|
dei monitor; eventuali monitor in più ripetono l'ultima sorgente.
|
||||||
|
"""
|
||||||
|
return self.uris[min(i, len(self.uris) - 1)]
|
||||||
|
|
||||||
|
# -- gestione monitor -------------------------------------------------
|
||||||
|
|
||||||
|
def _monitor_detect(self):
|
||||||
|
display = Gdk.Display.get_default()
|
||||||
|
screen = display.get_default_screen()
|
||||||
|
for i in range(display.get_n_monitors()):
|
||||||
|
self.windows[display.get_monitor(i)] = None
|
||||||
|
screen.connect("size-changed", self._on_size_changed)
|
||||||
|
display.connect("monitor-added", self._on_monitor_added)
|
||||||
|
display.connect("monitor-removed", self._on_monitor_removed)
|
||||||
|
|
||||||
|
def _on_size_changed(self, *args):
|
||||||
|
logger.info("size-changed")
|
||||||
|
for monitor, window in self.windows.items():
|
||||||
|
if window is not None:
|
||||||
|
self._place(window, monitor)
|
||||||
|
|
||||||
|
def _on_monitor_added(self, _, gdk_monitor, *args):
|
||||||
|
logger.info("monitor-added")
|
||||||
|
self.windows[gdk_monitor] = None
|
||||||
|
self._create_windows()
|
||||||
|
|
||||||
|
def _on_monitor_removed(self, _, gdk_monitor, *args):
|
||||||
|
logger.info("monitor-removed")
|
||||||
|
window = self.windows.pop(gdk_monitor, None)
|
||||||
|
if window is not None:
|
||||||
|
window.close()
|
||||||
|
|
||||||
|
# -- finestre ---------------------------------------------------------
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _place(window, monitor):
|
||||||
|
rect = monitor.get_geometry()
|
||||||
|
window.set_size_request(rect.width, rect.height)
|
||||||
|
window.move(rect.x, rect.y)
|
||||||
|
|
||||||
|
def _create_windows(self):
|
||||||
|
for i, (monitor, window) in enumerate(self.windows.items()):
|
||||||
|
uri = self._uri_for_index(i)
|
||||||
|
if window is None:
|
||||||
|
logger.info("Monitor %d -> %s", i, uri)
|
||||||
|
window = WebWindow(uri, application=self)
|
||||||
|
# Il trucco: hint DESKTOP => il WM la tratta come sfondo
|
||||||
|
window.set_type_hint(Gdk.WindowTypeHint.DESKTOP)
|
||||||
|
self._place(window, monitor)
|
||||||
|
window.load()
|
||||||
|
self.windows[monitor] = window
|
||||||
|
window.present()
|
||||||
|
# Lascia assestare il WM, poi porta le icone desktop sopra il wallpaper
|
||||||
|
# (riprovando un paio di volte: al login nemo-desktop potrebbe tardare)
|
||||||
|
GLib.timeout_add(1500, fix_desktop_icons)
|
||||||
|
|
||||||
|
def do_activate(self):
|
||||||
|
self._create_windows()
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv=None):
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="webpaper",
|
||||||
|
description="Usa una pagina web come sfondo del desktop (X11/XWayland).",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"source",
|
||||||
|
nargs="+",
|
||||||
|
help=(
|
||||||
|
"file HTML oppure URL (http://, https://, file://). "
|
||||||
|
"Con due sorgenti e due monitor, ogni monitor mostra la rispettiva pagina "
|
||||||
|
"(prima sorgente = primo monitor, seconda = secondo)."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
parser.add_argument("-d", "--debug", action="store_true", help="log di debug")
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-fix-icons",
|
||||||
|
action="store_true",
|
||||||
|
help="non sistemare le icone desktop (le icone resterebbero sotto il wallpaper)",
|
||||||
|
)
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.DEBUG if args.debug else logging.INFO,
|
||||||
|
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
uris = [to_uri(source) for source in args.source]
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
parser.error(str(e))
|
||||||
|
|
||||||
|
setup_render_device()
|
||||||
|
|
||||||
|
global _fix_icons_enabled
|
||||||
|
_fix_icons_enabled = not args.no_fix_icons
|
||||||
|
|
||||||
|
app = WebWallpaper(uris)
|
||||||
|
# Gtk.Application parsa sys.argv: togliamo i nostri argomenti
|
||||||
|
sys.argv = [sys.argv[0]]
|
||||||
|
return app.run(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user