use macroquad::prelude::*;
use crate::scene::FrameContext;
#[derive(Debug, Clone, Copy)]
pub struct GlowStyle {
pub rings: usize,
pub spread: f32,
pub alpha: f32,
}
impl GlowStyle {
pub fn soft() -> Self {
Self {
rings: 5,
spread: 18.0,
alpha: 0.18,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Particle {
pub orbit_radius: f32,
pub speed: f32,
pub phase: f32,
pub size: f32,
pub height_bias: f32,
}
#[derive(Debug, Clone)]
pub struct ParticleField {
particles: Vec<Particle>,
}
impl ParticleField {
pub fn orbiting(count: usize, radius_start: f32, radius_end: f32) -> Self {
let count = count.max(1);
let mut particles = Vec::with_capacity(count);
for index in 0..count {
let t = index as f32 / count as f32;
particles.push(Particle {
orbit_radius: radius_start + (radius_end - radius_start) * t,
speed: 0.18 + t * 0.95,
phase: t * std::f32::consts::TAU * 4.0,
size: 1.2 + (index % 6) as f32 * 0.45,
height_bias: -120.0 + ((index * 53) % 240) as f32,
});
}
Self { particles }
}
pub fn particles(&self) -> &[Particle] {
&self.particles
}
}
pub fn mix_color(a: Color, b: Color, t: f32) -> Color {
let t = t.clamp(0.0, 1.0);
Color::new(
a.r + (b.r - a.r) * t,
a.g + (b.g - a.g) * t,
a.b + (b.b - a.b) * t,
a.a + (b.a - a.a) * t,
)
}
pub fn draw_gradient_background(ctx: &FrameContext, top: Color, bottom: Color, steps: usize) {
let steps = steps.max(2);
for i in 0..steps {
let t = i as f32 / (steps - 1) as f32;
let color = mix_color(top, bottom, t);
draw_rectangle(
0.0,
ctx.height * t,
ctx.width,
ctx.height / steps as f32 + 1.0,
color,
);
}
}
pub fn draw_glow_circle(center: Vec2, radius: f32, color: Color, style: GlowStyle) {
for index in 0..style.rings {
let spread = radius + index as f32 * style.spread;
let alpha = color.a * (style.alpha - index as f32 * 0.025).max(0.02);
draw_circle(center.x, center.y, spread, Color::new(color.r, color.g, color.b, alpha));
}
}
pub fn draw_polyline(points: &[Vec2], thickness: f32, color: Color) {
for pair in points.windows(2) {
let start = pair[0];
let end = pair[1];
draw_line(start.x, start.y, end.x, end.y, thickness, color);
}
}
pub fn draw_particle_field<F>(
ctx: &FrameContext,
origin: Vec2,
field: &ParticleField,
mut color_at: F,
)
where
F: FnMut(&Particle, Vec2, f32) -> Color,
{
for particle in field.particles() {
let angle = ctx.time * particle.speed + particle.phase;
let local = vec2(
angle.cos() * particle.orbit_radius,
particle.height_bias + angle.sin() * (36.0 + particle.orbit_radius * 0.08),
);
let position = origin + local;
let shimmer = 0.2 + 0.8 * (ctx.time * 1.6 + particle.phase).sin().abs();
let color = color_at(particle, position, shimmer);
draw_circle(
position.x,
position.y,
particle.size + shimmer * 1.6,
color,
);
}
}