mappings: rewrite pixel maps to use the stronger geometry primitives and coordinate space APIs
This commit is contained in:
parent
e8f6a90d0f
commit
54e7506865
@ -95,10 +95,6 @@ impl<T: CoordLimits<Data = T>, S: CoordinateSpace> Coordinates<T, S> {
|
|||||||
pub struct Virtual {}
|
pub struct Virtual {}
|
||||||
impl CoordinateSpace for Virtual {}
|
impl CoordinateSpace for Virtual {}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
|
||||||
pub struct Physical {}
|
|
||||||
impl CoordinateSpace for Physical {}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||||
pub struct Coord8<S: CoordinateSpace> {
|
pub struct Coord8<S: CoordinateSpace> {
|
||||||
x: u8,
|
x: u8,
|
||||||
@ -107,7 +103,6 @@ pub struct Coord8<S: CoordinateSpace> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub type VirtualCoordinates = Coordinates<u8, Virtual>;
|
pub type VirtualCoordinates = Coordinates<u8, Virtual>;
|
||||||
pub type PhysicalCoordinates = Coordinates<usize, Physical>;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Copy, Clone, Debug, PartialOrd)]
|
#[derive(PartialEq, Eq, Copy, Clone, Debug, PartialOrd)]
|
||||||
pub struct Rectangle<CoordData: CoordLimits<Data = CoordData>, Space: CoordinateSpace> {
|
pub struct Rectangle<CoordData: CoordLimits<Data = CoordData>, Space: CoordinateSpace> {
|
||||||
|
237
src/mappings.rs
237
src/mappings.rs
@ -2,24 +2,25 @@ use crate::geometry::*;
|
|||||||
|
|
||||||
use crate::lib8::interpolate::scale8;
|
use crate::lib8::interpolate::scale8;
|
||||||
|
|
||||||
use std::cmp::max;
|
use std::cmp::{max, min};
|
||||||
use std::fmt::{Formatter, Debug};
|
use std::fmt::{Formatter, Debug};
|
||||||
use ansi_term::Color;
|
use ansi_term::Color;
|
||||||
use rgb::Rgb;
|
use rgb::Rgb;
|
||||||
|
|
||||||
pub trait CoordinateView: Debug {
|
pub trait CoordinateView<CoordData: CoordLimits<Data = CoordData>, Space: CoordinateSpace>: Debug {
|
||||||
fn next(&mut self) -> Option<(VirtualCoordinates, PhysicalCoordinates)>;
|
fn next(&mut self) -> Option<(Coordinates<u8, Virtual>, Coordinates<CoordData, Space>)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PixelMapping<CoordData: CoordLimits<Data = CoordData>> {
|
pub trait PixelMapping<CoordData: CoordLimits<Data = CoordData>, Space: CoordinateSpace> {
|
||||||
fn select(&self, rect: &Rectangle<CoordData, Virtual>) -> impl CoordinateView;
|
fn select(&self, rect: &Rectangle<u8, Virtual>) -> impl CoordinateView<CoordData, Space>;
|
||||||
|
fn to_idx(&self, coords: &Coordinates<CoordData, Space>) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DisplayDump<T: PixelMapping<CoordData>, CoordData: CoordLimits<Data = CoordData>> {
|
pub trait DisplayDump<T: PixelMapping<CoordData, Space>, CoordData: CoordLimits<Data = CoordData>, Space: CoordinateSpace> {
|
||||||
fn dump(&self, map: &T);
|
fn dump(&self, map: &T);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const PIXEL_NUM: usize> DisplayDump<LinearPixelMapping, u8> for [Rgb<u8>; PIXEL_NUM] {
|
impl<const PIXEL_NUM: usize> DisplayDump<LinearPixelMapping, usize, LinearSpace> for [Rgb<u8>; PIXEL_NUM] {
|
||||||
fn dump(&self, _map: &LinearPixelMapping) {
|
fn dump(&self, _map: &LinearPixelMapping) {
|
||||||
for ref pixel in self {
|
for ref pixel in self {
|
||||||
print!("{}", Color::RGB(pixel.r, pixel.g, pixel.b).paint("█"));
|
print!("{}", Color::RGB(pixel.r, pixel.g, pixel.b).paint("█"));
|
||||||
@ -28,7 +29,7 @@ impl<const PIXEL_NUM: usize> DisplayDump<LinearPixelMapping, u8> for [Rgb<u8>; P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const PIXEL_NUM: usize> DisplayDump<StrideMapping, u8> for [Rgb<u8>; PIXEL_NUM] {
|
impl<const PIXEL_NUM: usize> DisplayDump<StrideMapping, u8, StrideSpace> for [Rgb<u8>; PIXEL_NUM] {
|
||||||
fn dump(&self, map: &StrideMapping) {
|
fn dump(&self, map: &StrideMapping) {
|
||||||
for y in 0..map.stride_count {
|
for y in 0..map.stride_count {
|
||||||
let stride = &map.strides[y];
|
let stride = &map.strides[y];
|
||||||
@ -55,16 +56,23 @@ impl<const PIXEL_NUM: usize> DisplayDump<StrideMapping, u8> for [Rgb<u8>; PIXEL_
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct LinearCoordView {
|
struct LinearCoordView {
|
||||||
rect: Rectangle<u8, Virtual>,
|
rect: Rectangle<u8, Virtual>,
|
||||||
idx: u8,
|
idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoordinateView for LinearCoordView {
|
pub struct LinearSpace {}
|
||||||
fn next(&mut self) -> Option<(VirtualCoordinates, PhysicalCoordinates)> {
|
impl CoordinateSpace for LinearSpace {}
|
||||||
if self.idx == self.rect.bottom_right.x {
|
pub type LinearCoords = Coordinates<usize, LinearSpace>;
|
||||||
|
|
||||||
|
impl CoordinateView<usize, LinearSpace> for LinearCoordView {
|
||||||
|
fn next(&mut self) -> Option<(VirtualCoordinates, LinearCoords)> {
|
||||||
|
if self.idx as u8 == self.rect.bottom_right.x {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let virt = VirtualCoordinates::new(self.idx, 0);
|
let virt = VirtualCoordinates::new(self.idx as u8, 0); // FIXME: scale8
|
||||||
let phys = PhysicalCoordinates::new(self.idx as usize, 0);
|
let phys = LinearCoords::new(
|
||||||
|
self.idx as usize,
|
||||||
|
0
|
||||||
|
);
|
||||||
self.idx += 1;
|
self.idx += 1;
|
||||||
return Some((virt, phys))
|
return Some((virt, phys))
|
||||||
}
|
}
|
||||||
@ -80,13 +88,17 @@ impl LinearPixelMapping {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PixelMapping<u8> for LinearPixelMapping {
|
impl PixelMapping<usize, LinearSpace> for LinearPixelMapping {
|
||||||
fn select(&self, rect: &Rectangle<u8, Virtual>) -> impl CoordinateView {
|
fn select(&self, rect: &Rectangle<u8, Virtual>) -> impl CoordinateView<usize, LinearSpace> {
|
||||||
LinearCoordView {
|
LinearCoordView {
|
||||||
rect: rect.clone(),
|
rect: rect.clone(),
|
||||||
idx: 0,
|
idx: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_idx(&self, coords: &Coordinates<usize, LinearSpace>) -> usize {
|
||||||
|
coords.x
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
@ -98,15 +110,22 @@ pub struct Stride {
|
|||||||
pub physical_idx: usize
|
pub physical_idx: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Stride {
|
||||||
|
pub const fn pixel_idx_for_offset(&self, offset: u8) -> usize {
|
||||||
|
if self.reverse {
|
||||||
|
self.physical_idx + (self.length + self.y - 1 - offset) as usize
|
||||||
|
} else {
|
||||||
|
self.physical_idx + offset as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StrideMapping<const STRIDE_NUM: usize = 24> {
|
pub struct StrideMapping<const STRIDE_NUM: usize = 24> {
|
||||||
pub strides: [Stride; STRIDE_NUM],
|
pub strides: [Stride; STRIDE_NUM],
|
||||||
pub stride_count: usize,
|
pub stride_count: usize,
|
||||||
pixel_count: usize,
|
pub pixel_count: usize,
|
||||||
top: u8,
|
pub size: Rectangle<usize, StrideSpace>
|
||||||
left: u8,
|
|
||||||
height: u8,
|
|
||||||
width: u8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const STRIDE_NUM: usize> StrideMapping<STRIDE_NUM> {
|
impl<const STRIDE_NUM: usize> StrideMapping<STRIDE_NUM> {
|
||||||
@ -152,8 +171,7 @@ impl<const STRIDE_NUM: usize> StrideMapping<STRIDE_NUM> {
|
|||||||
let mut strides = [Stride::default(); STRIDE_NUM];
|
let mut strides = [Stride::default(); STRIDE_NUM];
|
||||||
let stride_count = stride_json.len();
|
let stride_count = stride_json.len();
|
||||||
let mut physical_idx = 0;
|
let mut physical_idx = 0;
|
||||||
let mut height = 0;
|
let mut size: Option<Rectangle<usize, StrideSpace>> = None;//Rectangle::new(Coordinates::new(usize::MAX, usize::MAX), Coordinates::new(0, 0));
|
||||||
let mut width = 0;
|
|
||||||
for stride_idx in 0..stride_count {
|
for stride_idx in 0..stride_count {
|
||||||
let json_data = stride_json[stride_idx];
|
let json_data = stride_json[stride_idx];
|
||||||
let x = json_data.0;
|
let x = json_data.0;
|
||||||
@ -168,11 +186,26 @@ impl<const STRIDE_NUM: usize> StrideMapping<STRIDE_NUM> {
|
|||||||
physical_idx
|
physical_idx
|
||||||
};
|
};
|
||||||
physical_idx += length as usize;
|
physical_idx += length as usize;
|
||||||
width = max(width, stride_idx + x as usize);
|
size = Some(match size.take() {
|
||||||
height = max(height, length as usize + y as usize)
|
None => Rectangle::new(
|
||||||
|
Coordinates::new(x.into(), y.into()),
|
||||||
|
Coordinates::new(x.into(), (y + length - 1).into()),
|
||||||
|
),
|
||||||
|
Some(s) => Rectangle::new(
|
||||||
|
Coordinates::new(
|
||||||
|
min(s.top_left.x, x.into()),
|
||||||
|
min(s.top_left.y, y.into())
|
||||||
|
),
|
||||||
|
Coordinates::new(
|
||||||
|
max(s.bottom_right.x, x.into()),
|
||||||
|
max(s.bottom_right.y, (y + length - 1).into())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
});
|
||||||
|
log::info!("stride={:?} size={:?}", strides[stride_idx], size);
|
||||||
}
|
}
|
||||||
let top = 0;
|
let s = size.take().unwrap();
|
||||||
let left = 0;
|
log::info!("size={:?}", s);
|
||||||
|
|
||||||
log::info!("strides={:?}", strides);
|
log::info!("strides={:?}", strides);
|
||||||
|
|
||||||
@ -180,119 +213,115 @@ impl<const STRIDE_NUM: usize> StrideMapping<STRIDE_NUM> {
|
|||||||
strides,
|
strides,
|
||||||
stride_count,
|
stride_count,
|
||||||
pixel_count: physical_idx,
|
pixel_count: physical_idx,
|
||||||
top,
|
size: s
|
||||||
left,
|
|
||||||
height: height as u8,
|
|
||||||
width: width as u8
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PixelMapping<u8> for StrideMapping {
|
impl PixelMapping<u8, StrideSpace> for StrideMapping {
|
||||||
fn select(&self, rect: &Rectangle<u8, Virtual>) -> impl CoordinateView {
|
fn select(&self, rect: &Rectangle<u8, Virtual>) -> impl CoordinateView<u8, StrideSpace> {
|
||||||
let ret = StrideView::new(self, rect);
|
StrideView::new(self, rect)
|
||||||
//log::info!("select={:?}", ret);
|
}
|
||||||
return ret;
|
|
||||||
|
fn to_idx(&self, coords: &Coordinates<u8, StrideSpace>) -> usize {
|
||||||
|
self.strides[coords.x as usize].pixel_idx_for_offset(coords.y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct StrideSpace {}
|
pub struct StrideSpace {}
|
||||||
impl CoordinateSpace for StrideSpace {}
|
impl CoordinateSpace for StrideSpace {}
|
||||||
|
pub type StrideCoords = Coordinates<u8, StrideSpace>;
|
||||||
type StrideCoords = Coordinates<u8, StrideSpace>;
|
|
||||||
|
|
||||||
struct StrideView<'a> {
|
struct StrideView<'a> {
|
||||||
map: &'a StrideMapping,
|
map: &'a StrideMapping,
|
||||||
rect: Rectangle<u8, Virtual>,
|
range: Rectangle<u8, StrideSpace>,
|
||||||
span_start: u8,
|
cur: StrideCoords,
|
||||||
span_end: u8,
|
step_size: VirtualCoordinates
|
||||||
offset_start: u8,
|
|
||||||
offset_end: u8,
|
|
||||||
virt_step_width: u8,
|
|
||||||
virt_step_height: u8,
|
|
||||||
cur_pixel: StrideCoords
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Debug for StrideView<'a> {
|
impl<'a> Debug for StrideView<'a> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("StrideView")
|
f.debug_struct("StrideView")
|
||||||
.field("rect", &self.rect)
|
.field("range", &self.range)
|
||||||
.field("span", &(self.span_start, self.span_end))
|
.field("step", &self.step_size)
|
||||||
.field("offset", &(self.offset_start, self.offset_end))
|
.field("cur", &self.cur).finish()
|
||||||
.field("step", &(self.virt_step_width, self.virt_step_height))
|
|
||||||
.field("cur", &self.cur_pixel).finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StrideView<'a> {
|
impl<'a> StrideView<'a> {
|
||||||
fn new(map: &'a StrideMapping, rect: &Rectangle<u8, Virtual>) -> Self {
|
fn new(map: &'a StrideMapping, rect: &Rectangle<u8, Virtual>) -> Self {
|
||||||
let span_start = scale8(rect.top_left.x, map.width);
|
// Zero-index shape of the pixel picking area
|
||||||
let span_end = scale8(rect.bottom_right.x, map.width);
|
let range: Rectangle<u8, StrideSpace> = Rectangle::new(
|
||||||
let offset_start = scale8(rect.top_left.y, map.height);
|
Coordinates::new(
|
||||||
let offset_end = scale8(rect.bottom_right.y, map.height);
|
scale8(map.size.width() as u8, rect.top_left.x) + map.size.left() as u8,
|
||||||
let virt_step_width = u8::MAX / std::cmp::max(1, span_end - span_start);
|
scale8(map.size.height() as u8, rect.top_left.y) + map.size.top() as u8
|
||||||
let virt_step_height = u8::MAX / std::cmp::max(1, offset_end - offset_start);
|
),
|
||||||
|
Coordinates::new(
|
||||||
|
scale8(map.size.width() as u8, rect.bottom_right.x) + map.size.left() as u8,
|
||||||
|
scale8(map.size.height() as u8, rect.bottom_right.y) + map.size.top() as u8
|
||||||
|
)
|
||||||
|
);
|
||||||
|
//log::info!("rect={:?} map.size={:?} range={:?}", rect, map.size, range);
|
||||||
|
debug_assert!(
|
||||||
|
range.bottom_right.x <= map.size.width() as u8 &&
|
||||||
|
range.bottom_right.y <= map.size.height() as u8,
|
||||||
|
"the range for this view is out of bounds range={:?} rect={:?}, map_size={:?}",
|
||||||
|
range,
|
||||||
|
rect,
|
||||||
|
(map.size.width(), map.size.height())
|
||||||
|
);
|
||||||
|
let step_size = VirtualCoordinates::new(
|
||||||
|
u8::MAX / std::cmp::max(1, range.width()),
|
||||||
|
u8::MAX / std::cmp::max(1, range.height())
|
||||||
|
//scale8(255, std::cmp::max(1, range.bottom_right.x - range.top_left.x)),
|
||||||
|
//scale8(255, std::cmp::max(1, range.bottom_right.y - range.top_left.y))
|
||||||
|
);
|
||||||
|
debug_assert_ne!(step_size.x, 0);
|
||||||
|
debug_assert_ne!(step_size.y, 0);
|
||||||
return Self {
|
return Self {
|
||||||
map,
|
map,
|
||||||
rect: rect.clone(),
|
range,
|
||||||
span_start,
|
step_size,
|
||||||
span_end,
|
cur: range.top_left.clone()
|
||||||
offset_start,
|
|
||||||
offset_end,
|
|
||||||
virt_step_width,
|
|
||||||
virt_step_height,
|
|
||||||
cur_pixel: StrideCoords::new(span_start, offset_start)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CoordinateView for StrideView<'a> {
|
impl<'a> CoordinateView<u8, StrideSpace> for StrideView<'a> {
|
||||||
fn next(&mut self) -> Option<(VirtualCoordinates, PhysicalCoordinates)> {
|
fn next(&mut self) -> Option<(VirtualCoordinates, StrideCoords)> {
|
||||||
let mut search_idx = self.cur_pixel.x;
|
// Keep scanning until we reach the far right of the range
|
||||||
let mut next_offset = self.cur_pixel.y + 1;
|
while self.cur.x <= self.range.bottom_right.x {
|
||||||
//log::info!("view={:?}", self);
|
debug_assert!((self.cur.x as usize) < self.map.strides.len(), "stride out of bounds {:?}", self);
|
||||||
|
let cur_stride: &Stride = &self.map.strides[self.cur.x as usize];
|
||||||
|
|
||||||
while search_idx <= self.span_end {
|
// Skip ahead to the top of the current stride if we are starting from higher above.
|
||||||
let cur = &self.map.strides[search_idx as usize];
|
if self.cur.y < cur_stride.y {
|
||||||
|
self.cur.y = cur_stride.y;
|
||||||
|
}
|
||||||
|
|
||||||
if next_offset < self.offset_start || next_offset < cur.y {
|
// If we are at the bottom of our rectangle, or our stride, go to the next stride.
|
||||||
search_idx += 1;
|
if self.cur.y > self.range.bottom_right.y || self.cur.y > cur_stride.y + cur_stride.length - 1 {
|
||||||
|
self.cur.x += 1;
|
||||||
|
self.cur.y = self.range.top_left.y;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if next_offset > cur.y + cur.length || next_offset > self.offset_end {
|
// By now, we must be safely somewhere inside our current stride
|
||||||
search_idx += 1;
|
debug_assert!(self.cur.y <= cur_stride.y + cur_stride.length - 1, "coords={:?} out of bounds for stride={:?} view={:?}", self.cur, cur_stride, self);
|
||||||
if search_idx <= self.span_end {
|
|
||||||
next_offset = self.map.strides[search_idx as usize].y;
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
//log::info!("search end");
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.cur_pixel = StrideCoords::new(search_idx, next_offset);
|
// Move to the next coord and return
|
||||||
let virt = VirtualCoordinates::new(
|
let physical_coords = self.cur.clone();
|
||||||
(self.cur_pixel.x * self.virt_step_width).try_into().unwrap(),
|
self.cur.y += 1;
|
||||||
((self.cur_pixel.y - cur.y) * self.virt_step_height).try_into().unwrap()
|
|
||||||
|
let virtual_coords = VirtualCoordinates::new(
|
||||||
|
(physical_coords.x as u8).saturating_mul(self.step_size.x),
|
||||||
|
(physical_coords.y as u8).saturating_mul(self.step_size.y)
|
||||||
);
|
);
|
||||||
let stride_pos = StrideCoords::new(self.cur_pixel.x.try_into().unwrap(), (self.cur_pixel.y - cur.y).try_into().unwrap());
|
|
||||||
|
|
||||||
let span = &self.map.strides[stride_pos.x as usize];
|
return Some((virtual_coords, physical_coords));
|
||||||
//log::info!("span={:?} stride={:?} cur={:?}", span, stride_pos, self.cur_pixel);
|
|
||||||
let phys_idx = if span.reverse {
|
|
||||||
span.physical_idx + (span.length - 1 - stride_pos.y) as usize
|
|
||||||
} else {
|
|
||||||
span.physical_idx + stride_pos.y as usize
|
|
||||||
};
|
|
||||||
let phys = PhysicalCoordinates::new(phys_idx, 0);
|
|
||||||
//log::info!("virt={:?} phys={:?}", virt, phys);
|
|
||||||
return Some((virt, phys));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//log::info!("end");
|
None
|
||||||
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user