1 | use 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)] |
11 | pub struct RowSpan { |
12 | size: usize, |
13 | } |
14 | |
15 | impl 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 | |
27 | impl<R> CellOption<R, ColoredConfig> for RowSpan |
28 | where |
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 | |
40 | fn 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 | |
59 | fn 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 | |
75 | fn 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 | |
90 | fn is_row_span_valid(row: usize, span: usize, count_rows: usize) -> bool { |
91 | span + row <= count_rows |
92 | } |
93 | |
94 | fn 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 | |
98 | fn 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 | |
108 | fn 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 | |