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