diff --git a/src/basics/SpaceGridIterator.cpp b/src/basics/SpaceGridIterator.cpp new file mode 100644 index 0000000..21fb3d0 --- /dev/null +++ b/src/basics/SpaceGridIterator.cpp @@ -0,0 +1,5 @@ +#include "SpaceGridIterator.h" + +SpaceGridIterator::SpaceGridIterator() +{ +} diff --git a/src/basics/SpaceGridIterator.h b/src/basics/SpaceGridIterator.h new file mode 100644 index 0000000..bec1944 --- /dev/null +++ b/src/basics/SpaceGridIterator.h @@ -0,0 +1,30 @@ +#ifndef SPACEGRIDITERATOR_H +#define SPACEGRIDITERATOR_H + +#include "basics_global.h" + +namespace paysages { +namespace basics { + +/** + * Iterator delegate that can receive sequential grid coordinates. + * + * This may be useful for ray marching algorithms for example. + */ +class BASICSSHARED_EXPORT SpaceGridIterator +{ +public: + SpaceGridIterator(); + + /** + * Abstract method to implement to receive grid coordinates. + * + * Return false to interrupt the iteration, true to continue. + */ + virtual bool onCell(int x, int y, int z) = 0; +}; + +} +} + +#endif // SPACEGRIDITERATOR_H diff --git a/src/basics/SpaceSegment.cpp b/src/basics/SpaceSegment.cpp index 2e718d1..bfec17e 100644 --- a/src/basics/SpaceSegment.cpp +++ b/src/basics/SpaceSegment.cpp @@ -1,5 +1,8 @@ #include "SpaceSegment.h" +#include +#include "SpaceGridIterator.h" + SpaceSegment::SpaceSegment(const Vector3& start, const Vector3& end): start(start), end(end) { @@ -54,3 +57,81 @@ bool SpaceSegment::intersectYInterval(double ymin, double ymax) return true; } + +SpaceSegment SpaceSegment::projectedOnXPlane(double x) const +{ + return SpaceSegment(Vector3(x, start.y, start.z), Vector3(x, end.y, end.z)); +} + +SpaceSegment SpaceSegment::projectedOnYPlane(double y) const +{ + return SpaceSegment(Vector3(start.x, y, start.z), Vector3(end.x, y, end.z)); +} + +SpaceSegment SpaceSegment::projectedOnZPlane(double z) const +{ + return SpaceSegment(Vector3(start.x, start.y, z), Vector3(end.x, end.y, z)); +} + +SpaceSegment SpaceSegment::scaled(double factor) const +{ + return SpaceSegment(start.scale(factor), end.scale(factor)); +} + +bool SpaceSegment::iterateOnGrid(SpaceGridIterator &delegate) +{ + Vector3 diff = end.sub(start); + + int stepX = diff.x < 0.0 ? -1 : 1; + int stepY = diff.y < 0.0 ? -1 : 1; + int stepZ = diff.z < 0.0 ? -1 : 1; + int X = (int)floor(start.x); + int Y = (int)floor(start.y); + int Z = (int)floor(start.z); + int limitX = (int)floor(end.x) + stepX; + int limitY = (int)floor(end.y) + stepY; + int limitZ = (int)floor(end.z) + stepZ; + double tDeltaX = diff.x == 0.0 ? 0.0 : 1.0 / fabs(diff.x); + double tDeltaY = diff.y == 0.0 ? 0.0 : 1.0 / fabs(diff.y); + double tDeltaZ = diff.z == 0.0 ? 0.0 : 1.0 / fabs(diff.z); + double tMaxX = diff.x == 0.0 ? INFINITY : ((double)(X + (stepX > 0 ? 1 : 0)) - start.x) / diff.x; + double tMaxY = diff.y == 0.0 ? INFINITY : ((double)(Y + (stepY > 0 ? 1 : 0)) - start.y) / diff.y; + double tMaxZ = diff.z == 0.0 ? INFINITY : ((double)(Z + (stepZ > 0 ? 1 : 0)) - start.z) / diff.z; + + do + { + if (not delegate.onCell(X, Y, Z)) + { + return false; + } + + if (tMaxX < tMaxY) + { + if (tMaxX < tMaxZ) + { + X = X + stepX; + tMaxX = tMaxX + tDeltaX; + } + else + { + Z = Z + stepZ; + tMaxZ = tMaxZ + tDeltaZ; + } + } + else + { + if(tMaxY < tMaxZ) + { + Y = Y + stepY; + tMaxY = tMaxY + tDeltaY; + } + else + { + Z= Z + stepZ; + tMaxZ = tMaxZ + tDeltaZ; + } + } + } while (X != limitX and Y != limitY and Z != limitZ); + + return true; +} diff --git a/src/basics/SpaceSegment.h b/src/basics/SpaceSegment.h index a7ffd8b..e6e0f4a 100644 --- a/src/basics/SpaceSegment.h +++ b/src/basics/SpaceSegment.h @@ -8,8 +8,8 @@ namespace paysages { namespace basics { -/*! - * \brief A segment in 3D space (mainly useful for rays). +/** + * A delimited segment in 3D space (mainly useful for rays). */ class BASICSSHARED_EXPORT SpaceSegment { @@ -17,19 +17,53 @@ public: SpaceSegment(const Vector3& start, const Vector3& end); SpaceSegment(): SpaceSegment(Vector3(), Vector3()) {} - inline Vector3 getStart() const {return start;} - inline Vector3 getEnd() const {return end;} + inline const Vector3 &getStart() const {return start;} + inline const Vector3 &getEnd() const {return end;} + inline Vector3 getDirection() const {return end.sub(start);} + inline double getLength() const {return end.sub(start).getNorm();} inline double getXDiff() const {return end.x - start.x;} inline double getYDiff() const {return end.y - start.y;} inline double getZDiff() const {return end.z - start.z;} - /*! - * \brief Keep only the intersection with a slice orthogonal to the Y axis. - * \return true if a segment remains + /** + * Keep only the intersection with a slice orthogonal to the Y axis. + * + * Return true if a segment remains. */ bool intersectYInterval(double ymin, double ymax); + /** + * Return a version of this segment, projected on a X plane. + */ + SpaceSegment projectedOnXPlane(double x=0.0) const; + /** + * Return a version of this segment, projected on a Y plane. + */ + SpaceSegment projectedOnYPlane(double y=0.0) const; + /** + * Return a version of this segment, projected on a Z plane. + */ + SpaceSegment projectedOnZPlane(double z=0.0) const; + + /** + * Return a scaled version of this segment. + * + * Pay attention, scaling is done from the coordinates origin (0,0,0), not the segment center. + */ + SpaceSegment scaled(double factor) const; + + /** + * Iterate inside a virtual grid. + * + * The space is considered as cut into 1.0-sized cubic cells, + * and the delegate will be called with coordinates of the cells + * traversed by this segment. + * + * Return false if the iteration was aborted by the delegate, true otherwise. + */ + bool iterateOnGrid(SpaceGridIterator &delegate); + private: Vector3 start; Vector3 end; diff --git a/src/basics/basics.pro b/src/basics/basics.pro index bc03fe0..976f73a 100644 --- a/src/basics/basics.pro +++ b/src/basics/basics.pro @@ -33,7 +33,8 @@ SOURCES += \ Texture3D.cpp \ Texture4D.cpp \ NoiseState.cpp \ - FractalNoise.cpp + FractalNoise.cpp \ + SpaceGridIterator.cpp HEADERS +=\ basics_global.h \ @@ -54,7 +55,8 @@ HEADERS +=\ Texture3D.h \ Texture4D.h \ NoiseState.h \ - FractalNoise.h + FractalNoise.h \ + SpaceGridIterator.h unix:!symbian { maemo5 { diff --git a/src/basics/basics_global.h b/src/basics/basics_global.h index 3711655..3cb1d2e 100644 --- a/src/basics/basics_global.h +++ b/src/basics/basics_global.h @@ -15,6 +15,7 @@ namespace basics { class Vector3; class Matrix4; class BoundingBox; + class SpaceGridIterator; class SpaceSegment; class Color; class NoiseGenerator; diff --git a/src/tests/SpaceSegment_Test.cpp b/src/tests/SpaceSegment_Test.cpp new file mode 100644 index 0000000..0a9a4aa --- /dev/null +++ b/src/tests/SpaceSegment_Test.cpp @@ -0,0 +1,61 @@ +#include "BaseTestCase.h" + +#include "SpaceSegment.h" +#include "SpaceGridIterator.h" + +class CollectGridIterator: public SpaceGridIterator +{ +public: + std::vector locations; +protected: + virtual bool onCell(int x, int y, int z) override + { + locations.push_back(Vector3(x, y, z)); + return true; + } +}; + +TEST(SpaceSegment, iterateOnGrid) +{ + CollectGridIterator it; + SpaceSegment segment(Vector3(0.5, 1.5, 0.0), Vector3(2.5, 0.5, 0.0)); + segment.iterateOnGrid(it); + + ASSERT_EQ(4, it.locations.size()); + EXPECT_VECTOR3_COORDS(it.locations[0], 0.0, 1.0, 0.0); + EXPECT_VECTOR3_COORDS(it.locations[1], 1.0, 1.0, 0.0); + EXPECT_VECTOR3_COORDS(it.locations[2], 1.0, 0.0, 0.0); + EXPECT_VECTOR3_COORDS(it.locations[3], 2.0, 0.0, 0.0); +} + +TEST(SpaceSegment, iterateOnGrid_Corner) +{ + CollectGridIterator it; + SpaceSegment segment(Vector3(0.5, 0.5, 0.5), Vector3(2.5, 2.5, 2.5)); + segment.iterateOnGrid(it); + + ASSERT_EQ(7, it.locations.size()); + EXPECT_VECTOR3_COORDS(it.locations[0], 0.0, 0.0, 0.0); + EXPECT_VECTOR3_COORDS(it.locations[3], 1.0, 1.0, 1.0); + EXPECT_VECTOR3_COORDS(it.locations[6], 2.0, 2.0, 2.0); +} + +TEST(SpaceSegment, iterateOnGrid_OneCell) +{ + CollectGridIterator it; + SpaceSegment segment(Vector3(8.1, 8.2, 8.9), Vector3(8.9, 8.3, 8.6)); + segment.iterateOnGrid(it); + + ASSERT_EQ(1, it.locations.size()); + EXPECT_VECTOR3_COORDS(it.locations[0], 8.0, 8.0, 8.0); +} + +TEST(SpaceSegment, iterateOnGrid_Negative) +{ + CollectGridIterator it; + SpaceSegment segment(Vector3(-8.1, -8.2, -8.9), Vector3(-8.9, -8.3, -8.6)); + segment.iterateOnGrid(it); + + ASSERT_EQ(1, it.locations.size()); + EXPECT_VECTOR3_COORDS(it.locations[0], -9.0, -9.0, -9.0); +} diff --git a/src/tests/tests.pro b/src/tests/tests.pro index 5aea89b..96aed40 100644 --- a/src/tests/tests.pro +++ b/src/tests/tests.pro @@ -18,6 +18,7 @@ SOURCES += main.cpp \ FluidMediumManager_Test.cpp \ VertexArray_Test.cpp \ FractalNoise_Test.cpp \ + SpaceSegment_Test.cpp \ Canvas_Test.cpp \ CanvasPortion_Test.cpp \ CanvasPreview_Test.cpp \