1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
use std::collections::HashSet;
use std::ops::Range;

use super::{Goban, MakeSvgError, MakeSvgOptions};

#[derive(Debug, Clone)]
pub enum GobanRange {
    ShrinkWrap,
    FullBoard,
    Ranged(Range<u8>, Range<u8>),
}

impl GobanRange {
    pub fn get_ranges(
        &self,
        goban: &Goban,
        options: &MakeSvgOptions,
    ) -> Result<(Range<u8>, Range<u8>), MakeSvgError> {
        let goban_size = goban.size();
        match self {
            Self::FullBoard => Ok((0..goban_size.0, 0..goban_size.1)),
            Self::ShrinkWrap => {
                let mut points: HashSet<_> = goban.stones().map(|s| (s.x, s.y)).collect();
                if options.draw_marks {
                    points.extend(goban.marks());
                }
                if options.draw_triangles {
                    points.extend(goban.triangles());
                }
                if options.draw_circles {
                    points.extend(goban.circles());
                }
                if options.draw_squares {
                    points.extend(goban.squares());
                }
                if options.draw_selected {
                    points.extend(goban.selected());
                }
                if options.draw_labels {
                    points.extend(goban.labels().map(|(p, _)| p))
                }
                if options.draw_lines {
                    points.extend(goban.lines().flat_map(|(p1, p2)| vec![p1, p2]))
                }
                if options.draw_arrows {
                    points.extend(goban.arrows().flat_map(|(p1, p2)| vec![p1, p2]))
                }
                // Don't necessarily include dimmed points!
                let x_start = {
                    let p = points
                        .iter()
                        .map(|&(x, _)| x)
                        .min()
                        .unwrap_or(0)
                        .saturating_sub(1);
                    if p == 1 {
                        0 // Include nearby board edge.
                    } else {
                        p
                    }
                };
                let x_end = {
                    let p = points
                        .iter()
                        .map(|&(x, _)| (x + 2).min(goban_size.0))
                        .max()
                        .unwrap_or(goban_size.0);
                    if p == goban_size.0 - 1 {
                        goban_size.0 // Include nearby board edge.
                    } else {
                        p
                    }
                };
                let y_start = {
                    let p = points
                        .iter()
                        .map(|&(_, y)| y)
                        .min()
                        .unwrap_or(0)
                        .saturating_sub(1);
                    if p == 1 {
                        0 // Include nearby board edge.
                    } else {
                        p
                    }
                };
                let y_end = {
                    let p = points
                        .iter()
                        .map(|&(_, y)| (y + 2).min(goban_size.1))
                        .max()
                        .unwrap_or(goban_size.1);
                    if p == goban_size.1 - 1 {
                        goban_size.1 // Include nearby board edge.
                    } else {
                        p
                    }
                };
                Ok((x_start..x_end, y_start..y_end))
            }
            Self::Ranged(a, b) => {
                if a.end > goban_size.0 || b.end > goban_size.1 {
                    Err(MakeSvgError::InvalidRange)
                } else {
                    Ok((a.clone(), b.clone()))
                }
            }
        }
    }
}