use std::ops::RangeInclusive;
use image::{Image, ImageMut};
use strategy::PixelStrategy;
use crate::util::vector::Vector;
use crate::visual::view::ViewMut;
pub mod image;
pub mod canvas;
pub mod sprite;
pub mod view;
pub mod pixel;
pub mod subpixel;
pub mod adapter;
pub mod strategy;
mod util;
pub fn printer<P>() -> impl FnMut(char, &dyn Image<P>) -> Vector<i32> {
let mut column = 0;
let mut line = 0;
move |code_point, representation| {
let result = (column, line).into();
if code_point == '\n' {
line += representation.height();
column = 0;
} else {
column += representation.width();
}
result
}
}
#[derive(Clone, Copy, Debug)]
enum Scan<T> {
None,
Single(T),
Inclusive(T, T),
}
impl<T> Scan<T> {
fn rev(self) -> Self {
match self {
Scan::None => Scan::None,
Scan::Single(a) => Scan::Single(a),
Scan::Inclusive(a, b) => Scan::Inclusive(b, a),
}
}
fn start_unchecked(self) -> T {
match self {
Scan::None => unimplemented!("There is no start value for Scan with None variant"),
Scan::Single(a) => a,
Scan::Inclusive(a, _) => a,
}
}
fn end_unchecked(self) -> T {
match self {
Scan::None => unimplemented!("There is no end value for Scan with None variant"),
Scan::Single(a) => a,
Scan::Inclusive(_, b) => b,
}
}
}
impl<T> Scan<T>
where
T: Ord,
{
fn sorted(self) -> Self {
if let Scan::Inclusive(a, b) = self {
if a > b {
Scan::Inclusive(b, a)
} else {
Scan::Inclusive(a, b)
}
} else {
self
}
}
}
impl IntoIterator for Scan<i32> {
type Item = i32;
type IntoIter = ScanIterator<i32>;
fn into_iter(self) -> Self::IntoIter {
match self {
Scan::None => ScanIterator {
current: 0,
scan: self,
exhausted: true,
},
Scan::Single(a) => ScanIterator {
current: a,
scan: self,
exhausted: false,
},
Scan::Inclusive(a, b) if a == b => ScanIterator {
current: a,
scan: Scan::Single(a),
exhausted: false,
},
Scan::Inclusive(a, _) => ScanIterator {
current: a,
scan: self,
exhausted: false,
},
}
}
}
struct ScanIterator<T> {
current: T,
scan: Scan<T>,
exhausted: bool,
}
impl Iterator for ScanIterator<i32> {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
if self.exhausted {
None
} else {
let result = self.current;
match self.scan {
Scan::None => unreachable!(),
Scan::Single(_) => {
self.exhausted = true;
}
Scan::Inclusive(a, b) => {
self.current += (b - a).signum();
self.exhausted = result == b;
}
}
Some(result)
}
}
}
pub struct Painter<'target, T> {
target: ViewMut<'target, T>,
offset: Vector<i32>,
}
impl<'target, T> Painter<'target, T> {
pub fn new<I>(image: I) -> Self
where
ViewMut<'target, T>: From<I>,
{
let offset = Vector::<i32>::zero();
let target = image.into();
Self { target, offset }
}
pub fn offset(&self) -> Vector<i32> {
self.offset
}
pub fn offset_mut(&mut self) -> &mut Vector<i32> {
&mut self.offset
}
pub fn with_offset(self, offset: Vector<i32>) -> Self {
Self { offset, ..self }
}
}
impl<T> Painter<'_, T>
where
T: Clone,
{
fn position_i32(&self, original: Vector<i32>) -> Vector<i32> {
original + self.offset
}
fn position_f32(&self, original: Vector<f32>) -> Vector<f32> {
original + self.offset.map(|v| v as _)
}
pub fn width(&self) -> i32 {
Image::width(&self.target)
}
pub fn height(&self) -> i32 {
Image::height(&self.target)
}
}
impl<T> Painter<'_, T>
where
T: Clone,
{
fn apply_strategy(&mut self, position: Vector<i32>, strategy: &mut PixelStrategy<T>) {
strategy.apply(position, &mut self.target);
}
pub fn clear(&mut self, clear_color: T) {
ImageMut::clear(&mut self.target, clear_color)
}
fn vertical_line(&mut self, x: i32, y: RangeInclusive<i32>, strategy: &mut PixelStrategy<T>) {
if x < 0 || x >= self.width() {
return;
}
let start = *y.start();
let end = *y.end();
let y = if start < end {
start.max(0)..=end.min(self.height() - 1)
} else {
end.max(0)..=start.min(self.height() - 1)
};
for y in y {
let pose = (x, y);
self.apply_strategy(pose.into(), strategy);
}
}
fn horizontal_line(&mut self, x: RangeInclusive<i32>, y: i32, strategy: &mut PixelStrategy<T>) {
strategy.apply_to_line(x, y, &mut self.target);
}
fn filled_rect(&mut self, from: Vector<i32>, to: Vector<i32>, strategy: &mut PixelStrategy<T>) {
for y in from.y()..=to.y() {
self.horizontal_line(from.x()..=to.x(), y, strategy);
}
}
}
pub trait Paint<T, C> {
fn pixel(&self, position: Vector<C>) -> Option<T>;
fn mod_pixel<'a, S>(&mut self, position: Vector<C>, strategy: S)
where
T: 'a,
S: Into<PixelStrategy<'a, T>>;
fn line<'a, S>(&mut self, from: Vector<C>, to: Vector<C>, strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>;
fn rect_f<'a, S>(&mut self, from: Vector<C>, dimensions: Vector<C>, strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>;
fn rect_b<'a, S>(&mut self, from: Vector<C>, dimensions: Vector<C>, strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>;
fn triangle_f<'a, S>(&mut self, vertices: [Vector<C>; 3], strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>;
fn triangle_b<'a, S>(&mut self, vertices: [Vector<C>; 3], strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>;
fn polygon_f<'a, S>(&mut self, vertices: &[Vector<C>], strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>;
fn polygon_b<'a, S>(&mut self, vertices: &[Vector<C>], strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>;
fn circle_f<'a, S>(&mut self, center: Vector<C>, radius: C, strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>;
fn circle_b<'a, S>(&mut self, center: Vector<C>, radius: C, strategy: S)
where
T: 'a,
S: 'a + Into<PixelStrategy<'a, T>>;
}