roast2d_internal 0.3.0-alpha.4

Roast2D internal crate
Documentation
use std::hash::{Hash, Hasher};

use glam::Vec2;
use ordered_float::OrderedFloat;

use crate::handle::Handle;

/// Rect
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Rect {
    pub min: Vec2,
    pub max: Vec2,
}

impl Rect {
    pub fn new(center: Vec2, radius: f32) -> Self {
        Self {
            min: center - Vec2::splat(radius),
            max: center + Vec2::splat(radius),
        }
    }

    pub fn is_touching(&self, other: &Self) -> bool {
        !(self.min.x > other.max.x
            || self.max.x < other.min.x
            || self.min.y > other.max.y
            || self.max.y < other.min.y)
    }

    pub fn contains_pos(&self, pos: Vec2) -> bool {
        let Rect { min, max } = self;
        pos.x >= min.x && pos.y >= min.y && pos.x <= max.x && pos.y <= max.y
    }

    pub fn center(&self) -> Vec2 {
        (self.min + self.max) * 0.5
    }

    pub fn size(&self) -> Vec2 {
        self.max - self.min
    }
    pub fn overlap(&self, other: &Self) -> Vec2 {
        (self.max - other.min).min(other.max - self.min)
    }
}

// Line pattern types
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LinePattern {
    Solid,
    Dashed { dash_length: f32, gap_length: f32 },
    Dotted { spacing: f32 },
}

impl Hash for LinePattern {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            LinePattern::Solid => 0.hash(state),
            LinePattern::Dashed {
                dash_length,
                gap_length,
            } => {
                1.hash(state);
                OrderedFloat(*dash_length).hash(state);
                OrderedFloat(*gap_length).hash(state);
            }
            LinePattern::Dotted { spacing } => {
                2.hash(state);
                OrderedFloat(*spacing).hash(state);
            }
        }
    }
}

impl Eq for LinePattern {}

impl PartialOrd for LinePattern {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for LinePattern {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        match (self, other) {
            (LinePattern::Solid, LinePattern::Solid) => std::cmp::Ordering::Equal,
            (LinePattern::Solid, _) => std::cmp::Ordering::Less,
            (_, LinePattern::Solid) => std::cmp::Ordering::Greater,
            (
                LinePattern::Dashed {
                    dash_length: d1,
                    gap_length: g1,
                },
                LinePattern::Dashed {
                    dash_length: d2,
                    gap_length: g2,
                },
            ) => OrderedFloat(*d1)
                .cmp(&OrderedFloat(*d2))
                .then(OrderedFloat(*g1).cmp(&OrderedFloat(*g2))),
            (LinePattern::Dashed { .. }, LinePattern::Dotted { .. }) => std::cmp::Ordering::Less,
            (LinePattern::Dotted { .. }, LinePattern::Dashed { .. }) => std::cmp::Ordering::Greater,
            (LinePattern::Dotted { spacing: s1 }, LinePattern::Dotted { spacing: s2 }) => {
                OrderedFloat(*s1).cmp(&OrderedFloat(*s2))
            }
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LineStyle {
    pub pattern: LinePattern,
    pub offset: f32,
}

impl Hash for LineStyle {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.pattern.hash(state);
        OrderedFloat(self.offset).hash(state);
    }
}

impl Eq for LineStyle {}

impl PartialOrd for LineStyle {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for LineStyle {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.pattern
            .cmp(&other.pattern)
            .then(OrderedFloat(self.offset).cmp(&OrderedFloat(other.offset)))
    }
}

impl Default for LineStyle {
    fn default() -> Self {
        Self {
            pattern: LinePattern::Solid,
            offset: 0.0,
        }
    }
}

#[derive(Debug, Clone)]
pub(crate) enum DrawContent {
    Texture(Handle), // For textured sprites
    Line {
        vector: Vec2,
        thickness: f32,
        style: LineStyle,
    }, // Vector = end - start
    Circle(f32),     // Radius only
}

impl Hash for DrawContent {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            DrawContent::Texture(handle) => {
                0.hash(state);
                handle.id().hash(state);
            }
            DrawContent::Line {
                vector,
                thickness,
                style,
            } => {
                1.hash(state);
                OrderedFloat(vector.x).hash(state);
                OrderedFloat(vector.y).hash(state);
                OrderedFloat(*thickness).hash(state);
                style.hash(state);
            }
            DrawContent::Circle(radius) => {
                2.hash(state);
                OrderedFloat(*radius).hash(state);
            }
        }
    }
}

impl PartialEq for DrawContent {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (DrawContent::Texture(h1), DrawContent::Texture(h2)) => h1.id() == h2.id(),
            (
                DrawContent::Line {
                    vector: v1,
                    thickness: t1,
                    style: s1,
                },
                DrawContent::Line {
                    vector: v2,
                    thickness: t2,
                    style: s2,
                },
            ) => {
                OrderedFloat(v1.x) == OrderedFloat(v2.x)
                    && OrderedFloat(v1.y) == OrderedFloat(v2.y)
                    && OrderedFloat(*t1) == OrderedFloat(*t2)
                    && s1 == s2
            }
            (DrawContent::Circle(r1), DrawContent::Circle(r2)) => {
                OrderedFloat(*r1) == OrderedFloat(*r2)
            }
            _ => false,
        }
    }
}

impl Eq for DrawContent {}

impl PartialOrd for DrawContent {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for DrawContent {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        match (self, other) {
            (DrawContent::Texture(h1), DrawContent::Texture(h2)) => h1.id().cmp(&h2.id()),
            (DrawContent::Texture(_), _) => std::cmp::Ordering::Less,
            (_, DrawContent::Texture(_)) => std::cmp::Ordering::Greater,
            (
                DrawContent::Line {
                    vector: v1,
                    thickness: t1,
                    style: s1,
                },
                DrawContent::Line {
                    vector: v2,
                    thickness: t2,
                    style: s2,
                },
            ) => OrderedFloat(v1.x)
                .cmp(&OrderedFloat(v2.x))
                .then(OrderedFloat(v1.y).cmp(&OrderedFloat(v2.y)))
                .then(OrderedFloat(*t1).cmp(&OrderedFloat(*t2)))
                .then(s1.cmp(s2)),
            (DrawContent::Line { .. }, DrawContent::Circle(_)) => std::cmp::Ordering::Less,
            (DrawContent::Circle(_), DrawContent::Line { .. }) => std::cmp::Ordering::Greater,
            (DrawContent::Circle(r1), DrawContent::Circle(r2)) => {
                OrderedFloat(*r1).cmp(&OrderedFloat(*r2))
            }
        }
    }
}

impl DrawContent {
    pub fn texture_handle(&self) -> Option<&Handle> {
        if let Self::Texture(texture) = self {
            Some(texture)
        } else {
            None
        }
    }
}