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