pub mod balanced_split;
pub mod grid_tiled;
pub mod multi_layer;
pub mod scheme_list;
pub mod tiled;
use palette::rgb::Rgba;
use thiserror::Error;
use crate::puzzle::{
coloring::Coloring,
grids::Grids,
label::{label::Label, rect_partition::Rect},
size::Size,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Error, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ColorSchemeError {
#[error("PositionOutOfBounds: position {pos:?} is out of bounds on a {size} puzzle")]
PositionOutOfBounds {
size: Size,
pos: (u64, u64),
},
}
pub trait ColorScheme {
#[must_use]
fn color(&self, size: Size, pos: (u64, u64)) -> Rgba;
fn try_color(&self, size: Size, pos: (u64, u64)) -> Result<Rgba, ColorSchemeError> {
if size.is_within_bounds(pos) {
Ok(self.color(size, pos))
} else {
Err(ColorSchemeError::PositionOutOfBounds { size, pos })
}
}
#[must_use]
fn fixed_size(self, size: Size) -> FixedSize<Self>
where
Self: Sized,
{
FixedSize { scheme: self, size }
}
#[must_use]
fn fixed_size_ref(&self, size: Size) -> FixedSize<&Self>
where
Self: Sized,
{
FixedSize { scheme: self, size }
}
}
impl<C: ColorScheme + ?Sized> ColorScheme for &C {
fn color(&self, size: Size, pos: (u64, u64)) -> Rgba {
(**self).color(size, pos)
}
}
impl<C: ColorScheme + ?Sized> ColorScheme for &mut C {
fn color(&self, size: Size, pos: (u64, u64)) -> Rgba {
(**self).color(size, pos)
}
}
impl<C: ColorScheme + ?Sized> ColorScheme for Box<C> {
fn color(&self, size: Size, pos: (u64, u64)) -> Rgba {
(**self).color(size, pos)
}
}
pub trait FixedSizeColorScheme {
#[must_use]
fn size(&self) -> Size;
#[must_use]
fn color(&self, pos: (u64, u64)) -> Rgba;
fn try_color(&self, pos: (u64, u64)) -> Result<Rgba, ColorSchemeError> {
let size = self.size();
if size.is_within_bounds(pos) {
Ok(self.color(pos))
} else {
Err(ColorSchemeError::PositionOutOfBounds { size, pos })
}
}
}
impl<C: FixedSizeColorScheme + ?Sized> FixedSizeColorScheme for &C {
fn size(&self) -> Size {
(**self).size()
}
fn color(&self, pos: (u64, u64)) -> Rgba {
(**self).color(pos)
}
}
impl<C: FixedSizeColorScheme + ?Sized> FixedSizeColorScheme for &mut C {
fn size(&self) -> Size {
(**self).size()
}
fn color(&self, pos: (u64, u64)) -> Rgba {
(**self).color(pos)
}
}
impl<C: FixedSizeColorScheme + ?Sized> FixedSizeColorScheme for Box<C> {
fn size(&self) -> Size {
(**self).size()
}
fn color(&self, pos: (u64, u64)) -> Rgba {
(**self).color(pos)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FixedSize<C: ColorScheme> {
scheme: C,
size: Size,
}
impl<C: ColorScheme> FixedSize<C> {
pub fn inner(&self) -> &C {
&self.scheme
}
pub fn into_inner(self) -> C {
self.scheme
}
}
impl<C: ColorScheme> FixedSizeColorScheme for FixedSize<C> {
fn size(&self) -> Size {
self.size
}
fn color(&self, pos: (u64, u64)) -> Rgba {
self.scheme.color(self.size, pos)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Scheme<L: Label, C: Coloring> {
label: L,
coloring: C,
}
impl<L: Label, C: Coloring> Scheme<L, C> {
#[must_use]
pub fn new(label: L, coloring: C) -> Self {
Self { label, coloring }
}
#[must_use]
pub fn label(&self) -> &L {
&self.label
}
#[must_use]
pub fn coloring(&self) -> &C {
&self.coloring
}
}
impl<L: Label, C: Coloring> ColorScheme for Scheme<L, C> {
fn color(&self, size: Size, pos: (u64, u64)) -> Rgba {
let label = self.label.position_label(size, pos);
let num_labels = self.label.num_labels(size);
self.coloring.color(label, num_labels)
}
}
impl<L: Label + Grids, C: Coloring> Grids for Scheme<L, C> {
fn grid_containing_pos(&self, size: Size, pos: (u64, u64)) -> Rect {
self.label().grid_containing_pos(size, pos)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Black;
impl ColorScheme for Black {
fn color(&self, _size: Size, _pos: (u64, u64)) -> Rgba {
Rgba::new(0.0, 0.0, 0.0, 1.0)
}
}