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