1 // Copyright (C) 2012-2019 The VPaint Developers.
2 // See the COPYRIGHT file at the top-level directory of this distribution
3 // and at https://github.com/dalboris/vpaint/blob/master/COPYRIGHT
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #include "BoundingBox.h"
18 
19 #include <limits>    // for infinity
20 #include <algorithm> // for min/max
21 #include <cmath>     // for abs and isnan
22 
23 namespace
24 {
25 
26 const double INF = std::numeric_limits<double>::infinity();
27 const double EPS = 1e-10;
28 
isnan_(double x)29 inline bool isnan_(double x)
30 {
31 #ifdef WIN32
32     return x != x;
33 #else
34     return std::isnan(x);
35 #endif
36 }
37 
inverted_(double min,double max)38 bool inverted_(double min, double max)
39 {
40     return min > max + EPS;
41 }
42 
distance_(double a,double b)43 double distance_(double a, double b)
44 {
45     if (a == b) return 0.0; // infinity case, avoid inf-inf = NaN
46     else        return std::abs(b-a);
47 }
48 
mid_(double min,double max)49 double mid_(double min, double max)
50 {
51     double res = 0.5*(min+max);
52 
53     // What did the above compute?
54     //   * if min and max finite:                          the correct finite   mid-value
55     //   * if min finite and max infinite (or vice-versa): the correct infinite mid-value
56     //   * if min and max infinite, same sign:             the correct infinite mid-value
57     //   * if min and max infinite, different signs:       NaN
58 
59     return isnan_(res) ? 0.0 : res;
60 }
61 
62 }
63 
64 namespace VectorAnimationComplex
65 {
66 
BoundingBox()67 BoundingBox::BoundingBox() :
68     xMin_(INF), xMax_(-INF),
69     yMin_(INF), yMax_(-INF)
70 {
71 }
72 
BoundingBox(double x,double y)73 BoundingBox::BoundingBox(double x, double y) :
74     xMin_(x), xMax_(x),
75     yMin_(y), yMax_(y)
76 {
77 }
78 
BoundingBox(double x1,double x2,double y1,double y2)79 BoundingBox::BoundingBox(double x1, double x2, double y1, double y2)
80 {
81     if (x1<x2) { xMin_ = x1; xMax_ = x2; }
82     else       { xMin_ = x2; xMax_ = x1; }
83 
84     if (y1<y2) { yMin_ = y1; yMax_ = y2; }
85     else       { yMin_ = y2; yMax_ = y1; }
86 }
87 
isEmpty() const88 bool BoundingBox::isEmpty() const
89 {
90     return inverted_(xMin_, xMax_);
91 }
92 
isDegenerate() const93 bool BoundingBox::isDegenerate() const
94 {
95     return height() <= EPS || width() <= EPS;
96 }
97 
isInfinite() const98 bool BoundingBox::isInfinite() const
99 {
100     return height() == INF || width() == INF;
101 }
102 
isProper() const103 bool BoundingBox::isProper() const
104 {
105     return !(isDegenerate() || isInfinite());
106 }
107 
xMid() const108 double BoundingBox::xMid() const
109 {
110     return mid_(xMin_, xMax_);
111 }
112 
yMid() const113 double BoundingBox::yMid() const
114 {
115     return mid_(yMin_, yMax_);
116 }
117 
width() const118 double BoundingBox::width() const
119 {
120     return isEmpty() ? 0.0 : distance_(xMin_, xMax_);
121 }
122 
height() const123 double BoundingBox::height() const
124 {
125     return isEmpty() ? 0.0 : distance_(yMin_, yMax_);
126 }
127 
area() const128 double BoundingBox::area() const
129 {
130     return isDegenerate() ? 0.0 : width() * height();
131 }
132 
united(const BoundingBox & other) const133 BoundingBox BoundingBox::united(const BoundingBox & other) const
134 {
135     BoundingBox res(*this);
136     res.unite(other);
137     return res;
138 }
139 
intersected(const BoundingBox & other) const140 BoundingBox BoundingBox::intersected(const BoundingBox & other) const
141 {
142     BoundingBox res(*this);
143     res.intersect(other);
144     return res;
145 }
146 
unite(const BoundingBox & other)147 void BoundingBox::unite(const BoundingBox & other)
148 {
149     xMin_ = std::min(xMin_, other.xMin_);
150     xMax_ = std::max(xMax_, other.xMax_);
151     yMin_ = std::min(yMin_, other.yMin_);
152     yMax_ = std::max(yMax_, other.yMax_);
153 }
154 
intersect(const BoundingBox & other)155 void BoundingBox::intersect(const BoundingBox & other)
156 {
157     // Compute intersection
158     xMin_ = std::max(xMin_, other.xMin_);
159     xMax_ = std::min(xMax_, other.xMax_);
160     yMin_ = std::max(yMin_, other.yMin_);
161     yMax_ = std::min(yMax_, other.yMax_);
162 
163     // Handle empty special case
164     if (inverted_(xMin_, xMax_) || inverted_(yMin_, yMax_))
165         *this = BoundingBox();
166 }
167 
intersects(const BoundingBox & other) const168 bool BoundingBox::intersects(const BoundingBox & other) const
169 {
170     return !intersected(other).isEmpty();
171 }
172 
operator ==(const BoundingBox & other) const173 bool BoundingBox::operator==(const BoundingBox & other) const
174 {
175     return distance_(xMin_, other.xMin_) < EPS &&
176            distance_(xMax_, other.xMax_) < EPS &&
177            distance_(yMin_, other.yMin_) < EPS &&
178            distance_(yMax_, other.yMax_) < EPS;
179 }
180 
operator !=(const BoundingBox & other) const181 bool BoundingBox::operator!=(const BoundingBox & other) const
182 {
183     return !(*this == other);
184 }
185 
186 }
187