PlayRho  1.1.0
An Interactive Real-Time-Oriented C++ Physics Engine & Library
RevoluteJoint.cpp

This is the googletest based unit testing file for the interfaces to playrho::d2::RevoluteJointConf.

/*
* Copyright (c) 2020 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/Dynamics/Joints/RevoluteJointConf.hpp>
#include <PlayRho/Dynamics/Joints/Joint.hpp>
#include <PlayRho/Dynamics/World.hpp>
#include <PlayRho/Dynamics/WorldBody.hpp>
#include <PlayRho/Dynamics/WorldJoint.hpp>
#include <PlayRho/Dynamics/WorldMisc.hpp>
#include <PlayRho/Dynamics/WorldFixture.hpp>
#include <PlayRho/Dynamics/StepConf.hpp>
#include <PlayRho/Dynamics/BodyConf.hpp>
#include <PlayRho/Collision/Shapes/DiskShapeConf.hpp>
#include <PlayRho/Collision/Shapes/PolygonShapeConf.hpp>
#include <cstring> // for std::memcmp
using namespace playrho;
using namespace playrho::d2;
TEST(RevoluteJointConf, ByteSize)
{
// Check size at test runtime instead of compile-time via static_assert to avoid stopping
// builds and to report actual size rather than just reporting that expected size is wrong.
switch (sizeof(Real)) {
case 4:
EXPECT_EQ(sizeof(RevoluteJointConf), std::size_t(128));
break;
case 8:
EXPECT_EQ(sizeof(RevoluteJointConf), std::size_t(248));
break;
case 16:
EXPECT_EQ(sizeof(RevoluteJointConf), std::size_t(496));
break;
default:
FAIL();
break;
}
}
TEST(RevoluteJoint, Construction)
{
World world;
const auto b0 = world.CreateBody();
const auto b1 = world.CreateBody();
auto jd = RevoluteJointConf{};
jd.bodyA = b0;
jd.bodyB = b1;
jd.collideConnected = true;
jd.localAnchorA = Length2(4_m, 5_m);
jd.localAnchorB = Length2(6_m, 7_m);
jd.enableLimit = true;
jd.enableMotor = true;
jd.motorSpeed = Real{4.4f} * RadianPerSecond;
jd.maxMotorTorque = 1_Nm;
jd.lowerAngle = 33_deg;
jd.upperAngle = 40_deg;
jd.referenceAngle = 45_deg;
auto joint = Joint{jd};
EXPECT_EQ(GetType(joint), GetTypeID<RevoluteJointConf>());
EXPECT_EQ(GetBodyA(joint), jd.bodyA);
EXPECT_EQ(GetBodyB(joint), jd.bodyB);
EXPECT_EQ(GetCollideConnected(joint), jd.collideConnected);
EXPECT_EQ(GetLinearReaction(joint), Momentum2{});
EXPECT_EQ(GetAngularReaction(joint), AngularMomentum{0});
EXPECT_EQ(GetLimitState(joint), LimitState::e_inactiveLimit);
EXPECT_EQ(GetLocalAnchorA(joint), jd.localAnchorA);
EXPECT_EQ(GetLocalAnchorB(joint), jd.localAnchorB);
EXPECT_EQ(GetAngularLowerLimit(joint), jd.lowerAngle);
EXPECT_EQ(GetAngularUpperLimit(joint), jd.upperAngle);
EXPECT_EQ(GetMotorSpeed(joint), jd.motorSpeed);
EXPECT_EQ(GetReferenceAngle(joint), jd.referenceAngle);
EXPECT_EQ(IsMotorEnabled(joint), jd.enableMotor);
EXPECT_EQ(GetMaxMotorTorque(joint), jd.maxMotorTorque);
EXPECT_EQ(IsLimitEnabled(joint), jd.enableLimit);
EXPECT_EQ(GetAngularMotorImpulse(joint), AngularMomentum{0});
const auto id = CreateJoint(world, joint);
EXPECT_EQ(GetAngularVelocity(world, id), 0 * RadianPerSecond);
EXPECT_EQ(GetAnchorA(world, id), Length2(4_m, 5_m));
EXPECT_EQ(GetAnchorB(world, id), Length2(6_m, 7_m));
EXPECT_EQ(GetMotorTorque(world, id, 1_Hz), 0 * NewtonMeter);
}
TEST(RevoluteJoint, EnableMotor)
{
World world;
const auto b0 = world.CreateBody(
BodyConf{}.UseType(BodyType::Dynamic).UseLinearAcceleration(EarthlyGravity));
const auto b1 = world.CreateBody(
BodyConf{}.UseType(BodyType::Dynamic).UseLinearAcceleration(EarthlyGravity));
ASSERT_EQ(GetVelocity(world, b0), Velocity{});
ASSERT_EQ(GetVelocity(world, b1), Velocity{});
auto jd = RevoluteJointConf{};
jd.bodyA = b0;
jd.bodyB = b1;
jd.localAnchorA = Length2(4_m, 5_m);
jd.localAnchorB = Length2(6_m, 7_m);
auto joint = Joint{jd};
ASSERT_FALSE(IsLimitEnabled(joint));
EXPECT_EQ(GetLimitState(joint), LimitState::e_inactiveLimit);
EXPECT_FALSE(IsMotorEnabled(joint));
EnableMotor(joint, false);
EXPECT_FALSE(IsMotorEnabled(joint));
EnableMotor(joint, true);
EXPECT_TRUE(IsMotorEnabled(joint));
}
TEST(RevoluteJoint, EnableMotorInWorld)
{
World world;
const auto b0 = world.CreateBody(
BodyConf{}.UseType(BodyType::Dynamic).UseLinearAcceleration(EarthlyGravity));
const auto b1 = world.CreateBody(
BodyConf{}.UseType(BodyType::Dynamic).UseLinearAcceleration(EarthlyGravity));
ASSERT_EQ(GetVelocity(world, b0), Velocity{});
ASSERT_EQ(GetVelocity(world, b1), Velocity{});
auto jd = RevoluteJointConf{};
jd.bodyA = b0;
jd.bodyB = b1;
jd.localAnchorA = Length2(4_m, 5_m);
jd.localAnchorB = Length2(6_m, 7_m);
const auto id = CreateJoint(world, jd);
ASSERT_NE(id, InvalidJointID);
ASSERT_EQ(GetVelocity(world, b0), Velocity{});
ASSERT_EQ(GetVelocity(world, b1), Velocity{});
EXPECT_FALSE(IsMotorEnabled(world, id));
EnableMotor(world, id, false);
EXPECT_FALSE(IsMotorEnabled(world, id));
EnableMotor(world, id, true);
EXPECT_TRUE(IsMotorEnabled(world, id));
const auto newValue = 5_Nm;
ASSERT_NE(GetMaxMotorTorque(world, id), newValue);
EXPECT_EQ(GetMaxMotorTorque(world, id), jd.maxMotorTorque);
SetMaxMotorTorque(world, id, newValue);
EXPECT_EQ(GetMaxMotorTorque(world, id), newValue);
EXPECT_EQ(GetAngularMotorImpulse(world, id), AngularMomentum(0));
const auto shape = Shape(DiskShapeConf{}.UseRadius(1_m).UseDensity(1_kgpm2));
CreateFixture(world, b0, shape);
CreateFixture(world, b1, shape);
ASSERT_NE(GetInvRotInertia(world, b0), InvRotInertia(0));
ASSERT_NE(GetInvRotInertia(world, b1), InvRotInertia(0));
auto stepConf = StepConf{};
Step(world, stepConf);
EXPECT_EQ(GetAngularMotorImpulse(world, id), AngularMomentum(0));
stepConf.doWarmStart = false;
Step(world, stepConf);
EXPECT_EQ(GetAngularMotorImpulse(world, id), AngularMomentum(0));
EXPECT_NE(GetVelocity(world, b0), Velocity{});
EXPECT_NE(GetVelocity(world, b1), Velocity{});
EnableLimit(world, id, true);
ASSERT_TRUE(IsLimitEnabled(world, id));
SetAngularLimits(world, id, -45_deg, -5_deg);
stepConf.doWarmStart = true;
Step(world, stepConf);
EXPECT_EQ(GetAngularMotorImpulse(world, id), AngularMomentum(0));
EXPECT_EQ(GetAngularReaction(world, id), AngularMomentum(0));
EXPECT_EQ(GetLimitState(world, id), LimitState::e_atUpperLimit);
EXPECT_NE(GetVelocity(world, b0), Velocity{});
EXPECT_NE(GetVelocity(world, b1), Velocity{});
SetAngularLimits(world, id, +55_deg, +95_deg);
stepConf.doWarmStart = true;
Step(world, stepConf);
EXPECT_EQ(GetAngularMotorImpulse(world, id), AngularMomentum(0));
EXPECT_EQ(GetAngularReaction(world, id), AngularMomentum(0));
EXPECT_EQ(GetLimitState(world, id), LimitState::e_atLowerLimit);
EXPECT_NE(GetVelocity(world, b0), Velocity{});
EXPECT_NE(GetVelocity(world, b1), Velocity{});
}
TEST(RevoluteJoint, MotorSpeed)
{
World world;
const auto b0 = world.CreateBody();
const auto b1 = world.CreateBody();
auto jd = RevoluteJointConf{};
jd.bodyA = b0;
jd.bodyB = b1;
jd.localAnchorA = Length2(4_m, 5_m);
jd.localAnchorB = Length2(6_m, 7_m);
const auto newValue = Real(5) * RadianPerSecond;
auto joint = Joint{jd};
ASSERT_NE(GetMotorSpeed(joint), newValue);
EXPECT_EQ(GetMotorSpeed(joint), jd.motorSpeed);
SetMotorSpeed(joint, newValue);
EXPECT_EQ(GetMotorSpeed(joint), newValue);
}
TEST(RevoluteJoint, EnableLimit)
{
auto world = World{};
const auto b0 = world.CreateBody(BodyConf{}.UseType(BodyType::Dynamic));
const auto b1 = world.CreateBody(BodyConf{}.UseType(BodyType::Dynamic));
ASSERT_EQ(GetInvRotInertia(world, b0), InvRotInertia(0));
ASSERT_EQ(GetInvRotInertia(world, b1), InvRotInertia(0));
auto jd = RevoluteJointConf{};
jd.bodyA = b0;
jd.bodyB = b1;
jd.localAnchorA = Length2(4_m, 5_m);
jd.localAnchorB = Length2(6_m, 7_m);
jd.enableLimit = false;
const auto joint = CreateJoint(world, jd);
ASSERT_EQ(GetLimitState(world, joint), LimitState::e_inactiveLimit);
ASSERT_FALSE(IsLimitEnabled(world, joint));
EnableLimit(world, joint, false);
EXPECT_FALSE(IsLimitEnabled(world, joint));
EnableLimit(world, joint, true);
EXPECT_TRUE(IsLimitEnabled(world, joint));
const auto id = CreateJoint(world, jd);
ASSERT_NE(id, InvalidJointID);
auto stepConf = StepConf{};
Step(world, stepConf);
EXPECT_TRUE(IsLimitEnabled(world, joint));
// since b0 & b1 inv rot inertia 0
EXPECT_EQ(GetLimitState(world, joint), LimitState::e_inactiveLimit);
const auto shape = Shape(DiskShapeConf{}.UseRadius(1_m).UseDensity(1_kgpm2));
CreateFixture(world, b0, shape);
CreateFixture(world, b1, shape);
ASSERT_NE(GetInvRotInertia(world, b0), InvRotInertia(0));
ASSERT_NE(GetInvRotInertia(world, b1), InvRotInertia(0));
Step(world, stepConf);
EXPECT_TRUE(IsLimitEnabled(world, joint));
EXPECT_EQ(GetLimitState(world, joint), LimitState::e_equalLimits);
SetAngularLimits(world, joint, -45_deg, +45_deg);
ASSERT_TRUE(IsLimitEnabled(world, joint));
ASSERT_EQ(GetLimitState(world, joint), LimitState::e_equalLimits);
Step(world, stepConf);
EXPECT_TRUE(IsLimitEnabled(world, joint));
EXPECT_EQ(GetLimitState(world, joint), LimitState::e_inactiveLimit);
EXPECT_EQ(GetAngularMotorImpulse(world, joint), AngularMomentum(0));
}
TEST(RevoluteJoint, SetAngularLimits)
{
World world;
const auto b0 = world.CreateBody();
const auto b1 = world.CreateBody();
auto jd = RevoluteJointConf{};
jd.bodyA = b0;
jd.bodyB = b1;
jd.localAnchorA = Length2(4_m, 5_m);
jd.localAnchorB = Length2(6_m, 7_m);
const auto upperValue = +5_deg;
const auto lowerValue = -8_deg;
auto joint = Joint{jd};
ASSERT_NE(GetAngularUpperLimit(joint), upperValue);
ASSERT_NE(GetAngularLowerLimit(joint), lowerValue);
SetAngularLimits(joint, lowerValue, upperValue);
EXPECT_EQ(GetAngularUpperLimit(joint), upperValue);
EXPECT_EQ(GetAngularLowerLimit(joint), lowerValue);
}
TEST(RevoluteJoint, MaxMotorTorque)
{
World world;
const auto b0 = world.CreateBody(BodyConf{}.UseType(BodyType::Dynamic));
const auto b1 = world.CreateBody(BodyConf{}.UseType(BodyType::Dynamic));
auto jd = RevoluteJointConf{};
jd.bodyA = b0;
jd.bodyB = b1;
jd.localAnchorA = Length2(4_m, 5_m);
jd.localAnchorB = Length2(6_m, 7_m);
const auto newValue = 5_Nm;
const auto joint = CreateJoint(world, jd);
ASSERT_NE(joint, InvalidJointID);
ASSERT_NE(GetMaxMotorTorque(world, joint), newValue);
EXPECT_EQ(GetMaxMotorTorque(world, joint), jd.maxMotorTorque);
SetMaxMotorTorque(world, joint, newValue);
EXPECT_EQ(GetMaxMotorTorque(world, joint), newValue);
EXPECT_EQ(GetAngularMotorImpulse(world, joint), AngularMomentum(0));
const auto shape = Shape(DiskShapeConf{}.UseRadius(1_m).UseDensity(1_kgpm2));
CreateFixture(world, b0, shape);
CreateFixture(world, b1, shape);
ASSERT_NE(GetInvRotInertia(world, b0), InvRotInertia(0));
ASSERT_NE(GetInvRotInertia(world, b1), InvRotInertia(0));
auto stepConf = StepConf{};
Step(world, stepConf);
EXPECT_EQ(GetAngularMotorImpulse(world, joint), AngularMomentum(0));
stepConf.doWarmStart = false;
Step(world, stepConf);
EXPECT_EQ(GetAngularMotorImpulse(world, joint), AngularMomentum(0));
}
TEST(RevoluteJoint, MovesDynamicCircles)
{
const auto circle = Shape{DiskShapeConf{}.UseRadius(0.2_m)};
World world;
const auto p1 = Length2{-1_m, 0_m};
const auto p2 = Length2{+1_m, 0_m};
const auto b1 = world.CreateBody(BodyConf{}
.UseLocation(p1)
.UseLinearAcceleration(EarthlyGravity));
const auto b2 = world.CreateBody(BodyConf{}
.UseLocation(p2)
.UseLinearAcceleration(EarthlyGravity));
CreateFixture(world, b1, circle);
CreateFixture(world, b2, circle);
auto jd = RevoluteJointConf{};
jd.bodyA = b1;
jd.bodyB = b2;
CreateJoint(world, Joint{jd});
auto step = StepConf{};
step.deltaTime = 1_s;
step.maxTranslation = Meter * Real(4);
Step(world, step);
EXPECT_NEAR(double(Real{GetX(GetLocation(world, b1)) / Meter}), 0, 0.001);
EXPECT_NEAR(double(Real{GetY(GetLocation(world, b1)) / Meter}), -4, 0.001);
EXPECT_NEAR(double(Real{GetX(GetLocation(world, b2)) / Meter}), 0, 0.01);
EXPECT_NEAR(double(Real{GetY(GetLocation(world, b2)) / Meter}), -4, 0.01);
EXPECT_EQ(GetAngle(world, b1), 0_deg);
EXPECT_EQ(GetAngle(world, b2), 0_deg);
}
TEST(RevoluteJoint, LimitEnabledDynamicCircles)
{
const auto circle = Shape{DiskShapeConf{}.UseRadius(0.2_m).UseDensity(1_kgpm2)};
World world;
const auto p1 = Length2{-1_m, 0_m};
const auto p2 = Length2{+1_m, 0_m};
const auto b1 = world.CreateBody(BodyConf{}
.UseLocation(p1)
.UseLinearAcceleration(EarthlyGravity));
const auto b2 = world.CreateBody(BodyConf{}
.UseLocation(p2)
.UseLinearAcceleration(EarthlyGravity));
CreateFixture(world, b1, circle);
CreateFixture(world, b2, circle);
auto jd = RevoluteJointConf{b1, b2, Length2{}};
jd.enableLimit = true;
ASSERT_EQ(jd.lowerAngle, 0_deg);
ASSERT_EQ(jd.upperAngle, 0_deg);
const auto joint = CreateJoint(world, jd);
ASSERT_NE(joint, InvalidJointID);
ASSERT_EQ(GetLimitState(world, joint), LimitState::e_inactiveLimit);
ASSERT_EQ(GetAngularLowerLimit(world, joint), jd.lowerAngle);
ASSERT_EQ(GetAngularUpperLimit(world, joint), jd.upperAngle);
ASSERT_EQ(GetReferenceAngle(world, joint), 0_deg);
ASSERT_EQ(GetAngle(world, joint), 0_deg);
auto step = StepConf{};
step.deltaTime = 1_s;
step.maxTranslation = Meter * Real(4);
Step(world, step);
EXPECT_EQ(GetAngle(world, joint), 0_deg);
EXPECT_EQ(GetReferenceAngle(world, joint), 0_deg);
EXPECT_EQ(GetLimitState(world, joint), LimitState::e_equalLimits);
// TODO: investigate why failing...
// EXPECT_NEAR(double(Real{GetX(GetLocation(world, b1)) / Meter}), -1.0, 0.001);
EXPECT_NEAR(double(Real{GetY(GetLocation(world, b1)) / Meter}), -4, 0.001);
// TODO: investigate why failing...
// EXPECT_NEAR(double(Real{GetX(GetLocation(world, b2)) / Meter}), +1.0, 0.01);
EXPECT_NEAR(double(Real{GetY(GetLocation(world, b2)) / Meter}), -4, 0.01);
EXPECT_EQ(GetAngle(world, b1), 0_deg);
EXPECT_EQ(GetAngle(world, b2), 0_deg);
EXPECT_TRUE(IsEnabled(world, joint));
UnsetAwake(world, b1);
UnsetAwake(world, b2);
ASSERT_FALSE(IsAwake(world, b1));
ASSERT_FALSE(IsAwake(world, b2));
SetAwake(world, joint);
EXPECT_TRUE(IsAwake(world, b1));
EXPECT_TRUE(IsAwake(world, b2));
EXPECT_EQ(GetWorldIndex(world, joint), std::size_t(0));
SetAngularLimits(world, joint, 45_deg, 90_deg);
EXPECT_EQ(GetAngularLowerLimit(world, joint), 45_deg);
EXPECT_EQ(GetAngularUpperLimit(world, joint), 90_deg);
Step(world, step);
EXPECT_EQ(GetReferenceAngle(world, joint), 0_deg);
EXPECT_EQ(GetLimitState(world, joint), LimitState::e_atLowerLimit);
// TODO: investigate why failing...
// EXPECT_NEAR(static_cast<double>(Real(GetAngle(world, joint)/1_rad)),
// 0.28610128164291382, 0.28610128164291382/100);
SetAngularLimits(world, joint, -90_deg, -45_deg);
EXPECT_EQ(GetAngularLowerLimit(world, joint), -90_deg);
EXPECT_EQ(GetAngularUpperLimit(world, joint), -45_deg);
Step(world, step);
EXPECT_EQ(GetReferenceAngle(world, joint), 0_deg);
EXPECT_EQ(GetLimitState(world, joint), LimitState::e_atUpperLimit);
// TODO: investigate why failing...
// EXPECT_NEAR(static_cast<double>(Real(GetAngle(world, joint)/1_rad)),
// -0.082102291285991669, 0.082102291285991669/100);
}
TEST(RevoluteJoint, DynamicJoinedToStaticStaysPut)
{
auto world = World{};
const auto p1 = Length2{0_m, 4_m}; // Vec2{-1, 0};
const auto p2 = Length2{0_m, -2_m}; // Vec2{+1, 0};
const auto b1 = world.CreateBody(BodyConf{}.UseType(BodyType::Static).UseLocation(p1));
const auto b2 = world.CreateBody(BodyConf{}.UseType(BodyType::Dynamic).UseLocation(p2));
const auto shape1 = Shape{PolygonShapeConf{}.SetAsBox(1_m, 1_m)};
CreateFixture(world, b1, shape1);
const auto shape2 = Shape{PolygonShapeConf{}.SetAsBox(0.5_m, 0.5_m).UseDensity(1_kgpm2)};
CreateFixture(world, b2, shape2);
auto jd = GetRevoluteJointConf(world, b1, b2, Length2{});
const auto joint = CreateJoint(world, Joint{jd});
for (auto i = 0; i < 1000; ++i) {
Step(world, 0.1_s);
EXPECT_EQ(GetLocation(world, b1), p1);
EXPECT_NEAR(double(Real{GetX(GetLocation(world, b2)) / Meter}),
double(Real{GetX(p2) / Meter}), 0.0001);
EXPECT_NEAR(double(Real{GetY(GetLocation(world, b2)) / Meter}),
double(Real{GetY(p2) / Meter}), 0.0001);
EXPECT_EQ(GetAngle(world, b2), 0_deg);
}
Destroy(world, joint);
for (auto i = 0; i < 10; ++i) {
Step(world, 0.1_s);
EXPECT_EQ(GetLocation(world, b1), p1);
EXPECT_NE(GetLocation(world, b2), p2);
EXPECT_EQ(GetAngle(world, b2), 0_deg);
}
}
TEST(RevoluteJointConf, GetRevoluteJointConfFromJoint)
{
auto conf = RevoluteJointConf{BodyID(0u), BodyID(1u)};
conf.UseCollideConnected(true);
conf.impulse = Vec3{Real(3), Real(4), Real(5)};
conf.angularMotorImpulse = AngularMomentum{Real(2) * NewtonMeterSecond};
conf.referenceAngle = 20_deg;
conf.enableLimit = true;
conf.lowerAngle = 10_deg;
conf.upperAngle = 30_deg;
conf.enableMotor = true;
conf.motorSpeed = 3_rpm;
conf.maxMotorTorque = Torque{Real(2.1) * NewtonMeter};
auto result = RevoluteJointConf{};
EXPECT_NO_THROW(result = GetRevoluteJointConf(Joint{conf}));
EXPECT_EQ(result.bodyA, conf.bodyA);
EXPECT_EQ(result.bodyB, conf.bodyB);
EXPECT_EQ(result.collideConnected, conf.collideConnected);
EXPECT_EQ(result.localAnchorA, conf.localAnchorA);
EXPECT_EQ(result.localAnchorB, conf.localAnchorB);
EXPECT_EQ(result.impulse, conf.impulse);
EXPECT_EQ(result.angularMotorImpulse, conf.angularMotorImpulse);
EXPECT_EQ(result.referenceAngle, conf.referenceAngle);
EXPECT_EQ(result.enableLimit, conf.enableLimit);
EXPECT_EQ(result.lowerAngle, conf.lowerAngle);
EXPECT_EQ(result.upperAngle, conf.upperAngle);
EXPECT_EQ(result.enableMotor, conf.enableMotor);
EXPECT_EQ(result.motorSpeed, conf.motorSpeed);
}
{
auto world = World{};
const auto bodyA = world.CreateBody();
const auto bodyB = world.CreateBody();
auto conf = RevoluteJointConf{bodyA, bodyB};
auto angle = Angle{};
EXPECT_NO_THROW(angle = GetAngle(world, conf));
EXPECT_EQ(angle, 0_deg);
// TODO: add tests for angles other than 0 degrees
}
{
auto world = World{};
const auto bodyA = world.CreateBody();
const auto bodyB = world.CreateBody();
auto conf = RevoluteJointConf{bodyA, bodyB};
auto angularVelocity = AngularVelocity{};
EXPECT_NO_THROW(angularVelocity = GetAngularVelocity(world, conf));
EXPECT_EQ(angularVelocity, 0_rpm);
// TODO: add tests for angularVelocity other than 0 rpm
}
{
auto jd = RevoluteJointConf{BodyID(0u), BodyID(1u)};
auto copy = RevoluteJointConf{};
// Do copy = jd without missing padding so memcmp works
std::memcpy(&copy, &jd, sizeof(RevoluteJointConf));
EXPECT_FALSE(ShiftOrigin(jd, Length2{0_m, 0_m}));
// Use memcmp since easier than writing full == suport pre C++20.
EXPECT_TRUE(std::memcmp(&jd, &copy, sizeof(RevoluteJointConf)) == 0);
}
{
auto conf = RevoluteJointConf{};
conf.angularMass = RotInertia{2_m2 * 3_kg / SquareRadian}; // L^2 M QP^-2
auto rotInertia = RotInertia{};
EXPECT_NO_THROW(rotInertia = GetAngularMass(Joint{conf}));
EXPECT_EQ(conf.angularMass, rotInertia);
}
TEST(RevoluteJointConf, GetLocalXAxisAThrowsInvalidArgument)
{
EXPECT_THROW(GetLocalXAxisA(Joint{RevoluteJointConf{}}), std::invalid_argument);
}
TEST(RevoluteJointConf, GetLocalYAxisAThrowsInvalidArgument)
{
EXPECT_THROW(GetLocalYAxisA(Joint{RevoluteJointConf{}}), std::invalid_argument);
}
TEST(RevoluteJointConf, GetMaxMotorForceThrowsInvalidArgument)
{
EXPECT_THROW(GetMaxMotorForce(Joint{RevoluteJointConf{}}), std::invalid_argument);
}
TEST(RevoluteJointConf, GetLinearLowerLimitThrowsInvalidArgument)
{
EXPECT_THROW(GetLinearLowerLimit(Joint{RevoluteJointConf{}}), std::invalid_argument);
}
TEST(RevoluteJointConf, GetLinearUpperLimitThrowsInvalidArgument)
{
EXPECT_THROW(GetLinearUpperLimit(Joint{RevoluteJointConf{}}), std::invalid_argument);
}
TEST(RevoluteJointConf, GetLinearMotorImpulseThrowsInvalidArgument)
{
EXPECT_THROW(GetLinearMotorImpulse(Joint{RevoluteJointConf{}}), std::invalid_argument);
}
TEST(RevoluteJointConf, EqualsOperator)
{
EXPECT_TRUE(RevoluteJointConf() == RevoluteJointConf());
{
auto conf = RevoluteJointConf{};
conf.localAnchorA = Length2{1.2_m, -3_m};
EXPECT_TRUE(conf == conf);
EXPECT_FALSE(RevoluteJointConf() == conf);
}
{
auto conf = RevoluteJointConf{};
conf.localAnchorB = Length2{1.2_m, -3_m};
EXPECT_TRUE(conf == conf);
EXPECT_FALSE(RevoluteJointConf() == conf);
}
{
auto conf = RevoluteJointConf{};
conf.referenceAngle = 12_deg;
EXPECT_TRUE(conf == conf);
EXPECT_FALSE(RevoluteJointConf() == conf);
}
// TODO: test remaining fields.
}
TEST(RevoluteJointConf, NotEqualsOperator)
{
EXPECT_FALSE(RevoluteJointConf() != RevoluteJointConf());
{
auto conf = RevoluteJointConf{};
EXPECT_FALSE(conf != conf);
EXPECT_TRUE(RevoluteJointConf() != conf);
}
// TODO: test remaining fields.
}
{
EXPECT_STREQ(GetName(GetTypeID<RevoluteJointConf>()), "d2::RevoluteJointConf");
}
playrho::BodyType::Static
@ Static
Static body type.
playrho::RadianPerSquareSecond
constexpr auto RadianPerSquareSecond
Radian per square second unit of angular acceleration.
Definition: Units.hpp:394
playrho::MeterPerSquareSecond
constexpr auto MeterPerSquareSecond
Meter per square second unit of linear acceleration.
Definition: Units.hpp:345
playrho::d2::BodyConf::UseType
constexpr BodyConf & UseType(BodyType t) noexcept
Use the given type.
Definition: BodyConf.hpp:166
playrho::d2::World::CreateBody
BodyID CreateBody(const BodyConf &def=GetDefaultBodyConf())
Creates a rigid body with the given configuration.
Definition: World.cpp:161
playrho::d2::PolygonShapeConf::SetAsBox
PolygonShapeConf & SetAsBox(Length hx, Length hy) noexcept
Sets the vertices to represent an axis-aligned box centered on the local origin.
Definition: PolygonShapeConf.cpp:42
playrho::Torque
PLAYRHO_QUANTITY(boost::units::si::torque) Torque
Torque quantity.
Definition: Units.hpp:255
playrho::d2
Name space for 2-dimensionally related PlayRho names.
Definition: AABB.cpp:34
playrho::d2::JointConf::bodyA
BodyID bodyA
1st attached body.
Definition: JointConf.hpp:36
playrho::d2::GetLocalYAxisA
UnitVec GetLocalYAxisA(const Joint &object)
Gets the given joint's local Y axis A if its type supports that.
Definition: Joint.cpp:242
playrho::d2::IsLimitEnabled
bool IsLimitEnabled(const Joint &object)
Gets the specified joint's limit property if it supports one.
Definition: Joint.cpp:534
playrho::InvalidJointID
constexpr auto InvalidJointID
Invalid joint ID value.
Definition: JointID.hpp:33
playrho::d2::GetAngularUpperLimit
Angle GetAngularUpperLimit(const Joint &object)
Gets the upper joint limit.
Definition: Joint.cpp:515
playrho::d2::GetAngularMotorImpulse
AngularMomentum GetAngularMotorImpulse(const Joint &object)
Gets the angular motor impulse of the joint if it has this property.
Definition: Joint.cpp:447
playrho::d2::IsAwake
bool IsAwake(const Body &body) noexcept
Gets the awake/asleep state of this body.
Definition: Body.hpp:808
playrho::StepConf::deltaTime
Time deltaTime
Delta time.
Definition: StepConf.hpp:53
playrho::d2::GetName
const char * GetName(Manifold::Type type) noexcept
Gets a unique name for the given manifold type.
Definition: Manifold.cpp:788
playrho::d2::ShapeBuilder::UseDensity
constexpr ConcreteConf & UseDensity(NonNegative< AreaDensity > value) noexcept
Uses the given density.
Definition: ShapeConf.hpp:111
playrho::GetX
constexpr auto & GetX(T &value)
Gets the "X" element of the given value - i.e. the first element.
Definition: Math.hpp:66
playrho
Name space for all PlayRho related names.
Definition: AABB.cpp:33
playrho::d2::SetMaxMotorTorque
void SetMaxMotorTorque(Joint &object, Torque value)
Sets the given joint's max motor torque if its type supports that.
Definition: Joint.cpp:363
playrho::d2::GetAngularVelocity
AngularVelocity GetAngularVelocity(const Body &body) noexcept
Gets the angular velocity.
Definition: Body.hpp:1178
playrho::d2::GetLocalAnchorA
Length2 GetLocalAnchorA(const Joint &object)
Get the anchor point on body-A in local coordinates.
Definition: Joint.cpp:62
playrho::RadianPerSecond
constexpr auto RadianPerSecond
Radian per second unit of angular velocity.
Definition: Units.hpp:384
playrho::d2::World
Definition of an independent and simulatable "world".
Definition: World.hpp:129
playrho::d2::GetAnchorB
Length2 GetAnchorB(const World &world, JointID id)
Get the anchor point on body-B in world coordinates.
Definition: WorldJoint.cpp:216
playrho::NewtonMeterSecond
constexpr auto NewtonMeterSecond
Newton meter second unit of angular momentum.
Definition: Units.hpp:418
playrho::d2::RevoluteJointConf::localAnchorA
Length2 localAnchorA
Local anchor point relative to body A's origin.
Definition: RevoluteJointConf.hpp:118
playrho::d2::GetInvRotInertia
InvRotInertia GetInvRotInertia(const Body &body) noexcept
Gets the inverse rotational inertia of the body.
Definition: Body.hpp:979
playrho::NewtonMeter
constexpr auto NewtonMeter
Newton meter unit of torque.
Definition: Units.hpp:408
playrho::d2::EnableMotor
void EnableMotor(Joint &object, bool value)
Enables the specified joint's motor property if it supports one.
Definition: Joint.cpp:575
playrho::d2::ShiftOrigin
constexpr bool ShiftOrigin(DistanceJointConf &, Length2) noexcept
Shifts the origin notion of the given configuration.
Definition: DistanceJointConf.hpp:177
playrho::d2::GetMotorTorque
Torque GetMotorTorque(const Joint &joint, Frequency inv_dt)
Gets the current motor torque for the given joint given the inverse time step.
Definition: Joint.hpp:791
playrho::d2::GetBodyB
BodyID GetBodyB(const Contact &contact) noexcept
Gets the body B ID of the given contact.
Definition: Contact.hpp:588
playrho::d2::GetCollideConnected
bool GetCollideConnected(const Joint &object) noexcept
Gets collide connected.
Definition: Joint.hpp:293
playrho::d2::GetBodyA
BodyID GetBodyA(const Contact &contact) noexcept
Gets the body A ID of the given contact.
Definition: Contact.hpp:581
playrho::d2::UnsetAwake
void UnsetAwake(Body &body) noexcept
Sets this body to asleep if sleeping is allowed.
Definition: Body.hpp:837
playrho::d2::GetReferenceAngle
Angle GetReferenceAngle(const Joint &object)
Gets the reference angle of the joint if it has one.
Definition: Joint.cpp:215
playrho::d2::IsEnabled
bool IsEnabled(const Body &body) noexcept
Gets the enabled/disabled state of the body.
Definition: Body.hpp:771
playrho::d2::GetLimitState
LimitState GetLimitState(const Joint &object)
Definition: Joint.cpp:631
playrho::d2::PolygonShapeConf
Polygon shape configuration.
Definition: PolygonShapeConf.hpp:42
playrho::d2::IsMotorEnabled
bool IsMotorEnabled(const Joint &object)
Gets the specified joint's motor property value if it supports one.
Definition: Joint.cpp:560
playrho::Meter
constexpr auto Meter
Meter unit of Length.
Definition: Units.hpp:337
playrho::d2::GetLinearUpperLimit
Length GetLinearUpperLimit(const Joint &object)
Gets the upper linear joint limit.
Definition: Joint.cpp:487
playrho::d2::GetLocalXAxisA
UnitVec GetLocalXAxisA(const Joint &object)
Gets the given joint's local X axis A if its type supports that.
Definition: Joint.cpp:230
playrho::d2::GetMaxMotorTorque
Torque GetMaxMotorTorque(const Joint &object)
Gets the given joint's max motor torque if its type supports that.
Definition: Joint.cpp:351
playrho::d2::CreateJoint
JointID CreateJoint(WorldImpl &world, const Joint &def)
Creates a new joint.
Definition: WorldImplJoint.cpp:47
playrho::d2::GetLinearMotorImpulse
Momentum GetLinearMotorImpulse(const Joint &object)
Definition: Joint.cpp:664
playrho::d2::Destroy
void Destroy(World &world, BodyID id)
Destroys the identified body.
Definition: WorldBody.cpp:73
playrho::d2::GetAngularLowerLimit
Angle GetAngularLowerLimit(const Joint &object)
Gets the lower joint limit.
Definition: Joint.cpp:506
playrho::d2::RevoluteJointConf::localAnchorB
Length2 localAnchorB
Local anchor point relative to body B's origin.
Definition: RevoluteJointConf.hpp:121
playrho::d2::GetLocalAnchorB
Length2 GetLocalAnchorB(const Joint &object)
Get the anchor point on body-B in local coordinates.
Definition: Joint.cpp:96
playrho::d2::GetAngle
Angle GetAngle(const UnitVec value)
Gets the angle of the given unit vector.
Definition: Math.hpp:718
playrho::d2::RevoluteJointConf::enableMotor
bool enableMotor
Flag to enable the joint motor.
Definition: RevoluteJointConf.hpp:146
playrho::AngularMomentum
PLAYRHO_QUANTITY(boost::units::si::angular_momentum) AngularMomentum
Angular momentum quantity.
Definition: Units.hpp:304
playrho::StepConf
Step configuration.
Definition: StepConf.hpp:42
playrho::d2::GetMotorSpeed
AngularVelocity GetMotorSpeed(const Joint &object)
Gets the given joint's motor speed if its type supports that.
Definition: Joint.cpp:254
playrho::d2::EarthlyGravity
constexpr auto EarthlyGravity
Earthly gravity in 2-dimensions.
Definition: Vector2.hpp:154
playrho::d2::SetAngularLimits
void SetAngularLimits(Joint &object, Angle lower, Angle upper)
Sets the joint limits.
Definition: Joint.cpp:524
playrho::d2::GetAngularMass
RotInertia GetAngularMass(const Joint &object)
Gets the given joint's angular mass.
Definition: Joint.cpp:287
playrho::InvRotInertia
PLAYRHO_QUANTITY(playrho::units::si::inverse_moment_of_inertia) InvRotInertia
Inverse rotational inertia quantity.
Definition: Units.hpp:282
playrho::d2::GetAngularReaction
constexpr AngularMomentum GetAngularReaction(const DistanceJointConf &) noexcept
Gets the current angular reaction for the given configuration.
Definition: DistanceJointConf.hpp:170
playrho::d2::CreateFixture
FixtureID CreateFixture(World &world, FixtureConf def, bool resetMassData)
Creates a fixture within the specified world.
Definition: WorldFixture.cpp:48
playrho::Real
float Real
Real-number type.
Definition: Real.hpp:69
playrho::d2::DiskShapeConf::UseRadius
constexpr DiskShapeConf & UseRadius(NonNegative< Length > r) noexcept
Uses the given value as the radius.
Definition: DiskShapeConf.hpp:65
playrho::d2::GetMaxMotorForce
Force GetMaxMotorForce(const Joint &object)
Gets the given joint's max motor force if its type supports that.
Definition: Joint.cpp:332
playrho::d2::BodyConf
Configuration for a body.
Definition: BodyConf.hpp:50
playrho::d2::GetLocation
constexpr Length2 GetLocation(const Transformation &value) noexcept
Gets the location information from the given transformation.
Definition: Transformation.hpp:69
playrho::BodyID
detail::IndexingNamedType< BodyCounter, struct BodyIdentifier > BodyID
Identifier for bodies.
Definition: BodyID.hpp:30
playrho::d2::Velocity
2-D velocity related data structure.
Definition: Velocity.hpp:38
playrho::SquareRadian
constexpr auto SquareRadian
Square radian unit type.
Definition: Units.hpp:379
playrho::d2::SetMotorSpeed
void SetMotorSpeed(Joint &object, AngularVelocity value)
Sets the given joint's motor speed if its type supports that.
Definition: Joint.cpp:269
playrho::Vector
Vector.
Definition: Vector.hpp:49
playrho::d2::GetAnchorA
Length2 GetAnchorA(const World &world, JointID id)
Get the anchor point on body-A in world coordinates.
Definition: WorldJoint.cpp:208
playrho::d2::SetAwake
void SetAwake(Body &body) noexcept
Awakens this body.
Definition: Body.hpp:822
playrho::d2::GetType
TypeID GetType(const Shape &shape) noexcept
Gets the type info of the use of the given shape.
Definition: Shape.hpp:329
playrho::d2::GetLinearReaction
constexpr Momentum2 GetLinearReaction(const DistanceJointConf &object) noexcept
Gets the current linear reaction for the given configuration.
Definition: DistanceJointConf.hpp:163
playrho::RotInertia
PLAYRHO_QUANTITY(boost::units::si::moment_of_inertia) RotInertia
Rotational inertia quantity.
Definition: Units.hpp:274
playrho::d2::Acceleration
2-D acceleration related data structure.
Definition: Acceleration.hpp:33
playrho::d2::GetRevoluteJointConf
RevoluteJointConf GetRevoluteJointConf(const Joint &joint)
Gets the definition data for the given joint.
Definition: RevoluteJointConf.cpp:102
playrho::d2::RevoluteJointConf::angularMass
RotInertia angularMass
Effective mass for motor/limit angular constraint.
Definition: RevoluteJointConf.hpp:157
playrho::d2::Joint
A joint-like constraint on one or more bodies.
Definition: Joint.hpp:144
playrho::d2::Shape
Shape.
Definition: Shape.hpp:183
playrho::GetY
constexpr auto & GetY(T &value)
Gets the "Y" element of the given value - i.e. the second element.
Definition: Math.hpp:73
playrho::Angle
PLAYRHO_QUANTITY(boost::units::si::plane_angle) Angle
Angle quantity.
Definition: Units.hpp:215
playrho::Length2
Vector2< Length > Length2
2-element vector of Length quantities.
Definition: Vector2.hpp:43
playrho::d2::GetWorldIndex
BodyCounter GetWorldIndex(const World &world, BodyID id) noexcept
Gets the world index for the given body.
Definition: WorldBody.cpp:202
playrho::d2::SetAccelerations
void SetAccelerations(World &world, Acceleration acceleration) noexcept
Sets the accelerations of all the world's bodies to the given value.
Definition: WorldBody.cpp:558
playrho::d2::RevoluteJointConf::referenceAngle
Angle referenceAngle
Reference angle.
Definition: RevoluteJointConf.hpp:134
playrho::d2::Step
StepStats Step(WorldImpl &world, const StepConf &conf)
Steps the given world the specified amount.
Definition: WorldImplMisc.cpp:85
playrho::d2::GetLinearLowerLimit
Length GetLinearLowerLimit(const Joint &object)
Gets the lower linear joint limit.
Definition: Joint.cpp:478
playrho::d2::EnableLimit
void EnableLimit(Joint &object, bool value)
Enables the specified joint's limit property if it supports one.
Definition: Joint.cpp:546
playrho::d2::GetVelocity
Velocity GetVelocity(const Body &body, Time h) noexcept
Gets the velocity of the body after the given time accounting for the body's acceleration and capped ...
Definition: Body.cpp:221
playrho::d2::RevoluteJointConf
Revolute joint definition.
Definition: RevoluteJointConf.hpp:64
playrho::d2::LimitState::e_inactiveLimit
@ e_inactiveLimit
Inactive limit.
playrho::AngularVelocity
PLAYRHO_QUANTITY(boost::units::si::angular_velocity) AngularVelocity
Angular velocity quantity.
Definition: Units.hpp:225
playrho::d2::DiskShapeConf
Disk shape configuration.
Definition: DiskShapeConf.hpp:42