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