]> git.tdb.fi Git - libs/math.git/blob - source/geometry/hyperbox.h
546ad0ec3984c5b338978c23b37f0c8ea5f97533
[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 };
37
38 template<typename T, unsigned D>
39 inline HyperBox<T, D>::HyperBox()
40 {
41         for(unsigned i=0; i<D; ++i)
42                 dimensions[i] = 1;
43 }
44
45 template<typename T, unsigned D>
46 inline HyperBox<T, D>::HyperBox(const LinAl::Vector<T, D> &d):
47         dimensions(d)
48 {
49         for(unsigned i=0; i<D; ++i)
50                 if(dimensions[i]<=T(0))
51                         throw std::invalid_argument("HyperBox::HyperBox");
52 }
53
54 template<typename T, unsigned D>
55 inline HyperBox<T, D> *HyperBox<T, D>::clone() const
56 {
57         return new HyperBox<T, D>(dimensions);
58 }
59
60 template<typename T, unsigned D>
61 inline T HyperBox<T, D>::get_dimension(unsigned i) const
62 {
63         return dimensions[i];
64 }
65
66 template<typename T, unsigned D>
67 inline BoundingBox<T, D> HyperBox<T, D>::get_axis_aligned_bounding_box() const
68 {
69         LinAl::Vector<T, D> half_dim = dimensions/T(2);
70         return BoundingBox<T, D>(-half_dim, half_dim);
71 }
72
73 template<typename T, unsigned D>
74 inline bool HyperBox<T, D>::contains(const LinAl::Vector<T, D> &point) const
75 {
76         using std::abs;
77
78         for(unsigned i=0; i<D; ++i)
79                 if(abs(point[i])>dimensions[i]/T(2))
80                         return false;
81         return true;
82 }
83
84 template<typename T, unsigned D>
85 inline unsigned HyperBox<T, D>::get_intersections(const Ray<T, D> &ray, SurfacePoint<T, D> *points, unsigned size) const
86 {
87         using std::abs;
88
89         if(size>2)
90                 size = 2;
91
92         LinAl::Vector<T, D> half_dim = dimensions/T(2);
93         unsigned n = 0;
94         for(unsigned i=0; (n<size && i<D); ++i)
95         {
96                 if(!ray.get_direction()[i])
97                         continue;
98
99                 for(int j=-1; (n<size && j<=1); j+=2)
100                 {
101                         T x = (T(j)*half_dim[i]-ray.get_start()[i])/ray.get_direction()[i];
102                         if(!ray.check_limits(x))
103                                 continue;
104
105                         LinAl::Vector<T, D> p = ray.get_start()+ray.get_direction()*x;
106
107                         bool inside = true;
108                         for(unsigned k=0; (inside && k<D); ++k)
109                                 inside = (k==i || abs(p[k])<=half_dim[k]);
110
111                         if(inside)
112                         {
113                                 if(points)
114                                 {
115                                         points[n].position = p;
116                                         points[n].normal = LinAl::Vector<T, D>();
117                                         points[n].normal[i] = j;
118                                         points[n].distance = x;
119                                         points[n].entry = (T(j)*ray.get_direction()[i]<T(0));
120
121                                         if(n==1 && x<points[0].distance)
122                                                 std::swap(points[0], points[1]);
123                                 }
124
125                                 ++n;
126                         }
127                 }
128         }
129
130         return n;
131 }
132
133 } // namespace Geometry
134 } // namespace Msp
135
136 #endif