From 09cc3a8648dd20e9a07d669b353c4a120b67c1c4 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 22 May 2013 20:34:01 +0300 Subject: [PATCH] Implement bounding boxes with a separate class This solves several problems with them, including positioning off the origin and bounding boxes of negated shapes. --- source/geometry/boundingbox.h | 146 +++++++++++++++++++++++++++++ source/geometry/compositeshape.h | 7 +- source/geometry/extrudedshape.h | 10 +- source/geometry/halfspace.h | 9 +- source/geometry/hyperbox.h | 10 +- source/geometry/hypersphere.h | 12 +-- source/geometry/intersection.h | 12 +-- source/geometry/negation.h | 8 +- source/geometry/shape.h | 4 +- source/geometry/transformedshape.h | 27 +++++- source/geometry/union.h | 12 +-- 11 files changed, 207 insertions(+), 50 deletions(-) create mode 100644 source/geometry/boundingbox.h diff --git a/source/geometry/boundingbox.h b/source/geometry/boundingbox.h new file mode 100644 index 0000000..120eedb --- /dev/null +++ b/source/geometry/boundingbox.h @@ -0,0 +1,146 @@ +#ifndef MSP_GEOMETRY_BOUNDINGBOX_H_ +#define MSP_GEOMETRY_BOUNDINGBOX_H_ + +#include +#include +#include + +namespace Msp { +namespace Geometry { + +template +class BoundingBox +{ +private: + LinAl::Vector min_pt; + LinAl::Vector max_pt; + bool empty; + bool negated; + +public: + BoundingBox(); + BoundingBox(const LinAl::Vector &, const LinAl::Vector &); + + static BoundingBox negate(const BoundingBox &); + + const LinAl::Vector &get_minimum_point() const { return min_pt; } + T get_minimum_coordinate(unsigned i) const { return min_pt[i]; } + const LinAl::Vector &get_maximum_point() const { return max_pt; } + T get_maximum_coordinate(unsigned i) const { return max_pt[i]; } + bool is_empty() const { return empty; } + bool is_negated() const { return negated; } +}; + +template +inline BoundingBox::BoundingBox(): + empty(true), + negated(false) +{ } + +template +inline BoundingBox::BoundingBox(const LinAl::Vector &n, const LinAl::Vector &x): + min_pt(n), + max_pt(x), + empty(false), + negated(false) +{ + for(unsigned i=0; imax_pt[i]) + throw std::invalid_argument("BoundingBox::BoundingBox"); +} + +template +inline BoundingBox BoundingBox::negate(const BoundingBox &bb) +{ + BoundingBox result = bb; + result.negated = !bb.negated; + return result; +} + +template +inline BoundingBox operator&(const BoundingBox &bb1, const BoundingBox &bb2) +{ + if(bb1.is_empty() || bb2.is_empty()) + return BoundingBox(); + + if(bb1.is_negated()) + { + if(bb2.is_negated()) + return ~((~bb1)|(~bb2)); + else + return bb2&bb1; + } + + LinAl::Vector result_min; + LinAl::Vector result_max; + + if(bb2.is_negated()) + { + // This is effectively subtraction of (non-negated) bb2 from bb1 + int uncovered_axis = -1; + for(unsigned i=0; ibb2.get_maximum_coordinate(i)) + { + if(uncovered_axis!=-1) + return bb1; + uncovered_axis = i; + } + + if(uncovered_axis==-1) + return BoundingBox(); + + result_min = bb1.get_minimum_point(); + result_max = bb1.get_maximum_point(); + if(bb2.get_minimum_coordinate(uncovered_axis)result_max[i]) + return BoundingBox(); + } + } + + return BoundingBox(result_min, result_max); +} + +template +inline BoundingBox operator|(const BoundingBox &bb1, const BoundingBox &bb2) +{ + if(bb1.is_empty()) + return bb2; + if(bb2.is_empty()) + return bb1; + + if(bb1.is_negated()) + return ~((~bb1)&(~bb2)); + else if(bb2.is_negated()) + return ~((~bb2)&(~bb1)); + + LinAl::Vector result_min; + LinAl::Vector result_max; + for(unsigned i=0; i(result_min, result_max); +} + +template +inline BoundingBox operator~(const BoundingBox &bb) +{ + return BoundingBox::negate(bb); +} + +} // namespace Geometry +} // namespace Msp + +#endif diff --git a/source/geometry/compositeshape.h b/source/geometry/compositeshape.h index 50075d9..4c640be 100644 --- a/source/geometry/compositeshape.h +++ b/source/geometry/compositeshape.h @@ -3,6 +3,7 @@ #include #include +#include "boundingbox.h" #include "shape.h" namespace Msp { @@ -29,7 +30,7 @@ protected: public: virtual ~CompositeShape(); - virtual HyperBox get_axis_aligned_bounding_box() const; + virtual BoundingBox get_axis_aligned_bounding_box() const; virtual bool contains(const LinAl::Vector &) const; virtual unsigned get_max_ray_intersections() const; virtual unsigned get_intersections(const Ray &, SurfacePoint *, unsigned) const; @@ -70,9 +71,9 @@ inline CompositeShape::~CompositeShape() } template -inline HyperBox CompositeShape::get_axis_aligned_bounding_box() const +inline BoundingBox CompositeShape::get_axis_aligned_bounding_box() const { - HyperBox aabb; + BoundingBox aabb; for(typename ShapeArray::const_iterator i=shapes.begin(); i!=shapes.end(); ++i) { if(i==shapes.begin()) diff --git a/source/geometry/extrudedshape.h b/source/geometry/extrudedshape.h index 83933b0..de582e3 100644 --- a/source/geometry/extrudedshape.h +++ b/source/geometry/extrudedshape.h @@ -31,7 +31,7 @@ public: const Shape &get_base() const { return *base; } T get_length() const { return length; } - virtual HyperBox get_axis_aligned_bounding_box() const; + virtual BoundingBox get_axis_aligned_bounding_box() const; virtual bool contains(const LinAl::Vector &) const; virtual unsigned get_max_ray_intersections() const; virtual unsigned get_intersections(const Ray &, SurfacePoint *, unsigned) const; @@ -74,10 +74,12 @@ inline ExtrudedShape *ExtrudedShape::clone() const } template -inline HyperBox ExtrudedShape::get_axis_aligned_bounding_box() const +inline BoundingBox ExtrudedShape::get_axis_aligned_bounding_box() const { - HyperBox base_bbox = base->get_axis_aligned_bounding_box(); - return HyperBox(LinAl::Vector(base_bbox.get_dimensions(), length)); + BoundingBox base_bbox = base->get_axis_aligned_bounding_box(); + T half_length = length/T(2); + return BoundingBox(LinAl::Vector(base_bbox.get_minimum_point(), -half_length), + LinAl::Vector(base_bbox.get_maximum_point(), half_length)); } template diff --git a/source/geometry/halfspace.h b/source/geometry/halfspace.h index 9ed80ff..60f0403 100644 --- a/source/geometry/halfspace.h +++ b/source/geometry/halfspace.h @@ -1,6 +1,7 @@ #ifndef MSP_GEOMETRY_HALFSPACE_H_ #define MSP_GEOMETRY_HALFSPACE_H_ +#include "boundingbox.h" #include "shape.h" namespace Msp { @@ -24,7 +25,7 @@ public: const LinAl::Vector &get_normal() const { return normal; } - virtual HyperBox get_axis_aligned_bounding_box() const; + virtual BoundingBox get_axis_aligned_bounding_box() const; virtual bool contains(const LinAl::Vector &) const; virtual unsigned get_max_ray_intersections() const { return 1; } virtual unsigned get_intersections(const Ray &, SurfacePoint *, unsigned) const; @@ -48,10 +49,10 @@ inline HalfSpace *HalfSpace::clone() const } template -inline HyperBox HalfSpace::get_axis_aligned_bounding_box() const +inline BoundingBox HalfSpace::get_axis_aligned_bounding_box() const { - // XXX Implement this properly - return HyperBox(); + // XXX If the normal is aligned to an axis, should the bounding box reflect that? + return ~BoundingBox(); } template diff --git a/source/geometry/hyperbox.h b/source/geometry/hyperbox.h index 11c197e..da50091 100644 --- a/source/geometry/hyperbox.h +++ b/source/geometry/hyperbox.h @@ -5,6 +5,7 @@ #include #include #include +#include "boundingbox.h" #include "ray.h" #include "shape.h" #include "surfacepoint.h" @@ -31,7 +32,7 @@ public: const LinAl::Vector &get_dimensions() const { return dimensions; } T get_dimension(unsigned) const; - virtual HyperBox get_axis_aligned_bounding_box() const { return *this; } + virtual BoundingBox get_axis_aligned_bounding_box() const; virtual bool contains(const LinAl::Vector &) const; virtual unsigned get_max_ray_intersections() const { return 2; } virtual unsigned get_intersections(const Ray &, SurfacePoint *, unsigned) const; @@ -65,6 +66,13 @@ inline T HyperBox::get_dimension(unsigned i) const return dimensions[i]; } +template +inline BoundingBox HyperBox::get_axis_aligned_bounding_box() const +{ + LinAl::Vector half_dim = dimensions/T(2); + return BoundingBox(-half_dim, half_dim); +} + template inline bool HyperBox::contains(const LinAl::Vector &point) const { diff --git a/source/geometry/hypersphere.h b/source/geometry/hypersphere.h index abdd193..3c04125 100644 --- a/source/geometry/hypersphere.h +++ b/source/geometry/hypersphere.h @@ -4,7 +4,7 @@ #include #include #include -#include "hyperbox.h" +#include "boundingbox.h" #include "ray.h" #include "shape.h" #include "surfacepoint.h" @@ -30,7 +30,7 @@ public: T get_radius() const { return radius; } - virtual HyperBox get_axis_aligned_bounding_box() const; + virtual BoundingBox get_axis_aligned_bounding_box() const; virtual bool contains(const LinAl::Vector &) const; virtual unsigned get_max_ray_intersections() const { return 2; } virtual unsigned get_intersections(const Ray &, SurfacePoint *, unsigned) const; @@ -51,12 +51,12 @@ inline HyperSphere *HyperSphere::clone() const } template -inline HyperBox HyperSphere::get_axis_aligned_bounding_box() const +inline BoundingBox HyperSphere::get_axis_aligned_bounding_box() const { - LinAl::Vector dimensions; + LinAl::Vector extent; for(unsigned i=0; i(dimensions); + extent[i] = radius; + return BoundingBox(-extent, extent); } template diff --git a/source/geometry/intersection.h b/source/geometry/intersection.h index c20aae1..01707cd 100644 --- a/source/geometry/intersection.h +++ b/source/geometry/intersection.h @@ -12,7 +12,7 @@ Forms a shape from the common parts of component shapes. template struct IntersectionOps { - static HyperBox combine_aabb(const HyperBox &, const HyperBox &); + static BoundingBox combine_aabb(const BoundingBox &a, const BoundingBox &b) { return a&b; } static bool init_inside() { return true; } static bool combine_inside(bool a, bool b) { return a && b; } static bool is_inside_decided(bool a) { return !a; } @@ -54,16 +54,6 @@ inline Intersection *Intersection::clone() const return new Intersection(*this); } - -template -inline HyperBox IntersectionOps::combine_aabb(const HyperBox &box1, const HyperBox &box2) -{ - LinAl::Vector dimensions; - for(unsigned i=0; i(dimensions); -} - } // namespace Geometry } // namespace Msp diff --git a/source/geometry/negation.h b/source/geometry/negation.h index 6746aed..beb3e4e 100644 --- a/source/geometry/negation.h +++ b/source/geometry/negation.h @@ -1,6 +1,7 @@ #ifndef MSP_GEOMETRY_NEGATION_H_ #define MSP_GEOMETRY_NEGATION_H_ +#include "boundingbox.h" #include "shape.h" namespace Msp { @@ -23,7 +24,7 @@ public: const Shape &get_shape() const { return *shape; } - virtual HyperBox get_axis_aligned_bounding_box() const; + virtual BoundingBox get_axis_aligned_bounding_box() const; virtual bool contains(const LinAl::Vector &) const; virtual unsigned get_max_ray_intersections() const { return shape->get_max_ray_intersections(); } virtual unsigned get_intersections(const Ray &, SurfacePoint *, unsigned) const; @@ -41,10 +42,9 @@ inline Negation *Negation::clone() const } template -inline HyperBox Negation::get_axis_aligned_bounding_box() const +inline BoundingBox Negation::get_axis_aligned_bounding_box() const { - // XXX How do we handle this correctly? In particular negation of a negation? - return HyperBox(); + return ~shape->get_axis_aligned_bounding_box(); } template diff --git a/source/geometry/shape.h b/source/geometry/shape.h index 7b1d190..bbe255f 100644 --- a/source/geometry/shape.h +++ b/source/geometry/shape.h @@ -8,7 +8,7 @@ namespace Msp { namespace Geometry { template -class HyperBox; +class BoundingBox; template class Ray; @@ -31,7 +31,7 @@ public: virtual Shape *clone() const = 0; - virtual HyperBox get_axis_aligned_bounding_box() const = 0; + virtual BoundingBox get_axis_aligned_bounding_box() const = 0; virtual bool contains(const LinAl::Vector &) const = 0; bool check_intersection(const Ray &) const; virtual unsigned get_max_ray_intersections() const = 0; diff --git a/source/geometry/transformedshape.h b/source/geometry/transformedshape.h index 02587bf..58e571a 100644 --- a/source/geometry/transformedshape.h +++ b/source/geometry/transformedshape.h @@ -2,6 +2,7 @@ #define MSP_GEOMETRY_TRANSFORMEDSHAPE_H_ #include "affinetransformation.h" +#include "boundingbox.h" #include "ray.h" #include "shape.h" @@ -30,7 +31,7 @@ public: const Shape &get_shape() const { return *shape; } const AffineTransformation &get_transformation() const { return transformation; } - virtual HyperBox get_axis_aligned_bounding_box() const; + virtual BoundingBox get_axis_aligned_bounding_box() const; virtual bool contains(const LinAl::Vector &) const; private: Ray make_local_ray(const Ray &) const; @@ -75,10 +76,28 @@ inline TransformedShape *TransformedShape::clone() const } template -inline HyperBox TransformedShape::get_axis_aligned_bounding_box() const +inline BoundingBox TransformedShape::get_axis_aligned_bounding_box() const { - // XXX This is not correct for most shapes - return shape->get_axis_aligned_bounding_box(); + BoundingBox inner_bbox = shape->get_axis_aligned_bounding_box(); + + LinAl::Vector min_pt; + LinAl::Vector max_pt; + for(unsigned i=0; i<(1< point; + for(unsigned j=0; j>j)&1 ? inner_bbox.get_maximum_coordinate(j) : inner_bbox.get_minimum_coordinate(j)); + + point = transformation.transform(point); + + for(unsigned j=0; j(min_pt, max_pt); } template diff --git a/source/geometry/union.h b/source/geometry/union.h index 13eb708..8461f84 100644 --- a/source/geometry/union.h +++ b/source/geometry/union.h @@ -12,7 +12,7 @@ Joins component shapes together into one. template struct UnionOps { - static HyperBox combine_aabb(const HyperBox &, const HyperBox &); + static BoundingBox combine_aabb(const BoundingBox &a, const BoundingBox &b) { return a|b; } static bool init_inside() { return false; } static bool combine_inside(bool a, bool b) { return a || b; } static bool is_inside_decided(bool a) { return a; } @@ -54,16 +54,6 @@ inline Union *Union::clone() const return new Union(*this); } - -template -inline HyperBox UnionOps::combine_aabb(const HyperBox &box1, const HyperBox &box2) -{ - LinAl::Vector dimensions; - for(unsigned i=0; i(dimensions); -} - } // namespace Geometry } // namespace Msp -- 2.43.0