cesium-native/CesiumGeospatial/test/TestGlobeAnchor.cpp

201 lines
6.7 KiB
C++

#include <CesiumGeometry/Transforms.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGeospatial/GlobeAnchor.h>
#include <CesiumGeospatial/LocalHorizontalCoordinateSystem.h>
#include <CesiumUtility/Math.h>
#include <doctest/doctest.h>
#include <glm/ext/matrix_double3x3.hpp>
#include <glm/ext/matrix_double4x4.hpp>
#include <glm/ext/quaternion_trigonometric.hpp>
#include <glm/ext/vector_double3.hpp>
#include <glm/fwd.hpp>
#include <glm/gtx/quaternion.hpp>
using namespace CesiumGeometry;
using namespace CesiumGeospatial;
using namespace CesiumUtility;
TEST_CASE("GlobeAnchor") {
Cartographic nullIsland = Cartographic(0.0, 0.0, 0.0);
LocalHorizontalCoordinateSystem leftHandedEastUpNorth(
nullIsland,
LocalDirection::East,
LocalDirection::Up,
LocalDirection::North,
1.0,
Ellipsoid::WGS84);
LocalHorizontalCoordinateSystem leftHandedEastUpNorth90(
Cartographic::fromDegrees(90.0, 0.0, 0.0),
LocalDirection::East,
LocalDirection::Up,
LocalDirection::North,
1.0,
Ellipsoid::WGS84);
SUBCASE("Identity transform in local is equivalent to the local") {
GlobeAnchor anchor = GlobeAnchor::fromAnchorToLocalTransform(
leftHandedEastUpNorth,
glm::dmat4(1.0));
CHECK(
anchor.getAnchorToFixedTransform() ==
leftHandedEastUpNorth.getLocalToEcefTransformation());
}
SUBCASE("Translation in local is represented correctly in ECEF") {
GlobeAnchor anchor = GlobeAnchor::fromAnchorToLocalTransform(
leftHandedEastUpNorth,
glm::dmat4(
glm::dvec4(1.0, 0.0, 0.0, 0.0),
glm::dvec4(0.0, 1.0, 0.0, 0.0),
glm::dvec4(0.0, 0.0, 1.0, 0.0),
glm::dvec4(1.0, 2.0, 3.0, 1.0)));
glm::dvec3 originInEcef =
glm::dvec3(leftHandedEastUpNorth.getLocalToEcefTransformation()[3]);
// +X in local is East, which is +Y in ECEF.
// +Y in local is Up, which is +X in ECEF.
// +Z in local is North, which is +Z in ECEF.
glm::dvec3 expectedPositionInEcef =
originInEcef + glm::dvec3(2.0, 1.0, 3.0);
glm::dvec3 actualPositionInEcef =
glm::dvec3(anchor.getAnchorToFixedTransform()[3]);
CHECK(Math::equalsEpsilon(
expectedPositionInEcef,
actualPositionInEcef,
0.0,
Math::Epsilon10));
}
SUBCASE(
"Translation-rotation-scale in local is represented correctly in ECEF") {
glm::dquat ninetyDegreesAboutX =
glm::angleAxis(Math::degreesToRadians(90.0), glm::dvec3(1.0, 0.0, 0.0));
glm::dmat4 anchorToLocal = Transforms::createTranslationRotationScaleMatrix(
glm::dvec3(1.0, 2.0, 3.0),
ninetyDegreesAboutX,
glm::dvec3(30.0, 20.0, 10.0));
GlobeAnchor anchor = GlobeAnchor::fromAnchorToLocalTransform(
leftHandedEastUpNorth,
anchorToLocal);
glm::dvec3 localPosition = glm::dvec3(7.0, 8.0, 9.0);
glm::dvec3 actualPositionInEcef = glm::dvec3(
anchor.getAnchorToFixedTransform() * glm::dvec4(localPosition, 1.0));
glm::dvec3 expectedPositionInEcef = glm::dvec3(
leftHandedEastUpNorth.getLocalToEcefTransformation() *
(anchorToLocal * glm::dvec4(localPosition, 1.0)));
CHECK(Math::equalsEpsilon(
expectedPositionInEcef,
actualPositionInEcef,
0.0,
Math::Epsilon10));
}
SUBCASE("Can transform between different local coordinate systems") {
glm::dmat4 toLocal = glm::dmat4(
glm::dvec4(1.0, 0.0, 0.0, 0.0),
glm::dvec4(0.0, 1.0, 0.0, 0.0),
glm::dvec4(0.0, 0.0, 1.0, 0.0),
glm::dvec4(1.0, 2.0, 3.0, 1.0));
GlobeAnchor anchor =
GlobeAnchor::fromAnchorToLocalTransform(leftHandedEastUpNorth, toLocal);
glm::dmat4 toLocal90 =
anchor.getAnchorToLocalTransform(leftHandedEastUpNorth90);
glm::dvec3 somePosition = glm::dvec3(123.0, 456.0, 789.0);
glm::dvec3 positionInLocal90 =
glm::dvec3(toLocal90 * glm::dvec4(somePosition, 1.0));
// +X in old local is East, which is +Y in ECEF, which is +Y in new local.
// +Y in old local is Up, which is +X in ECEF, which is -X in new local.
// +Z in old local is North, which is +Z in ECEF, which is +Z in new local.
glm::dvec3 oldOriginEcef =
glm::dvec3(Ellipsoid::WGS84.getMaximumRadius(), 0.0, 0.0);
glm::dvec3 newOriginEcef =
glm::dvec3(0.0, Ellipsoid::WGS84.getMaximumRadius(), 0.0);
glm::dvec3 offsetEcef = newOriginEcef - oldOriginEcef;
glm::dvec3 offset90 = glm::dvec3(-offsetEcef.x, offsetEcef.y, offsetEcef.z);
glm::dvec3 expectedPositionInLocal90 = -offset90 +
glm::dvec3(-2.0, 1.0, 3.0) +
glm::dvec3(-456.0, 123.0, 789.0);
CHECK(Math::equalsEpsilon(
expectedPositionInLocal90,
positionInLocal90,
0.0,
Math::Epsilon10));
}
SUBCASE("Moving in ECEF adjusts orientation if requested") {
glm::dmat4 toLocal = glm::dmat4(
glm::dvec4(1.0, 0.0, 0.0, 0.0),
glm::dvec4(0.0, 1.0, 0.0, 0.0),
glm::dvec4(0.0, 0.0, 1.0, 0.0),
glm::dvec4(0.0, 0.0, 0.0, 1.0));
GlobeAnchor anchor =
GlobeAnchor::fromAnchorToLocalTransform(leftHandedEastUpNorth, toLocal);
// Moving without adjusting orientation should leave the orientation
// unchanged.
GlobeAnchor first = anchor;
first.setAnchorToLocalTransform(
leftHandedEastUpNorth90,
toLocal,
false,
Ellipsoid::WGS84);
glm::dmat3 rotationScaleAfter =
glm::dmat3(first.getAnchorToLocalTransform(leftHandedEastUpNorth90));
CHECK(Math::equalsEpsilon(
rotationScaleAfter[0],
glm::dmat3(1.0)[0],
0.0,
Math::Epsilon10));
CHECK(Math::equalsEpsilon(
rotationScaleAfter[1],
glm::dmat3(1.0)[1],
0.0,
Math::Epsilon10));
CHECK(Math::equalsEpsilon(
rotationScaleAfter[2],
glm::dmat3(1.0)[2],
0.0,
Math::Epsilon10));
// But if we allow adjusting orientation, the object should stay upright.
GlobeAnchor second = anchor;
second.setAnchorToLocalTransform(
leftHandedEastUpNorth90,
toLocal,
true,
Ellipsoid::WGS84);
rotationScaleAfter =
glm::dmat3(second.getAnchorToLocalTransform(leftHandedEastUpNorth90));
glm::dmat3 expected = glm::dmat3(
glm::dvec3(0.0, -1.0, 0.0),
glm::dvec3(1.0, 0.0, 0.0),
glm::dvec3(0.0, 0.0, 1.0));
CHECK(Math::equalsEpsilon(
rotationScaleAfter[0],
expected[0],
0.0,
Math::Epsilon10));
CHECK(Math::equalsEpsilon(
rotationScaleAfter[1],
expected[1],
0.0,
Math::Epsilon10));
CHECK(Math::equalsEpsilon(
rotationScaleAfter[2],
expected[2],
0.0,
Math::Epsilon10));
}
}