From 2b3ecc7e358dc8871e61c2aea2df8edebc596e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Thu, 15 Oct 2015 20:21:32 +0200 Subject: [PATCH] Added geometry primitives --- src/basics/CappedCylinder.cpp | 79 +++++++++++++++ src/basics/CappedCylinder.h | 42 ++++++++ src/basics/Disk.cpp | 43 ++++++++ src/basics/Disk.h | 42 ++++++++ src/basics/InfiniteCylinder.cpp | 149 ++++++++++++++++++++++++++++ src/basics/InfiniteCylinder.h | 41 ++++++++ src/basics/InfinitePlane.cpp | 48 +++++++++ src/basics/InfinitePlane.h | 43 ++++++++ src/basics/InfiniteRay.cpp | 27 +++++ src/basics/InfiniteRay.h | 39 ++++++++ src/basics/Sphere.cpp | 62 ++++++++++++ src/basics/Sphere.h | 42 ++++++++ src/basics/basics.pro | 12 +++ src/basics/basics_global.h | 4 + src/tests/CappedCylinder_Test.cpp | 38 +++++++ src/tests/Disk_Test.cpp | 25 +++++ src/tests/InfiniteCylinder_Test.cpp | 41 ++++++++ src/tests/InfinitePlane_Test.cpp | 34 +++++++ src/tests/Sphere_Test.cpp | 24 +++++ src/tests/tests.pro | 5 + 20 files changed, 840 insertions(+) create mode 100644 src/basics/CappedCylinder.cpp create mode 100644 src/basics/CappedCylinder.h create mode 100644 src/basics/Disk.cpp create mode 100644 src/basics/Disk.h create mode 100644 src/basics/InfiniteCylinder.cpp create mode 100644 src/basics/InfiniteCylinder.h create mode 100644 src/basics/InfinitePlane.cpp create mode 100644 src/basics/InfinitePlane.h create mode 100644 src/basics/InfiniteRay.cpp create mode 100644 src/basics/InfiniteRay.h create mode 100644 src/basics/Sphere.cpp create mode 100644 src/basics/Sphere.h create mode 100644 src/tests/CappedCylinder_Test.cpp create mode 100644 src/tests/Disk_Test.cpp create mode 100644 src/tests/InfiniteCylinder_Test.cpp create mode 100644 src/tests/InfinitePlane_Test.cpp create mode 100644 src/tests/Sphere_Test.cpp diff --git a/src/basics/CappedCylinder.cpp b/src/basics/CappedCylinder.cpp new file mode 100644 index 0000000..908c295 --- /dev/null +++ b/src/basics/CappedCylinder.cpp @@ -0,0 +1,79 @@ +#include "CappedCylinder.h" + +#include "Vector3.h" +#include "PackStream.h" + +CappedCylinder::CappedCylinder() +{ +} + +CappedCylinder::CappedCylinder(const Vector3 &base, const Vector3 &direction, double radius, double length): + InfiniteCylinder(InfiniteRay(base, direction), radius), length(length) +{ +} + +int CappedCylinder::checkRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, Vector3 *second_intersection) const +{ + // TODO Apply the caps + int count = InfiniteCylinder::checkRayIntersection(ray, first_intersection, second_intersection); + + if (count == 0) + { + return 0; + } + else if (count == 2) + { + if (checkPointProjection(first_intersection)) + { + if (checkPointProjection(second_intersection)) + { + return 2; + } + else + { + return 1; + } + } + else + { + if (checkPointProjection(second_intersection)) + { + *first_intersection = *second_intersection; + return 1; + } + else + { + return 0; + } + } + } + else // count == 1 + { + if (checkPointProjection(first_intersection)) + { + return 1; + } + else + { + return 0; + } + } +} + +bool CappedCylinder::checkPointProjection(Vector3 *point) const +{ + double proj_length = axis.getDirection().dotProduct(point->sub(axis.getOrigin())); + return 0.0 <= proj_length && proj_length <= length; +} + +void CappedCylinder::save(PackStream *stream) const +{ + InfiniteCylinder::save(stream); + stream->write(&length); +} + +void CappedCylinder::load(PackStream *stream) +{ + InfiniteCylinder::load(stream); + stream->read(&length); +} diff --git a/src/basics/CappedCylinder.h b/src/basics/CappedCylinder.h new file mode 100644 index 0000000..75eb2e7 --- /dev/null +++ b/src/basics/CappedCylinder.h @@ -0,0 +1,42 @@ +#ifndef CAPPEDCYLINDER_H +#define CAPPEDCYLINDER_H + +#include "basics_global.h" + +#include "InfiniteCylinder.h" + +namespace paysages { +namespace basics { + +/** + * Geometric cylinder, with capped ends (not infinite). + */ +class BASICSSHARED_EXPORT CappedCylinder: public InfiniteCylinder +{ +public: + CappedCylinder(); + CappedCylinder(const Vector3 &base, const Vector3 &direction, double radius, double length); + + inline double getLength() const {return length;} + + /** + * Check the intersection between the cylinder and an infinite ray. + */ + int checkRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, Vector3 *second_intersection) const; + + /** + * Check if a point projects in the length of the finite cylinder. + */ + bool checkPointProjection(Vector3 *point) const; + + virtual void save(PackStream *stream) const override; + virtual void load(PackStream *stream) override; + +private: + double length; +}; + +} +} + +#endif // CAPPEDCYLINDER_H diff --git a/src/basics/Disk.cpp b/src/basics/Disk.cpp new file mode 100644 index 0000000..3b1cbba --- /dev/null +++ b/src/basics/Disk.cpp @@ -0,0 +1,43 @@ +#include "Disk.h" + +#include "PackStream.h" + +Disk::Disk() +{ +} + +Disk::Disk(const Vector3 &point, const Vector3 &normal, double radius): + InfinitePlane(point, normal), radius(radius) +{ + radius2 = radius * radius; +} + +int Disk::checkRayIntersection(const InfiniteRay &ray, Vector3 *intersection) const +{ + int result = InfinitePlane::checkRayIntersection(ray, intersection); + + if (result == 1) + { + Vector3 v = intersection->sub(getPoint()); + double d2 = v.dotProduct(v); + return (d2 <= radius2) ? 1 : 0; + } + else + { + return result; + } +} + +void Disk::save(PackStream *stream) const +{ + InfinitePlane::save(stream); + stream->write(&radius); + stream->write(&radius2); +} + +void Disk::load(PackStream *stream) +{ + InfinitePlane::load(stream); + stream->read(&radius); + stream->read(&radius2); +} diff --git a/src/basics/Disk.h b/src/basics/Disk.h new file mode 100644 index 0000000..5493314 --- /dev/null +++ b/src/basics/Disk.h @@ -0,0 +1,42 @@ +#ifndef DISK_H +#define DISK_H + +#include "basics_global.h" + +#include "InfinitePlane.h" + +namespace paysages { +namespace basics { + +/** + * Geometric plane disk. + */ +class BASICSSHARED_EXPORT Disk: public InfinitePlane +{ +public: + Disk(); + Disk(const Vector3 &point, const Vector3 &normal, double radius); + + inline double getRadius() const {return radius;} + + /** + * Check the intersection between the disk and an infinite ray. + * + * Returns the number of intersections (0, 1), and fill the intersection point. + * + * Returns -1 if the ray shares a segment with the disk. + */ + int checkRayIntersection(const InfiniteRay& ray, Vector3 *intersection) const; + + void save(PackStream *stream) const override; + void load(PackStream *stream) override; + +private: + double radius; + double radius2; +}; + +} +} + +#endif // DISK_H diff --git a/src/basics/InfiniteCylinder.cpp b/src/basics/InfiniteCylinder.cpp new file mode 100644 index 0000000..0f81911 --- /dev/null +++ b/src/basics/InfiniteCylinder.cpp @@ -0,0 +1,149 @@ +#include "InfiniteCylinder.h" + +#include "PackStream.h" + +#define EPS 1E-8 + +InfiniteCylinder::InfiniteCylinder() +{ +} + +InfiniteCylinder::InfiniteCylinder(const InfiniteRay &axis, double radius): + axis(axis), radius(radius) +{ +} + +int InfiniteCylinder::checkRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, Vector3 *second_intersection) const +{ + /* + * Original algorithm has been altered, because it didn't work with non-(0,0,0) axis origin. + * Maybe some optimizations could be made from this. + */ + + Vector3 U, V, F = axis.getDirection(), P, B, Q, G, AG, AQ; + double length, invLength, prod; + double R[3][3], A[3][3]; + double e0, e1, C, c0, c1, c2, discr, invC2, root0, root1, root; + + /* choose U, V so that U,V,F is orthonormal set */ + + if ( fabs(F.x) > fabs(F.y) && fabs(F.x) > fabs(F.z) ) + { + length = sqrt(F.x*F.x+F.y*F.y); + invLength = 1.0/length; + U.x = -F.y*invLength; + U.y = +F.x*invLength; + U.z = 0.0; + prod = -F.z*invLength; + V.x = F.x*prod; + V.y = F.y*prod; + V.z = length; + } + else + { + length = sqrt(F.y*F.y+F.z*F.z); + invLength = 1.0/length; + U.x = 0.0; + U.y = -F.z*invLength; + U.z = +F.y*invLength; + prod = -F.x*invLength; + V.x = length; + V.y = F.y*prod; + V.z = F.z*prod; + } + + /* orthonormal matrix */ + R[0][0] = U.x; R[0][1] = U.y; R[0][2] = U.z; + R[1][0] = V.x; R[1][1] = V.y; R[1][2] = V.z; + R[2][0] = F.x; R[2][1] = F.y; R[2][2] = F.z; + + /* matrix A */ + A[0][0] = R[0][0]*R[0][0]+R[1][0]*R[1][0]; + A[0][1] = R[0][0]*R[0][1]+R[1][0]*R[1][1]; + A[0][2] = R[0][0]*R[0][2]+R[1][0]*R[1][2]; + + A[1][0] = R[0][1]*R[0][0]+R[1][1]*R[1][0]; + A[1][1] = R[0][1]*R[0][1]+R[1][1]*R[1][1]; + A[1][2] = R[0][1]*R[0][2]+R[1][1]*R[1][2]; + + A[2][0] = R[0][2]*R[0][0]+R[1][2]*R[1][0]; + A[2][1] = R[0][2]*R[0][1]+R[1][2]*R[1][1]; + A[2][2] = R[0][2]*R[0][2]+R[1][2]*R[1][2]; + + /* vector B */ + P = Vector3(0.0, 0.0, 0.0); + B.x = -2.0*P.x; B.y = -2.0*P.y; B.z = -2.0*P.z; + + /* constant C */ + e0 = -2.0*(R[0][0]*P.x+R[0][1]*P.y+R[0][2]*P.z); + e1 = -2.0*(R[1][0]*P.x+R[1][1]*P.y+R[1][2]*P.z); + C = 0.25*(e0*e0+e1*e1) - radius*radius; + + /* line */ + Q = ray.getOrigin().sub(axis.getOrigin()); + G = ray.getDirection(); + + /* compute A*G */ + AG.x = A[0][0]*G.x+A[0][1]*G.y+A[0][2]*G.z; + AG.y = A[1][0]*G.x+A[1][1]*G.y+A[1][2]*G.z; + AG.z = A[2][0]*G.x+A[2][1]*G.y+A[2][2]*G.z; + + /* compute A*Q */ + AQ.x = A[0][0]*Q.x+A[0][1]*Q.y+A[0][2]*Q.z; + AQ.y = A[1][0]*Q.x+A[1][1]*Q.y+A[1][2]*Q.z; + AQ.z = A[2][0]*Q.x+A[2][1]*Q.y+A[2][2]*Q.z; + + /* compute quadratic equation c0+c1*t+c2*t^2 = 0 */ + c2 = G.x*AG.x+G.y*AG.y+G.z*AG.z; + c1 = B.x*G.x+B.y*G.y+B.z*G.z+2.0f*(Q.x*AG.x+Q.y*AG.y+Q.z*AG.z); + c0 = Q.x*AQ.x+Q.y*AQ.y+Q.z*AQ.z+B.x*Q.x+B.y*Q.y+B.z*Q.z+C; + + /* solve for intersections */ + int numIntersections; + discr = c1*c1-4.0*c0*c2; + if ( discr > EPS ) + { + numIntersections = 2; + discr = sqrt(discr); + invC2 = 1.0/c2; + root0 = -0.5*(c1+discr)*invC2; + root1 = -0.5*(c1-discr)*invC2; + first_intersection->x = Q.x+root0*G.x; + first_intersection->y = Q.y+root0*G.y; + first_intersection->z = Q.z+root0*G.z; + second_intersection->x = Q.x+root1*G.x; + second_intersection->y = Q.y+root1*G.y; + second_intersection->z = Q.z+root1*G.z; + + *first_intersection = first_intersection->add(axis.getOrigin()); + *second_intersection = second_intersection->add(axis.getOrigin()); + } + else if ( discr < -EPS ) + { + numIntersections = 0; + } + else + { + numIntersections = 1; + root = -0.5*c1/c2; + first_intersection->x = Q.x+root*G.x; + first_intersection->y = Q.y+root*G.y; + first_intersection->z = Q.z+root*G.z; + + *first_intersection = first_intersection->add(axis.getOrigin()); + } + + return numIntersections; +} + +void InfiniteCylinder::save(PackStream *stream) const +{ + axis.save(stream); + stream->write(&radius); +} + +void InfiniteCylinder::load(PackStream *stream) +{ + axis.load(stream); + stream->read(&radius); +} diff --git a/src/basics/InfiniteCylinder.h b/src/basics/InfiniteCylinder.h new file mode 100644 index 0000000..8bcdc2f --- /dev/null +++ b/src/basics/InfiniteCylinder.h @@ -0,0 +1,41 @@ +#ifndef INFINITECYLINDER_H +#define INFINITECYLINDER_H + +#include "basics_global.h" + +#include "InfiniteRay.h" + +namespace paysages { +namespace basics { + +/** + * Geometric cylinder, with infinite length. + */ +class BASICSSHARED_EXPORT InfiniteCylinder +{ +public: + InfiniteCylinder(); + InfiniteCylinder(const InfiniteRay &axis, double radius); + + inline const InfiniteRay& getAxis() const {return axis;} + inline double getRadius() const {return radius;} + + /** + * Check the intersection between the cylinder and an infinite ray. + * + * Returns the number of intersections (0, 1 or 2) and fill the intersection points. + */ + int checkRayIntersection(const InfiniteRay& ray, Vector3 *first_intersection, Vector3 *second_intersection) const; + + virtual void save(PackStream *stream) const; + virtual void load(PackStream *stream); + +protected: + InfiniteRay axis; + double radius; +}; + +} +} + +#endif // INFINITECYLINDER_H diff --git a/src/basics/InfinitePlane.cpp b/src/basics/InfinitePlane.cpp new file mode 100644 index 0000000..a4b9ba8 --- /dev/null +++ b/src/basics/InfinitePlane.cpp @@ -0,0 +1,48 @@ +#include "InfinitePlane.h" + +#include "PackStream.h" +#include "InfiniteRay.h" + +InfinitePlane::InfinitePlane() +{ +} + +InfinitePlane::InfinitePlane(const Vector3 &point, const Vector3 &normal): + point(point), normal(normal) +{ +} + +int InfinitePlane::checkRayIntersection(const InfiniteRay &ray, Vector3 *intersection) const +{ + Vector3 p1 = ray.getDirection(); + double d = normal.dotProduct(p1); + + if (fabs(d) < 1e-8) + { + if (normal.dotProduct(ray.getPointAtCursor(1.0).sub(point)) == 0) + { + return -1; + } + else + { + return 0; + } + } + + double u = point.sub(ray.getOrigin()).dotProduct(normal) / d; + *intersection = ray.getPointAtCursor(u); + return 1; +} + +void InfinitePlane::save(PackStream *stream) const +{ + point.save(stream); + normal.save(stream); +} + +void InfinitePlane::load(PackStream *stream) +{ + point.load(stream); + normal.load(stream); +} + diff --git a/src/basics/InfinitePlane.h b/src/basics/InfinitePlane.h new file mode 100644 index 0000000..e697311 --- /dev/null +++ b/src/basics/InfinitePlane.h @@ -0,0 +1,43 @@ +#ifndef INFINITEPLANE_H +#define INFINITEPLANE_H + +#include "basics_global.h" + +#include "Vector3.h" + +namespace paysages { +namespace basics { + +/** + * Infinite 3d geometric plane. + */ +class BASICSSHARED_EXPORT InfinitePlane +{ +public: + InfinitePlane(); + InfinitePlane(const Vector3 &point, const Vector3 &normal); + + /** + * Check the intersection between the plane and an infinite ray. + * + * Returns the number of intersections (0, 1), and fill the intersection point. + * + * Returns -1 if the ray is on the plane, and make for an infinite number of intersection points. + */ + int checkRayIntersection(const InfiniteRay& ray, Vector3 *intersection) const; + + inline const Vector3 &getPoint() const {return point;} + inline const Vector3 &getNormal() const {return normal;} + + virtual void save(PackStream *stream) const; + virtual void load(PackStream *stream); + +private: + Vector3 point; + Vector3 normal; +}; + +} +} + +#endif // INFINITEPLANE_H diff --git a/src/basics/InfiniteRay.cpp b/src/basics/InfiniteRay.cpp new file mode 100644 index 0000000..f3baa08 --- /dev/null +++ b/src/basics/InfiniteRay.cpp @@ -0,0 +1,27 @@ +#include "InfiniteRay.h" + +InfiniteRay::InfiniteRay() +{ +} + +InfiniteRay::InfiniteRay(const Vector3 &origin, const Vector3 &direction): + origin(origin), direction(direction.normalize()) +{ +} + +InfiniteRay InfiniteRay::fromPoints(const Vector3 &point1, const Vector3 &point2) +{ + return InfiniteRay(point1, point2.sub(point1).normalize()); +} + +void InfiniteRay::save(PackStream *stream) const +{ + origin.save(stream); + direction.save(stream); +} + +void InfiniteRay::load(PackStream *stream) +{ + origin.load(stream); + direction.load(stream); +} diff --git a/src/basics/InfiniteRay.h b/src/basics/InfiniteRay.h new file mode 100644 index 0000000..0c12ccf --- /dev/null +++ b/src/basics/InfiniteRay.h @@ -0,0 +1,39 @@ +#ifndef INFINITERAY_H +#define INFINITERAY_H + +#include "basics_global.h" + +#include "Vector3.h" + +namespace paysages { +namespace basics { + +/** + * Infinite ray (line). + */ +class BASICSSHARED_EXPORT InfiniteRay +{ +public: + InfiniteRay(); + InfiniteRay(const Vector3 &origin, const Vector3 &direction); + + static InfiniteRay fromPoints(const Vector3 &point1, const Vector3 &point2); + + inline const Vector3 &getOrigin() const {return origin;} + inline const Vector3 &getDirection() const {return direction;} + + inline double getCursor(const Vector3 &point) const {return point.sub(origin).dotProduct(direction);} + inline Vector3 getPointAtCursor(double distance) const {return origin.add(direction.scale(distance));} + + void save(PackStream *stream) const; + void load(PackStream *stream); + +private: + Vector3 origin; + Vector3 direction; +}; + +} +} + +#endif // INFINITERAY_H diff --git a/src/basics/Sphere.cpp b/src/basics/Sphere.cpp new file mode 100644 index 0000000..e7885df --- /dev/null +++ b/src/basics/Sphere.cpp @@ -0,0 +1,62 @@ +#include "Sphere.h" + +#include "PackStream.h" +#include "InfiniteRay.h" + +Sphere::Sphere() +{ +} + +Sphere::Sphere(const Vector3 ¢er, double radius): + center(center), radius(radius) +{ + radius2 = radius * radius; +} + +int Sphere::checkRayIntersection(const InfiniteRay &ray, Vector3 *first_intersection, Vector3 *second_intersection) const +{ + Vector3 L = ray.getOrigin().sub(center); + double b = 2.0 * ray.getDirection().dotProduct(L); + double c = L.dotProduct(L) - radius2; + + double discr = b * b - 4.0 * c; + if (discr < 0) + { + return 0; + } + else if (discr == 0) + { + *first_intersection = ray.getPointAtCursor(-0.5 * b); + return 1; + } + else + { + double x0 = (b > 0.0) ? -0.5 * (b + sqrt(discr)) : -0.5 * (b - sqrt(discr)); + double x1 = c / x0; + if (x0 > x1) + { + *first_intersection = ray.getPointAtCursor(x1); + *second_intersection = ray.getPointAtCursor(x0); + } + else + { + *first_intersection = ray.getPointAtCursor(x0); + *second_intersection = ray.getPointAtCursor(x1); + } + return 2; + } +} + +void Sphere::save(PackStream *stream) const +{ + center.save(stream); + stream->write(&radius); + stream->write(&radius2); +} + +void Sphere::load(PackStream *stream) +{ + center.load(stream); + stream->read(&radius); + stream->read(&radius2); +} diff --git a/src/basics/Sphere.h b/src/basics/Sphere.h new file mode 100644 index 0000000..c50d4a1 --- /dev/null +++ b/src/basics/Sphere.h @@ -0,0 +1,42 @@ +#ifndef SPHERE_H +#define SPHERE_H + +#include "basics_global.h" + +#include "Vector3.h" + +namespace paysages { +namespace basics { + +/** + * Geometric sphere. + */ +class BASICSSHARED_EXPORT Sphere +{ +public: + Sphere(); + Sphere(const Vector3 ¢er, double radius); + + inline const Vector3 &getCenter() const {return center;} + inline const double &getRadius() const {return radius;} + + /** + * Check the intersection between the sphere and an infinite ray. + * + * Returns the number of intersections (0, 1 or 2) and fill the intersection points. + */ + int checkRayIntersection(const InfiniteRay& ray, Vector3 *first_intersection, Vector3 *second_intersection) const; + + void save(PackStream *stream) const; + void load(PackStream *stream); + +private: + Vector3 center; + double radius; + double radius2; +}; + +} +} + +#endif // SPHERE_H diff --git a/src/basics/basics.pro b/src/basics/basics.pro index 976f73a..36bd35c 100644 --- a/src/basics/basics.pro +++ b/src/basics/basics.pro @@ -34,6 +34,12 @@ SOURCES += \ Texture4D.cpp \ NoiseState.cpp \ FractalNoise.cpp \ + CappedCylinder.cpp \ + InfiniteCylinder.cpp \ + InfiniteRay.cpp \ + Sphere.cpp \ + InfinitePlane.cpp \ + Disk.cpp \ SpaceGridIterator.cpp HEADERS +=\ @@ -56,6 +62,12 @@ HEADERS +=\ Texture4D.h \ NoiseState.h \ FractalNoise.h \ + CappedCylinder.h \ + InfiniteCylinder.h \ + InfiniteRay.h \ + Sphere.h \ + InfinitePlane.h \ + Disk.h \ SpaceGridIterator.h unix:!symbian { diff --git a/src/basics/basics_global.h b/src/basics/basics_global.h index 3cb1d2e..6190283 100644 --- a/src/basics/basics_global.h +++ b/src/basics/basics_global.h @@ -26,6 +26,10 @@ namespace basics { class Texture2D; class Texture3D; class Texture4D; + class CappedCylinder; + class InfiniteRay; + class Sphere; + class InfinitePlane; } } using namespace paysages::basics; diff --git a/src/tests/CappedCylinder_Test.cpp b/src/tests/CappedCylinder_Test.cpp new file mode 100644 index 0000000..cfa60cd --- /dev/null +++ b/src/tests/CappedCylinder_Test.cpp @@ -0,0 +1,38 @@ +#include "BaseTestCase.h" + +#include "CappedCylinder.h" + +TEST(CappedCylinder, checkRayIntersection) +{ + CappedCylinder cylinder(VECTOR_DOWN, VECTOR_UP, 1.0, 2.0); + + int intersect_count; + Vector3 p1, p2; + + intersect_count = cylinder.checkRayIntersection(InfiniteRay(Vector3(1.5, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + EXPECT_EQ(0, intersect_count); + + intersect_count = cylinder.checkRayIntersection(InfiniteRay(Vector3(1.0, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + EXPECT_EQ(1, intersect_count); + EXPECT_VECTOR3_COORDS(p1, 1.0, 0.0, 0.0); + + intersect_count = cylinder.checkRayIntersection(InfiniteRay(Vector3(0.5, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + EXPECT_EQ(2, intersect_count); + EXPECT_VECTOR3_COORDS(p1, 0.5, 0.0, -cos(asin(0.5))); + EXPECT_VECTOR3_COORDS(p2, 0.5, 0.0, cos(asin(0.5))); + + intersect_count = cylinder.checkRayIntersection(InfiniteRay(Vector3(0.5, -2.1, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + EXPECT_EQ(0, intersect_count); + + intersect_count = cylinder.checkRayIntersection(InfiniteRay(Vector3(0.5, 2.1, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + EXPECT_EQ(0, intersect_count); + + // diagonal cases (through a cap) + intersect_count = cylinder.checkRayIntersection(InfiniteRay(Vector3(-2.0, -1.0, 0.0), Vector3(1.0, 1.0, 0.0)), &p1, &p2); + EXPECT_EQ(1, intersect_count); + EXPECT_VECTOR3_COORDS(p1, -1.0, 0.0, 0.0); + + intersect_count = cylinder.checkRayIntersection(InfiniteRay(Vector3(-2.0, 3.0, 0.0), Vector3(1.0, -1.0, 0.0)), &p1, &p2); + EXPECT_EQ(1, intersect_count); + EXPECT_VECTOR3_COORDS(p1, 1.0, 0.0, 0.0); +} diff --git a/src/tests/Disk_Test.cpp b/src/tests/Disk_Test.cpp new file mode 100644 index 0000000..062d4e2 --- /dev/null +++ b/src/tests/Disk_Test.cpp @@ -0,0 +1,25 @@ +#include "BaseTestCase.h" + +#include "InfiniteRay.h" +#include "Disk.h" + +TEST(Disk, checkRayIntersection) +{ + Disk disk(VECTOR_UP, VECTOR_UP, 0.8); + + int result; + Vector3 point; + + result = disk.checkRayIntersection(InfiniteRay(Vector3(0.0, 2.0, 0.0), VECTOR_SOUTH), &point); + ASSERT_EQ(0, result); + + result = disk.checkRayIntersection(InfiniteRay(Vector3(1.5, 2.0, -1.0), VECTOR_DOWN), &point); + ASSERT_EQ(0, result); + + result = disk.checkRayIntersection(InfiniteRay(Vector3(0.5, 2.0, 0.0), VECTOR_DOWN), &point); + ASSERT_EQ(1, result); + EXPECT_VECTOR3_COORDS(point, 0.5, 1.0, 0.0); + + result = disk.checkRayIntersection(InfiniteRay(Vector3(1.0, 1.0, 0.0), VECTOR_EAST), &point); + ASSERT_EQ(-1, result); +} diff --git a/src/tests/InfiniteCylinder_Test.cpp b/src/tests/InfiniteCylinder_Test.cpp new file mode 100644 index 0000000..6ce8254 --- /dev/null +++ b/src/tests/InfiniteCylinder_Test.cpp @@ -0,0 +1,41 @@ +#include "BaseTestCase.h" + +#include "InfiniteRay.h" +#include "InfiniteCylinder.h" +#include + +TEST(InfiniteCylinder, checkRayIntersection) +{ + InfiniteRay ray(VECTOR_ZERO, VECTOR_UP); + InfiniteCylinder cylinder(ray, 1.0); + + int intersect_count; + Vector3 p1, p2; + + intersect_count = cylinder.checkRayIntersection(InfiniteRay(Vector3(1.5, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + EXPECT_EQ(0, intersect_count); + + intersect_count = cylinder.checkRayIntersection(InfiniteRay(Vector3(1.0, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + EXPECT_EQ(1, intersect_count); + EXPECT_VECTOR3_COORDS(p1, 1.0, 0.0, 0.0); + + intersect_count = cylinder.checkRayIntersection(InfiniteRay(Vector3(0.5, 0.0, 0.0), Vector3(0.0, 0.0, 1.0)), &p1, &p2); + EXPECT_EQ(2, intersect_count); + EXPECT_VECTOR3_COORDS(p1, 0.5, 0.0, -cos(asin(0.5))); + EXPECT_VECTOR3_COORDS(p2, 0.5, 0.0, cos(asin(0.5))); +} + +TEST(InfiniteCylinder, checkRayIntersection2) +{ + InfiniteRay ray(Vector3(-1.4, 1.5, 1.0), Vector3(1.0, 0.0, 0.0)); + InfiniteCylinder cylinder(ray, 0.5); + + int intersect_count; + Vector3 p1, p2; + + intersect_count = cylinder.checkRayIntersection(InfiniteRay::fromPoints(Vector3(0.0, 1.5, 0.0), Vector3(0.0, 1.5, 2.0)), &p1, &p2); + EXPECT_EQ(2, intersect_count); + EXPECT_VECTOR3_COORDS(p1, 0.0, 1.5, 0.5); + EXPECT_VECTOR3_COORDS(p2, 0.0, 1.5, 1.5); +} + diff --git a/src/tests/InfinitePlane_Test.cpp b/src/tests/InfinitePlane_Test.cpp new file mode 100644 index 0000000..8a92517 --- /dev/null +++ b/src/tests/InfinitePlane_Test.cpp @@ -0,0 +1,34 @@ +#include "BaseTestCase.h" + +#include "InfiniteRay.h" +#include "InfinitePlane.h" + +TEST(InfinitePlane, checkRayIntersection) +{ + InfinitePlane plane(VECTOR_UP, VECTOR_UP); + + int result; + Vector3 point; + + result = plane.checkRayIntersection(InfiniteRay(Vector3(0.0, 2.0, 0.0), VECTOR_SOUTH), &point); + ASSERT_EQ(0, result); + + result = plane.checkRayIntersection(InfiniteRay(Vector3(1.5, 2.0, -1.0), VECTOR_DOWN), &point); + ASSERT_EQ(1, result); + EXPECT_VECTOR3_COORDS(point, 1.5, 1.0, -1.0); + + result = plane.checkRayIntersection(InfiniteRay(Vector3(1.0, 1.0, 0.0), VECTOR_EAST), &point); + ASSERT_EQ(-1, result); +} + +TEST(InfinitePlane, checkRayIntersection_oblique) +{ + InfinitePlane plane(Vector3(14.0, -5.0, 3.5), Vector3(1.0, 0.0, 0.0)); + InfiniteRay ray(Vector3(2.0, 2.0, 2.0), Vector3(1.0, 1.0, 1.0).normalize()); + int result; + Vector3 point; + + result = plane.checkRayIntersection(ray, &point); + ASSERT_EQ(1, result); + EXPECT_VECTOR3_COORDS(point, 14.0, 14.0, 14.0); +} diff --git a/src/tests/Sphere_Test.cpp b/src/tests/Sphere_Test.cpp new file mode 100644 index 0000000..afdef99 --- /dev/null +++ b/src/tests/Sphere_Test.cpp @@ -0,0 +1,24 @@ +#include "BaseTestCase.h" + +#include "InfiniteRay.h" +#include "Sphere.h" + +TEST(Sphere, checkRayIntersection) +{ + Sphere sphere(Vector3(2.0, 1.0, 1.0), 0.5); + + int intersect_count; + Vector3 p1, p2; + + intersect_count = sphere.checkRayIntersection(InfiniteRay(Vector3(0.0, 0.0, 0.0), VECTOR_SOUTH), &p1, &p2); + ASSERT_EQ(0, intersect_count); + + intersect_count = sphere.checkRayIntersection(InfiniteRay(Vector3(1.5, 1.0, 0.0), VECTOR_SOUTH), &p1, &p2); + ASSERT_EQ(1, intersect_count); + EXPECT_VECTOR3_COORDS(p1, 1.5, 1.0, 1.0); + + intersect_count = sphere.checkRayIntersection(InfiniteRay(Vector3(2.0, 1.0, 0.0), VECTOR_SOUTH), &p1, &p2); + ASSERT_EQ(2, intersect_count); + EXPECT_VECTOR3_COORDS(p1, 2.0, 1.0, 0.5); + EXPECT_VECTOR3_COORDS(p2, 2.0, 1.0, 1.5); +} diff --git a/src/tests/tests.pro b/src/tests/tests.pro index 96aed40..9c180f1 100644 --- a/src/tests/tests.pro +++ b/src/tests/tests.pro @@ -34,6 +34,11 @@ SOURCES += main.cpp \ GodRaysSampler_Test.cpp \ Interpolation_Test.cpp \ Rasterizer_Test.cpp \ + CappedCylinder_Test.cpp \ + InfiniteCylinder_Test.cpp \ + Sphere_Test.cpp \ + InfinitePlane_Test.cpp \ + Disk_Test.cpp \ Vector3_Test.cpp HEADERS += \