diff --git a/src/Primitives/Capsule.cpp b/src/Primitives/Capsule.cpp index 8699c21a4..852d23b8c 100644 --- a/src/Primitives/Capsule.cpp +++ b/src/Primitives/Capsule.cpp @@ -25,13 +25,70 @@ #include "Capsule.h" #include "Math/Vector3.h" +#include "Math/Functions.h" #include "Primitives/Implementation/Spheroid.h" #include "Primitives/Implementation/WireframeSpheroid.h" +#include "Trade/MeshData2D.h" #include "Trade/MeshData3D.h" namespace Magnum { namespace Primitives { -Trade::MeshData3D Capsule::solid(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float halfLength, TextureCoords textureCoords) { +Trade::MeshData2D Capsule2D::wireframe(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, Float halfLength) { + CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1, "Capsule must have at least one hemisphere ring, one cylinder ring and three segments", Trade::MeshData2D(Mesh::Primitive::Lines, {}, {}, {})); + + std::vector positions; + positions.reserve(hemisphereRings*4+2+(cylinderRings-1)*2); + const Rad angleIncrement(Constants::pi()/(2.0f*hemisphereRings)); + const Float cylinderIncrement = 2.0f*halfLength/cylinderRings; + + /* Bottom cap vertex */ + positions.emplace_back(0.0f, -halfLength-1.0f); + + /* Bottom hemisphere */ + for(UnsignedInt i = 0; i != hemisphereRings; ++i) { + const Rad angle((i+1)*angleIncrement); + const Float x = Math::sin(angle); + const Float y = -Math::cos(angle)-halfLength; + positions.insert(positions.end(), {{-x, y}, {x, y}}); + } + + /* Cylinder (bottom and top vertices are done within caps */ + for(UnsignedInt i = 0; i != cylinderRings-1; ++i) { + const Float y = (i+1)*cylinderIncrement-halfLength; + positions.insert(positions.end(), {{-1.0f, y}, {1.0f, y}}); + } + + /* Top hemisphere */ + for(UnsignedInt i = 0; i != hemisphereRings; ++i) { + const Rad angle(i*angleIncrement); + const Float x = Math::cos(angle); + const Float y = Math::sin(angle)+halfLength; + positions.insert(positions.end(), {{-x, y}, {x, y}}); + } + + /* Top cap vertex */ + positions.emplace_back(0.0f, halfLength+1.0f); + + std::vector indices; + indices.reserve(hemisphereRings*8+cylinderRings*4); + + /* Bottom cap indices */ + indices.insert(indices.end(), {0, 1, 0, 2}); + + /* Side indices */ + for(UnsignedInt i = 0; i != cylinderRings+hemisphereRings*2-2; ++i) + indices.insert(indices.end(), {i*2+1, i*2+3, + i*2+2, i*2+4}); + + /* Top cap indices */ + indices.insert(indices.end(), + {UnsignedInt(positions.size())-3, UnsignedInt(positions.size())-1, + UnsignedInt(positions.size())-2, UnsignedInt(positions.size())-1}); + + return Trade::MeshData2D(Mesh::Primitive::Lines, std::move(indices), {std::move(positions)}, {}); +} + +Trade::MeshData3D Capsule3D::solid(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float halfLength, TextureCoords textureCoords) { CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1 && segments >= 3, "Capsule must have at least one hemisphere ring, one cylinder ring and three segments", Trade::MeshData3D(Mesh::Primitive::Triangles, {}, {}, {}, {})); Implementation::Spheroid capsule(segments, textureCoords == TextureCoords::Generate ? @@ -65,7 +122,7 @@ Trade::MeshData3D Capsule::solid(UnsignedInt hemisphereRings, UnsignedInt cylind return capsule.finalize(); } -Trade::MeshData3D Capsule::wireframe(const UnsignedInt hemisphereRings, const UnsignedInt cylinderRings, const UnsignedInt segments, const Float halfLength) { +Trade::MeshData3D Capsule3D::wireframe(const UnsignedInt hemisphereRings, const UnsignedInt cylinderRings, const UnsignedInt segments, const Float halfLength) { CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1 && segments >= 4 && segments%4 == 0, "Primitives::Capsule::wireframe(): improper parameters", Trade::MeshData3D(Mesh::Primitive::Lines, {}, {}, {}, {})); Implementation::WireframeSpheroid capsule(segments/4); diff --git a/src/Primitives/Capsule.h b/src/Primitives/Capsule.h index d5ab50416..64adb1dbd 100644 --- a/src/Primitives/Capsule.h +++ b/src/Primitives/Capsule.h @@ -33,12 +33,32 @@ namespace Magnum { namespace Primitives { +/** +@brief 2D capsule primitive + +%Cylinder of radius `1` along Y axis with hemispheres instead of caps. +*/ +class MAGNUM_PRIMITIVES_EXPORT Capsule2D { + public: + /** + * @brief Wireframe capsule + * @param hemisphereRings Number of (line) rings for each hemisphere. + * Must be larger or equal to 1. + * @param cylinderRings Number of (line) rings for cylinder. Must be + * larger or equal to 1. + * @param halfLength Half the length of cylinder part + * + * Indexed @ref Mesh::Primitive "Lines". + */ + static Trade::MeshData2D wireframe(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, Float halfLength); +}; + /** @brief 3D capsule primitive %Cylinder of radius `1` along Y axis with hemispheres instead of caps. */ -class MAGNUM_PRIMITIVES_EXPORT Capsule { +class MAGNUM_PRIMITIVES_EXPORT Capsule3D { public: /** @brief Whether to generate texture coordinates */ enum class TextureCoords: UnsignedByte { diff --git a/src/Primitives/Test/CapsuleTest.cpp b/src/Primitives/Test/CapsuleTest.cpp index 6a8670ab2..23efaba1e 100644 --- a/src/Primitives/Test/CapsuleTest.cpp +++ b/src/Primitives/Test/CapsuleTest.cpp @@ -29,6 +29,7 @@ #include #include "Math/Vector3.h" +#include "Trade/MeshData2D.h" #include "Trade/MeshData3D.h" #include "Primitives/Capsule.h" @@ -38,19 +39,66 @@ class CapsuleTest: public TestSuite::Tester { public: CapsuleTest(); - void solidWithoutTextureCoords(); - void solidWithTextureCoords(); - void wireframe(); + void wireframe2D(); + + void solid3DWithoutTextureCoords(); + void solid3DWithTextureCoords(); + void wireframe3D(); }; CapsuleTest::CapsuleTest() { - addTests({&CapsuleTest::solidWithoutTextureCoords, - &CapsuleTest::solidWithTextureCoords, - &CapsuleTest::wireframe}); + addTests({&CapsuleTest::wireframe2D, + &CapsuleTest::solid3DWithoutTextureCoords, + &CapsuleTest::solid3DWithTextureCoords, + &CapsuleTest::wireframe3D}); +} + +void CapsuleTest::wireframe2D() { + Trade::MeshData2D capsule = Capsule2D::wireframe(2, 4, 0.5f); + + CORRADE_COMPARE_AS(capsule.positions(0), (std::vector{ + {0.0f, -1.5f}, + + {-0.707107f, -1.20711f}, + {0.707107f, -1.20711f}, + + {-1.0f, -0.5f}, + {1.0f, -0.5f}, + + {-1.0f, -0.25f}, + {1.0f, -0.25f}, + + {-1.0f, 0.0f}, + {1.0f, 0.0f}, + + {-1.0f, 0.25f}, + {1.0f, 0.25f}, + + {-1.0f, 0.5f}, + {1.0f, 0.5f}, + + {-0.707107f, 1.20711f}, + {0.707107f, 1.20711f}, + + {0.0f, 1.5f} + }), TestSuite::Compare::Container); + + CORRADE_COMPARE_AS(capsule.indices(), (std::vector{ + 0, 1, 0, 2, + + 1, 3, 2, 4, + 3, 5, 4, 6, + 5, 7, 6, 8, + 7, 9, 8, 10, + 9, 11, 10, 12, + 11, 13, 12, 14, + + 13, 15, 14, 15 + }), TestSuite::Compare::Container); } -void CapsuleTest::solidWithoutTextureCoords() { - Trade::MeshData3D capsule = Capsule::solid(2, 4, 3, 1.0f); +void CapsuleTest::solid3DWithoutTextureCoords() { + Trade::MeshData3D capsule = Capsule3D::solid(2, 4, 3, 0.5f); CORRADE_COMPARE_AS(capsule.positions(0), (std::vector{ {0.0f, -1.5f, 0.0f}, @@ -132,8 +180,8 @@ void CapsuleTest::solidWithoutTextureCoords() { }), TestSuite::Compare::Container); } -void CapsuleTest::solidWithTextureCoords() { - Trade::MeshData3D capsule = Capsule::solid(2, 2, 3, 1.0f, Capsule::TextureCoords::Generate); +void CapsuleTest::solid3DWithTextureCoords() { + Trade::MeshData3D capsule = Capsule3D::solid(2, 2, 3, 0.5f, Capsule3D::TextureCoords::Generate); CORRADE_COMPARE_AS(capsule.positions(0), (std::vector{ {0.0f, -1.5f, 0.0f}, @@ -207,8 +255,8 @@ void CapsuleTest::solidWithTextureCoords() { }), TestSuite::Compare::Container); } -void CapsuleTest::wireframe() { - Trade::MeshData3D capsule = Capsule::wireframe(2, 2, 8, 1.0f); +void CapsuleTest::wireframe3D() { + Trade::MeshData3D capsule = Capsule3D::wireframe(2, 2, 8, 0.5f); CORRADE_COMPARE_AS(capsule.positions(0), (std::vector{ {0.0f, -1.5f, 0.0f},