]> git.tdb.fi Git - libs/math.git/blob - source/geometry/hyperbox.h
9a161f35fb0fdbc5ff604735d51eb6e1d060eeaf
[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 "ray.h"
9 #include "shape.h"
10 #include "surfacepoint.h"
11
12 namespace Msp {
13 namespace Geometry {
14
15 /**
16 A shape bounded by planar faces at right angles to each other.  Two- and three-
17 dimensional cases are Rectangle and Box, respectively.
18 */
19 template<typename T, unsigned D>
20 class HyperBox: public Shape<T, D>
21 {
22 private:
23         LinAl::Vector<T, D> dimensions;
24
25 public:
26         HyperBox();
27         explicit HyperBox(const LinAl::Vector<T, D> &);
28
29         virtual HyperBox *clone() const;
30
31         const LinAl::Vector<T, D> &get_dimensions() const { return dimensions; }
32         T get_dimension(unsigned) const;
33
34         virtual HyperBox<T, D> get_axis_aligned_bounding_box() const { return *this; }
35         virtual bool contains(const LinAl::Vector<T, D> &) const;
36         virtual bool check_intersection(const Ray<T, D> &) const;
37         virtual unsigned get_max_ray_intersections() const { return 2; }
38         virtual unsigned get_intersections(const Ray<T, D> &, SurfacePoint<T, D> *, unsigned) const;
39 };
40
41 template<typename T, unsigned D>
42 inline HyperBox<T, D>::HyperBox()
43 {
44         for(unsigned i=0; i<D; ++i)
45                 dimensions[i] = 1;
46 }
47
48 template<typename T, unsigned D>
49 inline HyperBox<T, D>::HyperBox(const LinAl::Vector<T, D> &d):
50         dimensions(d)
51 {
52         for(unsigned i=0; i<D; ++i)
53                 if(dimensions[i]<=T(0))
54                         throw std::invalid_argument("HyperBox::HyperBox");
55 }
56
57 template<typename T, unsigned D>
58 inline HyperBox<T, D> *HyperBox<T, D>::clone() const
59 {
60         return new HyperBox<T, D>(dimensions);
61 }
62
63 template<typename T, unsigned D>
64 inline T HyperBox<T, D>::get_dimension(unsigned i) const
65 {
66         return dimensions[i];
67 }
68
69 template<typename T, unsigned D>
70 inline bool HyperBox<T, D>::contains(const LinAl::Vector<T, D> &point) const
71 {
72         using std::abs;
73
74         for(unsigned i=0; i<D; ++i)
75                 if(abs(point[i])>dimensions[i]/T(2))
76                         return false;
77         return true;
78 }
79
80 template<typename T, unsigned D>
81 inline bool HyperBox<T, D>::check_intersection(const Ray<T, D> &ray) const
82 {
83         return get_intersections(ray, 0, 1);
84 }
85
86 template<typename T, unsigned D>
87 inline unsigned HyperBox<T, D>::get_intersections(const Ray<T, D> &ray, SurfacePoint<T, D> *points, unsigned size) const
88 {
89         using std::abs;
90
91         if(size>2)
92                 size = 2;
93
94         LinAl::Vector<T, D> half_dim = dimensions/T(2);
95         unsigned n = 0;
96         for(unsigned i=0; (n<size && i<D); ++i)
97         {
98                 if(!ray.get_direction()[i])
99                         continue;
100
101                 for(int j=-1; (n<size && j<=1); j+=2)
102                 {
103                         T x = (T(j)*half_dim[i]-ray.get_start()[i])/ray.get_direction()[i];
104                         if(!ray.check_limits(x))
105                                 continue;
106
107                         LinAl::Vector<T, D> p = ray.get_start()+ray.get_direction()*x;
108
109                         bool inside = true;
110                         for(unsigned k=0; (inside && k<D); ++k)
111                                 inside = (k==i || abs(p[k])<=half_dim[k]);
112
113                         if(inside)
114                         {
115                                 if(points)
116                                 {
117                                         points[n].position = p;
118                                         points[n].normal = LinAl::Vector<T, D>();
119                                         points[n].normal[i] = j;
120                                         points[n].distance = x;
121
122                                         if(n==1 && x<points[0].distance)
123                                                 std::swap(points[0], points[1]);
124                                 }
125
126                                 ++n;
127                         }
128                 }
129         }
130
131         return n;
132 }
133
134 } // namespace Geometry
135 } // namespace Msp
136
137 #endif