From: Mikko Rasa Date: Mon, 20 May 2013 20:12:04 +0000 (+0300) Subject: Add a class for extruded shapes X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=aee0d4c9f7d4dc6ac77b6ff3429973f16d86144b;p=libs%2Fmath.git Add a class for extruded shapes --- diff --git a/source/geometry/extrudedshape.h b/source/geometry/extrudedshape.h new file mode 100644 index 0000000..8fac4bc --- /dev/null +++ b/source/geometry/extrudedshape.h @@ -0,0 +1,199 @@ +#ifndef MSP_GEOMETRY_EXTRUDEDSHAPE_H_ +#define MSP_GEOMETRY_EXTRUDEDSHAPE_H_ + +#include +#include +#include "shape.h" + +namespace Msp { +namespace Geometry { + +/** +A shape embedded in space of dimension higher by one and extruded towards the +highest dimension. As an example, extruding a circle creates a cylinder. The +base shape's orientation is not changed. +*/ +template +class ExtrudedShape: public Shape +{ +private: + Shape *base; + T length; + +public: + ExtrudedShape(const Shape &, T); + ExtrudedShape(const ExtrudedShape &); + ExtrudedShape &operator=(const ExtrudedShape &); + virtual ~ExtrudedShape(); + + virtual ExtrudedShape *clone() const; + + const Shape &get_base() const { return *base; } + T get_length() const { return length; } + + virtual HyperBox get_axis_aligned_bounding_box() const; + virtual bool contains(const LinAl::Vector &) const; + virtual bool check_intersection(const Ray &) const; + virtual unsigned get_max_ray_intersections() const; + virtual unsigned get_intersections(const Ray &, SurfacePoint *, unsigned) const; +}; + +template +inline ExtrudedShape::ExtrudedShape(const Shape &b, T l): + length(l) +{ + if(l<=0) + throw std::invalid_argument("ExtrudedShape::ExtrudedShape"); + + base = b.clone(); +} + +template +inline ExtrudedShape::ExtrudedShape(const ExtrudedShape &other): + base(other.base.clone()), + length(other.length) +{ } + +template +inline ExtrudedShape &ExtrudedShape::operator=(const ExtrudedShape &other) +{ + delete base; + base = other.base.clone(); + length = other.length; +} + +template +inline ExtrudedShape::~ExtrudedShape() +{ + delete base; +} + +template +inline ExtrudedShape *ExtrudedShape::clone() const +{ + return new ExtrudedShape(*base, length); +} + +template +inline HyperBox 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)); +} + +template +inline bool ExtrudedShape::contains(const LinAl::Vector &point) const +{ + using std::abs; + + if(abs(point[D-1])>length/T(2)) + return false; + + return base->contains(LinAl::Vector(point)); +} + +template +inline bool ExtrudedShape::check_intersection(const Ray &ray) const +{ + return get_intersections(ray, 0, 1); +} + +template +inline unsigned ExtrudedShape::get_max_ray_intersections() const +{ + return std::max(base->get_max_ray_intersections(), 2U); +} + +template +inline unsigned ExtrudedShape::get_intersections(const Ray &ray, SurfacePoint *points, unsigned size) const +{ + using std::abs; + using std::sqrt; + using std::swap; + + unsigned n = 0; + T half_length = length/T(2); + const LinAl::Vector &ray_start = ray.get_start(); + const LinAl::Vector &ray_direction = ray.get_direction(); + LinAl::Vector base_dir(ray_direction); + + /* If the ray does not degenerate to a point in the base space, it could + intersect the base shape. */ + if(inner_product(base_dir, base_dir)!=T(0)) + { + T offset = T(); + T limit = T(); + if(ray.get_direction()[D-1]!=T(0)) + { + offset = (half_length-ray_start[D-1])/ray_direction[D-1]; + limit = (-half_length-ray_start[D-1])/ray_direction[D-1]; + if(offset>limit) + swap(offset, limit); + if(offset base_ray(LinAl::Vector(ray_start+ray_direction*offset), + base_dir, (limit-offset)*distortion); + + SurfacePoint *base_points = 0; + if(points) + /* Shamelessly reuse the provided storage. Align to the end of the array + so processing can start from the first (nearest) point. */ + base_points = reinterpret_cast *>(points+size)-size; + + unsigned count = base->get_intersections(base_ray, base_points, size); + for(unsigned i=0; i(base_points[i].normal, T(0)); + points[n].distance = x; + } + + ++n; + if(n==size) + return n; + } + } + + /* If the ray is not parallel to the base space, it may pass through the + caps. */ + if(ray_direction[D-1]) + { + for(int i=-1; i<=1; i+=2) + { + T x = (half_length*i-ray_start[D-1])/ray_direction[D-1]; + if(!ray.check_limits(x)) + continue; + + LinAl::Vector p = ray_start+ray_direction*x; + if(base->contains(LinAl::Vector(p)) && n(); + points[n].normal[D-1] = i; + points[n].distance = x; + + if(n==1 && x