Skip to main content

monospace_core/
buffer.rs

1//! The buffer: a window of cells. See _The buffer_ and _Stamping_ in
2//! [`docs/model.md`](../../../docs/model.md).
3
4use std::collections::HashMap;
5
6use crate::{Arm, Cell, Pos, ShapeId, StrokeCell, Window};
7
8/// Which side of an already-defined cell decides when a stamp lands on it. See _Stamping_ in
9/// [`docs/model.md`](../../../docs/model.md).
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum StampMode {
12    /// Overwrites the base stroke and every arm the stamp decides. An arm the stamp leaves
13    /// `Unset` keeps whatever the target already had.
14    Above,
15    /// Leaves the base stroke alone. Writes only the sides the target has left `Unset`, taking
16    /// them from the stamp; a side the target has already decided keeps its value.
17    Below,
18}
19
20/// What a stamp is handed to: one write operation and no reader.
21///
22/// **A sink is the whole of what writing needs**, which is what lets a figure layer hold one
23/// without holding a [`Buffer`], and lets an adapter stand between a drawing and the store that
24/// keeps it — measuring what passes, or sending it somewhere else — without the drawing knowing.
25/// [`Buffer`] is the sink this crate ships, and the one that keeps the cells and the owners.
26///
27/// The call is the one [`Surface::stamp`](crate::Surface::stamp) becomes, and it carries the two
28/// things a fragment is never told: which [`StampMode`] decides the composition, and which identity
29/// the stamp is made under. See _Stamping_ in [`docs/model.md`](../../../docs/model.md).
30pub trait StampSink {
31    /// Writes `cell` at the absolute position `at`, under `mode`, recorded under `owner`.
32    ///
33    /// **What a sink does with a stamp it cannot hold is its own answer**: a buffer writes nothing
34    /// outside its window.
35    fn stamp(&mut self, at: Pos, cell: Cell, mode: StampMode, owner: Option<ShapeId>);
36}
37
38/// What a caller reads a cell through: one position, answered from the store behind it.
39///
40/// **Reading a cell is the whole of what rendering needs**, which is what lets [`render`] ask a
41/// value for its cells without being handed a [`Buffer`] and without being able to ask who owns
42/// one. [`Buffer`] is the reader this crate ships.
43///
44/// [`render`]: crate::render
45pub trait CellReader {
46    /// The cell at the absolute position `at`, or `None` if it is undefined.
47    ///
48    /// An undefined cell and a position nothing was ever written at look the same: there is no
49    /// cell there either way, so there is nothing to distinguish them by.
50    #[must_use]
51    fn cell(&self, at: Pos) -> Option<&Cell>;
52}
53
54/// What a caller reads an owner through: one position, answered from the record behind it.
55///
56/// **Who wrote a cell is a question of its own**, asked by a front end deciding what a click
57/// landed on and by nothing that renders, so it rides on an interface separate from
58/// [`CellReader`]. [`Buffer`] answers both.
59pub trait OwnerReader {
60    /// The identity of whoever decided the cell at the position `at`, or `None` when nobody holds
61    /// that position.
62    ///
63    /// **The answer is a value rather than a borrow of one.** An identity is two bytes and `Copy`,
64    /// so a caller holds what it is given and may go on drawing into the buffer and ask again,
65    /// which is what a front end deciding what to do with a cell it was handed has to be able to
66    /// do. Nothing is owed by holding it and nothing is spent by copying it.
67    ///
68    /// **`None` covers two cases and distinguishes neither**, in the type or in the answer: a
69    /// position no shape wrote, and a position a shape wrote without deciding. Nothing was ever
70    /// recorded at either, so there is nothing to say which it was. A position outside the window is
71    /// the first of them: the record never holds one.
72    ///
73    /// **The position is one a caller already holds**, and reaching it from a click is
74    /// [`Window::position`]'s job rather than this method's: an offset counted from the window's
75    /// corner is the one position that offset reaches, and a caller that added the origin itself
76    /// would be asking about another cell.
77    #[must_use]
78    fn owner(&self, at: Pos) -> Option<ShapeId>;
79}
80
81/// A window of cells, and a record of which shape decided each of them.
82///
83/// Every position in a freshly created buffer is undefined: it holds no cell until [`stamp`] is
84/// called for it. The buffer is a temporary working surface, not the document; there is no erase.
85///
86/// **The record is kept beside the cells rather than inside one**, in a map of its own, for the
87/// reason two cells that render alike must stay one cell: an identity inside a [`Cell`] would be a
88/// part of the cell rather than a note about who wrote it, and every equality, every render and
89/// every catalog lookup would then depend on it. [`owner`] reads the record back.
90///
91/// [`owner`]: OwnerReader::owner
92/// [`cell`]: CellReader::cell
93/// [`stamp`]: StampSink::stamp
94pub struct Buffer {
95    window: Window,
96    cells: HashMap<(i16, i16), Cell>,
97    /// Who decided each position, for the positions somebody decided. A position absent here is
98    /// owned by nobody: either no shape wrote it, or the shape that did wrote nothing there.
99    owners: HashMap<(i16, i16), ShapeId>,
100}
101
102impl Buffer {
103    /// Creates a buffer with no positions defined, holding `window`.
104    ///
105    /// A width or height of zero is allowed and gives a buffer with no positions; construction
106    /// cannot fail.
107    #[must_use]
108    pub fn new(window: Window) -> Self {
109        Self {
110            window,
111            cells: HashMap::new(),
112            owners: HashMap::new(),
113        }
114    }
115
116    /// The window this buffer was created with.
117    ///
118    /// **A value rather than a borrow**: a window is two small `Copy` values, and a caller that
119    /// reads it while holding the buffer needs nothing back. What it is for is the translation an
120    /// offset into the window needs and the size a rendering asks for.
121    #[must_use]
122    pub fn window(&self) -> Window {
123        self.window
124    }
125
126    /// Whether `at` falls inside this buffer's window.
127    fn contains(&self, at: Pos) -> bool {
128        self.window.contains(at)
129    }
130}
131
132impl CellReader for Buffer {
133    fn cell(&self, at: Pos) -> Option<&Cell> {
134        self.cells.get(&(at.x, at.y))
135    }
136}
137
138impl OwnerReader for Buffer {
139    fn owner(&self, at: Pos) -> Option<ShapeId> {
140        self.owners.get(&(at.x, at.y)).copied()
141    }
142}
143
144impl StampSink for Buffer {
145    /// Writes `cell` at the absolute position `at`, under `mode`.
146    ///
147    /// A position outside the window is left unchanged. Writing an undefined position defines it
148    /// entirely, `Unset` arms included, whichever mode is given. Writing an already-defined
149    /// position consults `mode` for which side decides: [`StampMode::Above`] overwrites the base
150    /// stroke and reads the *stamp's* arms, leaving alone whichever ones `cell` itself leaves
151    /// `Unset`; [`StampMode::Below`] leaves the base stroke alone and reads the *target's* arms
152    /// instead, writing only the sides the cell already stored has left `Unset`. A cell holding a
153    /// literal glyph or a named symbol is decided on every side either way, so it behaves as the
154    /// fully decided case throughout — see _A cell can be a literal instead_ and _A cell can name
155    /// a symbol instead_ in [`docs/model.md`](../../../docs/model.md).
156    /// `owner` is the identity of whoever is stamping, or `None` for a stamp by nobody: a figure
157    /// this crate draws on its own gallery carries no identity, and neither does a caller with no
158    /// diagram to draw from. **`None` records nothing rather than recording an absence** — it is
159    /// not an owner the buffer holds, so a position keeps whoever holds it and answers `None` where
160    /// nobody does.
161    ///
162    /// What the stamp records follows the rules that decide the cell rather than sitting beside
163    /// them, which is what makes the record and the picture one thing instead of two: this stamp
164    /// owns the position when it decides at least one side of it, and under [`StampMode::Below`]
165    /// only when nobody holds it already. A shape behind therefore cannot take a cell from one in
166    /// front of it however much it decides there, and since every stamp in front takes, the record
167    /// comes out the same whichever order a diagram is drawn in.
168    fn stamp(&mut self, at: Pos, cell: Cell, mode: StampMode, owner: Option<ShapeId>) {
169        if !self.contains(at) {
170            return;
171        }
172
173        // Read before the match below, which moves `cell` in two of its arms. A stamp that decides
174        // no side at all is written like any other cell and owns nothing.
175        let decides_something = decides_a_side(&cell);
176
177        let takes = match self.cells.get_mut(&(at.x, at.y)) {
178            None => {
179                self.cells.insert((at.x, at.y), cell);
180                decides_something
181            }
182            Some(target) => {
183                *target = match mode {
184                    // Above onto a decided stamp always reproduces the stamp itself: every arm
185                    // it names wins outright, so the merge that would compute the same thing is
186                    // skipped. Mirrors the Below branch below it: the rule that the topmost
187                    // figure owns each side it decides names no stamp mode, so implementing it in
188                    // one direction only would read as a deliberate asymmetry. A literal or a
189                    // symbol is decided by definition, so this is also where it wins outright.
190                    StampMode::Above if cell.is_decided() => cell,
191                    StampMode::Above => merge(cell, target),
192                    // Below never changes a decided target: merging would reproduce it exactly,
193                    // so this returns instead of rebuilding and storing an identical cell. No
194                    // test can fail for this arm either way — deleting the guard
195                    // leaves every buffer byte-identical. An opaque target is decided too, so
196                    // this is also where it is left alone.
197                    //
198                    // The early return leaves the record alone too, and that is the point of it
199                    // rather than a side effect: a stamp that reproduces the cell it landed on
200                    // decided nothing there, so it owns nothing there either.
201                    StampMode::Below if target.is_decided() => return,
202                    StampMode::Below => merge(target.clone(), &cell),
203                };
204                decides_something
205                    && match mode {
206                        StampMode::Above => true,
207                        StampMode::Below => !self.owners.contains_key(&(at.x, at.y)),
208                    }
209            }
210        };
211
212        if takes && let Some(owner) = owner {
213            self.owners.insert((at.x, at.y), owner);
214        }
215    }
216}
217
218/// Builds the cell that results from merging `top` onto `bottom`, per _Stamping_ in
219/// [`docs/model.md`](../../../docs/model.md). Which cell plays `top` is the caller's choice, not
220/// this function's: an `Above` stamp is `top` over the target, and a `Below` stamp puts the target
221/// itself in that role.
222///
223/// Three cases, matched explicitly rather than folded into one arm-by-arm loop — an opaque cell
224/// has no arms, so [`merge_arm`] never sees one:
225///
226/// - **An opaque cell on top** — a literal or a symbol — always wins outright, whatever `bottom`
227///   is. Unreachable through [`StampSink::stamp`] today, because its two `is_decided` shortcuts
228///   catch every decided cell first — written as a returning branch rather than `unreachable!()`
229///   so those shortcuts stay deletable optimizations instead of becoming load-bearing.
230/// - **A stroke cell over an opaque cell** wins as a stroke cell, with every side it left `Unset`
231///   closed: an opaque cell has no arms to fall through to, only a refusal on every side, per _A
232///   cell can be a literal instead_ and _A cell can name a symbol instead_ in
233///   [`docs/model.md`](../../../docs/model.md).
234/// - **Two stroke cells** merge arm by arm: `top`'s base stroke, and each arm `top` decides, with
235///   an arm `top` leaves `Unset` falling through to `bottom`'s side.
236fn merge(top: Cell, bottom: &Cell) -> Cell {
237    match (top, bottom) {
238        (opaque @ (Cell::Literal(_) | Cell::Symbol(_)), _) => opaque,
239        (Cell::Strokes(stroke), Cell::Literal(_) | Cell::Symbol(_)) => {
240            close_unset_sides(stroke).into()
241        }
242        (Cell::Strokes(top), Cell::Strokes(bottom)) => merge_strokes(top, bottom).into(),
243    }
244}
245
246/// Whether `cell` decides at least one side, which is the test a stamp passes before it can own
247/// the position it landed on.
248///
249/// **Not the same question as [`Cell::is_decided`]**, which asks whether _every_ side is decided
250/// and is about whether a cell can still change. This asks the opposite end: whether there is
251/// anything here at all. A cell whose four arms are all `Unset` renders as a space whatever stands
252/// beside it and is nobody's, so a stamp of one takes nothing however it composes — which is a
253/// question the merge cannot answer, because a stamp that decides no side still writes its base
254/// stroke over an undefined position and changes what is stored.
255///
256/// A literal or a symbol is decided on every side by definition, so it always passes.
257fn decides_a_side(cell: &Cell) -> bool {
258    match cell {
259        Cell::Literal(_) | Cell::Symbol(_) => true,
260        Cell::Strokes(cell) => {
261            !matches!(cell.top, Arm::Unset)
262                || !matches!(cell.right, Arm::Unset)
263                || !matches!(cell.bottom, Arm::Unset)
264                || !matches!(cell.left, Arm::Unset)
265        }
266    }
267}
268
269/// `cell`, with every `Unset` arm closed. An opaque cell refuses every side it meets rather than
270/// leaving any open, per _A cell can be a literal instead_ and _A cell can name a symbol instead_
271/// in [`docs/model.md`](../../../docs/model.md) — there is no arm of its own to fall through to.
272fn close_unset_sides(cell: StrokeCell) -> StrokeCell {
273    let close = |arm| {
274        if matches!(arm, Arm::Unset) {
275            Arm::Closed
276        } else {
277            arm
278        }
279    };
280
281    StrokeCell {
282        base: cell.base,
283        top: close(cell.top),
284        right: close(cell.right),
285        bottom: close(cell.bottom),
286        left: close(cell.left),
287    }
288}
289
290/// `top`'s base stroke, and each arm `top` decides, with an arm `top` leaves `Unset` falling
291/// through to `bottom`'s side — per _Stamping_ in [`docs/model.md`](../../../docs/model.md): "the
292/// base stroke ends up owned by the topmost figure, and each arm ends up owned by the topmost
293/// figure that decided it, with abstentions falling through to the ones behind."
294fn merge_strokes(top: StrokeCell, bottom: &StrokeCell) -> StrokeCell {
295    StrokeCell {
296        base: top.base,
297        top: merge_arm(top.top, bottom.top.clone()),
298        right: merge_arm(top.right, bottom.right.clone()),
299        bottom: merge_arm(top.bottom, bottom.bottom.clone()),
300        left: merge_arm(top.left, bottom.left.clone()),
301    }
302}
303
304/// `top`, unless it is `Unset` — an abstaining arm never writes anything, so the side falls
305/// through to whatever `bottom` has. Only [`merge_strokes`] calls this: a literal has no arm to
306/// offer either side of the comparison.
307fn merge_arm(top: Arm, bottom: Arm) -> Arm {
308    if matches!(top, Arm::Unset) {
309        bottom
310    } else {
311        top
312    }
313}
314
315#[cfg(test)]
316mod tests {
317    use std::num::NonZeroU16;
318
319    use super::{Buffer, CellReader, OwnerReader, StampMode, StampSink};
320    use crate::{
321        Arm, BoxShape, Cell, FigureLayer, Glyph, Offset, Pos, Shape as _, ShapeId, Size, Stroke,
322        StrokeCell, Symbol, SymbolCell, SymbolStyleName, Window,
323    };
324
325    fn light() -> Stroke {
326        Stroke::from("light")
327    }
328
329    fn double() -> Stroke {
330        Stroke::from("double")
331    }
332
333    fn heavy() -> Stroke {
334        Stroke::from("heavy")
335    }
336
337    fn glyph(text: &str) -> Glyph {
338        Glyph::new(text).unwrap_or_else(|| panic!("{text:?} is one glyph"))
339    }
340
341    /// The Solid `DirectionUp` symbol as a cell, which is what the composition tests below put
342    /// where a literal would otherwise sit.
343    fn a_symbol_cell() -> Cell {
344        Cell::Symbol(SymbolCell {
345            symbol: Symbol::DirectionUp,
346            style: SymbolStyleName::from("solid"),
347        })
348    }
349
350    /// An identity of the ordinal `ordinal`, which is how every test below states who stamped.
351    fn id(ordinal: u16) -> ShapeId {
352        ShapeId::new(NonZeroU16::new(ordinal).expect("no test asks for zero"))
353    }
354
355    /// A buffer `width` by `height` at `origin`, which is how every test below states where the
356    /// positions it asks about are.
357    fn a_buffer(origin: Pos, width: u16, height: u16) -> Buffer {
358        Buffer::new(Window {
359            origin,
360            size: Size { width, height },
361        })
362    }
363
364    /// A cell deciding all four sides, which is what makes it immutable under `Below`.
365    fn decided() -> Cell {
366        Cell::from(StrokeCell {
367            base: light(),
368            top: Arm::Set(light()),
369            right: Arm::Set(light()),
370            bottom: Arm::Set(light()),
371            left: Arm::Set(light()),
372        })
373    }
374
375    /// A cell deciding its top and bottom and abstaining on the other two — a vertical run, and the
376    /// shape of a box's left or right border.
377    fn vertical_run() -> Cell {
378        Cell::from(StrokeCell {
379            base: light(),
380            top: Arm::Set(light()),
381            right: Arm::Unset,
382            bottom: Arm::Set(light()),
383            left: Arm::Unset,
384        })
385    }
386
387    /// A cell deciding its left and right and abstaining on the other two — a horizontal run.
388    fn horizontal_run() -> Cell {
389        Cell::from(StrokeCell {
390            base: light(),
391            top: Arm::Unset,
392            right: Arm::Set(light()),
393            bottom: Arm::Unset,
394            left: Arm::Set(light()),
395        })
396    }
397
398    /// A cell deciding no side at all: four `Unset` arms, which renders as a space whatever stands
399    /// beside it. Written rather than derived, because this is the cell the rule about deciding
400    /// nothing is *about*, and a rule whose counterexample is built from the thing it rules on
401    /// cannot fail.
402    fn deciding_nothing() -> Cell {
403        Cell::from(StrokeCell {
404            base: light(),
405            top: Arm::Unset,
406            right: Arm::Unset,
407            bottom: Arm::Unset,
408            left: Arm::Unset,
409        })
410    }
411
412    /// A four-by-three box at `at`: the size the spec's measured windows use throughout, and small
413    /// enough that its border and its interior are each more than one cell.
414    fn a_box(at: Pos, stroke: Stroke, fill: Option<&str>) -> BoxShape {
415        BoxShape {
416            at,
417            size: Size {
418                width: 4,
419                height: 3,
420            },
421            stroke,
422            fill: fill.map(glyph),
423        }
424    }
425
426    /// Every position of a window `width` by `height` at `origin`, written out once so that a test
427    /// can walk a whole window rather than one position somebody chose.
428    fn every_position(origin: Pos, width: u16, height: u16) -> Vec<Pos> {
429        (0..height)
430            .flat_map(|y| {
431                (0..width).map(move |x| Pos {
432                    x: origin.x + i16::try_from(x).expect("a window this narrow"),
433                    y: origin.y + i16::try_from(y).expect("a window this short"),
434                })
435            })
436            .collect()
437    }
438
439    #[test]
440    fn a_new_buffer_has_no_cell_anywhere() {
441        let buffer = Buffer::new(Window {
442            origin: Pos { x: -1, y: -1 },
443            size: Size {
444                width: 3,
445                height: 3,
446            },
447        });
448
449        for x in -2..3 {
450            for y in -2..3 {
451                assert!(buffer.cell(Pos { x, y }).is_none());
452            }
453        }
454    }
455
456    #[test]
457    fn stamping_outside_the_window_changes_nothing() {
458        let mut buffer = Buffer::new(Window {
459            origin: Pos { x: 0, y: 0 },
460            size: Size {
461                width: 2,
462                height: 1,
463            },
464        });
465
466        buffer.stamp(
467            Pos { x: 2, y: 0 },
468            StrokeCell {
469                base: light(),
470                top: Arm::Set(light()),
471                right: Arm::Set(light()),
472                bottom: Arm::Set(light()),
473                left: Arm::Set(light()),
474            }
475            .into(),
476            StampMode::Above,
477            None,
478        );
479
480        assert!(buffer.cell(Pos { x: 2, y: 0 }).is_none());
481    }
482
483    #[test]
484    fn stamping_an_undefined_position_defines_it_entirely() {
485        let mut buffer = Buffer::new(Window {
486            origin: Pos { x: 0, y: 0 },
487            size: Size {
488                width: 1,
489                height: 1,
490            },
491        });
492        let cell: Cell = StrokeCell {
493            base: light(),
494            top: Arm::Unset,
495            right: Arm::Set(light()),
496            bottom: Arm::Closed,
497            left: Arm::Unset,
498        }
499        .into();
500
501        buffer.stamp(Pos { x: 0, y: 0 }, cell.clone(), StampMode::Above, None);
502
503        assert_eq!(buffer.cell(Pos { x: 0, y: 0 }), Some(&cell));
504    }
505
506    /// The example named "An abstaining stamp": a second stamp decides three sides and abstains
507    /// on the top, so the cell's top arm survives even though everything else was overwritten.
508    #[test]
509    fn an_unset_arm_on_a_stamp_leaves_the_target_arm_alone() {
510        let mut buffer = Buffer::new(Window {
511            origin: Pos { x: 0, y: 0 },
512            size: Size {
513                width: 1,
514                height: 1,
515            },
516        });
517        buffer.stamp(
518            Pos { x: 0, y: 0 },
519            StrokeCell {
520                base: light(),
521                top: Arm::Set(light()),
522                right: Arm::Closed,
523                bottom: Arm::Closed,
524                left: Arm::Closed,
525            }
526            .into(),
527            StampMode::Above,
528            None,
529        );
530
531        buffer.stamp(
532            Pos { x: 0, y: 0 },
533            StrokeCell {
534                base: light(),
535                top: Arm::Unset,
536                right: Arm::Set(light()),
537                bottom: Arm::Closed,
538                left: Arm::Set(light()),
539            }
540            .into(),
541            StampMode::Above,
542            None,
543        );
544
545        assert_eq!(
546            buffer.cell(Pos { x: 0, y: 0 }),
547            Some(&Cell::from(StrokeCell {
548                base: light(),
549                top: Arm::Set(light()),
550                right: Arm::Set(light()),
551                bottom: Arm::Closed,
552                left: Arm::Set(light()),
553            }))
554        );
555    }
556
557    #[test]
558    fn stamping_outside_the_window_changes_nothing_under_below() {
559        let mut buffer = Buffer::new(Window {
560            origin: Pos { x: 0, y: 0 },
561            size: Size {
562                width: 2,
563                height: 1,
564            },
565        });
566
567        buffer.stamp(
568            Pos { x: 2, y: 0 },
569            StrokeCell {
570                base: light(),
571                top: Arm::Set(light()),
572                right: Arm::Set(light()),
573                bottom: Arm::Set(light()),
574                left: Arm::Set(light()),
575            }
576            .into(),
577            StampMode::Below,
578            None,
579        );
580
581        assert!(buffer.cell(Pos { x: 2, y: 0 }).is_none());
582    }
583
584    #[test]
585    fn stamping_an_undefined_position_with_below_defines_it_entirely() {
586        let mut buffer = Buffer::new(Window {
587            origin: Pos { x: 0, y: 0 },
588            size: Size {
589                width: 1,
590                height: 1,
591            },
592        });
593        let cell: Cell = StrokeCell {
594            base: light(),
595            top: Arm::Unset,
596            right: Arm::Set(light()),
597            bottom: Arm::Closed,
598            left: Arm::Unset,
599        }
600        .into();
601
602        buffer.stamp(Pos { x: 0, y: 0 }, cell.clone(), StampMode::Below, None);
603
604        assert_eq!(buffer.cell(Pos { x: 0, y: 0 }), Some(&cell));
605    }
606
607    /// The example named "The two modes on identical input": with `Below`, the target's own base
608    /// stroke and already-decided arms win, and only the side it left `Unset` is written.
609    #[test]
610    fn below_writes_only_the_targets_unset_sides_and_leaves_the_base_stroke_alone() {
611        let mut buffer = Buffer::new(Window {
612            origin: Pos { x: 0, y: 0 },
613            size: Size {
614                width: 1,
615                height: 1,
616            },
617        });
618        buffer.stamp(
619            Pos { x: 0, y: 0 },
620            StrokeCell {
621                base: light(),
622                top: Arm::Unset,
623                right: Arm::Set(light()),
624                bottom: Arm::Closed,
625                left: Arm::Unset,
626            }
627            .into(),
628            StampMode::Above,
629            None,
630        );
631
632        buffer.stamp(
633            Pos { x: 0, y: 0 },
634            StrokeCell {
635                base: Stroke::from("double"),
636                top: Arm::Set(Stroke::from("double")),
637                right: Arm::Closed,
638                bottom: Arm::Set(Stroke::from("double")),
639                left: Arm::Unset,
640            }
641            .into(),
642            StampMode::Below,
643            None,
644        );
645
646        assert_eq!(
647            buffer.cell(Pos { x: 0, y: 0 }),
648            Some(&Cell::from(StrokeCell {
649                base: light(),
650                top: Arm::Set(Stroke::from("double")),
651                right: Arm::Set(light()),
652                bottom: Arm::Closed,
653                left: Arm::Unset,
654            }))
655        );
656    }
657
658    /// The example named "A decided cell ignores a Below stamp": the merge that would produce
659    /// this cell is skipped, and the cell is the same as if it had run.
660    #[test]
661    fn below_onto_a_decided_cell_changes_nothing() {
662        let mut buffer = Buffer::new(Window {
663            origin: Pos { x: 0, y: 0 },
664            size: Size {
665                width: 1,
666                height: 1,
667            },
668        });
669        let decided: Cell = StrokeCell {
670            base: light(),
671            top: Arm::Set(light()),
672            right: Arm::Closed,
673            bottom: Arm::Set(light()),
674            left: Arm::Closed,
675        }
676        .into();
677        buffer.stamp(Pos { x: 0, y: 0 }, decided.clone(), StampMode::Above, None);
678
679        buffer.stamp(
680            Pos { x: 0, y: 0 },
681            StrokeCell {
682                base: Stroke::from("double"),
683                top: Arm::Set(Stroke::from("double")),
684                right: Arm::Set(Stroke::from("double")),
685                bottom: Arm::Set(Stroke::from("double")),
686                left: Arm::Set(Stroke::from("double")),
687            }
688            .into(),
689            StampMode::Below,
690            None,
691        );
692
693        assert_eq!(buffer.cell(Pos { x: 0, y: 0 }), Some(&decided));
694    }
695
696    /// The mirror of the example above: a fully decided `Above` stamp wins
697    /// outright, whatever the target already had — the merge that would produce this cell is
698    /// skipped, and the cell is the same as if it had run.
699    #[test]
700    fn above_with_a_decided_stamp_wins_outright() {
701        let mut buffer = Buffer::new(Window {
702            origin: Pos { x: 0, y: 0 },
703            size: Size {
704                width: 1,
705                height: 1,
706            },
707        });
708        buffer.stamp(
709            Pos { x: 0, y: 0 },
710            StrokeCell {
711                base: Stroke::from("double"),
712                top: Arm::Unset,
713                right: Arm::Set(Stroke::from("double")),
714                bottom: Arm::Unset,
715                left: Arm::Closed,
716            }
717            .into(),
718            StampMode::Above,
719            None,
720        );
721
722        let decided: Cell = StrokeCell {
723            base: light(),
724            top: Arm::Set(light()),
725            right: Arm::Closed,
726            bottom: Arm::Set(light()),
727            left: Arm::Closed,
728        }
729        .into();
730        buffer.stamp(Pos { x: 0, y: 0 }, decided.clone(), StampMode::Above, None);
731
732        assert_eq!(buffer.cell(Pos { x: 0, y: 0 }), Some(&decided));
733    }
734
735    /// The example named "The two orders agree": three figures overlap at one position, front to
736    /// back with `Below` and back to front with `Above`, and the two drawing orders are
737    /// equivalent, so the resulting cell must be the same either way.
738    #[test]
739    fn front_to_back_with_below_equals_back_to_front_with_above() {
740        let pos = Pos { x: 0, y: 0 };
741        let a = || {
742            Cell::from(StrokeCell {
743                base: Stroke::from("double"),
744                top: Arm::Unset,
745                right: Arm::Set(Stroke::from("double")),
746                bottom: Arm::Unset,
747                left: Arm::Closed,
748            })
749        };
750        let b = || {
751            Cell::from(StrokeCell {
752                base: Stroke::from("light"),
753                top: Arm::Set(Stroke::from("light")),
754                right: Arm::Closed,
755                bottom: Arm::Unset,
756                left: Arm::Set(Stroke::from("light")),
757            })
758        };
759        let c = || {
760            Cell::from(StrokeCell {
761                base: Stroke::from("heavy"),
762                top: Arm::Closed,
763                right: Arm::Set(Stroke::from("heavy")),
764                bottom: Arm::Set(Stroke::from("heavy")),
765                left: Arm::Set(Stroke::from("heavy")),
766            })
767        };
768
769        let mut front_to_back = Buffer::new(Window {
770            origin: Pos { x: 0, y: 0 },
771            size: Size {
772                width: 1,
773                height: 1,
774            },
775        });
776        front_to_back.stamp(pos, a(), StampMode::Below, None);
777        front_to_back.stamp(pos, b(), StampMode::Below, None);
778        front_to_back.stamp(pos, c(), StampMode::Below, None);
779
780        let mut back_to_front = Buffer::new(Window {
781            origin: Pos { x: 0, y: 0 },
782            size: Size {
783                width: 1,
784                height: 1,
785            },
786        });
787        back_to_front.stamp(pos, c(), StampMode::Above, None);
788        back_to_front.stamp(pos, b(), StampMode::Above, None);
789        back_to_front.stamp(pos, a(), StampMode::Above, None);
790
791        let expected = Some(&Cell::from(StrokeCell {
792            base: Stroke::from("double"),
793            top: Arm::Set(Stroke::from("light")),
794            right: Arm::Set(Stroke::from("double")),
795            bottom: Arm::Set(Stroke::from("heavy")),
796            left: Arm::Closed,
797        }));
798        assert_eq!(front_to_back.cell(pos), back_to_front.cell(pos));
799        assert_eq!(front_to_back.cell(pos), expected);
800    }
801
802    /// _Stamping_'s row "Arms, stamping a literal" under `Above`: the literal wins outright.
803    #[test]
804    fn a_literal_stamped_above_a_stroke_cell_replaces_it() {
805        let mut buffer = Buffer::new(Window {
806            origin: Pos { x: 0, y: 0 },
807            size: Size {
808                width: 1,
809                height: 1,
810            },
811        });
812        buffer.stamp(
813            Pos { x: 0, y: 0 },
814            StrokeCell {
815                base: light(),
816                top: Arm::Unset,
817                right: Arm::Set(light()),
818                bottom: Arm::Set(light()),
819                left: Arm::Set(light()),
820            }
821            .into(),
822            StampMode::Above,
823            None,
824        );
825
826        let literal = Cell::Literal(glyph("A"));
827        buffer.stamp(Pos { x: 0, y: 0 }, literal.clone(), StampMode::Above, None);
828
829        assert_eq!(buffer.cell(Pos { x: 0, y: 0 }), Some(&literal));
830    }
831
832    /// _Stamping_'s row "Arms, stamping a literal" under `Below`: the target stays a stroke cell,
833    /// and the sides it left `Unset` come out `Closed`, inherited from the literal.
834    #[test]
835    fn a_literal_stamped_below_a_stroke_cell_closes_its_unset_sides() {
836        let mut buffer = Buffer::new(Window {
837            origin: Pos { x: 0, y: 0 },
838            size: Size {
839                width: 1,
840                height: 1,
841            },
842        });
843        buffer.stamp(
844            Pos { x: 0, y: 0 },
845            StrokeCell {
846                base: light(),
847                top: Arm::Unset,
848                right: Arm::Set(light()),
849                bottom: Arm::Closed,
850                left: Arm::Set(light()),
851            }
852            .into(),
853            StampMode::Above,
854            None,
855        );
856
857        buffer.stamp(
858            Pos { x: 0, y: 0 },
859            Cell::Literal(glyph("A")),
860            StampMode::Below,
861            None,
862        );
863
864        assert_eq!(
865            buffer.cell(Pos { x: 0, y: 0 }),
866            Some(&Cell::from(StrokeCell {
867                base: light(),
868                top: Arm::Closed,
869                right: Arm::Set(light()),
870                bottom: Arm::Closed,
871                left: Arm::Set(light()),
872            }))
873        );
874    }
875
876    /// _Stamping_'s row "A literal, stamping arms" under `Above`: the target becomes a stroke
877    /// cell, and the sides the stamp leaves `Unset` come out `Closed`, inherited from the literal
878    /// it replaced.
879    #[test]
880    fn a_stroke_cell_stamped_above_a_literal_inherits_its_closed_sides_where_it_abstains() {
881        let mut buffer = Buffer::new(Window {
882            origin: Pos { x: 0, y: 0 },
883            size: Size {
884                width: 1,
885                height: 1,
886            },
887        });
888        buffer.stamp(
889            Pos { x: 0, y: 0 },
890            Cell::Literal(glyph("A")),
891            StampMode::Above,
892            None,
893        );
894
895        buffer.stamp(
896            Pos { x: 0, y: 0 },
897            StrokeCell {
898                base: light(),
899                top: Arm::Unset,
900                right: Arm::Set(light()),
901                bottom: Arm::Closed,
902                left: Arm::Set(light()),
903            }
904            .into(),
905            StampMode::Above,
906            None,
907        );
908
909        assert_eq!(
910            buffer.cell(Pos { x: 0, y: 0 }),
911            Some(&Cell::from(StrokeCell {
912                base: light(),
913                top: Arm::Closed,
914                right: Arm::Set(light()),
915                bottom: Arm::Closed,
916                left: Arm::Set(light()),
917            }))
918        );
919    }
920
921    /// _Stamping_'s row "A literal, stamping arms" under `Below`: the literal target is decided,
922    /// so it is left alone.
923    #[test]
924    fn a_stroke_cell_stamped_below_a_literal_changes_nothing() {
925        let mut buffer = Buffer::new(Window {
926            origin: Pos { x: 0, y: 0 },
927            size: Size {
928                width: 1,
929                height: 1,
930            },
931        });
932        let literal = Cell::Literal(glyph("A"));
933        buffer.stamp(Pos { x: 0, y: 0 }, literal.clone(), StampMode::Above, None);
934
935        buffer.stamp(
936            Pos { x: 0, y: 0 },
937            StrokeCell {
938                base: Stroke::from("double"),
939                top: Arm::Set(Stroke::from("double")),
940                right: Arm::Set(Stroke::from("double")),
941                bottom: Arm::Set(Stroke::from("double")),
942                left: Arm::Set(Stroke::from("double")),
943            }
944            .into(),
945            StampMode::Below,
946            None,
947        );
948
949        assert_eq!(buffer.cell(Pos { x: 0, y: 0 }), Some(&literal));
950    }
951
952    /// _Stamping_'s row "A literal, stamping a literal" under `Above`: the incoming literal wins
953    /// outright.
954    #[test]
955    fn a_literal_stamped_above_a_literal_replaces_it() {
956        let mut buffer = Buffer::new(Window {
957            origin: Pos { x: 0, y: 0 },
958            size: Size {
959                width: 1,
960                height: 1,
961            },
962        });
963        buffer.stamp(
964            Pos { x: 0, y: 0 },
965            Cell::Literal(glyph("A")),
966            StampMode::Above,
967            None,
968        );
969
970        let incoming = Cell::Literal(glyph("B"));
971        buffer.stamp(Pos { x: 0, y: 0 }, incoming.clone(), StampMode::Above, None);
972
973        assert_eq!(buffer.cell(Pos { x: 0, y: 0 }), Some(&incoming));
974    }
975
976    /// _Stamping_'s row "A literal, stamping a literal" under `Below`: the target is decided, so
977    /// it is left alone.
978    #[test]
979    fn a_literal_stamped_below_a_literal_changes_nothing() {
980        let mut buffer = Buffer::new(Window {
981            origin: Pos { x: 0, y: 0 },
982            size: Size {
983                width: 1,
984                height: 1,
985            },
986        });
987        let original = Cell::Literal(glyph("A"));
988        buffer.stamp(Pos { x: 0, y: 0 }, original.clone(), StampMode::Above, None);
989
990        buffer.stamp(
991            Pos { x: 0, y: 0 },
992            Cell::Literal(glyph("B")),
993            StampMode::Below,
994            None,
995        );
996
997        assert_eq!(buffer.cell(Pos { x: 0, y: 0 }), Some(&original));
998    }
999
1000    /// Rule 3 — a symbol cell composes exactly as a literal does. Above, it replaces what it
1001    /// meets; below, a stroke stamp leaves it alone; two of them compose as two literals, with the
1002    /// top one winning.
1003    #[test]
1004    fn a_symbol_cell_composes_as_a_literal_does() {
1005        let pos = Pos { x: 0, y: 0 };
1006
1007        let mut named = a_buffer(pos, 1, 1);
1008        named.stamp(pos, vertical_run(), StampMode::Above, None);
1009        named.stamp(pos, a_symbol_cell(), StampMode::Above, None);
1010
1011        let mut spelled = a_buffer(pos, 1, 1);
1012        spelled.stamp(pos, vertical_run(), StampMode::Above, None);
1013        spelled.stamp(pos, Cell::Literal(glyph("▲")), StampMode::Above, None);
1014
1015        assert_eq!(
1016            named.cell(pos),
1017            Some(&a_symbol_cell()),
1018            "the symbol replaces the stroke cell it was stamped over"
1019        );
1020        assert_eq!(spelled.cell(pos), Some(&Cell::Literal(glyph("▲"))));
1021
1022        named.stamp(pos, horizontal_run(), StampMode::Below, None);
1023        spelled.stamp(pos, horizontal_run(), StampMode::Below, None);
1024
1025        assert_eq!(
1026            named.cell(pos),
1027            Some(&a_symbol_cell()),
1028            "a stamp behind changes neither opaque kind"
1029        );
1030        assert_eq!(spelled.cell(pos), Some(&Cell::Literal(glyph("▲"))));
1031
1032        let incoming = Cell::Symbol(SymbolCell {
1033            symbol: Symbol::Circle,
1034            style: SymbolStyleName::from("solid"),
1035        });
1036        named.stamp(pos, incoming.clone(), StampMode::Above, None);
1037
1038        assert_eq!(
1039            named.cell(pos),
1040            Some(&incoming),
1041            "two symbol cells compose as two literals do, the top one winning"
1042        );
1043    }
1044
1045    /// The example named "Why the closed sides matter": a literal between two stroke figures in a
1046    /// stack. Without the literal's four `Closed` sides the two orders would disagree — the front
1047    /// figure would reach through it and fill a side the other order had already sealed.
1048    #[test]
1049    fn front_to_back_with_below_equals_back_to_front_with_above_with_a_literal_in_the_middle() {
1050        let pos = Pos { x: 0, y: 0 };
1051        let s1 = || {
1052            Cell::from(StrokeCell {
1053                base: light(),
1054                top: Arm::Unset,
1055                right: Arm::Set(light()),
1056                bottom: Arm::Set(light()),
1057                left: Arm::Set(light()),
1058            })
1059        };
1060        let literal = || Cell::Literal(glyph("A"));
1061        let s2 = || {
1062            Cell::from(StrokeCell {
1063                base: light(),
1064                top: Arm::Set(light()),
1065                right: Arm::Set(light()),
1066                bottom: Arm::Set(light()),
1067                left: Arm::Set(light()),
1068            })
1069        };
1070
1071        let mut front_to_back = Buffer::new(Window {
1072            origin: Pos { x: 0, y: 0 },
1073            size: Size {
1074                width: 1,
1075                height: 1,
1076            },
1077        });
1078        front_to_back.stamp(pos, s1(), StampMode::Below, None);
1079        front_to_back.stamp(pos, literal(), StampMode::Below, None);
1080        front_to_back.stamp(pos, s2(), StampMode::Below, None);
1081
1082        let mut back_to_front = Buffer::new(Window {
1083            origin: Pos { x: 0, y: 0 },
1084            size: Size {
1085                width: 1,
1086                height: 1,
1087            },
1088        });
1089        back_to_front.stamp(pos, s2(), StampMode::Above, None);
1090        back_to_front.stamp(pos, literal(), StampMode::Above, None);
1091        back_to_front.stamp(pos, s1(), StampMode::Above, None);
1092
1093        let expected = Some(&Cell::from(StrokeCell {
1094            base: light(),
1095            top: Arm::Closed,
1096            right: Arm::Set(light()),
1097            bottom: Arm::Set(light()),
1098            left: Arm::Set(light()),
1099        }));
1100        assert_eq!(front_to_back.cell(pos), back_to_front.cell(pos));
1101        assert_eq!(front_to_back.cell(pos), expected);
1102    }
1103
1104    /// The record is kept beside the cell and never inside it, so two cells that render alike stay
1105    /// one cell value whoever wrote them.
1106    ///
1107    /// **The two positions below are given the same literal by two different identities**, which is
1108    /// the shipped demonstration's own arrangement — its first two entries are two filled boxes
1109    /// whose fills print the same character at cells belonging to different shapes. The claim is
1110    /// the equality *and* the difference: the cells compare equal as cells, and the record says
1111    /// `1` wrote one and `2` wrote the other. An identity carried inside the cell would have made
1112    /// that equality false, which is the whole reason the record is a second map.
1113    #[test]
1114    fn two_cells_that_render_the_same_are_the_same_cell_and_the_record_does_not_change_that() {
1115        let mut buffer = a_buffer(Pos { x: 0, y: 0 }, 2, 1);
1116        let first = id(1);
1117        let second = id(2);
1118        let fill = Cell::Literal(glyph("░"));
1119
1120        buffer.stamp(
1121            Pos { x: 0, y: 0 },
1122            fill.clone(),
1123            StampMode::Above,
1124            Some(first),
1125        );
1126        buffer.stamp(
1127            Pos { x: 1, y: 0 },
1128            fill.clone(),
1129            StampMode::Below,
1130            Some(second),
1131        );
1132
1133        assert_eq!(
1134            buffer.cell(Pos { x: 0, y: 0 }),
1135            buffer.cell(Pos { x: 1, y: 0 }),
1136            "the same cell value written by two identities is one cell value"
1137        );
1138        assert_eq!(buffer.cell(Pos { x: 0, y: 0 }), Some(&fill));
1139        assert_eq!(buffer.owner(Pos { x: 0, y: 0 }), Some(first));
1140        assert_eq!(
1141            buffer.owner(Pos { x: 1, y: 0 }),
1142            Some(second),
1143            "the record tells the two apart where the cell cannot"
1144        );
1145    }
1146
1147    /// Every position of an overlap resolves to the front-most shape that decided it.
1148    ///
1149    /// **The arrangement is the spec's first measured window**, a seven-by-five window at `(-3, -2)`
1150    /// holding a light box at `(0, 0)`, a filled double box at `(-2, -1)` written last and therefore
1151    /// in front, and a heavy box at `(6, 3)` lying wholly outside. `3` is in the arrangement on
1152    /// purpose: it stamps nothing, so a buffer that recorded a stamp it did not perform would give
1153    /// it every position in the window.
1154    ///
1155    /// The expectation is **read off the two rectangles rather than sampled from the output**, and
1156    /// every position of the window is asked — 35 of them, not the seven the spec's table happens
1157    /// to name. An unfilled box writes its border alone and a filled one its whole rectangle, so
1158    /// what decides each position follows from where the two stand, with no reference to what the
1159    /// record says.
1160    #[test]
1161    fn in_an_overlap_every_position_resolves_to_the_front_most_shape_that_decided_it() {
1162        let origin = Pos { x: -3, y: -2 };
1163        let mut buffer = a_buffer(origin, 7, 5);
1164        let back = id(1);
1165        let front = id(2);
1166        let outside = id(3);
1167
1168        // Front to back, which is the order `Diagram::draw` uses: the front-most shape stamps
1169        // first and decides a shared cell before anything behind it. `3` is drawn last of the three
1170        // because it is the back-most of the three, and it writes nothing either way.
1171        a_box(Pos { x: -2, y: -1 }, double(), Some("░")).draw(&mut FigureLayer::stamped_by(
1172            &mut buffer,
1173            StampMode::Below,
1174            front,
1175        ));
1176        a_box(Pos { x: 0, y: 0 }, light(), None).draw(&mut FigureLayer::stamped_by(
1177            &mut buffer,
1178            StampMode::Below,
1179            back,
1180        ));
1181        a_box(Pos { x: 6, y: 3 }, heavy(), None).draw(&mut FigureLayer::stamped_by(
1182            &mut buffer,
1183            StampMode::Below,
1184            outside,
1185        ));
1186
1187        // `2` is filled, so it decides every position of its own rectangle; `1` is not, so it
1188        // decides only its border; `3` is outside the window and decides nothing at all.
1189        let decided_by_front = |at: Pos| (-2..=1).contains(&at.x) && (-1..=1).contains(&at.y);
1190        let decided_by_back = |at: Pos| {
1191            (0..=3).contains(&at.x)
1192                && (0..=2).contains(&at.y)
1193                && (at.x == 0 || at.x == 3 || at.y == 0 || at.y == 2)
1194        };
1195
1196        for at in every_position(origin, 7, 5) {
1197            let expected = if decided_by_front(at) {
1198                Some(front)
1199            } else if decided_by_back(at) {
1200                Some(back)
1201            } else {
1202                None
1203            };
1204            assert_eq!(
1205                buffer.owner(at),
1206                expected,
1207                "the record names {expected:?} at {at:?}"
1208            );
1209        }
1210    }
1211
1212    /// A stamp that changes nothing at a position takes nothing.
1213    ///
1214    /// `2` is stamped `Below` onto a cell `1` already decided, which is the one case where the
1215    /// merge provably reproduces the target and the buffer returns before writing. The cell is
1216    /// asserted unchanged as well as the record, because "changed nothing" and "changed the cell
1217    /// but not the record" are different defects and only one of them is this rule.
1218    ///
1219    /// **`Below` is the mode this rule is about**, and the reason is the one the test beside it
1220    /// pins: an `Above` stamp that reproduces the cell it lands on still takes, because under
1221    /// `Above` taking is how a shape in front of everything claims a cell the shapes behind it
1222    /// also wrote. See `the_record_is_the_same_whether_a_diagram_is_drawn_front_to_back_or_back_to_front`.
1223    #[test]
1224    fn a_shape_that_stamps_a_position_without_changing_anything_there_does_not_take_it() {
1225        let mut buffer = a_buffer(Pos { x: 0, y: 0 }, 1, 1);
1226        let first = id(1);
1227        let second = id(2);
1228
1229        buffer.stamp(Pos { x: 0, y: 0 }, decided(), StampMode::Above, Some(first));
1230        let before = buffer.cell(Pos { x: 0, y: 0 }).cloned();
1231        assert!(
1232            before.is_some(),
1233            "the first stamp wrote the cell this test reads back"
1234        );
1235
1236        buffer.stamp(
1237            Pos { x: 0, y: 0 },
1238            Cell::from(StrokeCell {
1239                base: heavy(),
1240                top: Arm::Set(heavy()),
1241                right: Arm::Set(heavy()),
1242                bottom: Arm::Set(heavy()),
1243                left: Arm::Set(heavy()),
1244            }),
1245            StampMode::Below,
1246            Some(second),
1247        );
1248
1249        assert_eq!(
1250            buffer.cell(Pos { x: 0, y: 0 }).cloned(),
1251            before,
1252            "a stamp below a decided cell reproduces it exactly, so the cell cannot have moved"
1253        );
1254        assert_eq!(
1255            buffer.owner(Pos { x: 0, y: 0 }),
1256            Some(first),
1257            "the stamp behind changed nothing there, so it owns nothing there"
1258        );
1259    }
1260
1261    /// A stamp that decides no side takes nothing, and the position keeps whoever holds it.
1262    ///
1263    /// **Both halves, and the order matters**: the ownerless first stamp puts a cell there that
1264    /// decides nothing, and the second stamp — which does decide two sides, and so *does* change
1265    /// the cell — is not allowed to take it. That second half is what separates this rule from the
1266    /// one beside it, and it is the only way to catch an implementation that records "whoever
1267    /// stamped last" rather than "whoever decided".
1268    #[test]
1269    fn a_stamp_that_decides_no_side_takes_nothing_and_leaves_the_owner_it_found() {
1270        let mut buffer = a_buffer(Pos { x: 0, y: 0 }, 1, 1);
1271        let nobody = id(1);
1272        let second = id(2);
1273
1274        // A stamp by nobody at an undefined position writes a cell and records nothing, which is
1275        // the starting state this rule needs: a position holding a cell and owned by nobody.
1276        buffer.stamp(
1277            Pos { x: 0, y: 0 },
1278            deciding_nothing(),
1279            StampMode::Above,
1280            Some(nobody),
1281        );
1282        assert_eq!(
1283            buffer.owner(Pos { x: 0, y: 0 }),
1284            None,
1285            "a cell deciding no side is written like any other and owns nothing"
1286        );
1287
1288        buffer.stamp(
1289            Pos { x: 0, y: 0 },
1290            horizontal_run(),
1291            StampMode::Above,
1292            Some(second),
1293        );
1294
1295        // And the same stamp onto a position somebody *does* hold leaves that owner standing.
1296        let mut held = a_buffer(Pos { x: 0, y: 0 }, 1, 1);
1297        let holder = id(3);
1298        held.stamp(
1299            Pos { x: 0, y: 0 },
1300            horizontal_run(),
1301            StampMode::Above,
1302            Some(holder),
1303        );
1304        held.stamp(
1305            Pos { x: 0, y: 0 },
1306            deciding_nothing(),
1307            StampMode::Above,
1308            Some(second),
1309        );
1310
1311        assert_eq!(
1312            held.cell(Pos { x: 0, y: 0 }),
1313            Some(&horizontal_run()),
1314            "a stamp deciding no side changes nothing whatever it lands on"
1315        );
1316        assert_eq!(
1317            held.owner(Pos { x: 0, y: 0 }),
1318            Some(holder),
1319            "the owner it found is the owner it leaves"
1320        );
1321    }
1322
1323    /// A shape behind cannot take a cell from one in front of it, however much it decides there.
1324    ///
1325    /// **The cell behind really does decide more than the cell in front** — it fills the two sides
1326    /// `1` abstained on — and the record does not move. That is the whole claim, and it is the one
1327    /// a reader cannot check by looking at the picture: the glyph at `(0, 0)` reads `┼`, four sides
1328    /// joined, and nothing about it says that one shape wrote two of them and the other two.
1329    #[test]
1330    fn a_shape_behind_cannot_take_a_cell_from_one_in_front_of_it_however_much_it_decides() {
1331        let mut buffer = a_buffer(Pos { x: 0, y: 0 }, 1, 1);
1332        let front = id(1);
1333        let behind = id(2);
1334
1335        buffer.stamp(
1336            Pos { x: 0, y: 0 },
1337            vertical_run(),
1338            StampMode::Above,
1339            Some(front),
1340        );
1341        buffer.stamp(
1342            Pos { x: 0, y: 0 },
1343            horizontal_run(),
1344            StampMode::Below,
1345            Some(behind),
1346        );
1347
1348        assert_eq!(
1349            buffer.cell(Pos { x: 0, y: 0 }),
1350            Some(&decided()),
1351            "the shape behind decided the two sides the one in front left open"
1352        );
1353        assert_eq!(
1354            buffer.owner(Pos { x: 0, y: 0 }),
1355            Some(front),
1356            "and the record names the one in front, because it reached the cell first"
1357        );
1358    }
1359
1360    /// The record is the same whether a diagram is drawn front to back or back to front.
1361    ///
1362    /// **The same two figures in the same two orders, compared on the record as well as on the
1363    /// cells**, and asked about at every offset of the window rather than at one: a record that
1364    /// agreed everywhere except one position would be a different record, and the picture would not
1365    /// show it.
1366    ///
1367    /// The arrangement is the one where the two orders disagree in the cells, not only in the
1368    /// record — a filled double box over a light box, where the front one's fill and the one behind
1369    /// it both write the two cells they share.
1370    #[test]
1371    fn the_record_is_the_same_whether_a_diagram_is_drawn_front_to_back_or_back_to_front() {
1372        let origin = Pos { x: 0, y: 0 };
1373        let back = id(1);
1374        let front = id(2);
1375
1376        let mut front_to_back = a_buffer(origin, 6, 4);
1377        a_box(Pos { x: 2, y: 1 }, double(), Some("▓")).draw(&mut FigureLayer::stamped_by(
1378            &mut front_to_back,
1379            StampMode::Below,
1380            front,
1381        ));
1382        a_box(Pos { x: 0, y: 0 }, light(), Some("░")).draw(&mut FigureLayer::stamped_by(
1383            &mut front_to_back,
1384            StampMode::Below,
1385            back,
1386        ));
1387
1388        let mut back_to_front = a_buffer(origin, 6, 4);
1389        a_box(Pos { x: 0, y: 0 }, light(), Some("░")).draw(&mut FigureLayer::stamped_by(
1390            &mut back_to_front,
1391            StampMode::Above,
1392            back,
1393        ));
1394        a_box(Pos { x: 2, y: 1 }, double(), Some("▓")).draw(&mut FigureLayer::stamped_by(
1395            &mut back_to_front,
1396            StampMode::Above,
1397            front,
1398        ));
1399
1400        for at in every_position(origin, 6, 4) {
1401            assert_eq!(
1402                front_to_back.cell(at),
1403                back_to_front.cell(at),
1404                "the two orders disagree about the cell at {at:?}"
1405            );
1406            assert_eq!(
1407                front_to_back.owner(at),
1408                back_to_front.owner(at),
1409                "the two orders disagree about who owns the position {at:?}"
1410            );
1411        }
1412
1413        // And the two cells the boxes share are `2`'s in both, which is what "the same record"
1414        // has to mean rather than merely agreeing.
1415        for at in [Pos { x: 2, y: 1 }, Pos { x: 3, y: 2 }] {
1416            assert_eq!(
1417                front_to_back.owner(at),
1418                Some(front),
1419                "the shape in front owns the cell it decided"
1420            );
1421        }
1422    }
1423
1424    /// `owner` answers for a position, and `None` where nobody holds one.
1425    ///
1426    /// **Every position of a window and the run past each of its two edges**, which is what makes
1427    /// the `None` a claim about the record rather than about one position somebody happened to
1428    /// leave blank. The window's origin is `(-1, -1)`, so a position and the offset that reaches it
1429    /// are visibly different numbers — and this asks by position, as the interface does.
1430    #[test]
1431    fn owner_answers_for_a_position_and_none_for_one_nobody_holds() {
1432        let origin = Pos { x: -1, y: -1 };
1433        let mut buffer = a_buffer(origin, 2, 2);
1434        let writer = id(1);
1435
1436        buffer.stamp(
1437            Pos { x: 0, y: 0 },
1438            decided(),
1439            StampMode::Above,
1440            Some(writer),
1441        );
1442
1443        for at in every_position(origin, 2, 2) {
1444            let written = at == (Pos { x: 0, y: 0 });
1445            assert_eq!(
1446                buffer.owner(at),
1447                written.then_some(writer),
1448                "{at:?} is the one position written, and only it is owned"
1449            );
1450        }
1451
1452        // And the run past each of the two edges, which is what makes the `None` above a claim
1453        // about the record rather than about one position somebody left blank.
1454        for at in [
1455            Pos { x: 1, y: 0 },
1456            Pos { x: -1, y: 1 },
1457            Pos { x: 1, y: 1 },
1458            Pos {
1459                x: i16::MAX,
1460                y: i16::MAX,
1461            },
1462        ] {
1463            assert_eq!(
1464                buffer.owner(at),
1465                None,
1466                "{at:?} is outside a two-by-two window"
1467            );
1468        }
1469    }
1470
1471    /// A caller that adds the window's origin to the position it already has asks about another
1472    /// cell.
1473    ///
1474    /// **The window's origin is `(-2, -1)` and exactly one position in it is owned**, so the sum
1475    /// and the position land on different cells and the wrong one is asked about. The position
1476    /// [`Window::position`](crate::Window::position) hands back already carries the origin; adding
1477    /// it a second time reaches a cell nobody clicked, and `owner` answers truthfully about that
1478    /// one.
1479    #[test]
1480    fn a_caller_that_adds_the_windows_origin_to_a_click_asks_about_another_cell() {
1481        let origin = Pos { x: -2, y: -1 };
1482        let mut buffer = a_buffer(origin, 4, 4);
1483        let writer = id(1);
1484        let clicked = Offset { x: 2, y: 3 };
1485
1486        buffer.stamp(
1487            Pos { x: 0, y: 2 },
1488            decided(),
1489            StampMode::Above,
1490            Some(writer),
1491        );
1492
1493        let at = buffer
1494            .window()
1495            .position(clicked)
1496            .expect("the click reaches a position");
1497        assert_eq!(
1498            buffer.owner(at),
1499            Some(writer),
1500            "the click reaches the cell as written"
1501        );
1502        assert_eq!(
1503            buffer.owner(Pos {
1504                x: at.x + origin.x,
1505                y: at.y + origin.y,
1506            }),
1507            None,
1508            "adding the origin a second time asks about a cell nobody clicked"
1509        );
1510    }
1511
1512    /// A window whose origin is `i16::MIN` carries every offset, and none of the positions they
1513    /// reach is owned when the drawing never landed inside it.
1514    ///
1515    /// **`i16::MIN` is a real origin rather than a theoretical one**, and it is the ordinary shape
1516    /// of the question: a window far enough to the left that the whole plane to its right is a run
1517    /// of offsets. `checked_add_unsigned` is what answers, and adding an offset that far left never
1518    /// overflows — the largest offset there is a position too.
1519    #[test]
1520    fn a_window_at_i16_min_carries_every_offset_and_owns_nothing_outside_it() {
1521        let mut buffer = a_buffer(Pos { x: i16::MIN, y: 0 }, 4, 3);
1522
1523        a_box(Pos { x: 0, y: 0 }, light(), Some("░")).draw(&mut FigureLayer::stamped_by(
1524            &mut buffer,
1525            StampMode::Below,
1526            id(1),
1527        ));
1528
1529        let window = buffer.window();
1530        for y in 0..3 {
1531            for x in 0..5 {
1532                let at = window
1533                    .position(Offset { x, y })
1534                    .expect("a window at i16::MIN carries every offset");
1535                assert_eq!(
1536                    buffer.owner(at),
1537                    None,
1538                    "{at:?} is not a cell the box wrote, which was drawn far from this window"
1539                );
1540            }
1541        }
1542        // And the run past the window's width: the offset is representable and so is the position
1543        // it reaches, and the answer is still nobody.
1544        let past = window
1545            .position(Offset { x: u16::MAX, y: 0 })
1546            .expect("a window at i16::MIN carries every offset");
1547        assert_eq!(buffer.owner(past), None);
1548        assert_eq!(
1549            buffer.cell(Pos { x: 0, y: 0 }),
1550            None,
1551            "and no cell was written there either, so nothing is owned in the first place"
1552        );
1553    }
1554
1555    /// A shape drawn by nobody owns nothing, even where it writes.
1556    ///
1557    /// **A whole box rather than one cell**, so that the twenty cells of its border and interior
1558    /// are all asked and a record that appeared on any of them fails: `FigureLayer::new` is the door the
1559    /// core's own gallery and every caller's own drawing go through, and it records nothing at all.
1560    /// The second half draws the same box through a layer bound to an identity, which is what makes
1561    /// the `None` above about the door rather than about a box that writes nothing.
1562    #[test]
1563    fn a_shape_drawn_by_nobody_owns_nothing_even_where_it_writes() {
1564        let mut by_nobody = a_buffer(Pos { x: 0, y: 0 }, 4, 3);
1565        a_box(Pos { x: 0, y: 0 }, light(), Some("░"))
1566            .draw(&mut FigureLayer::new(&mut by_nobody, StampMode::Above));
1567
1568        let mut by_somebody = a_buffer(Pos { x: 0, y: 0 }, 4, 3);
1569        let writer = id(1);
1570        a_box(Pos { x: 0, y: 0 }, light(), Some("░")).draw(&mut FigureLayer::stamped_by(
1571            &mut by_somebody,
1572            StampMode::Above,
1573            writer,
1574        ));
1575
1576        let mut wrote_something = false;
1577        for at in every_position(Pos { x: 0, y: 0 }, 4, 3) {
1578            assert!(by_nobody.cell(at).is_some(), "the box wrote {at:?}");
1579            wrote_something = true;
1580            assert_eq!(
1581                by_nobody.owner(at),
1582                None,
1583                "a shape drawn by nobody owns nothing at {at:?}"
1584            );
1585            assert_eq!(
1586                by_somebody.owner(at),
1587                Some(writer),
1588                "the same box under an identity owns every cell it decided"
1589            );
1590        }
1591        assert!(
1592            wrote_something,
1593            "the box above wrote nothing, so this test proved nothing"
1594        );
1595    }
1596}