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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
Copyright 2017 Martin Buck
This file is part of rust-3d.
rust-3d is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
rust-3d is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with rust-3d.  If not, see <http://www.gnu.org/licenses/>.
*/

//! BoundingBox2D, an axis aligned bounding box within 2D space

use prelude::*;

#[derive (Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
/// BoundingBox2D, an axis aligned bounding box within 2D space
pub struct BoundingBox2D {
    min: Point2D,
    max: Point2D
}

impl BoundingBox2D {
    /// Creates a new BoundingBox2D with the given min and max positions
    pub fn new<P1, P2>(min: &P1, max: &P2) -> Result<BoundingBox2D> where
        P1: Is2D,
        P2: Is2D {

        if min.x() == max.x() || min.y() == max.y() {
            Err(ErrorKind::MinMaxEqual)
        } else if min.x() > max.x() || min.y() > max.y() {
            Err(ErrorKind::MinMaxSwapped)
        } else {
            Ok(BoundingBox2D{min: Point2D{x: min.x(), y: min.y()}, max: Point2D{x: max.x(), y: max.y()}})
        }
    }
    /// Creates a new BoundBox2D which contains all the given positions
    pub fn from_iterator<'a, It2D,P>(source: It2D) -> Result<BoundingBox2D> where
        It2D: IntoIterator<Item=&'a Box<P>>,
        P: 'a + Is2D + Sized {

        let mut count = 0;

        let mut minx : f64 = 0.0;
        let mut miny : f64 = 0.0;
        let mut maxx : f64 = 0.0;
        let mut maxy : f64 = 0.0;

        for p in source {
            if count == 0 {
                minx = p.x();
                miny = p.y();
                maxx = p.x();
                maxy = p.y();
                count += 1;
                continue;
            }
            if p.x() < minx { minx = p.x(); }
            if p.y() < miny { miny = p.y(); }
            if p.x() > maxx { maxx = p.x(); }
            if p.y() > maxy { maxy = p.y(); }
            count += 1;
        }
        if count >= 2 {
            Self::new(&Point2D{x: minx, y: miny}, &Point2D{x: maxx, y: maxy})
        } else {
            Err(ErrorKind::TooFewPoints)
        }
    }
    /// Returns the minimum position of the bounding box
    pub fn min(&self) -> Point2D {
        self.min.clone()
    }
    /// Returns the maximum position of the bounding box
    pub fn max(&self) -> Point2D {
        self.max.clone()
    }
    /// Returns the size the bounding box within the x-dimension
    pub fn size_x(&self) -> Positive {
        Positive::new((self.max.x() - self.min.x()).abs()).unwrap() //safe since constrain enforced on construction
    }
    /// Returns the size the bounding box within the y-dimension
    pub fn size_y(&self) -> Positive {
        Positive::new((self.max.y() - self.min.y()).abs()).unwrap() //safe since constrain enforced on construction
    }
    /// Returns the center of the bounding box
    pub fn center_bb(&self) -> Point2D {
        Point2D{x: self.min.x() + (self.max.x() - self.min.x()) / 2.0,
                y: self.min.y() + (self.max.y() - self.min.y()) / 2.0}
    }
    /// Tests whether this bounding box is within the other
    pub fn is_inside(&self, other: &BoundingBox2D) -> bool {
           self.min.x() > other.min.x()
        && self.min.y() > other.min.y()
        && self.max.x() < other.max.x()
        && self.max.y() < other.max.y()
    }
    /// Tests whether this bounding box contains a position
    pub fn contains<P>(&self, other: &P) -> bool where
        Self: Sized, P: Is2D {

           other.x() > self.min.x()
        && other.x() < self.max.x()
        && other.y() > self.min.y()
        && other.y() < self.max.y()
    }
    /// Tests whether this bounding box contains the other
    pub fn has_inside(&self, other: &BoundingBox2D) -> bool {
           self.min.x() < other.min.x()
        && self.min.y() < other.min.y()
        && self.max.x() > other.max.x()
        && self.max.y() > other.max.y()
    }
    /// Tests whether this bounding box and the other overlap in any way
    pub fn collides_with(&self, other: &BoundingBox2D) -> bool {
           2.0 * self.center_bb().x - other.center_bb().x < ((self.size_x() + other.size_x()).get())
        && 2.0 * self.center_bb().y - other.center_bb().y < ((self.size_y() + other.size_y()).get())
    }
}

impl HasBoundingBox2D for BoundingBox2D {
    fn bounding_box(&self) -> Result<BoundingBox2D> {
        BoundingBox2D::new(&self.min, &self.max)
    }
}