use std::{ops::Range, slice::Iter};
use glam::Vec2;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Rect<T> {
pub min: T,
pub max: T,
}
impl<T> Rect<T> {
pub const fn new(min: T, max: T) -> Self {
Self { min, max }
}
}
impl<T: std::ops::Sub<Output = T>> Rect<T> {
#[inline]
pub fn size(self) -> T {
self.max - self.min
}
}
impl Rect<Vec2> {
pub const EMPTY: Self = Self {
max: Vec2::NEG_INFINITY,
min: Vec2::INFINITY,
};
#[inline]
pub fn union_point(&self, other: Vec2) -> Self {
Self {
min: self.min.min(other),
max: self.max.max(other),
}
}
}
pub fn quake_string_to_utf8_lossy(quake_str: &mut Vec<u8>) -> &str {
for byte in quake_str.iter_mut() {
if *byte > 127 {
*byte -= 128;
}
}
if quake_str.last().copied() == Some(0) {
quake_str.pop();
}
unsafe { str::from_utf8_unchecked(quake_str) }
}
pub fn quake_string_to_utf8(quake_str: &[u8], alt_prefix: &str, alt_suffix: &str) -> String {
let mut s = String::with_capacity(quake_str.len());
let mut was_alt = false;
for mut byte in quake_str.iter().copied() {
let is_alt = byte > 127;
if is_alt && !was_alt {
s.push_str(alt_prefix);
} else if !is_alt && was_alt {
s.push_str(alt_suffix);
}
if is_alt {
byte -= 128;
}
if byte == 0 {
return s;
}
s.push(unsafe { char::from_u32_unchecked(byte as u32) });
was_alt = is_alt;
}
if was_alt {
s.push_str(alt_suffix);
}
s
}
pub(crate) fn display_magic_number(bytes: &[u8]) -> String {
std::str::from_utf8(bytes)
.ok()
.and_then(|s| s.is_ascii().then_some(s))
.map(str::to_owned)
.unwrap_or(format!("{bytes:?}"))
}
#[derive(Debug)]
struct BitsIter {
byte: u8,
shift: u8,
}
impl BitsIter {
fn new(byte: u8) -> Self {
Self { byte, shift: 0 }
}
}
impl Iterator for BitsIter {
type Item = bool;
fn next(&mut self) -> Option<Self::Item> {
if self.shift >= 8 {
None
} else {
let out = (self.byte & 1 << self.shift) != 0;
self.shift += 1;
Some(out)
}
}
}
pub struct VisdataIter<'a> {
vis_leaves: Range<usize>,
data_bytes: Iter<'a, u8>,
cur_byte: Option<BitsIter>,
}
impl Iterator for VisdataIter<'_> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(is_visible) = self.cur_byte.as_mut().and_then(|byte| byte.next()) {
let value = self.vis_leaves.next();
if is_visible {
return value;
}
} else {
let next_byte = *self.data_bytes.next()?;
match next_byte {
0 => {
let advance_by = *self.data_bytes.next()?;
self.vis_leaves.start += 8 * advance_by as usize;
}
bits => {
self.cur_byte = Some(BitsIter::new(bits));
}
}
}
}
}
}
pub(crate) fn calculate_visdata_indices(vis_data: &[u8], num_leaves: usize) -> VisdataIter<'_> {
VisdataIter {
vis_leaves: 1..num_leaves,
data_bytes: vis_data.iter(),
cur_byte: None,
}
}
#[cfg(test)]
mod tests {
use crate::util::{calculate_visdata_indices, quake_string_to_utf8};
const TEST_VISDATA: &[u8] = &[0b1010_0111, 0, 5, 0b0000_0001, 0b0001_0000, 0, 12, 0b1000_0000];
#[test]
fn test_pvs_calculation() {
assert_eq!(
calculate_visdata_indices(TEST_VISDATA, 256).collect::<Vec<_>>(),
&[1, 2, 3, 6, 8, 49, 61, 168]
);
}
#[test]
fn char_number_range() {
for i in 0..=127u8 {
assert!(char::from_u32(i as u32).is_some());
}
}
#[test]
fn quake_string_alt_text_surrounding() {
let quake_string = [b'h', b'i', b' ', b't' + 128, b'h' + 128, b'e', b'r' + 128, b'e' + 128];
let out = quake_string_to_utf8(&quake_string, "<b>", "</b>");
assert_eq!(out, "hi <b>th</b>e<b>re</b>");
}
}