From 6ff13022b53830d35283905d562c2ef3af198cc1 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 15 May 2013 10:27:06 +0300 Subject: [PATCH] Beginnings of a geometry library --- Build | 1 + source/geometry/affinetransformation.h | 185 +++++++++++++++++++++ source/geometry/angle.h | 221 +++++++++++++++++++++++++ source/geometry/box.h | 26 +++ source/geometry/circle.h | 20 +++ source/geometry/dummy.cpp | 9 + source/geometry/hyperbox.h | 79 +++++++++ source/geometry/hypersphere.h | 71 ++++++++ source/geometry/ray.h | 39 +++++ source/geometry/rectangle.h | 25 +++ source/geometry/shape.h | 30 ++++ source/geometry/transformedshape.h | 77 +++++++++ source/linal/matrix4.h | 21 --- 13 files changed, 783 insertions(+), 21 deletions(-) create mode 100644 source/geometry/affinetransformation.h create mode 100644 source/geometry/angle.h create mode 100644 source/geometry/box.h create mode 100644 source/geometry/circle.h create mode 100644 source/geometry/dummy.cpp create mode 100644 source/geometry/hyperbox.h create mode 100644 source/geometry/hypersphere.h create mode 100644 source/geometry/ray.h create mode 100644 source/geometry/rectangle.h create mode 100644 source/geometry/shape.h create mode 100644 source/geometry/transformedshape.h delete mode 100644 source/linal/matrix4.h diff --git a/Build b/Build index 517bb2d..62e7375 100644 --- a/Build +++ b/Build @@ -3,6 +3,7 @@ package "mspmath" library "mspmath" { source "source/linal"; + source "source/geometry"; install true; install_map { diff --git a/source/geometry/affinetransformation.h b/source/geometry/affinetransformation.h new file mode 100644 index 0000000..e0ca370 --- /dev/null +++ b/source/geometry/affinetransformation.h @@ -0,0 +1,185 @@ +#ifndef MSP_GEOMETRY_AFFINETRANSFORMATION_H_ +#define MSP_GEOMETRY_AFFINETRANSFORMATION_H_ + +#include +#include "angle.h" + +namespace Msp { +namespace Geometry { + +template +class AffineTransformation; + + +/** +Helper class to provide specialized operations for AffineTransformation. +*/ +template +class AffineTransformationOps +{ +protected: + AffineTransformationOps() { } +}; + +template +class AffineTransformationOps +{ +protected: + AffineTransformationOps() { } + +public: + static AffineTransformation rotation(const Angle &); +}; + +template +class AffineTransformationOps +{ +protected: + AffineTransformationOps() { } + +public: + static AffineTransformation rotation(const Angle &, const LinAl::Vector &); +}; + + +/** +An affine transformation in D dimensions. Affine transformations preserve +straightness of lines and ratios of distances. Angles and distances themselves +may change. Internally this is represented by a square matrix of size D+1. +*/ +template +class AffineTransformation: public AffineTransformationOps +{ + friend class AffineTransformationOps; + +private: + LinAl::SquareMatrix matrix; + +public: + AffineTransformation(); + + static AffineTransformation translation(const LinAl::Vector &); + static AffineTransformation scaling(const LinAl::Vector &); + static AffineTransformation shear(const LinAl::Vector &, const LinAl::Vector &); + + const LinAl::SquareMatrix &get_matrix() const { return matrix; } + operator const LinAl::SquareMatrix &() const { return matrix; } + + LinAl::Vector transform(const LinAl::Vector &) const; + LinAl::Vector transform_linear(const LinAl::Vector &) const; +}; + +template +inline AffineTransformation::AffineTransformation() +{ + this->matrix = LinAl::SquareMatrix::identity(); +} + + +template +AffineTransformation AffineTransformation::translation(const LinAl::Vector &v) +{ + AffineTransformation r; + for(unsigned i=0; i +AffineTransformation AffineTransformation::scaling(const LinAl::Vector &factors) +{ + AffineTransformation r; + for(unsigned i=0; i +AffineTransformation AffineTransformation::shear(const LinAl::Vector &normal, const LinAl::Vector &shift) +{ + AffineTransformation r; + for(unsigned i=0; i +AffineTransformation AffineTransformationOps::rotation(const Angle &angle) +{ + AffineTransformation r; + T c = cos(angle); + T s = sin(angle); + r.matrix(0, 0) = c; + r.matrix(0, 1) = -s; + r.matrix(1, 0) = s; + r.matrix(1, 1) = c; + return r; +} + +template +AffineTransformation AffineTransformationOps::rotation(const Angle &angle, const LinAl::Vector &axis) +{ + AffineTransformation r; + LinAl::Vector axn = normalize(axis); + T c = cos(angle); + T s = sin(angle); + // http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle + r.matrix(0, 0) = c+axn.x*axn.x*(1-c); + r.matrix(0, 1) = axn.x*axn.y*(1-c)-axn.z*s; + r.matrix(0, 2) = axn.x*axn.z*(1-c)+axn.y*s; + r.matrix(1, 0) = axn.y*axn.x*(1-c)+axn.z*s; + r.matrix(1, 1) = c+axn.y*axn.y*(1-c); + r.matrix(1, 2) = axn.y*axn.z*(1-c)-axn.x*s; + r.matrix(2, 0) = axn.z*axn.x*(1-c)-axn.y*s; + r.matrix(2, 1) = axn.z*axn.y*(1-c)+axn.x*s; + r.matrix(2, 2) = c+axn.z*axn.z*(1-c); + return r; +} + + +template +inline LinAl::Vector augment_vector(const LinAl::Vector &v, T s) +{ + LinAl::Vector r; + for(unsigned i=0; i +inline LinAl::Vector reduce_vector(const LinAl::Vector &v) +{ + LinAl::Vector r; + for(unsigned i=0; i +inline LinAl::Vector divide_vector(const LinAl::Vector &v) +{ + LinAl::Vector r; + for(unsigned i=0; i +inline LinAl::Vector AffineTransformation::transform(const LinAl::Vector &v) const +{ + return reduce_vector(matrix*augment_vector(v, T(1))); +} + +template +inline LinAl::Vector AffineTransformation::transform_linear(const LinAl::Vector &v) const +{ + return reduce_vector(matrix*augment_vector(v, T(0))); +} + +} // namespace Geometry +} // namespace Msp + +#endif diff --git a/source/geometry/angle.h b/source/geometry/angle.h new file mode 100644 index 0000000..edd7521 --- /dev/null +++ b/source/geometry/angle.h @@ -0,0 +1,221 @@ +#ifndef MSP_GEOMETRY_ANGLE_H_ +#define MSP_GEOMETRY_ANGLE_H_ + +#include + +namespace Msp { +namespace Geometry { + +/** +A planar angle. Creating an Angle from a raw value or extracting it requires +specifying whether degrees or radians are used. This eliminates a common +source of errors. +*/ +template +class Angle +{ +private: + T value; + + explicit Angle(T); +public: + template + Angle(const Angle &); + + static Angle from_degrees(T); + static Angle from_radians(T); + + static Angle right(); + static Angle quarter_circle(); + static Angle straight(); + static Angle half_circle(); + static Angle full_circle(); + + T degrees() const; + T radians() const; + + Angle &operator+=(const Angle &); + Angle &operator-=(const Angle &); + Angle &operator*=(T); + Angle &operator/=(T); +}; + +template +inline Angle::Angle(T v): + value(v) +{ } + +template +template +inline Angle::Angle(const Angle &other): + value(other.value) +{ } + +template +inline Angle Angle::from_degrees(T d) +{ + return Angle(d*M_PI/180); +} + +template +inline Angle Angle::from_radians(T r) +{ + return Angle(r); +} + +template +inline Angle Angle::right() +{ + return from_radians(M_PI/2); +} + +template +inline Angle Angle::quarter_circle() +{ + return right(); +} + +template +inline Angle Angle::straight() +{ + return from_radians(M_PI); +} + +template +inline Angle Angle::half_circle() +{ + return straight(); +} + +template +inline Angle Angle::full_circle() +{ + return from_radians(M_PI*2); +} + +template +inline T Angle::degrees() const +{ + return value*180/M_PI; +} + +template +inline T Angle::radians() const +{ + return value; +} + +template +inline Angle &Angle::operator+=(const Angle &a) +{ + value += a.value; + return *this; +} + +template +inline Angle operator+(const Angle &a1, const Angle &a2) +{ + Angle r(a1); + return r += a2; +} + +template +inline Angle &Angle::operator-=(const Angle &a) +{ + value -= a.value; + return *this; +} + +template +inline Angle operator-(const Angle &a1, const Angle &a2) +{ + Angle r(a1); + return r -= a2; +} + +template +inline Angle &Angle::operator*=(T s) +{ + value *= s; + return *this; +} + +template +inline Angle operator*(const Angle &a, T s) +{ + Angle r(a); + return r *= s; +} + +template +inline Angle operator*(T s, const Angle &a) +{ + return a*s; +} + +template +inline Angle &Angle::operator/=(T s) +{ + value /= s; + return *this; +} + +template +inline Angle operator/(const Angle &a, T s) +{ + Angle r(a); + return r /= s; +} + +template +inline Angle operator/(T s, const Angle &a) +{ + return a/s; +} + +template +inline T sin(const Angle &angle) +{ + return std::sin(angle.radians()); +} + +template +inline Angle asin(T s) +{ + return Angle::from_radians(std::asin(s)); +} + +template +inline T cos(const Angle &angle) +{ + return std::cos(angle.radians()); +} + +template +inline Angle acos(T s) +{ + return Angle::from_radians(std::acos(s)); +} + +template +inline T tan(const Angle &angle) +{ + return std::tan(angle.radians()); +} + +template +inline Angle atan(T s) +{ + return Angle::from_radians(std::atan(s)); +} + +template +inline Angle atan2(T y, T x) +{ + return Angle::from_radians(std::atan2(y, x)); +} + +} // namespace Geometry +} // namespace Msp + +#endif diff --git a/source/geometry/box.h b/source/geometry/box.h new file mode 100644 index 0000000..bfcfa2f --- /dev/null +++ b/source/geometry/box.h @@ -0,0 +1,26 @@ +#ifndef MSP_GEOMETRY_BOX_H_ +#define MSP_GEOMETRY_BOX_H_ + +#include +#include "hyperbox.h" + +namespace Msp { +namespace Geometry { + +template +class Box: public HyperBox +{ +public: + Box() { } + explicit Box(const LinAl::Vector &d): HyperBox(d) { } + Box(T w, T h, T d): HyperBox(LinAl::Vector3(w, h, d)) { } + + T get_width() const { return this->get_dimension(0); } + T get_height() const { return this->get_dimension(1); } + T get_depth() const { return this->get_dimension(2); } +}; + +} // namespace Geometry +} // namespace Msp + +#endif diff --git a/source/geometry/circle.h b/source/geometry/circle.h new file mode 100644 index 0000000..ebc1c41 --- /dev/null +++ b/source/geometry/circle.h @@ -0,0 +1,20 @@ +#ifndef MSP_GEOMETRY_CIRCLE_H_ +#define MSP_GEOMETRY_CIRCLE_H_ + +#include "hypersphere.h" + +namespace Msp { +namespace Geometry { + +template +class Circle: public HyperSphere +{ +public: + Circle() { } + explicit Circle(T r): HyperSphere(r) { } +}; + +} // namespace Geometry +} // namespace Msp + +#endif diff --git a/source/geometry/dummy.cpp b/source/geometry/dummy.cpp new file mode 100644 index 0000000..885bc5a --- /dev/null +++ b/source/geometry/dummy.cpp @@ -0,0 +1,9 @@ +#include "affinetransformation.h" +#include "angle.h" +#include "box.h" +#include "circle.h" +#include "hyperbox.h" +#include "hypersphere.h" +#include "rectangle.h" +#include "shape.h" +#include "transformedshape.h" diff --git a/source/geometry/hyperbox.h b/source/geometry/hyperbox.h new file mode 100644 index 0000000..2ade28a --- /dev/null +++ b/source/geometry/hyperbox.h @@ -0,0 +1,79 @@ +#ifndef MSP_GEOMETRY_HYPERBOX_H_ +#define MSP_GEOMETRY_HYPERBOX_H_ + +#include +#include "ray.h" +#include "shape.h" + +namespace Msp { +namespace Geometry { + +template +class HyperBox: public Shape +{ +private: + LinAl::Vector dimensions; + +public: + HyperBox(); + explicit HyperBox(const LinAl::Vector &); + + virtual HyperBox *clone() const; + + const LinAl::Vector &get_dimensions() const { return dimensions; } + T get_dimension(unsigned) const; + + virtual HyperBox get_axis_aligned_bounding_box() const { return *this; } + virtual bool check_intersection(const Ray &) const; +}; + +template +inline HyperBox::HyperBox() +{ + for(unsigned i=0; i +inline HyperBox::HyperBox(const LinAl::Vector &d): + dimensions(d) +{ } + +template +inline HyperBox *HyperBox::clone() const +{ + return new HyperBox(dimensions); +} + +template +inline T HyperBox::get_dimension(unsigned i) const +{ + return dimensions[i]; +} + +template +inline bool HyperBox::check_intersection(const Ray &ray) const +{ + LinAl::Vector half_dim = dimensions/T(2); + for(unsigned i=0; i0) + { + LinAl::Vector p = ray.get_start()+ray.get_direction()*x; + bool inside = true; + for(unsigned k=0; (inside && k=-half_dim[k] && p[k] +#include "hyperbox.h" +#include "ray.h" +#include "shape.h" + +namespace Msp { +namespace Geometry { + +template +class HyperSphere: public Shape +{ +private: + T radius; + +public: + HyperSphere(); + explicit HyperSphere(T); + + virtual HyperSphere *clone() const; + + T get_radius() const { return radius; } + + virtual HyperBox get_axis_aligned_bounding_box() const; + virtual bool check_intersection(const Ray &) const; +}; + +template +inline HyperSphere::HyperSphere(): + radius(1) +{ } + +template +inline HyperSphere::HyperSphere(T r): + radius(r) +{ } + +template +inline HyperSphere *HyperSphere::clone() const +{ + return new HyperSphere(radius); +} + +template +inline HyperBox HyperSphere::get_axis_aligned_bounding_box() const +{ + LinAl::Vector dimensions; + for(unsigned i=0; i(dimensions); +} + +template +inline bool HyperSphere::check_intersection(const Ray &ray) const +{ + T x = inner_product(ray.get_direction(), ray.get_start()); + if(x>0) + return inner_product(ray.get_start(), ray.get_start())<=radius*radius; + else + { + LinAl::Vector nearest = ray.get_start()-ray.get_direction()*x; + return inner_product(nearest, nearest)<=radius*radius; + } +} + +} // namespace Geometry +} // namespace Msp + +#endif diff --git a/source/geometry/ray.h b/source/geometry/ray.h new file mode 100644 index 0000000..4825cfe --- /dev/null +++ b/source/geometry/ray.h @@ -0,0 +1,39 @@ +#ifndef MSP_GEOMETRY_RAY_H_ +#define MSP_GEOMETRY_RAY_H_ + +#include + +namespace Msp { +namespace Geometry { + +template +class Ray +{ +private: + LinAl::Vector start; + LinAl::Vector direction; + +public: + Ray(); + Ray(const LinAl::Vector &, const LinAl::Vector &); + + const LinAl::Vector &get_start() const { return start; } + const LinAl::Vector &get_direction() const { return direction; } +}; + +template +Ray::Ray() +{ + direction[0] = 1; +} + +template +Ray::Ray(const LinAl::Vector &s, const LinAl::Vector &d): + start(s), + direction(normalize(d)) +{ } + +} // namespace Geometry +} // namespace Msp + +#endif diff --git a/source/geometry/rectangle.h b/source/geometry/rectangle.h new file mode 100644 index 0000000..296ba6c --- /dev/null +++ b/source/geometry/rectangle.h @@ -0,0 +1,25 @@ +#ifndef MSP_GEOMETRY_RECTANGLE_H_ +#define MSP_GEOMETRY_RECTANGLE_H_ + +#include +#include "hyperbox.h" + +namespace Msp { +namespace Geometry { + +template +class Rectangle: public HyperBox +{ +public: + Rectangle() { } + explicit Rectangle(const LinAl::Vector &d): HyperBox(d) { } + Rectangle(T w, T h): HyperBox(LinAl::Vector2(w, h)) { } + + T get_width() const { return this->get_dimension(0); } + T get_height() const { return this->get_dimension(1); } +}; + +} // namespace Geometry +} // namespace Msp + +#endif diff --git a/source/geometry/shape.h b/source/geometry/shape.h new file mode 100644 index 0000000..528a77c --- /dev/null +++ b/source/geometry/shape.h @@ -0,0 +1,30 @@ +#ifndef MSP_GEOMETRY_SHAPE_H_ +#define MSP_GEOMETRY_SHAPE_H_ + +namespace Msp { +namespace Geometry { + +template +class HyperBox; + +template +class Ray; + +template +class Shape +{ +protected: + Shape() { } +public: + virtual ~Shape() { } + + virtual Shape *clone() const = 0; + + virtual HyperBox get_axis_aligned_bounding_box() const = 0; + virtual bool check_intersection(const Ray &) const = 0; +}; + +} // namespace Geometry +} // namespace Msp + +#endif diff --git a/source/geometry/transformedshape.h b/source/geometry/transformedshape.h new file mode 100644 index 0000000..9a98f31 --- /dev/null +++ b/source/geometry/transformedshape.h @@ -0,0 +1,77 @@ +#ifndef MSP_GEOMETRY_TRANSFORMEDSHAPE_H_ +#define MSP_GEOMETRY_TRANSFORMEDSHAPE_H_ + +#include "affinetransformation.h" +#include "ray.h" +#include "shape.h" + +namespace Msp { +namespace Geometry { + +template +class TransformedShape +{ +private: + Shape *shape; + AffineTransformation transformation; + +public: + TransformedShape(const Shape &, const AffineTransformation &); + TransformedShape(const TransformedShape &); + TransformedShape &operator=(const TransformedShape &); + ~TransformedShape(); + + virtual TransformedShape *clone() const; + + const Shape &get_shape() const { return *shape; } + const AffineTransformation &get_transformation() const { return transformation; } + + virtual bool check_intersection(const Ray &) const; +}; + +template +inline TransformedShape::TransformedShape(const Shape &s, const AffineTransformation &t): + shape(s.clone()), + transformation(t) +{ } + +template +inline TransformedShape::TransformedShape(const TransformedShape &other): + shape(other.shape->clone()), + transformation(other.transformation) +{ } + +template +inline TransformedShape &TransformedShape::operator=(const TransformedShape &other) +{ + delete shape; + shape = other.shape->clone(); + transformation = other.transformation(); +} + +template +inline TransformedShape::~TransformedShape() +{ + delete shape; +} + +template +inline TransformedShape *TransformedShape::clone() const +{ + return new TransformedShape(*this); +} + +template +inline bool TransformedShape::check_intersection(const Ray &ray) const +{ + // TODO cache the inverse transformation for performance + LinAl::SquareMatrix inverse_trans = LinAl::invert(transformation.get_matrix()); + Ray trans_ray(reduce_vector(inverse_trans*augment_vector(ray.get_start(), T(1))), + reduce_vector(inverse_trans*augment_vector(ray.get_direction(), T(0)))); + return shape->check_intersection(trans_ray); +} + +} // namespace Geometry +} // namespace Msp + +#endif diff --git a/source/linal/matrix4.h b/source/linal/matrix4.h deleted file mode 100644 index 8b89a65..0000000 --- a/source/linal/matrix4.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MSP_LINAL_MATRIX4_H_ -#define MSP_LINAL_MATRIX4_H_ - -#include "squarematrix.h" - -namespace Msp { -namespace LinAl { - -/** -A 4x4 square matrix, capable of expressing affine transformations in a -three-dimensional vector space. -*/ -template -class Matrix4: public SquareMatrix -{ -}; - -} // namespace LinAl -} // namespace Msp - -#endif -- 2.45.2