use egui::epaint::Mesh;
use egui::{Color32, Pos2, TextureId, vec2};
const MIN_SEGMENTS: usize = 12;
const MAX_SEGMENTS: usize = 48;
const CORE_RING: f32 = 0.15;
const INNER_RING: f32 = 0.45;
const RIM_RING: f32 = 1.0;
const CORE_ALPHA: f32 = 0.55;
const INNER_ALPHA: f32 = 0.18;
const DEFAULT_SOFTNESS: f32 = 1.0;
const MIN_SOFTNESS: f32 = 0.05;
const SIZE_ATTENUATION_POWER: f32 = 0.5;
const MIN_SIZE_ATTENUATION: f32 = 0.4;
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct GlowProfile {
core_ring: f32,
inner_ring: f32,
rim_ring: f32,
core_alpha: f32,
inner_alpha: f32,
}
impl GlowProfile {
pub(crate) const DEFAULT: Self = Self {
core_ring: CORE_RING,
inner_ring: INNER_RING,
rim_ring: RIM_RING,
core_alpha: CORE_ALPHA,
inner_alpha: INNER_ALPHA,
};
pub(crate) fn softness(softness: f32) -> Self {
if softness == DEFAULT_SOFTNESS {
return Self::DEFAULT;
}
let exponent = softness.max(MIN_SOFTNESS).recip();
Self {
core_ring: CORE_RING.powf(exponent),
inner_ring: INNER_RING.powf(exponent),
..Self::DEFAULT
}
}
}
pub(crate) fn glow_mesh(center: Pos2, radius: f32, color: Color32) -> Mesh {
glow_mesh_with(center, radius, color, GlowProfile::DEFAULT)
}
pub(crate) fn glow_mesh_with(
center: Pos2,
radius: f32,
color: Color32,
profile: GlowProfile,
) -> Mesh {
let segments = ((radius / 4.0).clamp(MIN_SEGMENTS as f32, MAX_SEGMENTS as f32)) as usize;
let mut mesh = Mesh::default();
mesh.texture_id = TextureId::default();
mesh.colored_vertex(center, color);
let rings = [
(
profile.core_ring * radius,
color.gamma_multiply(profile.core_alpha),
),
(
profile.inner_ring * radius,
color.gamma_multiply(profile.inner_alpha),
),
(profile.rim_ring * radius, Color32::TRANSPARENT),
];
for (ring_radius, ring_color) in rings {
for i in 0..segments {
let (sin, cos) = (i as f32 / segments as f32 * std::f32::consts::TAU).sin_cos();
mesh.colored_vertex(center + vec2(cos, sin) * ring_radius, ring_color);
}
}
for i in 0..segments {
let next = (i + 1) % segments;
mesh.add_triangle(0, 1 + i as u32, 1 + next as u32);
for ring in 0..(rings.len() as u32 - 1) {
let inner = 1 + ring * segments as u32 + i as u32;
let inner_next = 1 + ring * segments as u32 + next as u32;
let outer = 1 + (ring + 1) * segments as u32 + i as u32;
let outer_next = 1 + (ring + 1) * segments as u32 + next as u32;
mesh.add_triangle(inner, outer, outer_next);
mesh.add_triangle(inner, outer_next, inner_next);
}
}
mesh
}
pub(crate) fn size_attenuation(radius: f32, min_radius: f32, max_radius: f32) -> f32 {
let floor = min_radius.min(max_radius).max(f32::MIN_POSITIVE);
(floor / radius.max(floor))
.powf(SIZE_ATTENUATION_POWER)
.clamp(MIN_SIZE_ATTENUATION, 1.0)
}