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,2021 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