1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use super::Animation;
use crate::render::Canvas;
use noise::{NoiseFn, Perlin};
/// Aurora borealis effect
pub struct Aurora {
noise: Perlin,
}
impl Aurora {
#[allow(unused_variables)]
pub fn new(_width: usize, _height: usize, _scale: f64) -> Self {
Aurora {
noise: Perlin::new(42),
}
}
}
impl Animation for Aurora {
fn name(&self) -> &str {
"aurora"
}
fn update(&mut self, canvas: &mut Canvas, _dt: f64, time: f64) {
let w = canvas.width as f64;
let h = canvas.height as f64;
let t = time * 0.3;
canvas.clear();
// Draw faint stars in background
let star_seed = 12345u64;
for i in 0..80 {
let sx = ((star_seed.wrapping_mul(i * 7 + 3)) % canvas.width as u64) as usize;
let sy = ((star_seed.wrapping_mul(i * 13 + 7)) % (canvas.height as u64 / 3)) as usize;
let twinkle = (t * 2.0 + i as f64 * 0.7).sin() * 0.5 + 0.5;
if twinkle > 0.3 {
let b = (twinkle * 120.0) as u8;
canvas.set_colored(sx, sy, twinkle * 0.4, b, b, (b as u16 + 30).min(255) as u8);
}
}
for x in 0..canvas.width {
let fx = x as f64 / w;
// Multiple curtain layers with distinct colors
for layer in 0..4 {
let offset = layer as f64 * 0.8;
let speed = 0.8 + layer as f64 * 0.2;
let n1 = self.noise.get([fx * 3.0 + offset, t * speed * 0.5]);
let n2 = self
.noise
.get([fx * 7.0 + offset + 10.0, t * speed * 0.3 + 5.0]);
let curtain_y = h * (0.08 + n1 * 0.2 + n2 * 0.06);
let curtain_height =
h * (0.35 + self.noise.get([fx * 2.0, t * 0.15 + offset]) * 0.2);
for y in 0..canvas.height {
let fy = y as f64;
let dist = fy - curtain_y;
if dist < 0.0 || dist > curtain_height {
continue;
}
let vert_fade = 1.0 - (dist / curtain_height);
let vert_fade = vert_fade * vert_fade;
let shimmer = self.noise.get([fx * 12.0, fy * 0.08, t * 2.5 + offset]);
let intensity = (vert_fade * (0.5 + shimmer * 0.5)).clamp(0.0, 1.0);
if intensity < 0.03 {
continue;
}
// Vivid aurora palette — each layer a different hue
let (r, g, b) = match layer {
0 => {
// Bright green (classic aurora)
(
(20.0 * intensity) as u8,
(180.0 + 75.0 * intensity) as u8,
(40.0 + 60.0 * intensity) as u8,
)
}
1 => {
// Cyan-teal
(
(20.0 + 40.0 * intensity) as u8,
(140.0 + 115.0 * intensity) as u8,
(160.0 + 95.0 * intensity) as u8,
)
}
2 => {
// Purple-magenta
(
(120.0 + 100.0 * intensity) as u8,
(20.0 + 50.0 * intensity) as u8,
(160.0 + 95.0 * intensity) as u8,
)
}
_ => {
// Pink-red (rare top layer)
(
(180.0 + 75.0 * intensity) as u8,
(30.0 + 40.0 * intensity) as u8,
(80.0 + 80.0 * intensity) as u8,
)
}
};
// Layer blend — brighter layer wins, colors mix via max
let idx = y * canvas.width + x;
let existing_b = canvas.pixels[idx];
if intensity > existing_b {
canvas.set_colored(x, y, intensity, r, g, b);
} else if intensity > existing_b * 0.5 {
// Blend colors when layers overlap
let (er, eg, eb) = canvas.colors[idx];
let t = intensity / existing_b.max(0.01);
let mr = (er as f64 * (1.0 - t) + r as f64 * t) as u8;
let mg = (eg as f64 * (1.0 - t) + g as f64 * t) as u8;
let mb = (eb as f64 * (1.0 - t) + b as f64 * t) as u8;
canvas.set_colored(x, y, existing_b, mr, mg, mb);
}
}
}
}
}
}