| 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 | |