#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LayerType {
Background,
Foreground,
Overlay,
Matte,
}
impl LayerType {
#[must_use]
pub fn is_transparent(&self) -> bool {
matches!(self, Self::Overlay | Self::Matte)
}
}
#[derive(Debug, Clone)]
pub struct RenderLayer {
pub name: String,
pub layer_type: LayerType,
depth: i32,
pub opacity: f32,
}
impl RenderLayer {
#[must_use]
pub fn new(name: impl Into<String>, layer_type: LayerType, depth: i32) -> Self {
Self {
name: name.into(),
layer_type,
depth,
opacity: 1.0,
}
}
#[must_use]
pub fn z_order(&self) -> i32 {
self.depth
}
#[must_use]
pub fn with_opacity(mut self, opacity: f32) -> Self {
self.opacity = opacity.clamp(0.0, 1.0);
self
}
}
#[derive(Debug, Default)]
pub struct LayerStack {
layers: Vec<RenderLayer>,
}
impl LayerStack {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, layer: RenderLayer) {
self.layers.push(layer);
self.layers.sort_by_key(RenderLayer::z_order);
}
pub fn pop(&mut self) -> Option<RenderLayer> {
self.layers.pop()
}
#[must_use]
pub fn find_by_type(&self, layer_type: LayerType) -> Option<&RenderLayer> {
self.layers.iter().find(|l| l.layer_type == layer_type)
}
#[must_use]
pub fn len(&self) -> usize {
self.layers.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.layers.is_empty()
}
#[must_use]
pub fn layers(&self) -> &[RenderLayer] {
&self.layers
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_background_not_transparent() {
assert!(!LayerType::Background.is_transparent());
}
#[test]
fn test_foreground_not_transparent() {
assert!(!LayerType::Foreground.is_transparent());
}
#[test]
fn test_overlay_is_transparent() {
assert!(LayerType::Overlay.is_transparent());
}
#[test]
fn test_matte_is_transparent() {
assert!(LayerType::Matte.is_transparent());
}
#[test]
fn test_render_layer_z_order() {
let layer = RenderLayer::new("bg", LayerType::Background, 0);
assert_eq!(layer.z_order(), 0);
let layer2 = RenderLayer::new("fg", LayerType::Foreground, 10);
assert_eq!(layer2.z_order(), 10);
}
#[test]
fn test_render_layer_opacity_clamped() {
let layer = RenderLayer::new("hud", LayerType::Overlay, 100).with_opacity(1.5);
assert!((layer.opacity - 1.0).abs() < 1e-6);
let layer2 = RenderLayer::new("hud2", LayerType::Overlay, 100).with_opacity(-0.5);
assert!((layer2.opacity).abs() < 1e-6);
}
#[test]
fn test_stack_push_orders_by_z() {
let mut stack = LayerStack::new();
stack.push(RenderLayer::new("overlay", LayerType::Overlay, 50));
stack.push(RenderLayer::new("bg", LayerType::Background, 0));
stack.push(RenderLayer::new("fg", LayerType::Foreground, 20));
let orders: Vec<i32> = stack
.layers()
.iter()
.map(super::RenderLayer::z_order)
.collect();
assert_eq!(orders, vec![0, 20, 50]);
}
#[test]
fn test_stack_pop_returns_topmost() {
let mut stack = LayerStack::new();
stack.push(RenderLayer::new("bg", LayerType::Background, 0));
stack.push(RenderLayer::new("overlay", LayerType::Overlay, 99));
let popped = stack.pop().expect("should succeed in test");
assert_eq!(popped.z_order(), 99);
}
#[test]
fn test_stack_find_by_type() {
let mut stack = LayerStack::new();
stack.push(RenderLayer::new("bg", LayerType::Background, 0));
stack.push(RenderLayer::new("matte", LayerType::Matte, 5));
let found = stack.find_by_type(LayerType::Matte);
assert!(found.is_some());
assert_eq!(found.expect("should succeed in test").name, "matte");
}
#[test]
fn test_stack_find_missing_type() {
let mut stack = LayerStack::new();
stack.push(RenderLayer::new("bg", LayerType::Background, 0));
assert!(stack.find_by_type(LayerType::Overlay).is_none());
}
#[test]
fn test_stack_len_and_is_empty() {
let mut stack = LayerStack::new();
assert!(stack.is_empty());
stack.push(RenderLayer::new("bg", LayerType::Background, 0));
assert_eq!(stack.len(), 1);
assert!(!stack.is_empty());
}
#[test]
fn test_stack_pop_empty() {
let mut stack = LayerStack::new();
assert!(stack.pop().is_none());
}
}