#![allow(dead_code)]
use std::f32::consts::FRAC_PI_4;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StampBlend {
Multiply,
Overlay,
Normal,
Add,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct DecalStamp {
pub position: [f32; 3],
pub normal: [f32; 3],
pub half_size: [f32; 2],
pub opacity: f32,
pub rotation_rad: f32,
pub blend: StampBlend,
pub enabled: bool,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct DecalStampSet {
stamps: Vec<DecalStamp>,
max_stamps: usize,
}
pub fn new_decal_stamp_set(max_stamps: usize) -> DecalStampSet {
DecalStampSet {
stamps: Vec::new(),
max_stamps,
}
}
pub fn default_decal_stamp() -> DecalStamp {
DecalStamp {
position: [0.0; 3],
normal: [0.0, 1.0, 0.0],
half_size: [0.5, 0.5],
opacity: 1.0,
rotation_rad: 0.0,
blend: StampBlend::Normal,
enabled: true,
}
}
pub fn ds_add_stamp(set: &mut DecalStampSet, stamp: DecalStamp) -> Option<usize> {
if set.stamps.len() >= set.max_stamps {
return None;
}
set.stamps.push(stamp);
Some(set.stamps.len() - 1)
}
pub fn ds_remove_stamp(set: &mut DecalStampSet, idx: usize) {
if idx < set.stamps.len() {
set.stamps.remove(idx);
}
}
pub fn ds_count(set: &DecalStampSet) -> usize {
set.stamps.len()
}
pub fn ds_enabled_count(set: &DecalStampSet) -> usize {
set.stamps.iter().filter(|s| s.enabled).count()
}
pub fn ds_clear(set: &mut DecalStampSet) {
set.stamps.clear();
}
pub fn ds_set_opacity(set: &mut DecalStampSet, idx: usize, opacity: f32) {
if let Some(s) = set.stamps.get_mut(idx) {
s.opacity = opacity.clamp(0.0, 1.0);
}
}
pub fn ds_set_enabled(set: &mut DecalStampSet, idx: usize, enabled: bool) {
if let Some(s) = set.stamps.get_mut(idx) {
s.enabled = enabled;
}
}
pub fn ds_project_point(stamp: &DecalStamp, point: [f32; 3]) -> Option<[f32; 2]> {
let ref_angle = FRAC_PI_4; let _ = ref_angle;
let dx = point[0] - stamp.position[0];
let dz = point[2] - stamp.position[2];
let cos_r = stamp.rotation_rad.cos();
let sin_r = stamp.rotation_rad.sin();
let lx = cos_r * dx + sin_r * dz;
let lz = -sin_r * dx + cos_r * dz;
let u = lx / stamp.half_size[0];
let v = lz / stamp.half_size[1];
if (0.0..=1.0).contains(&u.abs()) && (0.0..=1.0).contains(&v.abs()) {
Some([u * 0.5 + 0.5, v * 0.5 + 0.5])
} else {
None
}
}
pub fn ds_to_json(set: &DecalStampSet) -> String {
format!(
r#"{{"count":{},"enabled":{}}}"#,
ds_count(set),
ds_enabled_count(set)
)
}
#[cfg(test)]
mod tests {
use super::*;
fn make_set() -> DecalStampSet {
new_decal_stamp_set(10)
}
#[test]
fn empty_set_zero_count() {
assert_eq!(ds_count(&make_set()), 0);
}
#[test]
fn add_stamp_increments_count() {
let mut s = make_set();
ds_add_stamp(&mut s, default_decal_stamp());
assert_eq!(ds_count(&s), 1);
}
#[test]
fn max_stamps_respected() {
let mut s = new_decal_stamp_set(1);
ds_add_stamp(&mut s, default_decal_stamp());
let r = ds_add_stamp(&mut s, default_decal_stamp());
assert!(r.is_none());
}
#[test]
fn remove_stamp_decrements() {
let mut s = make_set();
ds_add_stamp(&mut s, default_decal_stamp());
ds_remove_stamp(&mut s, 0);
assert_eq!(ds_count(&s), 0);
}
#[test]
fn clear_empties_set() {
let mut s = make_set();
ds_add_stamp(&mut s, default_decal_stamp());
ds_clear(&mut s);
assert_eq!(ds_count(&s), 0);
}
#[test]
fn set_opacity_clamps() {
let mut s = make_set();
ds_add_stamp(&mut s, default_decal_stamp());
ds_set_opacity(&mut s, 0, 5.0);
assert!((s.stamps[0].opacity - 1.0).abs() < 1e-5);
}
#[test]
fn project_point_inside_returns_some() {
let stamp = default_decal_stamp();
let result = ds_project_point(&stamp, [0.0, 0.0, 0.0]);
assert!(result.is_some());
}
#[test]
fn project_point_outside_returns_none() {
let stamp = default_decal_stamp();
let result = ds_project_point(&stamp, [10.0, 0.0, 10.0]);
assert!(result.is_none());
}
#[test]
fn json_has_count() {
assert!(ds_to_json(&make_set()).contains("count"));
}
}