use enum_iterator::IntoEnumIterator;
use itertools::structs::Product;
use itertools::Itertools;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use ux::u3;
use crate::ppu::clock::Clock;
#[derive(Clone, Copy)]
pub struct PixelIndex {
column: PixelColumn,
row: PixelRow,
}
impl PixelIndex {
pub const PIXEL_COUNT: usize = PixelColumn::COLUMN_COUNT * PixelRow::ROW_COUNT;
pub fn iter() -> PixelIndexIterator {
PixelIndexIterator::new()
}
pub fn try_from_clock(clock: &Clock) -> Option<PixelIndex> {
PixelIndex::try_from_scanline_cycle(clock.scanline(), clock.cycle())
}
pub fn to_column_row(self) -> (PixelColumn, PixelRow) {
(self.column, self.row)
}
pub fn to_usize(self) -> usize {
PixelColumn::COLUMN_COUNT * self.row.to_usize() + self.column.to_usize()
}
fn try_from_scanline_cycle(scanline: u16, cycle: u16) -> Option<PixelIndex> {
let column = PixelColumn::try_from_u16(cycle - 1)?;
let row = PixelRow::try_from_u16(scanline)?;
Some(PixelIndex { column, row })
}
}
pub struct PixelIndexIterator(Product<PixelRowIterator, PixelColumnIterator>);
impl PixelIndexIterator {
pub fn new() -> PixelIndexIterator {
PixelIndexIterator(PixelRow::iter().cartesian_product(PixelColumn::iter()))
}
}
impl Iterator for PixelIndexIterator {
type Item = PixelIndex;
fn next(&mut self) -> Option<PixelIndex> {
self.0
.next()
.map(|(row, column)| PixelIndex { column, row })
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
pub struct PixelColumn(u8);
impl PixelColumn {
pub const COLUMN_COUNT: usize = 256;
pub const MAX: PixelColumn = PixelColumn::new(255);
pub fn iter() -> PixelColumnIterator {
PixelColumnIterator(0)
}
pub const fn new(pixel_column: u8) -> PixelColumn {
PixelColumn(pixel_column)
}
pub fn try_from_u16(pixel_column: u16) -> Option<PixelColumn> {
Some(PixelColumn::new(pixel_column.try_into().ok()?))
}
pub fn add_column_in_tile(self, column_in_tile: ColumnInTile) -> Option<PixelColumn> {
let value = self.0.checked_add(column_in_tile as u8)?;
Some(PixelColumn::new(value))
}
pub fn offset(self, offset: i16) -> Option<PixelColumn> {
let column: i16 = i16::from(self.0) + offset;
if column < 0 {
return None;
}
column.try_into().ok().map(PixelColumn)
}
pub fn is_in_left_margin(self) -> bool {
self.0 < 8
}
pub fn to_u8(self) -> u8 {
self.0
}
pub fn to_usize(self) -> usize {
self.0 as usize
}
}
#[derive(Clone, Copy)]
pub struct PixelColumnIterator(u16);
impl Iterator for PixelColumnIterator {
type Item = PixelColumn;
fn next(&mut self) -> Option<PixelColumn> {
let result = PixelColumn::try_from_u16(self.0);
self.0 += 1;
result
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
pub struct PixelRow(u8);
impl PixelRow {
pub const ROW_COUNT: usize = 240;
pub const MAX: PixelRow = PixelRow(PixelRow::ROW_COUNT as u8 - 1);
pub fn iter() -> PixelRowIterator {
PixelRowIterator(0)
}
pub const fn try_from_u8(pixel_row: u8) -> Option<PixelRow> {
if (pixel_row as usize) < PixelRow::ROW_COUNT {
Some(PixelRow(pixel_row))
} else {
None
}
}
pub fn try_from_u16(pixel_row: u16) -> Option<PixelRow> {
PixelRow::try_from_u8(pixel_row.try_into().ok()?)
}
pub const fn saturate_from_u8(pixel_row: u8) -> PixelRow {
if let Some(row) = PixelRow::try_from_u8(pixel_row) {
row
} else {
PixelRow::MAX
}
}
pub fn add_row_in_tile(self, row_in_tile: RowInTile) -> Option<PixelRow> {
let value = self.0.checked_add(row_in_tile as u8)?;
PixelRow::try_from_u8(value)
}
pub fn offset(self, offset: i16) -> Option<PixelRow> {
let row: i16 = i16::from(self.0) + offset;
if (0..240).contains(&row) {
Some(PixelRow::try_from_u8(row as u8).unwrap())
} else {
None
}
}
pub fn difference(self, other: PixelRow) -> Option<u8> {
self.to_u8().checked_sub(other.to_u8())
}
pub fn to_u8(self) -> u8 {
self.0
}
pub fn to_usize(self) -> usize {
usize::from(self.0)
}
}
#[derive(Clone, Copy)]
pub struct PixelRowIterator(u8);
impl Iterator for PixelRowIterator {
type Item = PixelRow;
fn next(&mut self) -> Option<PixelRow> {
let result = PixelRow::try_from_u8(self.0);
self.0 += 1;
result
}
}
#[derive(
PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, FromPrimitive, IntoEnumIterator,
)]
pub enum ColumnInTile {
Zero,
One,
Two,
Three,
Four,
Five,
Six,
Seven,
}
impl ColumnInTile {
pub fn flip(self) -> ColumnInTile {
FromPrimitive::from_u8(7 - (self as u8)).unwrap()
}
}
impl From<u3> for ColumnInTile {
fn from(value: u3) -> Self {
FromPrimitive::from_u8(value.into()).unwrap()
}
}
impl From<ColumnInTile> for u8 {
fn from(value: ColumnInTile) -> Self {
value as u8
}
}
#[derive(
PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, FromPrimitive, IntoEnumIterator,
)]
pub enum RowInTile {
Zero,
One,
Two,
Three,
Four,
Five,
Six,
Seven,
}
impl RowInTile {
pub fn flip(self) -> RowInTile {
FromPrimitive::from_u8(7 - (self as u8)).unwrap()
}
pub fn increment_low_bits(&mut self) {
self.increment();
*self = RowInTile::from_u8((*self as u8) & 0b11).unwrap();
}
pub fn increment(&mut self) -> bool {
let will_wrap = *self == RowInTile::Seven;
if will_wrap {
*self = RowInTile::Zero;
} else {
*self = FromPrimitive::from_u8(*self as u8 + 1).unwrap();
}
will_wrap
}
pub fn decrement(self) -> RowInTile {
FromPrimitive::from_u8((self as u8).wrapping_sub(1)).unwrap()
}
}
impl From<u3> for RowInTile {
fn from(value: u3) -> Self {
FromPrimitive::from_u8(value.into()).unwrap()
}
}
impl From<RowInTile> for u8 {
fn from(value: RowInTile) -> Self {
value as u8
}
}
impl From<RowInTile> for u16 {
fn from(value: RowInTile) -> Self {
value as u16
}
}