]> git.tdb.fi Git - libs/math.git/blob - source/geometry/hyperbox.h
c3bac57d97e3464d20535a66928106079777801a
[libs/math.git] / source / geometry / hyperbox.h
1 #ifndef MSP_GEOMETRY_HYPERBOX_H_
2 #define MSP_GEOMETRY_HYPERBOX_H_
3
4 #include <algorithm>
5 #include <cmath>
6 #include <stdexcept>
7 #include <msp/linal/vector.h>
8 #include "shape.h"
9
10 namespace Msp {
11 namespace Geometry {
12
13 /**
14 A shape bounded by planar faces at right angles to each other.  Two- and three-
15 dimensional cases are Rectangle and Box, respectively.
16 */
17 template<typename T, unsigned D>
18 class HyperBox: public Shape<T, D>
19 {
20 private:
21         LinAl::Vector<T, D> dimensions;
22
23 public:
24         HyperBox();
25         explicit HyperBox(const LinAl::Vector<T, D> &);
26
27         virtual HyperBox *clone() const;
28
29         const LinAl::Vector<T, D> &get_dimensions() const { return dimensions; }
30         T get_dimension(unsigned) const;
31
32         virtual BoundingBox<T, D> get_axis_aligned_bounding_box() const;
33         virtual bool contains(const LinAl::Vector<T, D> &) const;
34         virtual unsigned get_max_ray_intersections() const { return 2; }
35         virtual unsigned get_intersections(const Ray<T, D> &, SurfacePoint<T, D> *, unsigned) const;
36         virtual Coverage get_coverage(const BoundingBox<T, D> &) const;
37 };
38
39 template<typename T, unsigned D>
40 inline HyperBox<T, D>::HyperBox()
41 {
42         for(unsigned i=0; i<D; ++i)
43                 dimensions[i] = 1;
44 }
45
46 template<typename T, unsigned D>
47 inline HyperBox<T, D>::HyperBox(const LinAl::Vector<T, D> &d):
48         dimensions(d)
49 {
50         for(unsigned i=0; i<D; ++i)
51                 if(dimensions[i]<=T(0))
52                         throw std::invalid_argument("HyperBox::HyperBox");
53 }
54
55 template<typename T, unsigned D>
56 inline HyperBox<T, D> *HyperBox<T, D>::clone() const
57 {
58         return new HyperBox<T, D>(dimensions);
59 }
60
61 template<typename T, unsigned D>
62 inline T HyperBox<T, D>::get_dimension(unsigned i) const
63 {
64         return dimensions[i];
65 }
66
67 template<typename T, unsigned D>
68 inline BoundingBox<T, D> HyperBox<T, D>::get_axis_aligned_bounding_box() const
69 {
70         LinAl::Vector<T, D> half_dim = dimensions/T(2);
71         return BoundingBox<T, D>(-half_dim, half_dim);
72 }
73
74 template<typename T, unsigned D>
75 inline bool HyperBox<T, D>::contains(const LinAl::Vector<T, D> &point) const
76 {
77         using std::abs;
78
79         for(unsigned i=0; i<D; ++i)
80                 if(abs(point[i])>dimensions[i]/T(2))
81                         return false;
82         return true;
83 }
84
85 template<typename T, unsigned D>
86 inline unsigned HyperBox<T, D>::get_intersections(const Ray<T, D> &ray, SurfacePoint<T, D> *points, unsigned size) const
87 {
88         using std::abs;
89
90         if(size>2)
91                 size = 2;
92
93         LinAl::Vector<T, D> half_dim = dimensions/T(2);
94         unsigned n = 0;
95         for(unsigned i=0; (n<size && i<D); ++i)
96         {
97                 if(!ray.get_direction()[i])
98                         continue;
99
100                 for(int j=-1; (n<size && j<=1); j+=2)
101                 {
102                         T x = (T(j)*half_dim[i]-ray.get_start()[i])/ray.get_direction()[i];
103                         if(!ray.check_limits(x))
104                                 continue;
105
106                         LinAl::Vector<T, D> p = ray.get_start()+ray.get_direction()*x;
107
108                         bool inside = true;
109                         for(unsigned k=0; (inside && k<D); ++k)
110                                 inside = (k==i || abs(p[k])<=half_dim[k]);
111
112                         if(inside)
113                         {
114                                 if(points)
115                                 {
116                                         bool entry = (T(j)*ray.get_direction()[i]<T(0));
117                                         unsigned k = 0;
118                                         if(n>0 && entry!=points[0].entry)
119                                         {
120                                                 if(entry)
121                                                         points[1] = points[0];
122                                                 else
123                                                         ++k;
124                                         }
125                                         if(k<n && entry==points[k].entry)
126                                                 --n;
127
128                                         points[k].position = p;
129                                         points[k].normal = LinAl::Vector<T, D>();
130                                         points[k].normal[i] = j;
131                                         points[k].distance = x;
132                                         points[k].entry = (T(j)*ray.get_direction()[i]<T(0));
133                                 }
134
135                                 ++n;
136                         }
137                 }
138         }
139
140         return n;
141 }
142
143 template<typename T, unsigned D>
144 inline Coverage HyperBox<T, D>::get_coverage(const BoundingBox<T, D> &bbox) const
145 {
146         const LinAl::Vector<T, D> &min_pt = bbox.get_minimum_point();
147         const LinAl::Vector<T, D> &max_pt = bbox.get_maximum_point();
148         LinAl::Vector<T, D> half_dim = dimensions/T(2);
149
150         Coverage coverage = FULL_COVERAGE;
151         for(unsigned i=0; i<D; ++i)
152         {
153                 if(max_pt[i]<-half_dim[i] || min_pt[i]>half_dim[i])
154                         return NO_COVERAGE;
155                 if(min_pt[i]<-half_dim[i] || max_pt[i]>half_dim[i])
156                         coverage = PARTIAL_COVERAGE;
157         }
158
159         return coverage;
160 }
161
162 } // namespace Geometry
163 } // namespace Msp
164
165 #endif