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
111
112
113
use geo::{Coordinate, MultiPolygon};
use std::fmt;

/// A rectangular region with square edges.
#[derive(Debug, Clone)]
pub struct Rect<U = super::Unit> {
    rect: geo::Rect<f64>,
    inner: U,
}

impl Rect {
    /// Constructs a new rectangle using the provided corners.
    pub fn new(top_left: Coordinate<f64>, bottom_right: Coordinate<f64>) -> Self {
        Self {
            rect: geo::Rect::new(top_left, bottom_right),
            inner: super::Unit,
        }
    }

    /// Constructs a new rectangle given a center point and sizes.
    pub fn with_center(center: Coordinate<f64>, width: f64, height: f64) -> Self {
        Self {
            rect: geo::Rect::new(
                center
                    + Coordinate {
                        x: -width / 2.,
                        y: -height / 2.,
                    },
                center
                    + Coordinate {
                        x: width / 2.,
                        y: height / 2.,
                    },
            ),
            inner: super::Unit,
        }
    }
}

impl<U: super::InnerFeature + Clone + std::fmt::Debug> Rect<U> {
    /// Constructs a rectangle surrounding the inner feature. The
    /// origin of the inner feature will match the centeroid of the
    /// rectangle.
    pub fn with_inner(inner: U) -> Self {
        let tl: Coordinate<f64> = [-1f64, -1f64].into();
        let br: Coordinate<f64> = [1f64, 1f64].into();
        let rect = geo::Rect::new(tl, br);
        Self { rect, inner }
    }

    /// Returns a new rectangle around the provided center.
    pub fn dimensions(mut self, center: Coordinate<f64>, width: f64, height: f64) -> Self {
        let rect = geo::Rect::new(
            center
                + Coordinate {
                    x: -width / 2.,
                    y: -height / 2.,
                },
            center
                + Coordinate {
                    x: width / 2.,
                    y: height / 2.,
                },
        );
        self.inner.translate(center);
        Self {
            rect,
            inner: self.inner,
        }
    }

    /// Returns a new rectangle using the provided bounds.
    pub fn bounds(mut self, top_left: Coordinate<f64>, bottom_right: Coordinate<f64>) -> Self {
        let rect = geo::Rect::new(top_left, bottom_right);
        self.inner.translate(rect.center());
        Self {
            rect,
            inner: self.inner,
        }
    }
}

impl<U: super::InnerFeature> fmt::Display for Rect<U> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "rect({:?}, {:?}, U = {})",
            self.rect.min(),
            self.rect.max(),
            self.inner
        )
    }
}

impl<U: super::InnerFeature + Clone + std::fmt::Debug> super::Feature for Rect<U> {
    fn name(&self) -> &'static str {
        "rect"
    }

    fn edge_union(&self) -> Option<MultiPolygon<f64>> {
        Some(self.rect.clone().to_polygon().into())
    }

    fn translate(&mut self, v: Coordinate<f64>) {
        use geo::algorithm::translate::Translate;
        self.rect.translate_inplace(v.x, v.y);
        self.inner.translate(v);
    }

    fn interior(&self) -> Vec<super::InnerAtom> {
        self.inner.atoms()
    }
}