1pub mod cursor;
7pub mod style;
8
9use shuyi_core::{Pos, TerrainId};
10
11use super::layout::{CELL_PX, Grid};
12use super::path;
13use super::scene::MapScene;
14use super::skin::{GridSkin, Looks, MapColors, UnitPainter, WalkSpeeds};
15use super::threat;
16use crate::frame::color::EngineColor;
17use crate::frame::glyph_buffer::{Cell, GlyphBuffer, Rect};
18
19pub use style::{CursorLook, FlashLook, GlyphStyle, RangeLook, ThreatLook};
20
21pub const TILE_W_CELLS: i32 = 2;
23
24pub const fn tile_px() -> (i32, i32) {
27 (TILE_W_CELLS * CELL_PX.0, CELL_PX.1)
28}
29
30pub const OVERLAY_BLEND: f32 = 0.75;
33
34#[derive(Debug, Clone, Copy, PartialEq)]
37pub struct GlyphSkin {
38 pub colors: MapColors,
41 pub walk: WalkSpeeds,
43 pub foot_px: i32,
46 pub style: GlyphStyle,
50}
51
52impl<U> GridSkin<U> for GlyphSkin {
53 fn name(&self) -> &'static str {
54 "glyph"
55 }
56
57 fn tile_size(&self) -> (i32, i32) {
58 tile_px()
59 }
60
61 fn view_tiles(&self, area: Rect) -> (i32, i32) {
62 self.style.view_tiles(area)
63 }
64
65 fn grid(&self, scene: &MapScene<U>, area: Rect) -> Grid {
66 self.style.layout(scene, area).grid()
67 }
68
69 fn walk(&self) -> WalkSpeeds {
70 self.walk
71 }
72
73 fn paint(
78 &self,
79 looks: &Looks,
80 scene: &MapScene<U>,
81 area: Rect,
82 units: &dyn UnitPainter<U>,
83 buf: &mut GlyphBuffer,
84 ) {
85 let style = &self.style;
86 let layout = style.layout(scene, area);
87 let grid = layout.grid();
88 style.draw_tiles(looks, buf, scene, &layout);
89 units.paint_under(scene, &grid, buf);
90 units.paint(scene, &grid, buf);
91 let color = looks.path(&self.colors);
92 for overlay in path::path_overlays(&scene.path, &grid, color) {
93 buf.add_overlay(overlay);
94 }
95 units.paint_over(scene, &grid, buf);
96 style.draw_cursor(self.cursor_ink(looks), buf, scene, &layout, area);
97 let colors = style.threat_colors(looks, &self.colors);
99 threat::paint(scene, &grid, threat::Start::Edge, colors, buf);
100 }
101}
102
103impl GlyphSkin {
104 pub const fn new(colors: MapColors, walk: WalkSpeeds, foot_px: i32) -> Self {
107 Self {
108 colors,
109 walk,
110 foot_px,
111 style: GlyphStyle::PLAIN,
112 }
113 }
114
115 #[must_use]
117 pub const fn styled(mut self, style: GlyphStyle) -> Self {
118 self.style = style;
119 self
120 }
121
122 pub fn cursor_ink(&self, looks: &Looks) -> cursor::CursorInk {
124 cursor::CursorInk {
125 color: looks.cursor(&self.colors),
126 dark: looks.dark(),
127 foot_px: self.foot_px,
128 }
129 }
130
131 pub fn draw_cursor<U>(
134 &self,
135 looks: &Looks,
136 buf: &mut GlyphBuffer,
137 scene: &MapScene<U>,
138 layout: &Layout,
139 ) {
140 if let Some(c) = &scene.cursor
141 && let Some(at) = tile_to_cell(c.pos, layout)
142 {
143 cursor::draw_cursor(buf, self.cursor_ink(looks), c, layout, at);
144 }
145 }
146}
147
148pub fn view_tiles(area: Rect) -> (i32, i32) {
151 (area.w.max(0) / TILE_W_CELLS, area.h.max(0))
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub struct Layout {
157 origin: Pos,
159 cell: (i32, i32),
161 tiles: (i32, i32),
163}
164
165impl Layout {
166 pub fn new<U>(scene: &MapScene<U>, area: Rect) -> Self {
168 Self::fitted(scene, (area.x, area.y), view_tiles(area))
169 }
170
171 pub fn fitted<U>(scene: &MapScene<U>, corner: (i32, i32), fit: (i32, i32)) -> Self {
175 Self {
176 origin: scene.origin,
177 cell: corner,
178 tiles: (scene.size.0.clamp(0, fit.0), scene.size.1.clamp(0, fit.1)),
179 }
180 }
181
182 pub fn cells(&self) -> Rect {
184 let (x, y) = self.cell;
185 Rect::new(x, y, TILE_W_CELLS * self.tiles.0, self.tiles.1)
186 }
187
188 pub fn grid(&self) -> Grid {
190 let (cw, ch) = CELL_PX;
191 Grid::top_down(
192 self.origin,
193 (self.cell.0 * cw, self.cell.1 * ch),
194 (TILE_W_CELLS * cw, ch),
195 self.tiles,
196 1,
197 )
198 }
199}
200
201pub fn tile_to_cell(tile: Pos, layout: &Layout) -> Option<(i32, i32)> {
205 let dx = tile.x.checked_sub(layout.origin.x)?;
206 let dy = tile.y.checked_sub(layout.origin.y)?;
207 let inside = (0..layout.tiles.0).contains(&dx) && (0..layout.tiles.1).contains(&dy);
208 inside.then(|| (layout.cell.0 + TILE_W_CELLS * dx, layout.cell.1 + dy))
209}
210
211pub fn cell_of(grid: &Grid, tile: Pos) -> Option<(i32, i32)> {
215 let px = grid.rect(tile)?;
216 let (cw, ch) = CELL_PX;
217 Some((px.x.div_euclid(cw), px.y.div_euclid(ch)))
218}
219
220pub fn draw_terrain(looks: &Looks, buf: &mut GlyphBuffer, id: TerrainId, (x, y): (i32, i32)) {
223 let Some(t) = looks.terrains.glyphs(id) else {
224 return;
225 };
226 let fg = looks.named(t.fg, EngineColor::Text);
227 let bg = looks.named(t.bg, EngineColor::Background);
228 for (i, glyph) in (0..).zip(t.glyphs) {
229 buf.set(x + i, y, Cell::new(glyph, fg, bg));
230 }
231}
232
233pub fn draw_tiles<U>(looks: &Looks, buf: &mut GlyphBuffer, scene: &MapScene<U>, layout: &Layout) {
240 GlyphStyle::PLAIN.draw_tiles(looks, buf, scene, layout);
241}
242
243#[cfg(test)]
244pub(crate) mod tests {
245 use proptest::prelude::*;
246
247 use super::*;
248 use crate::frame::color::Rgb;
249 use crate::frame::glyph_buffer::{Layer, Overlay};
250 use crate::grid::layout::px_rect;
251 use crate::grid::scene::{CursorStyle, CursorView, RangeName, ThreatView};
252 use crate::grid::skin::NoUnits;
253 use crate::grid::testing::{any_area, any_scene};
254 use crate::prelude::*;
255 use crate::toy::grid::{GLYPH, GRASS, Kit, Letters, Piece, SAND, WATER};
256
257 pub(crate) const AREA: Rect = Rect::new(0, 0, 70, 30);
259
260 pub(crate) fn layout_at(origin: Pos) -> Layout {
262 let size = view_tiles(AREA);
263 Layout::new(&MapScene::<Piece>::new(origin, size), AREA)
264 }
265
266 fn p(x: i32, y: i32) -> Pos {
267 Pos::new(x, y)
268 }
269
270 const MOVE: RangeName = RangeName("move");
271 const DANGER: RangeName = RangeName("danger");
272 const HEAL: RangeName = RangeName("heal");
273
274 fn blank() -> GlyphBuffer {
276 GlyphBuffer::new(100, 32, fill())
277 }
278
279 fn fill() -> Cell {
280 Cell::new('x', Rgb::new(1, 2, 3), Rgb::new(4, 5, 6))
281 }
282
283 fn paint(kit: &Kit, scene: &MapScene<Piece>, area: Rect) -> GlyphBuffer {
284 let mut buf = blank();
285 GLYPH.paint(&kit.looks(), scene, area, &Letters, &mut buf);
286 buf
287 }
288
289 #[test]
290 fn a_70_by_30_cell_area_shows_35_by_30_tiles() {
291 let skin: &dyn GridSkin<Piece> = &GLYPH;
292 assert_eq!(skin.name(), "glyph");
293 assert_eq!(skin.view_tiles(AREA), (35, 30));
294 assert_eq!(skin.tile_size(), (16, 16));
295 assert_eq!(skin.walk(), GLYPH.walk);
296 assert_eq!(skin.view_tiles(Rect::new(3, 4, 7, 5)), (3, 5));
298 assert_eq!(skin.view_tiles(Rect::new(0, 0, -4, -1)), (0, 0));
299 }
300
301 #[test]
302 fn tile_to_cell_maps_two_cells_per_tile() {
303 let c = layout_at(p(5, 2));
304 assert_eq!(tile_to_cell(Pos::new(5, 2), &c), Some((0, 0)));
305 assert_eq!(tile_to_cell(Pos::new(6, 3), &c), Some((2, 1)));
306 assert_eq!(tile_to_cell(Pos::new(39, 31), &c), Some((68, 29)));
307 assert_eq!(tile_to_cell(Pos::new(40, 2), &c), None);
308 assert_eq!(tile_to_cell(Pos::new(5, 32), &c), None);
309 assert_eq!(tile_to_cell(Pos::new(4, 2), &c), None);
310 assert_eq!(tile_to_cell(Pos::new(5, 1), &c), None);
311 let small = layout_at(p(-10, -11));
312 assert_eq!(tile_to_cell(Pos::new(0, 0), &small), Some((20, 11)));
313 assert_eq!(tile_to_cell(Pos::new(i32::MIN, 0), &c), None);
314 assert_eq!(tile_to_cell(Pos::new(0, i32::MIN), &c), None);
315 let grid = c.grid();
317 for tile in [p(5, 2), p(6, 3), p(39, 31), p(40, 2), p(4, 2), p(5, 1)] {
318 assert_eq!(cell_of(&grid, tile), tile_to_cell(tile, &c), "{tile:?}");
319 }
320 let off = Layout::new(
322 &MapScene::<Piece>::new(p(0, 0), (3, 3)),
323 Rect::new(-6, -2, 6, 3),
324 );
325 assert_eq!(cell_of(&off.grid(), p(0, 0)), Some((-6, -2)));
326 assert_eq!(tile_to_cell(p(1, 1), &off), Some((-4, -1)));
327 assert_eq!(cell_of(&off.grid(), p(1, 1)), Some((-4, -1)));
328 }
329
330 #[test]
331 fn a_layout_starts_at_its_area_and_draws_no_more_tiles_than_fit() {
332 let area = Rect::new(4, 3, 7, 2);
333 let big = Layout::new(&MapScene::<Piece>::new(p(1, 1), (9, 9)), area);
335 assert_eq!(big.cells(), Rect::new(4, 3, 6, 2));
336 assert_eq!(tile_to_cell(p(1, 1), &big), Some((4, 3)));
337 assert_eq!(tile_to_cell(p(3, 2), &big), Some((8, 4)));
338 assert_eq!(tile_to_cell(p(4, 2), &big), None);
339 assert_eq!(tile_to_cell(p(3, 3), &big), None);
340 assert_eq!(big.grid().tile_px(p(1, 1)), (32, 48));
341 assert_eq!(big.grid().tile_px(p(0, 3)), (16, 80));
342 assert_eq!(big.grid().bounds(), px_rect(big.cells()));
343 assert_eq!(big.grid().tile, (16, 16));
344 assert_eq!(tile_px(), (16, 16));
345 let small = Layout::new(&MapScene::<Piece>::new(p(1, 1), (2, 1)), area);
347 assert_eq!(small.cells(), Rect::new(4, 3, 4, 1));
348 assert_eq!(tile_to_cell(p(2, 1), &small), Some((6, 3)));
349 assert_eq!(tile_to_cell(p(3, 1), &small), None);
350 assert_eq!(tile_to_cell(p(1, 2), &small), None);
351 let fitted = Layout::fitted(&MapScene::<Piece>::new(p(1, 1), (9, 9)), (5, 0), (2, 3));
353 assert_eq!(fitted.cells(), Rect::new(5, 0, 4, 3));
354 assert_eq!(layout_at(p(0, 0)).cells(), AREA);
355 }
356
357 #[test]
358 fn tile_px_is_the_16_px_square_of_a_visible_tile() {
359 let scene = MapScene::<Piece>::new(p(-10, -11), view_tiles(AREA));
360 let px = |tile| GLYPH.tile_px(&scene, AREA, tile);
361 assert_eq!(px(p(0, 0)), Some(Rect::new(160, 176, 16, 16)));
362 assert_eq!(px(p(24, 18)), Some(Rect::new(544, 464, 16, 16)));
363 assert_eq!(px(p(25, 18)), None);
364 assert_eq!(px(p(-11, 0)), None);
365 assert_eq!(
366 GLYPH.tile_cells(&scene, AREA, p(3, 5)),
367 Some(Rect::new(26, 16, 2, 1))
368 );
369 let area = Rect::new(4, 3, 7, 2);
371 assert_eq!(
372 GLYPH.tile_px(&scene, area, p(-9, -10)),
373 Some(Rect::new(48, 64, 16, 16))
374 );
375 }
376
377 #[test]
380 fn threat_arrows_and_marks_are_painted_last_over_everything() {
381 let kit = Kit::new();
382 let looks = kit.looks();
383 let area = Rect::new(10, 5, 8, 3);
384 let mut scene = MapScene::new(p(2, 2), (4, 3));
385 scene.push_unit(Piece::new(4, 'f', p(5, 3)));
386 scene.path = vec![p(2, 2), p(3, 2)];
387 scene.cursor = Some(CursorView {
388 pos: p(3, 2),
389 brightness: 1.0,
390 style: CursorStyle::Corners,
391 });
392 let buf = paint(&kit, &scene, area);
393 let before = buf.overlays();
394 let threat = ThreatView {
395 tile: p(3, 2),
396 enemies: vec![p(5, 3)],
397 };
398 scene.threat = Some(threat.clone());
399 let with = paint(&kit, &scene, area);
400 for y in 0..32 {
402 for x in 0..100 {
403 assert_eq!(with.get(x, y), buf.get(x, y), "({x}, {y})");
404 }
405 }
406 let colors = looks.threat(&GLYPH.colors);
407 let grid = Layout::new(&scene, area).grid();
408 let expect = threat::threat_overlays(&threat, &grid, threat::Start::Edge, colors);
409 let after = with.overlays();
410 assert_eq!(&after[..before.len()], before);
411 assert_eq!(&after[before.len()..], expect);
412 let arrows: Vec<_> = expect.iter().filter(|o| o.color == colors.arrow).collect();
417 assert_eq!(arrows[0].rect, Rect::new(127, 100, 1, 1));
418 let tile = Rect::new(128, 96, 16, 16);
419 assert!(arrows.iter().all(|o| o.rect.intersect(&tile).is_none()));
420 assert!(arrows.iter().all(|o| o.layer == Layer::Over));
421 let marks: Vec<_> = expect.iter().filter(|o| o.color == colors.mark).collect();
422 assert_eq!(marks[0].rect, Rect::new(134, 84, 3, 6));
423 assert_eq!(marks[1].rect, Rect::new(134, 91, 3, 2));
424 assert_eq!(marks.len(), 2);
425 }
426
427 #[test]
428 fn paints_terrain_then_tints_then_units_path_and_cursor() {
429 let kit = Kit::new();
430 let looks = kit.looks();
431 let pal = &kit.palette;
432 let grass = looks.terrains.glyphs(GRASS).unwrap();
433 let (fg, bg) = (pal.lookup(grass.fg).unwrap(), pal.lookup(grass.bg).unwrap());
434 let area = Rect::new(10, 5, 8, 3);
435 let mut scene = MapScene::new(p(2, 2), (4, 3));
436 for pos in [p(2, 2), p(3, 2), p(4, 2), p(5, 2), p(2, 3)] {
437 scene.tile_mut(pos).unwrap().terrain = Some(GRASS);
438 }
439 scene.tile_mut(p(3, 3)).unwrap().terrain = Some(TerrainId(999));
441 scene.tint([p(3, 2)], DANGER);
442 scene.tint([p(3, 2), p(4, 2)], MOVE);
443 scene.tint([p(4, 4)], HEAL);
445 scene.push_unit(Piece::new(4, 'f', p(4, 2)));
446 scene.path = vec![p(2, 2), p(3, 2)];
447 scene.cursor = Some(CursorView {
448 pos: p(5, 2),
449 brightness: 1.0,
450 style: CursorStyle::Corners,
451 });
452 let buf = paint(&kit, &scene, area);
453 let cell = |x, y| *buf.get(x, y).unwrap();
454 assert_eq!(cell(10, 5), Cell::new(grass.glyphs[0], fg, bg));
456 assert_eq!(cell(11, 5), Cell::new(grass.glyphs[1], fg, bg));
457 let blend = |bg: Rgb, kind| bg.lerp(looks.range(kind), OVERLAY_BLEND);
459 let both = blend(blend(bg, DANGER), MOVE);
460 assert_eq!(cell(12, 5), Cell::new(grass.glyphs[0], fg, both));
461 assert_eq!(cell(13, 5).bg, both);
462 let moved = blend(bg, MOVE);
464 let piece = pal.lookup("piece").unwrap();
465 assert_eq!(cell(14, 5), Cell::new('f', piece, moved));
466 assert_eq!(cell(15, 5), Cell::new(grass.glyphs[1], fg, moved));
467 assert_eq!(cell(12, 6), fill());
470 assert_eq!(cell(14, 6), fill());
471 let healed = Cell {
472 bg: blend(fill().bg, HEAL),
473 ..fill()
474 };
475 assert_eq!((cell(14, 7), cell(15, 7)), (healed, healed));
476 assert_eq!(cell(16, 7), fill());
477 let colors: Vec<Rgb> = buf.overlays().iter().map(|o| o.color).collect();
479 let path = looks.path(&GLYPH.colors);
480 let mut expect = vec![piece, path];
481 expect.extend([path; 6]);
482 expect.extend([looks.cursor(&GLYPH.colors); 8]);
483 assert_eq!(colors, expect);
484 assert_eq!(buf.overlays()[0].rect, Rect::new(113, 94, 14, 2));
487 }
488
489 #[test]
490 fn a_changed_tile_flashes_and_a_tile_that_would_change_shows_what_it_becomes() {
491 let kit = Kit::new();
492 let looks = kit.looks();
493 let pal = &kit.palette;
494 let look = |id| {
495 let t = looks.terrains.glyphs(id).unwrap();
496 (
497 t.glyphs,
498 pal.lookup(t.fg).unwrap(),
499 pal.lookup(t.bg).unwrap(),
500 )
501 };
502 let (grass, water) = (look(GRASS), look(WATER));
503 let mut scene = MapScene::new(p(0, 0), (5, 1));
504 for tile in &mut scene.tiles {
505 tile.terrain = Some(GRASS);
506 }
507 scene.tile_mut(p(0, 0)).unwrap().flashes = vec![0.5];
509 scene.tile_mut(p(1, 0)).unwrap().flashes = vec![1.0];
510 scene.tile_mut(p(4, 0)).unwrap().flashes = vec![1.0, 0.5];
511 scene.tint([p(1, 0), p(2, 0), p(3, 0)], RangeName("attack"));
512 scene.tile_mut(p(2, 0)).unwrap().becomes = Some(WATER);
514 scene.tile_mut(p(3, 0)).unwrap().becomes = Some(TerrainId(999));
515 let buf = paint(&kit, &scene, Rect::new(0, 0, 10, 1));
516 let cell = |x| *buf.get(x, 0).unwrap();
517 let (glyphs, fg, bg) = grass;
518 let attack = looks.range(RangeName("attack"));
519 let half = bg.lerp(fg, OVERLAY_BLEND * 0.5);
521 assert_eq!(cell(0), Cell::new(glyphs[0], fg, half));
522 assert_eq!(cell(1), Cell::new(glyphs[1], fg, half));
523 let full = bg.lerp(fg, OVERLAY_BLEND);
525 assert_eq!(cell(2).bg, full.lerp(attack, OVERLAY_BLEND));
526 assert_eq!(cell(4), Cell::new(water.0[0], water.1, water.2));
528 assert_eq!(cell(5), Cell::new(water.0[1], water.1, water.2));
529 assert_eq!(
531 cell(6),
532 Cell::new(glyphs[0], fg, bg.lerp(attack, OVERLAY_BLEND))
533 );
534 assert_eq!(cell(8).bg, full.lerp(fg, OVERLAY_BLEND * 0.5));
536 assert_ne!(water, grass);
537 }
538
539 #[test]
540 fn a_terrain_whose_colors_the_palette_lacks_is_drawn_in_the_engines() {
541 let kit = Kit::new();
542 let looks = kit.looks();
543 let mut scene = MapScene::<Piece>::new(p(0, 0), (1, 1));
544 scene.tiles[0].terrain = Some(SAND);
545 let mut buf = blank();
546 GLYPH.paint(&looks, &scene, Rect::new(0, 0, 2, 1), &NoUnits, &mut buf);
547 let sand = looks.terrains.glyphs(SAND).unwrap();
548 let text = kit.palette.engine(EngineColor::Text);
549 let background = kit.palette.engine(EngineColor::Background);
550 assert!(kit.palette.lookup(sand.fg).is_none());
551 assert!(kit.palette.lookup(sand.bg).is_none());
552 assert_eq!(
553 buf.get(0, 0),
554 Some(&Cell::new(sand.glyphs[0], text, background))
555 );
556 assert_eq!(
557 buf.get(1, 0),
558 Some(&Cell::new(sand.glyphs[1], text, background))
559 );
560 }
561
562 struct Marked;
565
566 const MARK: Rgb = Rgb::new(9, 8, 7);
568
569 impl UnitPainter<Piece> for Marked {
570 fn paint(&self, scene: &MapScene<Piece>, grid: &Grid, buf: &mut GlyphBuffer) {
571 Letters.paint(scene, grid, buf);
572 }
573
574 fn paint_over(&self, scene: &MapScene<Piece>, grid: &Grid, buf: &mut GlyphBuffer) {
575 for piece in &scene.units {
576 if let (Some((x, y)), Some(tile)) = (cell_of(grid, piece.pos), grid.rect(piece.pos))
577 && let Some(&cell) = buf.get(x, y)
578 {
579 buf.set(x, y, Cell { glyph: '^', ..cell });
580 buf.add_overlay(Overlay::new(tile, MARK, Layer::Over));
581 }
582 }
583 }
584 }
585
586 #[test]
589 fn what_lies_over_the_units_is_painted_under_the_cursor() {
590 let kit = Kit::new();
591 let looks = kit.looks();
592 let area = Rect::new(10, 5, 8, 3);
593 let mut scene = MapScene::new(p(2, 2), (4, 3));
594 scene.push_unit(Piece::new(4, 'f', p(4, 2)));
595 scene.path = vec![p(2, 2), p(3, 2)];
596 let paint = |scene: &MapScene<Piece>, units: &dyn UnitPainter<Piece>| {
597 let mut buf = blank();
598 GLYPH.paint(&looks, scene, area, units, &mut buf);
599 buf
600 };
601 let bare = paint(&scene, &Marked);
602 assert_eq!(bare.get(14, 5).map(|c| c.glyph), Some('^'));
604 let colors: Vec<Rgb> = bare.overlays().iter().map(|o| o.color).collect();
606 let path = looks.path(&GLYPH.colors);
607 let piece = kit.palette.lookup("piece").unwrap();
608 let mut expect = vec![piece];
609 expect.extend([path; 7]);
610 expect.push(MARK);
611 assert_eq!(colors, expect);
612 scene.cursor = Some(CursorView {
614 pos: p(4, 2),
615 brightness: 1.0,
616 style: CursorStyle::Corners,
617 });
618 let with = paint(&scene, &Marked);
619 let n = bare.overlays().len();
620 assert_eq!(&with.overlays()[..n], bare.overlays());
621 let cursor = looks.cursor(&GLYPH.colors);
622 assert_eq!(with.overlays()[n..].len(), 8);
623 assert!(with.overlays()[n..].iter().all(|o| o.color == cursor));
624 let letters = paint(&scene, &Letters);
627 assert_eq!(letters.overlays().len(), n + 8 - 1);
628 assert_eq!(letters.get(14, 5).map(|c| c.glyph), Some('f'));
629 }
630
631 #[test]
632 fn units_and_the_cursor_off_the_view_are_not_drawn() {
633 let kit = Kit::new();
634 let area = Rect::new(0, 0, 4, 1);
635 let mut scene = MapScene::new(p(0, 0), (2, 1));
636 scene.units.push(Piece::new(4, 'f', p(2, 0)));
638 scene.cursor = Some(CursorView {
639 pos: p(0, 1),
640 brightness: 1.0,
641 style: CursorStyle::TileGlow,
642 });
643 assert_eq!(paint(&kit, &scene, area), blank());
644 }
645
646 proptest! {
647 #[test]
651 fn glyph_skin_paints_only_the_area(scene in any_scene(), area in any_area()) {
652 let kit = Kit::new();
653 let buf = paint(&kit, &scene, area);
654 for y in 0..32 {
655 for x in 0..100 {
656 if !area.contains(x, y) {
657 prop_assert_eq!(buf.get(x, y), Some(&fill()), "cell ({}, {})", x, y);
658 }
659 }
660 }
661 let px = px_rect(area);
662 for item in buf.items() {
663 let seen = item.visible();
664 prop_assert_eq!(seen.intersect(&px), Some(seen), "{:?} in {:?}", item, area);
665 }
666 }
667 }
668}