1 /********************************************************************************
2 * *
3 * D o u b l e - P r e c i s i o n R a n g e C l a s s *
4 * *
5 *********************************************************************************
6 * Copyright (C) 2004,2020 by Jeroen van der Zijp. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU Lesser General Public License as published by *
10 * the Free Software Foundation; either version 3 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This library is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public License *
19 * along with this program. If not, see <http://www.gnu.org/licenses/> *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "FXArray.h"
26 #include "FXHash.h"
27 #include "FXStream.h"
28 #include "FXVec2d.h"
29 #include "FXVec3d.h"
30 #include "FXVec4d.h"
31 #include "FXMat4d.h"
32 #include "FXSphered.h"
33 #include "FXRanged.h"
34
35 /*
36 Notes:
37 - Serializes in the same order old FXRange.
38 */
39
40
41 using namespace FX;
42
43 /************************** R a n g e C l a s s *************************/
44
45 namespace FX {
46
47 // Initialize from bounding sphere
FXRanged(const FXSphered & sphere)48 FXRanged::FXRanged(const FXSphered& sphere):
49 lower(sphere.center.x-sphere.radius,sphere.center.y-sphere.radius,sphere.center.z-sphere.radius),
50 upper(sphere.center.x+sphere.radius,sphere.center.y+sphere.radius,sphere.center.z+sphere.radius){
51 }
52
53
54 // Longest side
longest() const55 FXdouble FXRanged::longest() const {
56 FXdouble x=upper.x-lower.x;
57 FXdouble y=upper.y-lower.y;
58 FXdouble z=upper.z-lower.z;
59 return Math::fmax(Math::fmax(x,y),z);
60 }
61
62
63 // Shortest side
shortest() const64 FXdouble FXRanged::shortest() const {
65 FXdouble x=upper.x-lower.x;
66 FXdouble y=upper.y-lower.y;
67 FXdouble z=upper.z-lower.z;
68 return Math::fmin(Math::fmin(x,y),z);
69 }
70
71
72 // Length of diagonal
diameter() const73 FXdouble FXRanged::diameter() const {
74 FXdouble x=upper.x-lower.x;
75 FXdouble y=upper.y-lower.y;
76 FXdouble z=upper.z-lower.z;
77 return Math::sqrt(x*x+y*y+z*z);
78 }
79
80
81 // Get radius of box
radius() const82 FXdouble FXRanged::radius() const {
83 return diameter()*0.5;
84 }
85
86
87 // Get diagonal of box
diagonal() const88 FXVec3d FXRanged::diagonal() const {
89 return upper-lower;
90 }
91
92
93 // Get center of box
center() const94 FXVec3d FXRanged::center() const {
95 return 0.5*(upper+lower);
96 }
97
98
99 // Test if empty
empty() const100 FXbool FXRanged::empty() const {
101 return upper.x<lower.x || upper.y<lower.y || upper.z<lower.z;
102 }
103
104
105 // Test if box contains point x,y,z
contains(FXdouble x,FXdouble y,FXdouble z) const106 FXbool FXRanged::contains(FXdouble x,FXdouble y,FXdouble z) const {
107 return lower.x<=x && x<=upper.x && lower.y<=y && y<=upper.y && lower.z<=z && z<=upper.z;
108 }
109
110
111 // Test if box contains point p
contains(const FXVec3d & p) const112 FXbool FXRanged::contains(const FXVec3d& p) const {
113 return lower.x<=p.x && p.x<=upper.x && lower.y<=p.y && p.y<=upper.y && lower.z<=p.z && p.z<=upper.z;
114 }
115
116
117 // Test if box contains another box
contains(const FXRanged & bounds) const118 FXbool FXRanged::contains(const FXRanged& bounds) const {
119 return lower.x<=bounds.lower.x && bounds.upper.x<=upper.x && lower.y<=bounds.lower.y && bounds.upper.y<=upper.y && lower.z<=bounds.lower.z && bounds.upper.z<=upper.z;
120 }
121
122
123 // Test if box contains sphere
contains(const FXSphered & sphere) const124 FXbool FXRanged::contains(const FXSphered& sphere) const {
125 return lower.x<=sphere.center.x-sphere.radius && sphere.center.x+sphere.radius<=upper.x && lower.y<=sphere.center.y-sphere.radius && sphere.center.y+sphere.radius<=upper.y && lower.z<=sphere.center.z-sphere.radius && sphere.center.z+sphere.radius<=upper.z;
126 }
127
128
129 // Include point into range
include(FXdouble x,FXdouble y,FXdouble z)130 FXRanged& FXRanged::include(FXdouble x,FXdouble y,FXdouble z){
131 lower.x=Math::fmin(x,lower.x);
132 lower.y=Math::fmin(y,lower.y);
133 lower.z=Math::fmin(z,lower.z);
134 upper.x=Math::fmax(x,upper.x);
135 upper.y=Math::fmax(y,upper.y);
136 upper.z=Math::fmax(z,upper.z);
137 return *this;
138 }
139
140
141 // Include point into range
include(const FXVec3d & v)142 FXRanged& FXRanged::include(const FXVec3d& v){
143 return include(v.x,v.y,v.z);
144 }
145
146
147 // Include given box into box's range
include(const FXRanged & box)148 FXRanged& FXRanged::include(const FXRanged& box){
149 lower.x=Math::fmin(box.lower.x,lower.x);
150 lower.y=Math::fmin(box.lower.y,lower.y);
151 lower.z=Math::fmin(box.lower.z,lower.z);
152 upper.x=Math::fmax(box.upper.x,upper.x);
153 upper.y=Math::fmax(box.upper.y,upper.y);
154 upper.z=Math::fmax(box.upper.z,upper.z);
155 return *this;
156 }
157
158
159 // Include given sphere into this box
include(const FXSphered & sphere)160 FXRanged& FXRanged::include(const FXSphered& sphere){
161 FXVec3d lo(sphere.center.x-sphere.radius,sphere.center.y-sphere.radius,sphere.center.z-sphere.radius);
162 FXVec3d hi(sphere.center.x+sphere.radius,sphere.center.y+sphere.radius,sphere.center.z+sphere.radius);
163 lower.x=Math::fmin(lo.x,lower.x);
164 lower.y=Math::fmin(lo.y,lower.y);
165 lower.z=Math::fmin(lo.z,lower.z);
166 upper.x=Math::fmax(hi.x,upper.x);
167 upper.y=Math::fmax(hi.y,upper.y);
168 upper.z=Math::fmax(hi.z,upper.z);
169 return *this;
170 }
171
172
173 // Test if overlap
overlap(const FXRanged & a,const FXRanged & b)174 FXbool overlap(const FXRanged& a,const FXRanged& b){
175 return a.upper.x>=b.lower.x && a.lower.x<=b.upper.x && a.upper.y>=b.lower.y && a.lower.y<=b.upper.y && a.upper.z>=b.lower.z && a.lower.z<=b.upper.z;
176 }
177
178
179 // Union of two boxes
unite(const FXRanged & a,const FXRanged & b)180 FXRanged unite(const FXRanged& a,const FXRanged& b){
181 return FXRanged(lo(a.lower,b.lower),hi(a.upper,b.upper));
182 }
183
184
185 // Intersection of two boxes
intersect(const FXRanged & a,const FXRanged & b)186 FXRanged intersect(const FXRanged& a,const FXRanged& b){
187 return FXRanged(hi(a.lower,b.lower),lo(a.upper,b.upper));
188 }
189
190
191 // Intersect box with normalized plane ax+by+cz+w; returns -1,0,+1
intersect(const FXVec4d & plane) const192 FXint FXRanged::intersect(const FXVec4d& plane) const {
193 FXVec3d lo;
194 FXVec3d hi;
195
196 // Diagonal
197 if(plane.x>0.0){
198 lo.x=lower.x;
199 hi.x=upper.x;
200 }
201 else{
202 lo.x=upper.x;
203 hi.x=lower.x;
204 }
205
206 if(plane.y>0.0){
207 lo.y=lower.y;
208 hi.y=upper.y;
209 }
210 else{
211 lo.y=upper.y;
212 hi.y=lower.y;
213 }
214
215 if(plane.z>0.0){
216 lo.z=lower.z;
217 hi.z=upper.z;
218 }
219 else{
220 lo.z=upper.z;
221 hi.z=lower.z;
222 }
223
224 // Lower point on positive side of plane
225 if(plane.x*lo.x+plane.y*lo.y+plane.z*lo.z+plane.w>=0.0) return 1;
226
227 // Upper point on negative side of plane
228 if(plane.x*hi.x+plane.y*hi.y+plane.z*hi.z+plane.w<=0.0) return -1;
229
230 // Overlap
231 return 0;
232 }
233
234
235 // Intersect box with ray u-v
intersect(const FXVec3d & u,const FXVec3d & v) const236 FXbool FXRanged::intersect(const FXVec3d& u,const FXVec3d& v) const {
237 FXdouble hits[2];
238 return intersect(u,v-u,hits) && 0.0<=hits[1] && hits[0]<=1.0;
239 }
240
241
242 // Intersect box with ray p+lambda*d, returning true if hit
intersect(const FXVec3d & pos,const FXVec3d & dir,FXdouble hit[]) const243 FXbool FXRanged::intersect(const FXVec3d& pos,const FXVec3d& dir,FXdouble hit[]) const {
244 FXdouble f= DBL_MAX;
245 FXdouble n=-DBL_MAX;
246 FXdouble ni,fi;
247 if(__likely(dir.x!=0.0)){
248 if(0.0<dir.x){
249 ni=(lower.x-pos.x)/dir.x;
250 fi=(upper.x-pos.x)/dir.x;
251 }
252 else{
253 ni=(upper.x-pos.x)/dir.x;
254 fi=(lower.x-pos.x)/dir.x;
255 }
256 if(ni>n) n=ni;
257 if(fi<f) f=fi;
258 }
259 else{
260 if((pos.x<lower.x) || (pos.x>upper.x)) return false;
261 }
262 if(__likely(dir.y!=0.0)){
263 if(0.0<dir.y){
264 ni=(lower.y-pos.y)/dir.y;
265 fi=(upper.y-pos.y)/dir.y;
266 }
267 else{
268 ni=(upper.y-pos.y)/dir.y;
269 fi=(lower.y-pos.y)/dir.y;
270 }
271 if(ni>n) n=ni;
272 if(fi<f) f=fi;
273 if(n>f) return false;
274 }
275 else{
276 if((pos.y<lower.y) || (pos.y>upper.y)) return false;
277 }
278 if(__likely(dir.z!=0.0)){
279 if(0.0<dir.z){
280 ni=(lower.z-pos.z)/dir.z;
281 fi=(upper.z-pos.z)/dir.z;
282 }
283 else{
284 ni=(upper.z-pos.z)/dir.z;
285 fi=(lower.z-pos.z)/dir.z;
286 }
287 if(ni>n) n=ni;
288 if(fi<f) f=fi;
289 if(n>f) return false;
290 }
291 else{
292 if((pos.z<lower.z) || (pos.z>upper.z)) return false;
293 }
294 hit[0]=n;
295 hit[1]=f;
296 return true;
297 }
298
299
300 // Transform range by 4x4 matrix
transform(const FXMat4d & mat) const301 FXRanged FXRanged::transform(const FXMat4d& mat) const {
302 FXRanged result(corner(0)*mat);
303 result.include(corner(1)*mat);
304 result.include(corner(2)*mat);
305 result.include(corner(3)*mat);
306 result.include(corner(4)*mat);
307 result.include(corner(5)*mat);
308 result.include(corner(6)*mat);
309 result.include(corner(7)*mat);
310 return result;
311 }
312
313
314 // Saving
operator <<(FXStream & store,const FXRanged & bounds)315 FXStream& operator<<(FXStream& store,const FXRanged& bounds){
316 store << bounds.lower.x << bounds.upper.x;
317 store << bounds.lower.y << bounds.upper.y;
318 store << bounds.lower.z << bounds.upper.z;
319 return store;
320 }
321
322
323 // Loading
operator >>(FXStream & store,FXRanged & bounds)324 FXStream& operator>>(FXStream& store,FXRanged& bounds){
325 store >> bounds.lower.x >> bounds.upper.x;
326 store >> bounds.lower.y >> bounds.upper.y;
327 store >> bounds.lower.z >> bounds.upper.z;
328 return store;
329 }
330
331 }
332
333