PlayRho  2.0.0
An interactive physics engine & library.
WorldShape.cpp

This is the googletest based unit testing file for the free function interfaces to playrho::d2::World shape member functions and additional functionality.

/*
* Copyright (c) 2023 Louis Langholtz https://github.com/louis-langholtz/PlayRho
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "UnitTests.hpp"
#include <playrho/d2/part/Compositor.hpp>
using namespace playrho;
using namespace playrho::d2;
TEST(WorldShape, CreateAttachDetach)
{
auto world = World{};
EXPECT_THROW(Attach(world, BodyID(0u), ShapeID(0u)), std::out_of_range);
EXPECT_THROW(Detach(world, BodyID(0u), ShapeID(0u)), std::out_of_range);
const auto shapeId = CreateShape(world, DiskShapeConf{});
const auto body = CreateBody(world);
EXPECT_NO_THROW(Attach(world, body, shapeId));
auto shapeIds = std::vector<ShapeID>{};
EXPECT_NO_THROW(shapeIds = GetShapes(world, body));
ASSERT_EQ(size(shapeIds), 1u);
EXPECT_EQ(*begin(shapeIds), shapeId);
EXPECT_NO_THROW(Detach(world, body, shapeId));
EXPECT_NO_THROW(shapeIds = GetShapes(world, body));
EXPECT_EQ(size(shapeIds), 0u);
}
TEST(WorldShape, CreateMatchesConf)
{
const auto density = 2_kgpm2;
const auto friction = Real(0.5);
const auto restitution = Real(0.4);
const auto isSensor = true;
const auto conf = DiskShapeConf{}.UseFriction(friction).UseRestitution(restitution).UseDensity(density).UseIsSensor(isSensor);
const auto shapeA = Shape(conf);
World world;
const auto shapeId = CreateShape(world, shapeA);
EXPECT_EQ(GetShape(world, shapeId), shapeA);
EXPECT_EQ(GetDensity(world, shapeId), density);
EXPECT_EQ(GetFriction(world, shapeId), friction);
EXPECT_EQ(GetRestitution(world, shapeId), restitution);
EXPECT_EQ(IsSensor(world, shapeId), isSensor);
}
TEST(WorldShape, SetFilterData)
{
auto world = World{};
const auto shapeId = CreateShape(world, DiskShapeConf{});
const auto original = GetFilterData(world, shapeId);
auto filter = original;
ASSERT_EQ(filter, Filter());
filter.categoryBits = ~filter.categoryBits;
filter.groupIndex = ~filter.groupIndex;
filter.maskBits = ~filter.maskBits;
ASSERT_NE(original.categoryBits, filter.categoryBits);
ASSERT_NE(original.groupIndex, filter.groupIndex);
ASSERT_NE(original.maskBits, filter.maskBits);
EXPECT_NO_THROW(SetFilterData(world, shapeId, filter));
EXPECT_EQ(GetFilterData(world, shapeId).categoryBits, filter.categoryBits);
EXPECT_EQ(GetFilterData(world, shapeId).groupIndex, filter.groupIndex);
EXPECT_EQ(GetFilterData(world, shapeId).maskBits, filter.maskBits);
}
TEST(WorldShape, SetSensor)
{
World world;
const auto shapeId = CreateShape(world, DiskShapeConf{});
EXPECT_NO_THROW(SetSensor(world, shapeId, true));
EXPECT_TRUE(IsSensor(world, shapeId));
EXPECT_NO_THROW(SetSensor(world, shapeId, true));
EXPECT_TRUE(IsSensor(world, shapeId));
EXPECT_NO_THROW(SetSensor(world, shapeId, false));
EXPECT_FALSE(IsSensor(world, shapeId));
}
TEST(WorldShape, SetFriction)
{
World world;
const auto shapeId = CreateShape(world, DiskShapeConf{});
auto value = Real(0);
EXPECT_NO_THROW(SetFriction(world, shapeId, value));
EXPECT_EQ(GetFriction(world, shapeId), value);
value = Real(0.5);
EXPECT_NO_THROW(SetFriction(world, shapeId, value));
EXPECT_EQ(GetFriction(world, shapeId), value);
value = Real(1.0);
EXPECT_NO_THROW(SetFriction(world, shapeId, value));
EXPECT_EQ(GetFriction(world, shapeId), value);
}
TEST(WorldShape, SetRestitution)
{
World world;
const auto shapeId = CreateShape(world, DiskShapeConf{});
auto value = Real(0);
EXPECT_NO_THROW(SetRestitution(world, shapeId, value));
EXPECT_EQ(GetRestitution(world, shapeId), value);
value = Real(0.5);
EXPECT_NO_THROW(SetRestitution(world, shapeId, value));
EXPECT_EQ(GetRestitution(world, shapeId), value);
value = Real(1.0);
EXPECT_NO_THROW(SetRestitution(world, shapeId, value));
EXPECT_EQ(GetRestitution(world, shapeId), value);
}
TEST(WorldShape, SetDensity)
{
World world;
const auto shapeId = CreateShape(world, DiskShapeConf{});
auto value = AreaDensity(0_kgpm2);
EXPECT_NO_THROW(SetDensity(world, shapeId, value));
EXPECT_EQ(GetDensity(world, shapeId), value);
value = AreaDensity{1_kgpm2};
EXPECT_NO_THROW(SetDensity(world, shapeId, value));
EXPECT_EQ(GetDensity(world, shapeId), value);
value = AreaDensity(2_kgpm2);
EXPECT_NO_THROW(SetDensity(world, shapeId, value));
EXPECT_EQ(GetDensity(world, shapeId), value);
}
TEST(WorldShape, TranslateDiskShape)
{
World world;
const auto location0 = Length2{1_m, 2_m};
const auto shapeId = CreateShape(world, DiskShapeConf{}.UseLocation(location0));
auto shape = Shape{};
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
ASSERT_EQ(GetChild(shape, 0u).GetVertex(0), location0);
auto value = Length2{0_m, 0_m};
EXPECT_NO_THROW(Translate(world, shapeId, value));
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
EXPECT_EQ(GetChild(shape, 0u).GetVertex(0), location0);
value = Length2{2_m, 3_m};
EXPECT_NO_THROW(Translate(world, shapeId, value));
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
EXPECT_EQ(GetChild(shape, 0u).GetVertex(0), location0 + value);
}
TEST(WorldShape, TranslateStaticRectangle)
{
World world;
const auto location0 = Length2{+0.5_m, -0.5_m};
const auto shapeId = CreateShape(world, ::playrho::d2::part::Compositor<>{});
auto shape = Shape{};
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
ASSERT_EQ(GetChild(shape, 0u).GetVertex(0), location0);
auto value = Length2{0_m, 0_m};
EXPECT_NO_THROW(Translate(world, shapeId, value));
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
EXPECT_EQ(GetChild(shape, 0u).GetVertex(0), location0);
value = Length2{2_m, 3_m};
EXPECT_THROW(Translate(world, shapeId, value), InvalidArgument);
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
EXPECT_EQ(GetChild(shape, 0u).GetVertex(0), location0);
}
TEST(WorldShape, TranslateDynamicRectangle)
{
using namespace ::playrho::d2::part;
World world;
const auto location0 = Length2{+0.5_m, -0.5_m};
const auto shapeId = CreateShape(world, Compositor<GeometryIs<DynamicRectangle<>>>{});
auto shape = Shape{};
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
ASSERT_EQ(GetChild(shape, 0u).GetVertex(0), location0);
EXPECT_NO_THROW(Translate(world, shapeId, Length2{0_m, 0_m}));
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
EXPECT_EQ(GetChild(shape, 0u).GetVertex(0), location0);
const auto offset = Length2{2_m, 3_m};
EXPECT_NO_THROW(Translate(world, shapeId, offset));
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
EXPECT_EQ(GetChild(shape, 0u).GetVertex(0), location0 + offset);
}
TEST(WorldShape, TestPointFreeFunction)
{
const auto shapeA = Shape{DiskShapeConf{}};
const auto bodyCtrPos = Length2(1_m, 2_m);
World world;
const auto shapeId = CreateShape(world, shapeA);
const auto bodyId = CreateBody(world, BodyConf{}.UseLocation(bodyCtrPos));
EXPECT_TRUE(TestPoint(world, bodyId, shapeId, bodyCtrPos));
EXPECT_FALSE(TestPoint(world, bodyId, shapeId, Length2{}));
}
TEST(WorldShape, GetShapeRange)
{
auto world = World{};
EXPECT_EQ(GetShapeRange(world), 0u);
EXPECT_NO_THROW(CreateShape(world, DiskShapeConf{}));
EXPECT_EQ(GetShapeRange(world), 1u);
EXPECT_NO_THROW(CreateShape(world, DiskShapeConf{}));
EXPECT_EQ(GetShapeRange(world), 2u);
EXPECT_NO_THROW(CreateShape(world, DiskShapeConf{}));
EXPECT_EQ(GetShapeRange(world), 3u);
EXPECT_NO_THROW(Destroy(world, ShapeID(1u)));
EXPECT_EQ(GetShapeRange(world), 3u);
EXPECT_NO_THROW(Destroy(world, ShapeID(2u)));
EXPECT_EQ(GetShapeRange(world), 3u);
EXPECT_NO_THROW(Destroy(world, ShapeID(0u)));
EXPECT_EQ(GetShapeRange(world), 3u);
EXPECT_NO_THROW(Clear(world));
EXPECT_EQ(GetShapeRange(world), 0u);
}
TEST(WorldShape, Destroy)
{
auto shape = Shape{};
auto world = World{};
EXPECT_THROW(Destroy(world, ShapeID(2u)), std::out_of_range);
auto shapeId = InvalidShapeID;
ASSERT_NO_THROW(shapeId = CreateShape(world, DiskShapeConf{}));
ASSERT_EQ(shapeId, ShapeID(0u));
ASSERT_NO_THROW(shape = GetShape(world, ShapeID(0u)));
ASSERT_EQ(GetChildCount(shape), 1u);
ASSERT_NO_THROW(shapeId = CreateShape(world, DiskShapeConf{}));
ASSERT_EQ(shapeId, ShapeID(1u));
ASSERT_NO_THROW(shape = GetShape(world, ShapeID(1u)));
ASSERT_EQ(GetChildCount(shape), 1u);
EXPECT_THROW(Destroy(world, ShapeID(2u)), std::out_of_range);
EXPECT_NO_THROW(Destroy(world, ShapeID(1u)));
EXPECT_NO_THROW(shape = GetShape(world, ShapeID(1u)));
EXPECT_EQ(GetChildCount(shape), 0u);
}
TEST(WorldShape, GetType)
{
auto shapeId = InvalidShapeID;
auto typeId = TypeID{};
auto world = World{};
EXPECT_THROW(typeId = GetType(world, ShapeID(0u)), std::out_of_range);
ASSERT_NO_THROW(shapeId = CreateShape(world, DiskShapeConf{}));
ASSERT_EQ(shapeId, ShapeID(0u));
EXPECT_NO_THROW(typeId = GetType(world, ShapeID(0u)));
EXPECT_EQ(typeId, GetTypeID<DiskShapeConf>());
}
TEST(WorldShape, Scale)
{
auto shape = Shape{};
auto shapeId = InvalidShapeID;
auto world = World{};
const auto v0 = Length2{-0.5_m, +0.0_m};
const auto v1 = Length2{+0.5_m, +0.0_m};
ASSERT_NO_THROW(shapeId = CreateShape(world, EdgeShapeConf{v0, v1}));
ASSERT_EQ(shapeId, ShapeID(0u));
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
ASSERT_EQ(GetChildCount(shape), ChildCounter(1));
auto distanceProxy = DistanceProxy{};
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
ASSERT_NO_THROW(distanceProxy = GetChild(shape, 0u));
ASSERT_EQ(distanceProxy.GetVertexCount(), 2u);
ASSERT_EQ(distanceProxy.GetVertex(0u), Length2(-0.5_m, +0.0_m));
ASSERT_EQ(distanceProxy.GetVertex(1u), Length2(+0.5_m, -0.0_m));
EXPECT_NO_THROW(Scale(world, shapeId, Vec2(Real(2), Real(3))));
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
ASSERT_NO_THROW(distanceProxy = GetChild(shape, 0u));
EXPECT_EQ(distanceProxy.GetVertex(0u), Length2(-1.0_m, +0.0_m));
EXPECT_EQ(distanceProxy.GetVertex(1u), Length2(+1.0_m, +0.0_m));
}
TEST(WorldShape, Rotate)
{
auto shape = Shape{};
auto shapeId = InvalidShapeID;
auto world = World{};
const auto v0 = Length2{-0.5_m, +0.0_m};
const auto v1 = Length2{+0.5_m, +0.0_m};
ASSERT_NO_THROW(shapeId = CreateShape(world, EdgeShapeConf{v0, v1}));
ASSERT_EQ(shapeId, ShapeID(0u));
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
ASSERT_EQ(GetChildCount(shape), ChildCounter(1));
auto distanceProxy = DistanceProxy{};
ASSERT_NO_THROW(distanceProxy = GetChild(shape, 0u));
ASSERT_EQ(distanceProxy.GetVertexCount(), 2u);
ASSERT_EQ(distanceProxy.GetVertex(0u), Length2(-0.5_m, +0.0_m));
ASSERT_EQ(distanceProxy.GetVertex(1u), Length2(+0.5_m, -0.0_m));
EXPECT_NO_THROW(Rotate(world, shapeId, UnitVec::GetUp()));
ASSERT_NO_THROW(shape = GetShape(world, shapeId));
ASSERT_NO_THROW(distanceProxy = GetChild(shape, 0u));
EXPECT_EQ(distanceProxy.GetVertex(0u), Length2(0.0_m, -0.5_m));
EXPECT_EQ(distanceProxy.GetVertex(1u), Length2(0.0_m, +0.5_m));
}
TEST(WorldShape, GetMassData)
{
EXPECT_THROW(GetMassData(World{}, ShapeID(0)), std::out_of_range);
auto world = World{};
auto shapeId = InvalidShapeID;
const auto v0 = Length2{-0.5_m, +0.0_m};
const auto v1 = Length2{+0.5_m, +0.0_m};
const auto shape = EdgeShapeConf{v0, v1};
const auto expected = GetMassData(shape);
ASSERT_NO_THROW(shapeId = CreateShape(world, shape));
ASSERT_EQ(shapeId, ShapeID(0u));
auto massData = MassData{};
EXPECT_NO_THROW(massData = GetMassData(world, ShapeID(0)));
EXPECT_EQ(expected, massData);
}
Definition of the DiskShapeConf class and closely related code.
Definition of the EdgeShapeConf class and closely related code.
Declarations of free functions of World for bodies identified by BodyID.
Declarations of free functions of World for shapes identified by ShapeID.
Definitions of the World class and closely related code.
static constexpr UnitVec GetUp() noexcept
Gets the up-ward oriented unit vector.
Definition: UnitVec.hpp:128
A class template for compositing shaped part types eligible for use with classes like the playrho::d2...
Definition: Compositor.hpp:633
detail::surface_density AreaDensity
Area (surface) density quantity.
Definition: Units.hpp:295
bool TestPoint(const DistanceProxy &proxy, const Length2 &point) noexcept
Tests a point for containment in the given distance proxy.
Definition: DistanceProxy.cpp:107
Definition: Compositor.hpp:43
Definition: AABB.hpp:48
void Rotate(ChainShapeConf &arg, const UnitVec &value)
Rotates the given shape's vertices by the given amount.
Definition: ChainShapeConf.hpp:270
std::size_t size(const DynamicTree &tree) noexcept
Gets the "size" of the given tree.
Definition: DynamicTree.hpp:715
::playrho::detail::MassData< 2 > MassData
Mass data alias for 2-D objects.
Definition: MassData.hpp:88
NonNegative< AreaDensity > GetDensity(const Shape &shape) noexcept
Gets the density of the given shape.
Definition: Shape.hpp:353
NonNegativeFF< Real > GetFriction(const Shape &shape) noexcept
Gets the coefficient of friction.
Definition: Shape.hpp:325
void Scale(ChainShapeConf &arg, const Vec2 &value)
Scales the given shape's vertices by the given amount.
Definition: ChainShapeConf.hpp:264
BodyID CreateBody(AabbTreeWorld &world, Body body=Body{})
Creates a rigid body that's a copy of the given one.
Definition: AabbTreeWorld.cpp:1019
BodyType GetType(const Body &body) noexcept
Gets the type of this body.
Definition: Body.hpp:748
void SetRestitution(Shape &shape, Real value)
Sets the coefficient of restitution value of the given shape.
Definition: Shape.hpp:344
ShapeID CreateShape(AabbTreeWorld &world, Shape def)
Creates an identifiable copy of the given shape within this world.
Definition: AabbTreeWorld.cpp:1234
ShapeCounter GetShapeRange(const AabbTreeWorld &world) noexcept
Gets the extent of the currently valid shape range.
Definition: AabbTreeWorld.cpp:1229
void Clear(AabbTreeWorld &world) noexcept
Clears this world.
Definition: AabbTreeWorld.cpp:954
void SetFriction(Shape &shape, NonNegative< Real > value)
Sets the coefficient of friction.
Definition: Shape.hpp:330
void Translate(ChainShapeConf &arg, const Length2 &value)
Translates the given shape's vertices by the given amount.
Definition: ChainShapeConf.hpp:258
void Attach(AabbTreeWorld &world, BodyID id, ShapeID shapeID)
Associates a validly identified shape with the validly identified body.
Definition: AabbTreeWorld.cpp:2896
Real GetRestitution(const Shape &shape) noexcept
Gets the coefficient of restitution value of the given shape.
Definition: Shape.hpp:339
bool IsSensor(const Shape &shape) noexcept
Gets whether or not the given shape is a sensor.
Definition: Shape.hpp:381
void SetDensity(Shape &shape, NonNegative< AreaDensity > value)
Sets the density of the given shape.
Definition: Shape.hpp:358
void Destroy(AabbTreeWorld &world, BodyID id)
Destroys the identified body.
Definition: AabbTreeWorld.cpp:1062
DistanceProxy GetChild(const ChainShapeConf &arg, ChildCounter index)
Gets the "child" shape for a given chain shape configuration.
Definition: ChainShapeConf.hpp:209
const Shape & GetShape(const AabbTreeWorld &world, ShapeID id)
Gets the identified shape.
Definition: AabbTreeWorld.cpp:1279
Filter GetFilterData(const World &world, ShapeID id)
Gets the filter data for the identified shape.
Definition: WorldShape.cpp:74
bool Detach(AabbTreeWorld &world, BodyID id, ShapeID shapeID)
Disassociates a validly identified shape from the validly identified body.
Definition: AabbTreeWorld.cpp:2903
MassData GetMassData(const ChainShapeConf &arg)
Gets the mass data for a given chain shape configuration.
Definition: ChainShapeConf.hpp:215
void SetSensor(Shape &shape, bool value)
Sets whether or not the given shape is a sensor.
Definition: Shape.hpp:386
void SetFilterData(World &world, ShapeID id, const Filter &filter)
Convenience function for setting the contact filtering data.
Definition: WorldShape.cpp:103
const std::vector< ShapeID > & GetShapes(const AabbTreeWorld &world, BodyID id)
Disassociates all of the associated shape from the validly identified body.
Definition: AabbTreeWorld.cpp:2913
ChildCounter GetChildCount(const ChainShapeConf &arg) noexcept
Gets the child count for a given chain shape configuration.
Definition: ChainShapeConf.hpp:203
Definition: ArrayList.hpp:43
std::remove_const_t< decltype(MaxChildCount)> ChildCounter
Child counter type.
Definition: Settings.hpp:57
float Real
Real-number type.
Definition: Real.hpp:69
auto begin(ReversionWrapper< T > w)
Begin function for getting a reversed order iterator.
Definition: Templates.hpp:111
detail::IndexingNamedType< ShapeCounter, struct ShapeIdentifier > ShapeID
Shape identifier.
Definition: ShapeID.hpp:44
constexpr auto InvalidShapeID
Invalid shape ID value.
Definition: ShapeID.hpp:50
Vector2< Length > Length2
2-element vector of Length quantities.
Definition: Vector2.hpp:51
detail::IndexingNamedType< BodyCounter, struct BodyIdentifier > BodyID
Body identifier.
Definition: BodyID.hpp:44
Vector2< Real > Vec2
Vector with 2 Real elements.
Definition: Vector2.hpp:47
bits_type categoryBits
The collision category bits.
Definition: Filter.hpp:51
index_type groupIndex
Group index.
Definition: Filter.hpp:61
bits_type maskBits
The collision mask bits.
Definition: Filter.hpp:55