1use std::mem::swap;
2
3use crate::widgets::Widget;
4use crate::{Pixel, PixelBuffer, ScreenPos, ScreenSize};
5
6pub struct Viewport {
8 pub position: ScreenPos,
11
12 pub size: ScreenSize,
15 new_buf: PixelBuffer,
16 old_buf: PixelBuffer,
17 dirty: bool,
18 buf: Vec<Pixel>,
22}
23
24impl Viewport {
25 pub fn new(position: ScreenPos, size: ScreenSize) -> Self {
27 Self {
28 position,
29 size,
30 new_buf: PixelBuffer::new(size),
31 old_buf: PixelBuffer::new(size),
32 dirty: true,
33 buf: Vec::new(),
34 }
35 }
36
37 pub fn resize(&mut self, width: u16, height: u16) {
41 self.size = ScreenSize::new(width, height);
42 self.new_buf = PixelBuffer::new(self.size);
43 self.old_buf = PixelBuffer::new(self.size);
44 self.dirty = true;
45 }
46
47 pub fn draw_pixels(&mut self, pixels: &[Pixel]) {
50 pixels.into_iter().for_each(|pixel| {
51 self.draw_pixel(*pixel);
52 });
53 self.dirty = true;
54 }
55
56 pub fn draw_pixel(&mut self, pixel: Pixel) {
61 if self.in_view(pixel.pos) {
62 self.new_buf.set_pixel(pixel);
63 }
64 self.dirty = true;
65 }
66
67 pub fn draw_widget(&mut self, widget: &impl Widget, offset: ScreenPos) {
69 widget.pixels(self.size).into_iter().for_each(|mut p| {
70 p.pos.x += offset.x;
71 p.pos.y += offset.y;
72 self.draw_pixel(p);
73 })
74 }
75
76 fn in_view(&self, pos: ScreenPos) -> bool {
77 pos.x < self.size.width && pos.y < self.size.height
78 }
79
80 fn offset(&self, pos: ScreenPos) -> ScreenPos {
81 ScreenPos::new(pos.x + self.position.x, pos.y + self.position.y)
82 }
83
84 pub fn swap_buffers(&mut self) {
85 swap(&mut self.new_buf, &mut self.old_buf);
86 self.new_buf.pixels.iter_mut().for_each(|opt| {
87 opt.take();
88 });
89 self.dirty = true;
90 }
91
92 pub(crate) fn pixels(&mut self) -> impl Iterator<Item = &Pixel> {
93 if self.dirty {
94 let mut pixels = Vec::<Pixel>::new();
95
96 for (new, old) in self
97 .new_buf
98 .pixels
99 .iter()
100 .enumerate()
101 .zip(&self.old_buf.pixels)
102 {
103 match (new, old) {
104 ((index, Some(pixel)), _) => {
105 let pos = self.offset(self.new_buf.index_to_coords(index));
106 let mut pixel = *pixel;
107 pixel.pos = pos;
108 pixels.push(pixel);
109 }
110 ((index, None), Some(_)) => {
111 let pos = self.offset(self.new_buf.index_to_coords(index));
112 pixels.push(Pixel::white(' ', pos));
113 }
114 ((_, None), None) => {}
115 }
116 }
117 self.buf = pixels;
118 }
119
120 self.buf.iter()
121 }
122}
123
124#[cfg(test)]
125mod test {
126 use super::*;
127 use crate::*;
128
129 fn camera(viewport: &Viewport) -> Camera<camera::NoLimit> {
130 let pos = WorldPos::new(30, 30);
131 Camera::from_viewport(pos, viewport)
132 }
133
134 fn viewport() -> Viewport {
135 let pos = ScreenPos::new(2, 2);
136 let size = ScreenSize::new(6, 6);
137 Viewport::new(pos, size)
138 }
139
140 #[test]
141 fn draw_corners() {
142 let mut view = viewport();
143 let cam = camera(&view);
144
145 let min_x = cam.bounding_box.min_x();
146 let max_x = cam.bounding_box.max_x();
147 let min_y = cam.bounding_box.min_y();
148 let max_y = cam.bounding_box.max_y();
149
150 let a = WorldPos::new(min_x, min_y);
151 let b = WorldPos::new(max_x - 1, min_y);
152 let c = WorldPos::new(min_x, max_y - 1);
153 let d = WorldPos::new(max_x - 1, max_y - 1);
154
155 let positions = vec![a, b, c, d];
156 let glyphs = vec!['A', 'B', 'C', 'D'];
157 let pixels = positions
158 .into_iter()
159 .zip(glyphs)
160 .map(|(p, g)| Pixel::new(g, cam.to_screen(p), None, None))
161 .collect::<Vec<_>>();
162
163 view.draw_pixels(&pixels);
164
165 let a = Pixel::new('A', ScreenPos::new(2, 2), None, None);
166 let b = Pixel::new('B', ScreenPos::new(7, 2), None, None);
167 let c = Pixel::new('C', ScreenPos::new(2, 7), None, None);
168 let d = Pixel::new('D', ScreenPos::new(7, 7), None, None);
169
170 let drawn_pixels = view.pixels().cloned().collect::<Vec<_>>();
171
172 assert_eq!(&drawn_pixels, &[a, b, c, d]);
173 }
174}