1use crate::{
2 grid::{
3 config::{ColoredConfig, Entity, Position, SpannedConfig},
4 records::{ExactRecords, Records},
5 },
6 settings::CellOption,
7};
8
9/// Row (vertical) span.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
11pub struct RowSpan {
12 size: usize,
13}
14
15impl RowSpan {
16 /// Creates a new row (vertical) span.
17 pub const fn new(size: usize) -> Self {
18 Self { size }
19 }
20
21 /// Creates a new row (vertical) span with a maximux value possible.
22 pub const fn max() -> Self {
23 Self::new(size:usize::MAX)
24 }
25}
26
27impl<R> CellOption<R, ColoredConfig> for RowSpan
28where
29 R: Records + ExactRecords,
30{
31 fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
32 let count_rows: usize = records.count_rows();
33 let count_cols: usize = records.count_columns();
34
35 set_row_spans(cfg, self.size, entity, (count_rows, count_cols));
36 remove_false_spans(cfg);
37 }
38}
39
40fn set_row_spans(cfg: &mut SpannedConfig, span: usize, entity: Entity, shape: (usize, usize)) {
41 for pos: (usize, usize) in entity.iter(count_rows:shape.0, count_cols:shape.1) {
42 if !is_valid_pos(pos, shape) {
43 continue;
44 }
45
46 let mut span: usize = span;
47 if !is_row_span_valid(row:pos.0, span, count_rows:shape.0) {
48 span = shape.0 - pos.0;
49 }
50
51 if span_has_intersections(cfg, pos, span) {
52 continue;
53 }
54
55 set_span_row(cfg, pos, span);
56 }
57}
58
59fn set_span_row(cfg: &mut SpannedConfig, pos: (usize, usize), span: usize) {
60 if span == 0 {
61 let (row: usize, col: usize) = pos;
62 if row == 0 {
63 return;
64 }
65
66 if let Some(closerow: usize) = closest_visible_row(cfg, (row - 1, col)) {
67 let span: usize = row + 1 - closerow;
68 cfg.set_row_span((closerow, col), span);
69 }
70 }
71
72 cfg.set_row_span(pos, span);
73}
74
75fn closest_visible_row(cfg: &SpannedConfig, mut pos: Position) -> Option<usize> {
76 loop {
77 if cfg.is_cell_visible(pos) {
78 return Some(pos.0);
79 }
80
81 if pos.0 == 0 {
82 // can happen if we have a above horizontal spanned cell
83 return None;
84 }
85
86 pos.0 -= 1;
87 }
88}
89
90fn is_row_span_valid(row: usize, span: usize, count_rows: usize) -> bool {
91 span + row <= count_rows
92}
93
94fn is_valid_pos((row: usize, col: usize): Position, (count_rows: usize, count_cols: usize): (usize, usize)) -> bool {
95 row < count_rows && col < count_cols
96}
97
98fn span_has_intersections(cfg: &SpannedConfig, (row: usize, col: usize): Position, span: usize) -> bool {
99 for row: usize in row..row + span {
100 if !cfg.is_cell_visible((row, col)) {
101 return true;
102 }
103 }
104
105 false
106}
107
108fn remove_false_spans(cfg: &mut SpannedConfig) {
109 for (pos: (usize, usize), _) in cfg.get_column_spans() {
110 if cfg.is_cell_visible(pos) {
111 continue;
112 }
113
114 cfg.set_row_span(pos, span:1);
115 cfg.set_column_span(pos, span:1);
116 }
117
118 for (pos: (usize, usize), _) in cfg.get_row_spans() {
119 if cfg.is_cell_visible(pos) {
120 continue;
121 }
122
123 cfg.set_row_span(pos, span:1);
124 cfg.set_column_span(pos, span:1);
125 }
126}
127