1 | use crate::{ |
2 | grid::{ |
3 | config::{ColoredConfig, Entity, Position, SpannedConfig}, |
4 | records::{ExactRecords, Records}, |
5 | }, |
6 | settings::CellOption, |
7 | }; |
8 | |
9 | /// Columns (Vertical) span. |
10 | #[derive (Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
11 | pub struct ColumnSpan { |
12 | size: usize, |
13 | } |
14 | |
15 | impl ColumnSpan { |
16 | /// Creates a new column (vertical) span. |
17 | pub fn new(size: usize) -> Self { |
18 | Self { size } |
19 | } |
20 | |
21 | /// Creates a new column (vertical) span with a maximux value possible. |
22 | pub fn max() -> Self { |
23 | Self::new(size:usize::MAX) |
24 | } |
25 | } |
26 | |
27 | impl<R> CellOption<R, ColoredConfig> for ColumnSpan |
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_col_spans(cfg, self.size, entity, (count_rows, count_cols)); |
36 | remove_false_spans(cfg); |
37 | } |
38 | } |
39 | |
40 | fn set_col_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_column_span_valid(col:pos.1, span, count_cols:shape.1) { |
48 | span = shape.1 - pos.1; |
49 | } |
50 | |
51 | if span_has_intersections(cfg, pos, span) { |
52 | continue; |
53 | } |
54 | |
55 | set_span_column(cfg, pos, span); |
56 | } |
57 | } |
58 | |
59 | fn set_span_column(cfg: &mut SpannedConfig, pos: (usize, usize), span: usize) { |
60 | if span == 0 { |
61 | let (row: usize, col: usize) = pos; |
62 | if col == 0 { |
63 | return; |
64 | } |
65 | |
66 | if let Some(closecol: usize) = closest_visible(cfg, (row, col - 1)) { |
67 | let span: usize = col + 1 - closecol; |
68 | cfg.set_column_span((row, closecol), span); |
69 | } |
70 | } |
71 | |
72 | cfg.set_column_span(pos, span); |
73 | } |
74 | |
75 | fn closest_visible(cfg: &SpannedConfig, mut pos: Position) -> Option<usize> { |
76 | loop { |
77 | if cfg.is_cell_visible(pos) { |
78 | return Some(pos.1); |
79 | } |
80 | |
81 | if pos.1 == 0 { |
82 | return None; |
83 | } |
84 | |
85 | pos.1 -= 1; |
86 | } |
87 | } |
88 | |
89 | fn is_column_span_valid(col: usize, span: usize, count_cols: usize) -> bool { |
90 | span + col <= count_cols |
91 | } |
92 | |
93 | fn is_valid_pos((row: usize, col: usize): Position, (count_rows: usize, count_cols: usize): (usize, usize)) -> bool { |
94 | row < count_rows && col < count_cols |
95 | } |
96 | |
97 | fn span_has_intersections(cfg: &SpannedConfig, (row: usize, col: usize): Position, span: usize) -> bool { |
98 | for col: usize in col..col + span { |
99 | if !cfg.is_cell_visible((row, col)) { |
100 | return true; |
101 | } |
102 | } |
103 | |
104 | false |
105 | } |
106 | |
107 | fn remove_false_spans(cfg: &mut SpannedConfig) { |
108 | for (pos: (usize, usize), _) in cfg.get_column_spans() { |
109 | if cfg.is_cell_visible(pos) { |
110 | continue; |
111 | } |
112 | |
113 | cfg.set_row_span(pos, span:1); |
114 | cfg.set_column_span(pos, span:1); |
115 | } |
116 | |
117 | for (pos: (usize, usize), _) in cfg.get_row_spans() { |
118 | if cfg.is_cell_visible(pos) { |
119 | continue; |
120 | } |
121 | |
122 | cfg.set_row_span(pos, span:1); |
123 | cfg.set_column_span(pos, span:1); |
124 | } |
125 | } |
126 | |