monospace-diagram 0.1.2

An ordered set of shapes that draws as an ASCII or box-drawing diagram
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
//! Where something stands: a point, or a side of another figure it hangs from. See the
//! _Vocabulary_ and _Positions_ rows in
//! [`docs/diagram-model.md`](../../../docs/diagram-model.md).
//!
//! **A position is either absolute or a reference, and resolving one never fails.** A reference is
//! a shape's identity, one of its four sides and a gap from that side, and resolving it means asking
//! the named shape where that side stands now and adding the gap — derived when it is needed rather
//! than stored once, which is what makes movement carry everything hanging from the figure. What
//! cannot be resolved is **not drawn**: a reference naming a shape the diagram does not hold, or an
//! anchor that kind does not answer, resolves to nothing, and a figure with no resolved position is
//! absent from the picture, writes nothing and answers no side of its own, so anything hanging from
//! it resolves to nothing in turn. There is no error, no panic and no report, and the rest of the
//! diagram draws normally. That silence is the deliberate answer rather than a missing feature —
//! drawing must never fail, and a reference naming a figure that is not added yet is an ordinary
//! state of a diagram being built — and what it costs is that a diagram which drew nothing and a
//! diagram whose every reference is broken look identical, with nothing to ask about the difference.

use monospace_core::{Direction, Orientation, Pos, Size};

use crate::{Delta, ShapeId};

/// One of the four sides of a figure a position can name.
///
/// Four variants and no fifth: the model's _Anchor points_ names the four side centers, and the
/// corners and the center are not among them.
///
/// It is not [`Direction`](monospace_core::Direction), which names which way a figure leaves rather
/// than where it is. The two coincide for a side today — a figure hanging off a box's right side
/// usually leaves rightward — and one name would mean the wrong thing the day a corner arrives,
/// since a corner has two sides and no single direction out of it.
///
/// `Copy` because it holds nothing, so a caller passes one without cloning it the way it has to
/// clone a [`Reference`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Anchor {
    /// The middle of the figure's first row.
    Top,
    /// The middle of the figure's last column.
    Right,
    /// The middle of the figure's last row.
    Bottom,
    /// The middle of the figure's first column.
    Left,
}

impl Anchor {
    /// Which way a connector leaves when hanging from this side.
    #[must_use]
    pub const fn leaving(self) -> Direction {
        match self {
            Self::Top => Direction::Up,
            Self::Right => Direction::Right,
            Self::Bottom => Direction::Down,
            Self::Left => Direction::Left,
        }
    }

    /// `out` cells away from the shape through this side, in the screen axes
    /// [`Delta`](crate::Delta) carries.
    ///
    /// **One rule read in four directions**, and the rule is that a side's outward normal runs along
    /// the axis the side itself does not: `top` and `bottom` are horizontal borders, so they move a
    /// point vertically, and `left` and `right` are vertical borders, so they move one horizontally.
    /// Each amount is negated or not so that **a positive `out` is always further from the shape**,
    /// which is what lets a caller name a gap without knowing which way up the screen is and which
    /// way the figure faces.
    ///
    /// The four signs belong to this crate rather than to whoever reads a file, because a sign is a
    /// fact about which way a side faces and this is the module that answers that. A caller that
    /// wants the screen axes anyway holds the [`offset`](Reference::offset), which is two `i16`s it
    /// can put anything into — this is a second spelling of a gap, not a replacement for one.
    ///
    /// `saturating_neg` rather than `-`, because `i16::MIN` has no positive counterpart and a plain
    /// negation of it overflows, which in a debug build is a panic on a value a description is free
    /// to spell. Saturating puts that one input a cell nearer than it could have gone rather than
    /// turning a picture into a crash, which is the same trade [`Delta::apply`](crate::Delta)
    /// makes and for the same reason.
    pub(crate) fn outward(self, out: i16) -> Delta {
        match self {
            Self::Top => Delta {
                dx: 0,
                dy: out.saturating_neg(),
            },
            Self::Right => Delta { dx: out, dy: 0 },
            Self::Bottom => Delta { dx: 0, dy: out },
            Self::Left => Delta {
                dx: out.saturating_neg(),
                dy: 0,
            },
        }
    }
}

/// Another figure and one of its four sides, which is what a position may name instead of a point.
///
/// Three fields, and that is the whole of it: the model's _Vocabulary_ gives a reference an
/// identity, an anchor and two offsets, and the third field is the offsets arrived.
///
/// The offset is a gap **from the side** rather than a point, added to whatever that side answers now — so displacing the figure a
/// reference hangs from carries the endpoint with it and the gap does not change. An offset of
/// nothing puts the endpoint on the side itself, and a negative amount puts it on the far side of
/// it; neither is checked against the side, and
/// [#90](https://github.com/andresmoschini/monospace/issues/90) is where a corner would be.
///
/// All four fields are public, as [`Delta`](crate::Delta)'s are, so a caller names a reference as
/// a value. [`ShapeId::new`] builds the identity it holds, and the identity a diagram's own
/// [`add`](crate::Diagram::add) handed back is the one the reference should hold.
///
/// It is `Copy`, because every field is: an identity is two bytes, `Delta` is two `i16`s, and `out`
/// is an `i16`, and a reference is a value a caller hands on rather than one it has to keep track
/// of.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Reference {
    /// The identity of the shape the position hangs from.
    pub id: ShapeId,
    /// Which of that shape's four sides it hangs from.
    pub anchor: Anchor,
    /// How far from that side, along each screen axis, in cells.
    pub offset: Delta,
    /// How far outward from that side, in cells.
    pub out: i16,
}

impl Reference {
    /// A reference hanging from `anchor` on `id`, standing `offset` from that side plus `out`
    /// cells outward from it, in the side's own words.
    ///
    /// The four fields are stored directly. The model's §4 _Positions_ says an `out` is written
    /// beside an offset and added to it when resolving, so a figure may write either one or both —
    /// and one that writes neither is a reference standing on the border, which is this constructor
    /// with a zero offset and a zero `out`.
    ///
    /// `out` is signed and is not checked against the side it is measured from, so a negative one is
    /// a point inside the shape and an `out` and an offset pushing the same axis in opposite
    /// directions add and may cancel. Neither is reported: a reference that resolves to nothing
    /// resolves to nothing however large its gap, which is §4's answer for an unresolved reference
    /// and no exception to it.
    #[must_use]
    pub fn new(id: ShapeId, anchor: Anchor, offset: Delta, out: i16) -> Self {
        Self {
            id,
            anchor,
            offset,
            out,
        }
    }
}

/// Where something stands: a point, or a reference to a side of a shape.
///
/// Two variants and no third, because the model's _Positions_ says a position is one or the other
/// and names nothing else. A point is [`Position::Absolute`] and a hanging position is
/// [`Position::Reference`].
///
/// It is `Copy`, for the reason [`Reference`] gives.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Position {
    /// A point on the canvas, which resolves to itself whatever the diagram holds.
    Absolute(Pos),
    /// Another figure and one of its four sides, which resolves to whatever that side is now.
    Reference(Reference),
}

impl From<Pos> for Position {
    /// Wraps a point as a position that never fails to resolve — the second way in, for a caller
    /// holding a point rather than a position. It builds one and does nothing else.
    ///
    /// It is here for the same reason [`From<(i16, i16)> for Delta`](crate::Delta) is: the field
    /// this type lands on is filled from a point at every site that builds an endpoint, and without
    /// the conversion each of them would name the variant.
    fn from(at: Pos) -> Self {
        Self::Absolute(at)
    }
}

impl Position {
    /// This position, moved by `by`, changing nothing else.
    ///
    /// A displacement adds coordinates, and a point has coordinates to add to. A
    /// [`Position::Reference`] has none — it names **which** figure and **which** of its four sides,
    /// and neither is a place — so what it has instead is a gap, and the gap is what grows: `offset`
    /// takes `by` on each screen axis and comes back a larger [`Delta`].
    ///
    /// **The two fields that name are unchanged, and the reason each is unchanged is what makes the
    /// third one move.** `id` and `anchor` are read at draw time, so they are where the endpoint
    /// will be, and a displacement does not change where a figure it did not name is going to be;
    /// `offset` is a gap from that side rather than a point, and a gap is measured from something
    /// that stayed. A displacement therefore slides the endpoint away from the figure it hangs from
    /// — which is what a displacement of **one** figure means, and why the model's §4 says a
    /// displacement is a property of one figure rather than of a diagram.
    ///
    /// Nothing here is cloned: the identity is two bytes and the side is a unit variant, so the
    /// only field a displacement grows is the gap. A displacement of nothing returns the position
    /// equal to itself, and a reference naming something this diagram does not hold comes back the
    /// same reference with a larger offset and still resolves to nothing — the arithmetic builds a
    /// value and cannot fail, so there is no error path here and nothing to report.
    #[must_use]
    pub(crate) fn displaced_by(self, by: Delta) -> Self {
        match self {
            Self::Absolute(at) => Self::Absolute(by.apply(at)),
            Self::Reference(reference) => Self::Reference(Reference {
                id: reference.id,
                anchor: reference.anchor,
                offset: reference.offset.grow(by),
                out: reference.out,
            }),
        }
    }
}

/// The middle of one side of a figure occupying `at` through `at + (size - 1)`.
///
/// Two rules rather than four, and the four anchors are those two read in four directions: a side
/// across the figure is the middle of its extent, and a side along it is the far end of its own.
/// The division is a floor, so a box four cells wide has its top and bottom centers on cells 1 and
/// 2 rather than on a half-cell that does not exist.
///
/// `saturating_sub` on each extent and `saturating_add` on each axis is what makes a box one cell
/// wide or one cell tall fall out of the general rule rather than needing an exception: at width 1
/// the left and right centers are the same cell, and the top and bottom are its two ends. It costs
/// nothing to be right at extent 0, where the figure draws nothing and both answers are its own
/// `at`.
pub(crate) fn side_centre(at: Pos, size: Size, anchor: Anchor) -> Pos {
    let width = size.width.saturating_sub(1);
    let height = size.height.saturating_sub(1);

    match anchor {
        Anchor::Top => Pos {
            x: at.x.saturating_add_unsigned(width / 2),
            y: at.y,
        },
        Anchor::Right => Pos {
            x: at.x.saturating_add_unsigned(width),
            y: at.y.saturating_add_unsigned(height / 2),
        },
        Anchor::Bottom => Pos {
            x: at.x.saturating_add_unsigned(width / 2),
            y: at.y.saturating_add_unsigned(height),
        },
        Anchor::Left => Pos {
            x: at.x,
            y: at.y.saturating_add_unsigned(height / 2),
        },
    }
}

/// A line's extent read as a box: `(len, 1)` along a row, `(1, len)` up a column.
///
/// A line has no width and no height, only a length and a way to lie, so the one helper that
/// answers the middle of a side of a box needs something box-shaped to be asked about. This is it,
/// and it is the only reason a line answers the same four centers a box does.
pub(crate) fn flat_size(len: u16, orientation: Orientation) -> Size {
    match orientation {
        Orientation::Horizontal => Size {
            width: len,
            height: 1,
        },
        Orientation::Vertical => Size {
            width: 1,
            height: len,
        },
    }
}

#[cfg(test)]
mod tests {
    use std::num::NonZeroU16;

    use monospace_core::{
        Buffer, Direction, GlyphCatalog, Orientation, Pos, Size, Stroke, Terminal, Window, render,
    };

    use super::Anchor;
    use crate::{Delta, Diagram, Endpoint, EndpointAt, Position, Reference, Shape, ShapeId};

    /// An identity of the ordinal `ordinal`, which is how every test below names one.
    fn identity(ordinal: u16) -> ShapeId {
        ShapeId::new(NonZeroU16::new(ordinal).expect("no test names zero"))
    }

    fn light() -> Stroke {
        Stroke::from("light")
    }

    /// The four-by-three box at the origin that the two offset tests below resolve against.
    ///
    /// Built here rather than spelled out in each test, and asked through `Shape::anchor` — the
    /// crate-private query the drawing itself goes through — so a test could not disagree with the
    /// picture by construction. Each test adds it to a diagram of its own and keeps the identity
    /// `add` issued, because an identity is the diagram's to hand out and not the test's to write
    /// down (D3).
    fn the_box_at_the_origin() -> Shape {
        Shape::Box {
            at: Pos { x: 0, y: 0 },
            size: Size {
                width: 4,
                height: 3,
            },
            stroke: light(),
            fill: None,
            text: None,
        }
    }

    fn resolve(reference: Reference, diagram: &Diagram) -> Option<Pos> {
        Endpoint::reference(reference, Terminal::Arm).resolve(diagram)
    }

    #[test]
    fn each_anchor_leaves_toward_its_outward_normal() {
        assert_eq!(Anchor::Top.leaving(), Direction::Up);
        assert_eq!(Anchor::Right.leaving(), Direction::Right);
        assert_eq!(Anchor::Bottom.leaving(), Direction::Down);
        assert_eq!(Anchor::Left.leaving(), Direction::Left);
    }

    /// `resolve` asked rather than drawn, and the offset is a gap from the side.
    ///
    /// **Each answer is pinned against the absolute point, never against the other side of the
    /// comparison.** A `resolve` that added `dx` to the y and `dy` to the x would satisfy "the two
    /// agree with each other" while both were wrong; asked separately, each has to be right on its
    /// own. `{3, 1}` and `{1, 2}` are the right and bottom side centres, measured rather than
    /// chosen — `a_box_answers_its_four_side_centres` below already holds them.
    #[test]
    fn a_reference_resolves_to_its_anchors_point_moved_by_the_offset() {
        let diagram = Diagram::new();
        let (diagram, id) = diagram.add(the_box_at_the_origin());

        let right = Reference::new(id, Anchor::Right, Delta { dx: 2, dy: 0 }, 0);
        let bottom = Reference::new(id, Anchor::Bottom, Delta { dx: 0, dy: 1 }, 0);

        assert_eq!(resolve(right, &diagram), Some(Pos { x: 5, y: 1 }));
        assert_eq!(resolve(bottom, &diagram), Some(Pos { x: 1, y: 3 }));
    }

    /// The `assert_ne!` is the whole of it: a reference carrying no offset resolves to the point
    /// its anchor answers, and is **not** the bare point that answers it.
    ///
    /// A `resolve` that added nothing at all would pass every equality above, because each offset
    /// there is zero on the axis that would show it. Only the inequality catches that — and it
    /// compares two `Position`s the way the displacement test below does, because a bare point
    /// standing in the same cell is a *different position* from a reference standing on that side,
    /// and a caller may rely on telling the two apart.
    #[test]
    fn a_reference_with_no_offset_stands_on_its_side_and_is_not_the_bare_point() {
        let diagram = Diagram::new();
        let the_box = the_box_at_the_origin();
        let (diagram, id) = diagram.add(the_box.clone());

        let on_the_side = Reference::new(id, Anchor::Right, Delta { dx: 0, dy: 0 }, 0);
        let the_side_middle = the_box
            .anchor(Anchor::Right)
            .expect("a box answers all four of its sides");

        assert_eq!(resolve(on_the_side, &diagram), Some(the_side_middle));
        assert_ne!(
            Position::Reference(on_the_side),
            Position::Absolute(the_side_middle)
        );
    }

    /// A four-by-three box at the origin answers all four of its side centers, and each answer is
    /// the absolute point rather than one of the other three.
    ///
    /// Pinned by coordinates and not by a picture, because nothing draws an anchor. `{3, 1}` is not
    /// an arbitrary number here: it is where the shipped demonstration's connector already stands,
    /// and where §6 _Attachment_ of the model shows an endpoint hanging on that border.
    #[test]
    fn a_box_answers_its_four_side_centres() {
        let the_box = Shape::Box {
            at: Pos { x: 0, y: 0 },
            size: Size {
                width: 4,
                height: 3,
            },
            stroke: light(),
            fill: None,
            text: None,
        };

        assert_eq!(the_box.anchor(Anchor::Top), Some(Pos { x: 1, y: 0 }));
        assert_eq!(the_box.anchor(Anchor::Right), Some(Pos { x: 3, y: 1 }));
        assert_eq!(the_box.anchor(Anchor::Bottom), Some(Pos { x: 1, y: 2 }));
        assert_eq!(the_box.anchor(Anchor::Left), Some(Pos { x: 0, y: 1 }));
    }

    /// Spec's edge case: a box one cell wide and a box one cell tall, each asked for all four.
    ///
    /// Their centers coincide in pairs — and they coincide **by the general rule**, with no branch
    /// for the degenerate case. That is the whole claim: a test that only asked a four-by-three
    /// would pass on an implementation that special-cased the ordinary figure and got these two
    /// wrong, and a branch written as an exception is a branch nothing else has to satisfy.
    #[test]
    fn a_box_one_cell_wide_or_one_cell_tall_answers_the_same_rule() {
        let one_wide = Shape::Box {
            at: Pos { x: 0, y: 0 },
            size: Size {
                width: 1,
                height: 3,
            },
            stroke: light(),
            fill: None,
            text: None,
        };
        let one_tall = Shape::Box {
            at: Pos { x: 0, y: 0 },
            size: Size {
                width: 4,
                height: 1,
            },
            stroke: light(),
            fill: None,
            text: None,
        };

        assert_eq!(one_wide.anchor(Anchor::Top), Some(Pos { x: 0, y: 0 }));
        assert_eq!(one_wide.anchor(Anchor::Right), Some(Pos { x: 0, y: 1 }));
        assert_eq!(one_wide.anchor(Anchor::Bottom), Some(Pos { x: 0, y: 2 }));
        assert_eq!(one_wide.anchor(Anchor::Left), Some(Pos { x: 0, y: 1 }));

        assert_eq!(one_tall.anchor(Anchor::Top), Some(Pos { x: 1, y: 0 }));
        assert_eq!(one_tall.anchor(Anchor::Right), Some(Pos { x: 3, y: 0 }));
        assert_eq!(one_tall.anchor(Anchor::Bottom), Some(Pos { x: 1, y: 0 }));
        assert_eq!(one_tall.anchor(Anchor::Left), Some(Pos { x: 0, y: 0 }));
    }

    /// A line answers the same four centers a flat box of the same extent would, in both
    /// orientations.
    ///
    /// Both, because one of the two is the one an implementation gets wrong by transposition: a
    /// horizontal line has one cell for a row and any number for a column, so its top and bottom
    /// centers are **the same point asked twice** and its left and right are its two ends — which a
    /// vertical line turns sideways rather than repeating. The flat box is built here by the same
    /// arithmetic the crate uses, so the two sides cannot be wrong together.
    #[test]
    fn a_line_answers_the_four_side_centres_of_a_flat_box() {
        let line = |at: Pos, len: u16, orientation| Shape::Line {
            at,
            len,
            orientation,
            stroke: light(),
        };
        let flat = |at: Pos, len: u16, orientation| Shape::Box {
            at,
            size: super::flat_size(len, orientation),
            stroke: light(),
            fill: None,
            text: None,
        };

        for (at, len, orientation) in [
            (Pos { x: 0, y: 0 }, 5, Orientation::Horizontal),
            (Pos { x: 0, y: 0 }, 5, Orientation::Vertical),
        ] {
            for anchor in [Anchor::Top, Anchor::Right, Anchor::Bottom, Anchor::Left] {
                assert_eq!(
                    line(at, len, orientation).anchor(anchor),
                    flat(at, len, orientation).anchor(anchor),
                    "{orientation:?} at {anchor:?}"
                );
            }
        }

        let across = line(Pos { x: 0, y: 0 }, 5, Orientation::Horizontal);
        assert_eq!(across.anchor(Anchor::Top), Some(Pos { x: 2, y: 0 }));
        assert_eq!(across.anchor(Anchor::Bottom), Some(Pos { x: 2, y: 0 }));
        assert_eq!(across.anchor(Anchor::Left), Some(Pos { x: 0, y: 0 }));
        assert_eq!(across.anchor(Anchor::Right), Some(Pos { x: 4, y: 0 }));
    }

    /// A connector answers none of the four, and `None` is an ordinary answer rather than a
    /// failure — no error, no report, no panic.
    ///
    /// All four rather than one, because a match written with a default arm would answer the one it
    /// happened to spell. This is the answer that keeps a chain of references one link long: a
    /// reference to a connector resolves to nothing, so nothing can hang from a thing that only
    /// hangs.
    #[test]
    fn a_connector_answers_none_of_its_four_side_centres() {
        let connector = Shape::Connector {
            from: Endpoint::absolute(Pos { x: 0, y: 0 }, Direction::Right, Terminal::Arm),
            to: Endpoint::absolute(Pos { x: 6, y: 0 }, Direction::Left, Terminal::Arm),
            stroke: light(),
        };

        for anchor in [Anchor::Top, Anchor::Right, Anchor::Bottom, Anchor::Left] {
            assert_eq!(connector.anchor(anchor), None, "{anchor:?}");
        }
    }

    /// A displacement moves an absolute position, and grows a reference's offset while its
    /// identity and its anchor come back as they went in.
    ///
    /// **Rewritten rather than deleted, because one sentence of the old test is still true and only
    /// its reason was wrong.** It read "a displacement moves a point and leaves a reference alone",
    /// and "a reference comes back equal to itself" is still true — of a displacement of **nothing**.
    /// What was false was the reason, and the reason was the whole claim.
    ///
    /// Compared by value rather than by picture, because a gap that failed to grow is what there is
    /// nothing to see. Both sides carry an `assert_ne!`, and that is the point of the rewrite: this
    /// test was the bug's own hiding place, since a `displaced_by` that changed **nothing at all**
    /// would satisfy every equality here by doing exactly what the code used to do.
    #[test]
    fn a_displacement_moves_a_point_and_grows_a_references_offset() {
        let by = Delta { dx: 4, dy: 0 };
        let reference = super::Position::Reference(crate::Reference::new(
            identity(1),
            Anchor::Right,
            Delta { dx: 0, dy: 0 },
            0,
        ));
        let point = super::Position::Absolute(Pos { x: 1, y: 1 });

        assert_eq!(
            point.displaced_by(by),
            super::Position::Absolute(Pos { x: 5, y: 1 })
        );
        assert_ne!(point.displaced_by(by), point);

        // The one field that grows, asked on its own so a rule that grew `id` or `anchor` instead
        // cannot pass on the other two being right.
        let super::Position::Reference(moved) = reference.displaced_by(by) else {
            unreachable!("the position above is a reference")
        };
        assert_eq!(moved.offset, by);
        assert_ne!(reference.displaced_by(by), reference);

        // The two fields that name, each equal to what went in — and the offset is asserted not
        // equal to its old value, since an offset of nothing would satisfy both equalities.
        let super::Position::Reference(went_in) = &reference else {
            unreachable!("the position above is a reference")
        };
        assert_eq!(moved.id, went_in.id);
        assert_eq!(moved.anchor, went_in.anchor);
        assert_ne!(moved.offset, went_in.offset);

        // A displacement of nothing is the sentence that survived the rewrite, and it is a
        // statement about the arithmetic rather than an exception to it.
        let nothing = Delta { dx: 0, dy: 0 };
        assert_eq!(reference.displaced_by(nothing), reference);
        assert_eq!(point.displaced_by(nothing), point);
    }

    /// Draws `diagram` into a `size` window at the origin and renders it as text, so that a claim
    /// about what a gap draws is made in the vocabulary a reader reads a picture in.
    ///
    /// Half the rules of §4 _Positions_ are about the picture rather than about the fields, and a
    /// diagram that resolved a point and drew something else from it would satisfy every field
    /// comparison below.
    fn picture_of(diagram: &Diagram, size: Size) -> String {
        let origin = Pos { x: 0, y: 0 };
        let mut buffer = Buffer::new(Window { origin, size });
        diagram.draw(&mut buffer);
        render(&buffer, &GlyphCatalog::light(), buffer.window())
    }

    /// A connector whose `from` is whatever `hanging_from` builds from the identity the box holds,
    /// and whose far end is the fixed point below it — so two runs of this differ only in how the
    /// near end was spelled.
    ///
    /// The identity is handed to the closure rather than spelled beside it, because an identity is
    /// the diagram's to issue and not a test's to write down (D3) — the same reason
    /// [`the_box_at_the_origin`]'s own tests keep the one `add` returned. A reference naming an
    /// identity this diagram does not hold is reachable only by spelling one, which is what
    /// `an_out_on_a_reference_that_resolves_to_nothing_still_draws_nothing` does.
    fn diagram_hanging_from(hanging_from: impl FnOnce(ShapeId) -> Reference) -> Diagram {
        let diagram = Diagram::new();
        let (mut diagram, id) = diagram.add(the_box_at_the_origin());
        diagram = diagram
            .add(Shape::Connector {
                from: Endpoint::reference(hanging_from(id), Terminal::Arm),
                to: Endpoint::absolute(Pos { x: 1, y: 5 }, Direction::Up, Terminal::Arm),
                stroke: light(),
            })
            .0;
        diagram
    }

    /// The `out` is **added to** the offset rather than put in its place, so all three go into one
    /// answer: the anchor's point, then the offset, then the outward amount.
    ///
    /// The box is the four-by-three one every other test here resolves against, whose bottom centre
    /// `{1, 2}` `a_box_answers_its_four_side_centres` already holds. **The two amounts are non-zero
    /// and on different axes** — one cell along the side and three down it — because a test that put
    /// them on the same axis would satisfy itself whether the outward one was added, overwritten or
    /// dropped.
    #[test]
    fn a_reference_with_an_out_resolves_to_its_side_moved_by_the_offset_and_by_the_out() {
        let diagram = Diagram::new();
        let (diagram, id) = diagram.add(the_box_at_the_origin());

        let reference = Reference::new(id, Anchor::Bottom, Delta { dx: 1, dy: 3 }, 1);

        assert_eq!(
            reference.offset,
            Delta { dx: 1, dy: 3 },
            "offset remains separate without folding out into it"
        );
        assert_eq!(reference.out, 1, "out is stored in its own field");
        assert_eq!(
            resolve(reference, &diagram),
            Some(Pos { x: 2, y: 6 }),
            "{{1, 2}} plus (1, 3) plus (0, 1) is {{2, 6}}"
        );
    }

    /// A negative `out` is a point inside the shape rather than a fault, which is what makes the
    /// amount signed: one cell out of a side means one cell further from it whichever way that is.
    ///
    /// **The point is asserted rather than the absence of a report**, because a description format
    /// has no channel to report on and a reference naming a point inside a figure is an ordinary
    /// arrangement — §4 says the offset's own amounts are unchecked against their side too.
    #[test]
    fn a_negative_out_is_a_point_into_the_shape_rather_than_an_error() {
        let diagram = Diagram::new();
        let (diagram, id) = diagram.add(the_box_at_the_origin());
        let nothing = Delta { dx: 0, dy: 0 };

        let into_the_box = Reference::new(id, Anchor::Bottom, nothing, -1);

        assert_eq!(
            resolve(into_the_box, &diagram),
            Some(Pos { x: 1, y: 1 }),
            "one cell into the box from its bottom centre {{1, 2}} is {{1, 1}}"
        );
    }

    /// All four signs, and **each one moves a point along the axis its own side does not run in**.
    ///
    /// A five-by-three box at the origin, an `out` of one on each of the four anchors and no offset
    /// at all, so every answer is the side's own point plus one cell away from it. Both halves are
    /// asked: the four points, and then — separately — that no side moved along the axis it runs
    /// in, which is the half a transposed sign would satisfy while the four points above still
    /// looked right to a reader.
    #[test]
    fn the_four_signs_are_the_outward_normal_of_the_side_they_are_named_from() {
        let diagram = Diagram::new();
        let (diagram, id) = diagram.add(Shape::Box {
            at: Pos { x: 0, y: 0 },
            size: Size {
                width: 5,
                height: 3,
            },
            stroke: light(),
            fill: None,
            text: None,
        });
        let nothing = Delta { dx: 0, dy: 0 };
        let stands_at = |anchor| {
            let reference = Reference::new(id, anchor, nothing, 1);
            (
                reference.offset,
                reference.out,
                resolve(reference, &diagram),
            )
        };

        assert_eq!(
            stands_at(Anchor::Top),
            (Delta { dx: 0, dy: 0 }, 1, Some(Pos { x: 2, y: -1 }))
        );
        assert_eq!(
            stands_at(Anchor::Right),
            (Delta { dx: 0, dy: 0 }, 1, Some(Pos { x: 5, y: 1 }))
        );
        assert_eq!(
            stands_at(Anchor::Bottom),
            (Delta { dx: 0, dy: 0 }, 1, Some(Pos { x: 2, y: 3 }))
        );
        assert_eq!(
            stands_at(Anchor::Left),
            (Delta { dx: 0, dy: 0 }, 1, Some(Pos { x: -1, y: 1 }))
        );

        // A `top` or a `bottom` is a horizontal border and moves a point vertically; a `left` or a
        // `right` is a vertical one and moves it horizontally. An amount of three rather than one,
        // so a rule that dropped a non-zero amount cannot pass this by being zero.
        for (anchor, dx, dy) in [
            (Anchor::Top, 0, -3),
            (Anchor::Right, 3, 0),
            (Anchor::Bottom, 0, 3),
            (Anchor::Left, -3, 0),
        ] {
            let outward = Anchor::outward(anchor, 3);
            assert_eq!(
                (outward.dx, outward.dy),
                (dx, dy),
                "{anchor:?} moved along the axis its own side runs in"
            );
        }
    }

    /// An `out` of zero is the reference written without the field: the same three fields, the same
    /// resolution and the same picture.
    ///
    /// **The three spellings are compared as values and as pictures, and the third one differs from
    /// them.** A picture alone would be satisfied by a `new` that ignored its fourth argument
    /// altogether, because the offset is zero in every case here — so a non-zero `out` is drawn too,
    /// which is what makes the equalities mean something.
    #[test]
    fn an_out_of_zero_is_the_reference_written_without_it() {
        let size = Size {
            width: 6,
            height: 6,
        };
        let nothing = Delta { dx: 0, dy: 0 };

        assert_eq!(
            Reference::new(identity(1), Anchor::Bottom, nothing, 0),
            Reference {
                id: identity(1),
                anchor: Anchor::Bottom,
                offset: nothing,
                out: 0,
            },
            "an `out` of zero is the four fields written with zero out"
        );

        let as_an_out = picture_of(
            &diagram_hanging_from(|id| Reference::new(id, Anchor::Bottom, nothing, 0)),
            size,
        );
        let as_an_offset = picture_of(
            &diagram_hanging_from(|id| Reference {
                id,
                anchor: Anchor::Bottom,
                offset: nothing,
                out: 0,
            }),
            size,
        );
        let a_cell_clear = picture_of(
            &diagram_hanging_from(|id| Reference::new(id, Anchor::Bottom, nothing, 1)),
            size,
        );

        assert_eq!(as_an_out, as_an_offset, "the same picture");
        assert_ne!(
            as_an_out, a_cell_clear,
            "an `out` of zero drew the same as an `out` of one, so the field is being dropped"
        );
    }

    /// A reference keeps its offset and its out distinct as fields, while resolving to the same
    /// point in the diagram.
    ///
    /// Storing both fields separately allows editing them independently, while `Position::resolve`
    /// applies both the offset and outward normal to reach the identical point on the canvas.
    #[test]
    fn a_reference_keeps_its_offset_and_its_out_distinct_while_resolving_to_the_same_point() {
        let diagram = Diagram::new();
        let (diagram, id) = diagram.add(the_box_at_the_origin());
        let nothing = Delta { dx: 0, dy: 0 };
        let one_down = Delta { dx: 0, dy: 1 };

        let as_offset = Reference::new(id, Anchor::Bottom, one_down, 0);
        let as_out = Reference::new(id, Anchor::Bottom, nothing, 1);

        assert_ne!(
            as_offset, as_out,
            "offset and out are held in separate fields on Reference"
        );
        assert_eq!(as_offset.offset, one_down);
        assert_eq!(as_offset.out, 0);
        assert_eq!(as_out.offset, nothing);
        assert_eq!(as_out.out, 1);

        assert_eq!(
            resolve(as_offset, &diagram),
            resolve(as_out, &diagram),
            "both spellings resolve to the exact same position in the diagram"
        );
        assert_eq!(resolve(as_out, &diagram), Some(Pos { x: 1, y: 3 }));
    }

    /// A displacement grows the offset of a reference written with an `out` exactly as it grows any
    /// other: the gap comes out of the same field and the point slides by the displacement.
    ///
    /// The displacement is of **the figure holding the reference**, which is what the rule is about —
    /// a gap from a side grows with the figure it is hung on rather than traveling with the figure
    /// the side belongs to. The box is left where it is precisely so that the slide is the only
    /// thing the two resolutions can differ by.
    #[test]
    fn a_displacement_grows_the_offset_of_a_reference_written_with_an_out_as_it_grows_any_other() {
        let by = Delta { dx: 0, dy: 3 };
        let diagram = Diagram::new();
        let (diagram, id) = diagram.add(the_box_at_the_origin());
        let hanging = Reference::new(id, Anchor::Bottom, Delta { dx: 0, dy: 0 }, 1);
        let connector = Shape::Connector {
            from: Endpoint::reference(hanging, Terminal::Arm),
            to: Endpoint::absolute(Pos { x: 1, y: 5 }, Direction::Up, Terminal::Arm),
            stroke: light(),
        };

        let Shape::Connector { from, .. } = connector.displaced_by(by) else {
            unreachable!("the figure above is a connector")
        };
        let EndpointAt::Reference(moved) = from.at else {
            unreachable!("its near end was a reference")
        };

        assert_eq!(
            moved.offset,
            Delta { dx: 0, dy: 3 },
            "three rows of displacement added to the offset"
        );
        assert_eq!(moved.out, 1, "out is preserved across displacement");
        assert_eq!(
            resolve(moved, &diagram),
            Some(Pos { x: 1, y: 6 }),
            "{{1, 3}} slid three rows down and nothing else moved"
        );
        assert_eq!(
            resolve(hanging, &diagram),
            Some(Pos { x: 1, y: 3 }),
            "the gap the caller wrote is the same reference's: {{1, 2}} and one cell out"
        );
    }

    /// An `out` and an offset pushing the same axis in opposite directions add, and may cancel —
    /// and the cancellation is a point rather than a report.
    ///
    /// **Both directions, because either one alone is the easy half.** An offset of one cell up
    /// against an `out` of one is the side itself, which a rule that dropped either amount would
    /// also produce; the offset of three is what shows that the two really are added rather than one
    /// of them winning.
    #[test]
    fn an_out_and_an_offset_against_each_other_add_and_nothing_reports_the_cancellation() {
        let diagram = Diagram::new();
        let (diagram, id) = diagram.add(the_box_at_the_origin());

        let cancelling = Reference::new(id, Anchor::Bottom, Delta { dx: 0, dy: -1 }, 1);
        let past_the_side = Reference::new(id, Anchor::Bottom, Delta { dx: 0, dy: -3 }, 1);

        assert_eq!(cancelling.offset, Delta { dx: 0, dy: -1 });
        assert_eq!(cancelling.out, 1);
        assert_eq!(
            resolve(cancelling, &diagram),
            Some(Pos { x: 1, y: 2 }),
            "one cell up against one cell down is the side itself, which is a point and not a fault"
        );
        assert_eq!(past_the_side.offset, Delta { dx: 0, dy: -3 });
        assert_eq!(past_the_side.out, 1);
        assert_eq!(
            resolve(past_the_side, &diagram),
            Some(Pos { x: 1, y: 0 }),
            "three cells up against one cell down leaves two, neither clamped nor reported"
        );
    }

    /// An `out` on a reference that resolves to nothing still draws nothing, and **both ways of not
    /// resolving are asked**: an identity nothing holds, and a side a connector does not answer.
    ///
    /// Each reference carries an `out` of one, so this is the claim that an outward amount on an
    /// unresolvable reference never reaches a diagram — there is no cell left for it to move. The
    /// picture compared against is the box on its own, so a connector that drew something over it,
    /// or something anywhere else, fails.
    #[test]
    fn an_out_on_a_reference_that_resolves_to_nothing_still_draws_nothing() {
        let size = Size {
            width: 6,
            height: 6,
        };
        let nothing = Delta { dx: 0, dy: 0 };
        let arm = |reference: Reference, to: Pos| Shape::Connector {
            from: Endpoint::reference(reference, Terminal::Arm),
            to: Endpoint::absolute(to, Direction::Up, Terminal::Arm),
            stroke: light(),
        };

        let mut chain = Diagram::new();
        chain = chain.add(the_box_at_the_origin()).0;
        let (mut chain, the_first) = chain.add(arm(
            Reference::new(identity(99), Anchor::Bottom, nothing, 1),
            Pos { x: 1, y: 5 },
        ));
        chain = chain
            .add(arm(
                Reference::new(the_first, Anchor::Right, nothing, 1),
                Pos { x: 3, y: 5 },
            ))
            .0;

        let mut just_the_box = Diagram::new();
        just_the_box = just_the_box.add(the_box_at_the_origin()).0;

        assert_eq!(
            picture_of(&chain, size),
            picture_of(&just_the_box, size),
            "a reference that resolves to nothing drew something, whatever its `out`"
        );
    }

    /// A line answers the four centres of a flat box, so on a horizontal line the top centre and the
    /// bottom centre are **one point** — and an `out` from either of them stands the same one cell
    /// clear of that line, one above it and one below it.
    ///
    /// **The gap is asserted as a distance from the line rather than as two absolute points.** The
    /// two sides face away from each other, so the cells they reach are not one cell, and this is
    /// the whole claim: a one-cell-tall figure has no interior for its two sides to share, and
    /// naming either of them reaches the same distance from it. The two cells are named beside it,
    /// so the distance is not the only thing being said — a test that compared them against each
    /// other alone would pass on two implementations that both moved up.
    #[test]
    fn an_out_from_the_top_or_the_bottom_of_a_horizontal_line_moves_the_same_one_cell() {
        let diagram = Diagram::new();
        let the_line = Shape::Line {
            at: Pos { x: 0, y: 0 },
            len: 5,
            orientation: Orientation::Horizontal,
            stroke: light(),
        };
        let (diagram, id) = diagram.add(the_line.clone());
        let on_the_line = the_line
            .anchor(Anchor::Top)
            .expect("a line answers all four of its sides");
        let nothing = Delta { dx: 0, dy: 0 };
        let stands_at = |anchor| resolve(Reference::new(id, anchor, nothing, 1), &diagram);

        for (anchor, at) in [
            (Anchor::Top, Pos { x: 2, y: -1 }),
            (Anchor::Bottom, Pos { x: 2, y: 1 }),
        ] {
            assert_eq!(stands_at(anchor), Some(at), "{anchor:?}");
            assert_eq!(
                at.x, on_the_line.x,
                "{anchor:?} moved the point along the line rather than away from it"
            );
            assert_eq!(
                (at.y - on_the_line.y).abs(),
                1,
                "{anchor:?} did not stand one cell clear of the line"
            );
        }
    }
}