mirror of https://github.com/mosra/magnum.git
Browse Source
Useful for using with animation tracks that target the translation/rotation/scaling properties separately.pull/191/head
10 changed files with 1222 additions and 0 deletions
@ -0,0 +1,223 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||||
|
Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Corrade/TestSuite/Tester.h> |
||||||
|
|
||||||
|
#include "Magnum/SceneGraph/TranslationRotationScalingTransformation2D.h" |
||||||
|
#include "Magnum/SceneGraph/Scene.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace SceneGraph { namespace Test { |
||||||
|
|
||||||
|
typedef Object<TranslationRotationScalingTransformation2D> Object2D; |
||||||
|
typedef Scene<TranslationRotationScalingTransformation2D> Scene2D; |
||||||
|
|
||||||
|
struct TranslationRotationScalingTransformation2DTest: TestSuite::Tester { |
||||||
|
explicit TranslationRotationScalingTransformation2DTest(); |
||||||
|
|
||||||
|
void fromMatrix(); |
||||||
|
void toMatrix(); |
||||||
|
void compose(); |
||||||
|
void inverted(); |
||||||
|
|
||||||
|
void defaults(); |
||||||
|
void setTransformation(); |
||||||
|
void resetTransformation(); |
||||||
|
|
||||||
|
void translate(); |
||||||
|
void rotate(); |
||||||
|
void scale(); |
||||||
|
}; |
||||||
|
|
||||||
|
TranslationRotationScalingTransformation2DTest::TranslationRotationScalingTransformation2DTest() { |
||||||
|
addTests({&TranslationRotationScalingTransformation2DTest::fromMatrix, |
||||||
|
&TranslationRotationScalingTransformation2DTest::toMatrix, |
||||||
|
&TranslationRotationScalingTransformation2DTest::compose, |
||||||
|
&TranslationRotationScalingTransformation2DTest::inverted, |
||||||
|
|
||||||
|
&TranslationRotationScalingTransformation2DTest::defaults, |
||||||
|
&TranslationRotationScalingTransformation2DTest::setTransformation, |
||||||
|
&TranslationRotationScalingTransformation2DTest::resetTransformation, |
||||||
|
|
||||||
|
&TranslationRotationScalingTransformation2DTest::translate, |
||||||
|
&TranslationRotationScalingTransformation2DTest::rotate, |
||||||
|
&TranslationRotationScalingTransformation2DTest::scale}); |
||||||
|
} |
||||||
|
|
||||||
|
using namespace Math::Literals; |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation2DTest::fromMatrix() { |
||||||
|
Matrix3 m = Matrix3::rotation(17.0_degf)*Matrix3::translation({1.0f, -0.3f}); |
||||||
|
CORRADE_COMPARE(Implementation::Transformation<TranslationRotationScalingTransformation2D>::fromMatrix(m), m); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation2DTest::toMatrix() { |
||||||
|
Matrix3 m = Matrix3::rotation(17.0_degf)*Matrix3::translation({1.0f, -0.3f}); |
||||||
|
CORRADE_COMPARE(Implementation::Transformation<TranslationRotationScalingTransformation2D>::toMatrix(m), m); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation2DTest::compose() { |
||||||
|
Matrix3 parent = Matrix3::rotation(17.0_degf); |
||||||
|
Matrix3 child = Matrix3::translation({1.0f, -0.3f}); |
||||||
|
CORRADE_COMPARE(Implementation::Transformation<TranslationRotationScalingTransformation2D>::compose(parent, child), parent*child); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation2DTest::inverted() { |
||||||
|
Matrix3 m = Matrix3::rotation(17.0_degf)*Matrix3::translation({1.0f, -0.3f}); |
||||||
|
CORRADE_COMPARE(Implementation::Transformation<TranslationRotationScalingTransformation2D>::inverted(m)*m, Matrix3()); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation2DTest::defaults() { |
||||||
|
Object2D o; |
||||||
|
CORRADE_COMPARE(o.translation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(o.rotation(), Complex{}); |
||||||
|
CORRADE_COMPARE(o.scaling(), Vector2{1.0f}); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), Matrix3{}); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation2DTest::setTransformation() { |
||||||
|
/* Dirty after setting transformation */ |
||||||
|
Object2D o; |
||||||
|
o.setClean(); |
||||||
|
CORRADE_VERIFY(!o.isDirty()); |
||||||
|
o.setTransformation( |
||||||
|
Matrix3::translation({7.0f, -1.0f})* |
||||||
|
Matrix3::rotation(17.0_degf)* |
||||||
|
Matrix3::scaling({1.5f, 0.5f})); |
||||||
|
CORRADE_VERIFY(o.isDirty()); |
||||||
|
CORRADE_COMPARE(o.translation(), (Vector2{7.0f, -1.0f})); |
||||||
|
CORRADE_COMPARE(o.rotation(), Complex::rotation(17.0_degf)); |
||||||
|
CORRADE_COMPARE(o.scaling(), (Vector2{1.5f, 0.5f})); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix3::translation({7.0f, -1.0f})* |
||||||
|
Matrix3::rotation(17.0_degf)* |
||||||
|
Matrix3::scaling({1.5f, 0.5f})); |
||||||
|
|
||||||
|
/* Scene cannot be transformed */ |
||||||
|
Scene2D s; |
||||||
|
s.setClean(); |
||||||
|
CORRADE_VERIFY(!s.isDirty()); |
||||||
|
s.setTransformation(Matrix3::rotation(17.0_degf)); |
||||||
|
CORRADE_VERIFY(!s.isDirty()); |
||||||
|
CORRADE_COMPARE(s.transformationMatrix(), Matrix3()); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation2DTest::resetTransformation() { |
||||||
|
Object2D o; |
||||||
|
o.rotate(17.0_degf); |
||||||
|
CORRADE_VERIFY(o.transformationMatrix() != Matrix3()); |
||||||
|
o.resetTransformation(); |
||||||
|
CORRADE_COMPARE(o.translation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(o.rotation(), Complex{}); |
||||||
|
CORRADE_COMPARE(o.scaling(), Vector2{1.0f}); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), Matrix3()); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation2DTest::translate() { |
||||||
|
{ |
||||||
|
Object2D o; |
||||||
|
o.setTransformation(Matrix3::rotation(17.0_degf)); |
||||||
|
o.translate({1.0f, -0.3f}) |
||||||
|
.translate({1.0f, 0.1f}); |
||||||
|
CORRADE_COMPARE(o.translation(), (Vector2{2.0f, -0.2f})); |
||||||
|
CORRADE_COMPARE(o.rotation(), Complex::rotation(17.0_degf)); |
||||||
|
CORRADE_COMPARE(o.scaling(), Vector2{1.0f}); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix3::translation({1.0f, 0.1f})* |
||||||
|
Matrix3::translation({1.0f, -0.3f})* |
||||||
|
Matrix3::rotation(17.0_degf)); |
||||||
|
} { |
||||||
|
Object2D o; |
||||||
|
o.setTransformation(Matrix3::rotation(17.0_degf)); |
||||||
|
o.translateLocal({1.0f, -0.3f}) |
||||||
|
.translateLocal({1.0f, 0.1f}); |
||||||
|
CORRADE_COMPARE(o.translation(), (Vector2{2.0f, -0.2f})); |
||||||
|
CORRADE_COMPARE(o.rotation(), Complex::rotation(17.0_degf)); |
||||||
|
CORRADE_COMPARE(o.scaling(), Vector2{1.0f}); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix3::translation({1.0f, -0.3f})* |
||||||
|
Matrix3::translation({1.0f, 0.1f})* |
||||||
|
Matrix3::rotation(17.0_degf)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation2DTest::rotate() { |
||||||
|
{ |
||||||
|
Object2D o; |
||||||
|
o.setTransformation(Matrix3::translation({1.0f, -0.3f})); |
||||||
|
o.rotate(17.0_degf) |
||||||
|
.rotate(-96.0_degf); |
||||||
|
CORRADE_COMPARE(o.translation(), (Vector2{1.0f, -0.3f})); |
||||||
|
CORRADE_COMPARE(o.rotation(), Complex::rotation(-79.0_degf)); |
||||||
|
CORRADE_COMPARE(o.scaling(), Vector2{1.0f}); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix3::translation({1.0f, -0.3f})* |
||||||
|
Matrix3::rotation(-96.0_degf)* |
||||||
|
Matrix3::rotation(17.0_degf)); |
||||||
|
} { |
||||||
|
Object2D o; |
||||||
|
o.setTransformation(Matrix3::translation({1.0f, -0.3f})); |
||||||
|
o.rotateLocal(17.0_degf) |
||||||
|
.rotateLocal(-96.0_degf); |
||||||
|
CORRADE_COMPARE(o.translation(), (Vector2{1.0f, -0.3f})); |
||||||
|
CORRADE_COMPARE(o.rotation(), Complex::rotation(-79.0_degf)); |
||||||
|
CORRADE_COMPARE(o.scaling(), Vector2{1.0f}); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix3::translation({1.0f, -0.3f})* |
||||||
|
Matrix3::rotation(17.0_degf)* |
||||||
|
Matrix3::rotation(-96.0_degf)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation2DTest::scale() { |
||||||
|
{ |
||||||
|
Object2D o; |
||||||
|
o.setTransformation(Matrix3::rotation(17.0_degf)); |
||||||
|
o.scale({1.0f, -0.3f}) |
||||||
|
.scale({0.5f, 1.1f}); |
||||||
|
CORRADE_COMPARE(o.translation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(o.rotation(), Complex::rotation(17.0_degf)); |
||||||
|
CORRADE_COMPARE(o.scaling(), (Vector2{0.5f, -0.33f})); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix3::rotation(17.0_degf)* |
||||||
|
Matrix3::scaling({0.5f, 1.1f})* |
||||||
|
Matrix3::scaling({1.0f, -0.3f})); |
||||||
|
} { |
||||||
|
Object2D o; |
||||||
|
o.setTransformation(Matrix3::rotation(17.0_degf)); |
||||||
|
o.scaleLocal({1.0f, -0.3f}) |
||||||
|
.scaleLocal({0.5f, 1.1f}); |
||||||
|
CORRADE_COMPARE(o.translation(), Vector2{}); |
||||||
|
CORRADE_COMPARE(o.rotation(), Complex::rotation(17.0_degf)); |
||||||
|
CORRADE_COMPARE(o.scaling(), (Vector2{0.5f, -0.33f})); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix3::rotation(17.0_degf)* |
||||||
|
Matrix3::scaling({1.0f, -0.3f})* |
||||||
|
Matrix3::scaling({0.5f, 1.1f})); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}}} |
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::SceneGraph::Test::TranslationRotationScalingTransformation2DTest) |
||||||
@ -0,0 +1,239 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||||
|
Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Corrade/TestSuite/Tester.h> |
||||||
|
|
||||||
|
#include "Magnum/SceneGraph/TranslationRotationScalingTransformation3D.h" |
||||||
|
#include "Magnum/SceneGraph/Scene.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace SceneGraph { namespace Test { |
||||||
|
|
||||||
|
typedef Object<TranslationRotationScalingTransformation3D> Object3D; |
||||||
|
typedef Scene<TranslationRotationScalingTransformation3D> Scene3D; |
||||||
|
|
||||||
|
struct TranslationRotationScalingTransformation3DTest: TestSuite::Tester { |
||||||
|
explicit TranslationRotationScalingTransformation3DTest(); |
||||||
|
|
||||||
|
void fromMatrix(); |
||||||
|
void toMatrix(); |
||||||
|
void compose(); |
||||||
|
void inverted(); |
||||||
|
|
||||||
|
void defaults(); |
||||||
|
void setTransformation(); |
||||||
|
void resetTransformation(); |
||||||
|
|
||||||
|
void translate(); |
||||||
|
void rotate(); |
||||||
|
void scale(); |
||||||
|
}; |
||||||
|
|
||||||
|
TranslationRotationScalingTransformation3DTest::TranslationRotationScalingTransformation3DTest() { |
||||||
|
addTests({&TranslationRotationScalingTransformation3DTest::fromMatrix, |
||||||
|
&TranslationRotationScalingTransformation3DTest::toMatrix, |
||||||
|
&TranslationRotationScalingTransformation3DTest::compose, |
||||||
|
&TranslationRotationScalingTransformation3DTest::inverted, |
||||||
|
|
||||||
|
&TranslationRotationScalingTransformation3DTest::defaults, |
||||||
|
&TranslationRotationScalingTransformation3DTest::setTransformation, |
||||||
|
&TranslationRotationScalingTransformation3DTest::resetTransformation, |
||||||
|
|
||||||
|
&TranslationRotationScalingTransformation3DTest::translate, |
||||||
|
&TranslationRotationScalingTransformation3DTest::rotate, |
||||||
|
&TranslationRotationScalingTransformation3DTest::scale}); |
||||||
|
} |
||||||
|
|
||||||
|
using namespace Math::Literals; |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation3DTest::fromMatrix() { |
||||||
|
Matrix4 m = Matrix4::rotationX(17.0_degf)*Matrix4::translation({1.0f, -0.3f, 2.3f})*Matrix4::scaling({2.0f, 1.4f, -2.1f}); |
||||||
|
CORRADE_COMPARE(Implementation::Transformation<TranslationRotationScalingTransformation3D>::fromMatrix(m), m); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation3DTest::toMatrix() { |
||||||
|
Matrix4 m = Matrix4::rotationX(17.0_degf)*Matrix4::translation({1.0f, -0.3f, 2.3f})*Matrix4::scaling({2.0f, 1.4f, -2.1f}); |
||||||
|
CORRADE_COMPARE(Implementation::Transformation<TranslationRotationScalingTransformation3D>::toMatrix(m), m); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation3DTest::compose() { |
||||||
|
Matrix4 parent = Matrix4::rotationX(17.0_degf); |
||||||
|
Matrix4 child = Matrix4::translation({1.0f, -0.3f, 2.3f}); |
||||||
|
CORRADE_COMPARE(Implementation::Transformation<TranslationRotationScalingTransformation3D>::compose(parent, child), parent*child); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation3DTest::inverted() { |
||||||
|
Matrix4 m = Matrix4::rotationX(17.0_degf)*Matrix4::translation({1.0f, -0.3f, 2.3f})*Matrix4::scaling({2.0f, 1.4f, -2.1f}); |
||||||
|
CORRADE_COMPARE(Implementation::Transformation<TranslationRotationScalingTransformation3D>::inverted(m)*m, Matrix4()); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation3DTest::defaults() { |
||||||
|
Object3D o; |
||||||
|
CORRADE_COMPARE(o.translation(), Vector3{}); |
||||||
|
CORRADE_COMPARE(o.rotation(), Quaternion{}); |
||||||
|
CORRADE_COMPARE(o.scaling(), Vector3{1.0f}); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), Matrix4{}); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation3DTest::setTransformation() { |
||||||
|
/* Dirty after setting transformation */ |
||||||
|
Object3D o; |
||||||
|
o.setClean(); |
||||||
|
CORRADE_VERIFY(!o.isDirty()); |
||||||
|
o.setTransformation( |
||||||
|
Matrix4::translation({7.0f, -1.0f, 2.2f})* |
||||||
|
Matrix4::rotationX(17.0_degf)* |
||||||
|
Matrix4::scaling({1.5f, 0.5f, 3.0f})); |
||||||
|
CORRADE_VERIFY(o.isDirty()); |
||||||
|
CORRADE_COMPARE(o.translation(), (Vector3{7.0f, -1.0f, 2.2f})); |
||||||
|
CORRADE_COMPARE(o.rotation(), Quaternion::rotation(17.0_degf, Vector3::xAxis())); |
||||||
|
CORRADE_COMPARE(o.scaling(), (Vector3{1.5f, 0.5f, 3.0f})); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix4::translation({7.0f, -1.0f, 2.2f})* |
||||||
|
Matrix4::rotationX(17.0_degf)* |
||||||
|
Matrix4::scaling({1.5f, 0.5f, 3.0f})); |
||||||
|
|
||||||
|
/* Scene cannot be transformed */ |
||||||
|
Scene3D s; |
||||||
|
s.setClean(); |
||||||
|
CORRADE_VERIFY(!s.isDirty()); |
||||||
|
s.setTransformation(Matrix4::rotationX(17.0_degf)); |
||||||
|
CORRADE_VERIFY(!s.isDirty()); |
||||||
|
CORRADE_COMPARE(s.transformationMatrix(), Matrix4()); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation3DTest::resetTransformation() { |
||||||
|
Object3D o; |
||||||
|
o.rotateX(17.0_degf); |
||||||
|
CORRADE_VERIFY(o.transformationMatrix() != Matrix4()); |
||||||
|
o.resetTransformation(); |
||||||
|
CORRADE_COMPARE(o.translation(), Vector3{}); |
||||||
|
CORRADE_COMPARE(o.rotation(), Quaternion{}); |
||||||
|
CORRADE_COMPARE(o.scaling(), Vector3{1.0f}); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), Matrix4()); |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation3DTest::translate() { |
||||||
|
{ |
||||||
|
Object3D o; |
||||||
|
o.setTransformation(Matrix4::rotationX(17.0_degf)); |
||||||
|
o.translate({1.0f, -0.3f, 2.3f}) |
||||||
|
.translate({1.0f, 0.1f, 0.2f}); |
||||||
|
CORRADE_COMPARE(o.translation(), (Vector3{2.0f, -0.2f, 2.5f})); |
||||||
|
CORRADE_COMPARE(o.rotation(), Quaternion::rotation(17.0_degf, Vector3::xAxis())); |
||||||
|
CORRADE_COMPARE(o.scaling(), Vector3{1.0f}); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix4::translation({1.0f, 0.1f, 0.2f})* |
||||||
|
Matrix4::translation({1.0f, -0.3f, 2.3f})* |
||||||
|
Matrix4::rotationX(17.0_degf)); |
||||||
|
} { |
||||||
|
Object3D o; |
||||||
|
o.setTransformation(Matrix4::rotationX(17.0_degf)); |
||||||
|
o.translateLocal({1.0f, -0.3f, 2.3f}) |
||||||
|
.translateLocal({1.0f, 0.1f, 0.2f}); |
||||||
|
CORRADE_COMPARE(o.translation(), (Vector3{2.0f, -0.2f, 2.5f})); |
||||||
|
CORRADE_COMPARE(o.rotation(), Quaternion::rotation(17.0_degf, Vector3::xAxis())); |
||||||
|
CORRADE_COMPARE(o.scaling(), Vector3{1.0f}); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix4::translation({1.0f, -0.3f, 2.3f})* |
||||||
|
Matrix4::translation({1.0f, 0.1f, 0.2f})* |
||||||
|
Matrix4::rotationX(17.0_degf)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation3DTest::rotate() { |
||||||
|
{ |
||||||
|
Object3D o; |
||||||
|
o.setTransformation(Matrix4::translation({1.0f, -0.3f, 2.3f})); |
||||||
|
o.rotateX(17.0_degf) |
||||||
|
.rotateY(25.0_degf) |
||||||
|
.rotateZ(-23.0_degf) |
||||||
|
.rotate(96.0_degf, Vector3{1.0f/Constants::sqrt3()}); |
||||||
|
CORRADE_COMPARE(o.translation(), (Vector3{1.0f, -0.3f, 2.3f})); |
||||||
|
CORRADE_COMPARE(o.rotation(), |
||||||
|
Quaternion::rotation(96.0_degf, Vector3{1.0f/Constants::sqrt3()})* |
||||||
|
Quaternion::rotation(-23.0_degf, Vector3::zAxis())* |
||||||
|
Quaternion::rotation(25.0_degf, Vector3::yAxis())* |
||||||
|
Quaternion::rotation(17.0_degf, Vector3::xAxis())); |
||||||
|
CORRADE_COMPARE(o.scaling(), Vector3{1.0f}); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix4::translation({1.0f, -0.3f, 2.3f})* |
||||||
|
Matrix4::rotation(96.0_degf, Vector3{1.0f/Constants::sqrt3()})* |
||||||
|
Matrix4::rotationZ(-23.0_degf)* |
||||||
|
Matrix4::rotationY(25.0_degf)* |
||||||
|
Matrix4::rotationX(17.0_degf)); |
||||||
|
} { |
||||||
|
Object3D o; |
||||||
|
o.setTransformation(Matrix4::translation({1.0f, -0.3f, 2.3f})); |
||||||
|
o.rotateXLocal(17.0_degf) |
||||||
|
.rotateYLocal(25.0_degf) |
||||||
|
.rotateZLocal(-23.0_degf) |
||||||
|
.rotateLocal(96.0_degf, Vector3{1.0f/Constants::sqrt3()}); |
||||||
|
CORRADE_COMPARE(o.translation(), (Vector3{1.0f, -0.3f, 2.3f})); |
||||||
|
CORRADE_COMPARE(o.rotation(), |
||||||
|
Quaternion::rotation(17.0_degf, Vector3::xAxis())* |
||||||
|
Quaternion::rotation(25.0_degf, Vector3::yAxis())* |
||||||
|
Quaternion::rotation(-23.0_degf, Vector3::zAxis())* |
||||||
|
Quaternion::rotation(96.0_degf, Vector3(1.0f/Constants::sqrt3()))); |
||||||
|
CORRADE_COMPARE(o.scaling(), Vector3{1.0f}); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix4::translation({1.0f, -0.3f, 2.3f})* |
||||||
|
Matrix4::rotationX(17.0_degf)* |
||||||
|
Matrix4::rotationY(25.0_degf)* |
||||||
|
Matrix4::rotationZ(-23.0_degf)* |
||||||
|
Matrix4::rotation(96.0_degf, Vector3{1.0f/Constants::sqrt3()})); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void TranslationRotationScalingTransformation3DTest::scale() { |
||||||
|
{ |
||||||
|
Object3D o; |
||||||
|
o.setTransformation(Matrix4::rotationX(17.0_degf)); |
||||||
|
o.scale({1.0f, -0.3f, 2.3f}) |
||||||
|
.scale({0.5f, 1.1f, 2.0f}); |
||||||
|
CORRADE_COMPARE(o.translation(), Vector3{}); |
||||||
|
CORRADE_COMPARE(o.rotation(), Quaternion::rotation(17.0_degf, Vector3::xAxis())); |
||||||
|
CORRADE_COMPARE(o.scaling(), (Vector3{0.5f, -0.33f, 4.6f})); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix4::rotationX(17.0_degf)* |
||||||
|
Matrix4::scaling({0.5f, 1.1f, 2.0f})* |
||||||
|
Matrix4::scaling({1.0f, -0.3f, 2.3f})); |
||||||
|
} { |
||||||
|
Object3D o; |
||||||
|
o.setTransformation(Matrix4::rotationX(17.0_degf)); |
||||||
|
o.scaleLocal({1.0f, -0.3f, 2.3f}) |
||||||
|
.scaleLocal({0.5f, 1.1f, 2.0f}); |
||||||
|
CORRADE_COMPARE(o.translation(), Vector3{}); |
||||||
|
CORRADE_COMPARE(o.rotation(), Quaternion::rotation(17.0_degf, Vector3::xAxis())); |
||||||
|
CORRADE_COMPARE(o.scaling(), (Vector3{0.5f, -0.33f, 4.6f})); |
||||||
|
CORRADE_COMPARE(o.transformationMatrix(), |
||||||
|
Matrix4::rotationX(17.0_degf)* |
||||||
|
Matrix4::scaling({1.0f, -0.3f, 2.3f})* |
||||||
|
Matrix4::scaling({0.5f, 1.1f, 2.0f})); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}}} |
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::SceneGraph::Test::TranslationRotationScalingTransformation3DTest) |
||||||
@ -0,0 +1,320 @@ |
|||||||
|
#ifndef Magnum_SceneGraph_TranslationRotationScalingTransformation2D_h |
||||||
|
#define Magnum_SceneGraph_TranslationRotationScalingTransformation2D_h |
||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||||
|
Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @file
|
||||||
|
* @brief Class @ref Magnum::SceneGraph::BasicTranslationRotationScalingTransformation2D, typedef @ref Magnum::SceneGraph::TranslationRotationScalingTransformation2D |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Magnum/Math/Complex.h" |
||||||
|
#include "Magnum/Math/Matrix3.h" |
||||||
|
#include "Magnum/SceneGraph/AbstractTranslationRotationScaling2D.h" |
||||||
|
#include "Magnum/SceneGraph/Object.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace SceneGraph { |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Two-dimensional transformation implemented using translation, rotation and scaling |
||||||
|
|
||||||
|
Similar to @ref BasicMatrixTransformation2D, but stores translation, rotation |
||||||
|
and scaling separately. This makes it more suitable for e.g. animation, where |
||||||
|
there are usually separate animation tracks for translation, rotation and |
||||||
|
scaling. This separation also imposes some constraints --- for given object, |
||||||
|
scaling is always applied first, rotation second and translation last. In |
||||||
|
particular, unlike with matrix-based transformation implementation, it's not |
||||||
|
possible to rotate a translated object, for example --- one has to apply the |
||||||
|
rotation first and then translate using a rotated vector. |
||||||
|
@see @ref scenegraph, @ref TranslationRotationScalingTransformation2D, |
||||||
|
@ref BasicTranslationRotationScalingTransformation3D |
||||||
|
*/ |
||||||
|
template<class T> class BasicTranslationRotationScalingTransformation2D: public AbstractBasicTranslationRotationScaling2D<T> { |
||||||
|
public: |
||||||
|
/** @brief Underlying transformation type */ |
||||||
|
typedef Math::Matrix3<T> DataType; |
||||||
|
|
||||||
|
/** @brief Object transformation */ |
||||||
|
Math::Matrix3<T> transformation() const; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set transformation |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Expects that the transformation doesn't contain shear or reflection. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation2D<T>>& setTransformation(const Math::Matrix3<T>& transformation); |
||||||
|
|
||||||
|
/** @brief Object translation */ |
||||||
|
Math::Vector2<T> translation() const { return _translation; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set translation |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Translation is always applied last, after rotation and scaling. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation2D<T>>& setTranslation(const Math::Vector2<T>& translation); |
||||||
|
|
||||||
|
/** @brief Object rotation */ |
||||||
|
Math::Complex<T> rotation() const { return _rotation; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set rotation |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Rotation is always applied after scaling and before translation. |
||||||
|
* Expects that the quaternion is normalized. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation2D<T>>& setRotation(const Math::Complex<T>& rotation); |
||||||
|
|
||||||
|
/** @brief Object scaling */ |
||||||
|
Math::Vector2<T> scaling() const { return _scaling; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set scaling |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Scaling is always applied first, before rotation and translation. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation2D<T>>& setScaling(const Math::Vector2<T>& scaling); |
||||||
|
|
||||||
|
/** @copydoc AbstractTranslationRotationScaling2D::resetTransformation() */ |
||||||
|
Object<BasicTranslationRotationScalingTransformation2D<T>>& resetTransformation() { |
||||||
|
return setTransformation({}); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Translate object |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Note that translation is always applied last, after rotation and |
||||||
|
* scaling. |
||||||
|
* @see @ref translateLocal(), @ref Math::Vector2::xAxis(), |
||||||
|
* @ref Math::Vector2::yAxis() |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation2D<T>>& translate(const Math::Vector2<T>& vector) { |
||||||
|
return setTranslation(vector + _translation); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Translate object as a local transformation |
||||||
|
* |
||||||
|
* Equivalent to the above, as translation is commutative. Note that |
||||||
|
* translation is always applied last, after rotation and scaling. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation2D<T>>& translateLocal(const Math::Vector2<T>& vector) { |
||||||
|
return setTranslation(_translation + vector); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object using a complex number |
||||||
|
* @param complex Normalized complex number |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Note that rotation is always applied after scaling and before |
||||||
|
* translation. Expects that the complex number is normalized. |
||||||
|
* @see @ref rotate(Math::Rad<T>), |
||||||
|
* @ref rotateLocal(const Math::Complex<T>&) |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation2D<T>>& rotate(const Math::Complex<T>& complex) { |
||||||
|
return setRotation(complex*_rotation); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object using a complex number as a local transformation |
||||||
|
* |
||||||
|
* Equivalent to the above, as 2D rotation is commutative. Note that |
||||||
|
* rotation is always applied after scaling and before translation. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation2D<T>>& rotateLocal(const Math::Complex<T>& complex) { |
||||||
|
return setRotation(_rotation*complex); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object |
||||||
|
* @param angle Angle (counterclockwise) |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Same as calling @ref rotate(const Math::Complex<T>&) with |
||||||
|
* @ref Math::Complex::rotation(). Note that rotation is always applied after |
||||||
|
* scaling and before translation. |
||||||
|
* @see @ref rotateLocal() |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation2D<T>>& rotate(Math::Rad<T> angle) { |
||||||
|
return rotate(Math::Complex<T>::rotation(angle)); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object as a local transformation |
||||||
|
* |
||||||
|
* Similar to the above, except that the rotation is applied before all |
||||||
|
* other rotations. Note that rotation is always applied after scaling |
||||||
|
* and before translation. Same as calling |
||||||
|
* @ref rotateLocal(const Math::Complex<T>&) with |
||||||
|
* @ref Math::Complex::rotation(). |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation2D<T>>& rotateLocal(Math::Rad<T> angle) { |
||||||
|
return rotateLocal(Math::Complex<T>::rotation(angle)); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scale object |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Note that scaling is always applied first, before rotation and |
||||||
|
* translation. |
||||||
|
* @see @ref scaleLocal(), @ref Math::Vector2::xScale(), |
||||||
|
* @ref Math::Vector2::yScale() |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation2D<T>>& scale(const Math::Vector2<T>& vector) { |
||||||
|
return setScaling(vector*_scaling); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scale object as a local transformation |
||||||
|
* |
||||||
|
* Equivalent to the above, as scaling is commutative. Note that |
||||||
|
* scaling is always first, before rotation and translation. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation2D<T>>& scaleLocal(const Math::Vector2<T>& vector) { |
||||||
|
return setScaling(_scaling*vector); |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
/* Allow construction only from Object */ |
||||||
|
explicit BasicTranslationRotationScalingTransformation2D() = default; |
||||||
|
|
||||||
|
private: |
||||||
|
void doResetTransformation() override final { resetTransformation(); } |
||||||
|
|
||||||
|
void doTranslate(const Math::Vector2<T>& vector) override final { translate(vector); } |
||||||
|
void doTranslateLocal(const Math::Vector2<T>& vector) override final { translateLocal(vector); } |
||||||
|
|
||||||
|
void doRotate(Math::Rad<T> angle) override final { |
||||||
|
rotate(angle); |
||||||
|
} |
||||||
|
void doRotateLocal(Math::Rad<T> angle) override final { |
||||||
|
rotateLocal(angle); |
||||||
|
} |
||||||
|
|
||||||
|
void doScale(const Math::Vector2<T>& vector) override final { scale(vector); } |
||||||
|
void doScaleLocal(const Math::Vector2<T>& vector) override final { scaleLocal(vector); } |
||||||
|
|
||||||
|
Math::Vector2<T> _translation; |
||||||
|
Math::Complex<T> _rotation; |
||||||
|
Math::Vector2<T> _scaling{T(1)}; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Two-dimensional transformation for float scenes implemented using translation, rotation and scaling |
||||||
|
|
||||||
|
@see @ref TranslationRotationScalingTransformation3D |
||||||
|
*/ |
||||||
|
typedef BasicTranslationRotationScalingTransformation2D<Float> TranslationRotationScalingTransformation2D; |
||||||
|
|
||||||
|
template<class T> Math::Matrix3<T> BasicTranslationRotationScalingTransformation2D<T>::transformation() const { |
||||||
|
return Math::Matrix3<T>::from(_rotation.toMatrix(), _translation)* |
||||||
|
Math::Matrix3<T>::scaling(_scaling); |
||||||
|
} |
||||||
|
|
||||||
|
template<class T> Object<BasicTranslationRotationScalingTransformation2D<T>>& BasicTranslationRotationScalingTransformation2D<T>::setTransformation(const Math::Matrix3<T>& transformation) { |
||||||
|
/* Setting transformation is forbidden for the scene */ |
||||||
|
/** @todo Assert for this? */ |
||||||
|
/** @todo Do this in some common code so we don't need to include Object? */ |
||||||
|
if(!static_cast<Object<BasicTranslationRotationScalingTransformation2D<T>>*>(this)->isScene()) { |
||||||
|
_translation = transformation.translation(); |
||||||
|
_rotation = Math::Complex<T>::fromMatrix(transformation.rotationShear()); |
||||||
|
_scaling = transformation.scaling(); |
||||||
|
static_cast<Object<BasicTranslationRotationScalingTransformation2D<T>>*>(this)->setDirty(); |
||||||
|
} |
||||||
|
|
||||||
|
return static_cast<Object<BasicTranslationRotationScalingTransformation2D<T>>&>(*this); |
||||||
|
} |
||||||
|
|
||||||
|
template<class T> Object<BasicTranslationRotationScalingTransformation2D<T>>& BasicTranslationRotationScalingTransformation2D<T>::setTranslation(const Math::Vector2<T>& translation) { |
||||||
|
/* Setting transformation is forbidden for the scene */ |
||||||
|
/** @todo Assert for this? */ |
||||||
|
/** @todo Do this in some common code so we don't need to include Object? */ |
||||||
|
if(!static_cast<Object<BasicTranslationRotationScalingTransformation2D<T>>*>(this)->isScene()) { |
||||||
|
_translation = translation; |
||||||
|
static_cast<Object<BasicTranslationRotationScalingTransformation2D<T>>*>(this)->setDirty(); |
||||||
|
} |
||||||
|
|
||||||
|
return static_cast<Object<BasicTranslationRotationScalingTransformation2D<T>>&>(*this); |
||||||
|
} |
||||||
|
|
||||||
|
template<class T> Object<BasicTranslationRotationScalingTransformation2D<T>>& BasicTranslationRotationScalingTransformation2D<T>::setRotation(const Math::Complex<T>& rotation) { |
||||||
|
/* Setting transformation is forbidden for the scene */ |
||||||
|
/** @todo Assert for this? */ |
||||||
|
/** @todo Do this in some common code so we don't need to include Object? */ |
||||||
|
if(!static_cast<Object<BasicTranslationRotationScalingTransformation2D<T>>*>(this)->isScene()) { |
||||||
|
_rotation = rotation; |
||||||
|
static_cast<Object<BasicTranslationRotationScalingTransformation2D<T>>*>(this)->setDirty(); |
||||||
|
} |
||||||
|
|
||||||
|
return static_cast<Object<BasicTranslationRotationScalingTransformation2D<T>>&>(*this); |
||||||
|
} |
||||||
|
|
||||||
|
template<class T> Object<BasicTranslationRotationScalingTransformation2D<T>>& BasicTranslationRotationScalingTransformation2D<T>::setScaling(const Math::Vector2<T>& scaling) { |
||||||
|
/* Setting transformation is forbidden for the scene */ |
||||||
|
/** @todo Assert for this? */ |
||||||
|
/** @todo Do this in some common code so we don't need to include Object? */ |
||||||
|
if(!static_cast<Object<BasicTranslationRotationScalingTransformation2D<T>>*>(this)->isScene()) { |
||||||
|
_scaling = scaling; |
||||||
|
static_cast<Object<BasicTranslationRotationScalingTransformation2D<T>>*>(this)->setDirty(); |
||||||
|
} |
||||||
|
|
||||||
|
return static_cast<Object<BasicTranslationRotationScalingTransformation2D<T>>&>(*this); |
||||||
|
} |
||||||
|
|
||||||
|
namespace Implementation { |
||||||
|
|
||||||
|
template<class T> struct Transformation<BasicTranslationRotationScalingTransformation2D<T>> { |
||||||
|
constexpr static Math::Matrix3<T> fromMatrix(const Math::Matrix3<T>& matrix) { |
||||||
|
return matrix; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr static Math::Matrix3<T> toMatrix(const Math::Matrix3<T>& transformation) { |
||||||
|
return transformation; |
||||||
|
} |
||||||
|
|
||||||
|
static Math::Matrix3<T> compose(const Math::Matrix3<T>& parent, const Math::Matrix3<T>& child) { |
||||||
|
return parent*child; |
||||||
|
} |
||||||
|
|
||||||
|
static Math::Matrix3<T> inverted(const Math::Matrix3<T>& transformation) { |
||||||
|
return transformation.inverted(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
#if defined(CORRADE_TARGET_WINDOWS) && !defined(__MINGW32__) |
||||||
|
extern template class MAGNUM_SCENEGRAPH_EXPORT Object<BasicTranslationRotationScalingTransformation2D<Float>>; |
||||||
|
#endif |
||||||
|
|
||||||
|
}} |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,410 @@ |
|||||||
|
#ifndef Magnum_SceneGraph_TranslationRotationScalingTransformation3D_h |
||||||
|
#define Magnum_SceneGraph_TranslationRotationScalingTransformation3D_h |
||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||||
|
Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @file
|
||||||
|
* @brief Class @ref Magnum::SceneGraph::BasicTranslationRotationScalingTransformation3D, typedef @ref Magnum::SceneGraph::TranslationRotationScalingTransformation3D |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Magnum/Math/Matrix4.h" |
||||||
|
#include "Magnum/Math/Quaternion.h" |
||||||
|
#include "Magnum/SceneGraph/AbstractTranslationRotationScaling3D.h" |
||||||
|
#include "Magnum/SceneGraph/Object.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace SceneGraph { |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Three-dimensional transformation implemented using translation, rotation and scaling |
||||||
|
|
||||||
|
Similar to @ref BasicMatrixTransformation3D, but stores translation, rotation |
||||||
|
and scaling separately. This makes it more suitable for e.g. animation, where |
||||||
|
there are usually separate animation tracks for translation, rotation and |
||||||
|
scaling. This separation also imposes some constraints --- for given object, |
||||||
|
scaling is always applied first, rotation second and translation last. In |
||||||
|
particular, unlike with matrix-based transformation implementation, it's not |
||||||
|
possible to rotate a translated object, for example --- one has to apply the |
||||||
|
rotation first and then translate using a rotated vector. |
||||||
|
@see @ref scenegraph, @ref TranslationRotationScalingTransformation3D, |
||||||
|
@ref BasicTranslationRotationScalingTransformation2D |
||||||
|
*/ |
||||||
|
template<class T> class BasicTranslationRotationScalingTransformation3D: public AbstractBasicTranslationRotationScaling3D<T> { |
||||||
|
public: |
||||||
|
/** @brief Underlying transformation type */ |
||||||
|
typedef Math::Matrix4<T> DataType; |
||||||
|
|
||||||
|
/** @brief Object transformation */ |
||||||
|
Math::Matrix4<T> transformation() const; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set transformation |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Expects that the transformation doesn't contain shear or reflection. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& setTransformation(const Math::Matrix4<T>& transformation); |
||||||
|
|
||||||
|
/** @brief Object translation */ |
||||||
|
Math::Vector3<T> translation() const { return _translation; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set translation |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Translation is always applied last, after rotation and scaling. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& setTranslation(const Math::Vector3<T>& translation); |
||||||
|
|
||||||
|
/** @brief Object rotation */ |
||||||
|
Math::Quaternion<T> rotation() const { return _rotation; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set rotation |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Rotation is always applied after scaling and before translation. |
||||||
|
* Expects that the quaternion is normalized. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& setRotation(const Math::Quaternion<T>& rotation); |
||||||
|
|
||||||
|
/** @brief Object scaling */ |
||||||
|
Math::Vector3<T> scaling() const { return _scaling; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set scaling |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Scaling is always applied first, before rotation and translation. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& setScaling(const Math::Vector3<T>& scaling); |
||||||
|
|
||||||
|
/** @copydoc AbstractTranslationRotationScaling3D::resetTransformation() */ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& resetTransformation() { |
||||||
|
return setTransformation({}); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Translate object |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Note that translation is always applied last, after rotation and |
||||||
|
* scaling. |
||||||
|
* @see @ref translateLocal(), @ref Math::Vector3::xAxis(), |
||||||
|
* @ref Math::Vector3::yAxis(), @ref Math::Vector3::zAxis() |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& translate(const Math::Vector3<T>& vector) { |
||||||
|
return setTranslation(vector + _translation); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Translate object as a local transformation |
||||||
|
* |
||||||
|
* Equivalent to the above, as translation is commutative. Note that |
||||||
|
* translation is always applied last, after rotation and scaling. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& translateLocal(const Math::Vector3<T>& vector) { |
||||||
|
return setTranslation(_translation + vector); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object using a quaternion |
||||||
|
* @param quaternion Normalized quaternion |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Note that rotation is always applied after scaling and before |
||||||
|
* translation. Expects that the quaternion is normalized. |
||||||
|
* @see @ref rotate(Math::Rad<T>, const Math::Vector3<T>&), |
||||||
|
* @ref rotateLocal(const Math::Quaternion<T>&), @ref rotateX(), |
||||||
|
* @ref rotateY(), @ref rotateZ(), @ref Math::Vector3::xAxis(), |
||||||
|
* @ref Math::Vector3::yAxis(), @ref Math::Vector3::zAxis() |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& rotate(const Math::Quaternion<T>& quaternion) { |
||||||
|
return setRotation(quaternion*_rotation); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object using a quaternion as a local transformation |
||||||
|
* |
||||||
|
* Similar to the above, except that the rotation is applied before all |
||||||
|
* other rotations. Note that rotation is always applied after scaling |
||||||
|
* and before translation. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& rotateLocal(const Math::Quaternion<T>& quaternion) { |
||||||
|
return setRotation(_rotation*quaternion); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object |
||||||
|
* @param angle Angle (counterclockwise) |
||||||
|
* @param normalizedAxis Normalized rotation axis |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Same as calling @ref rotate(const Math::Quaternion<T>&) |
||||||
|
* with @ref Quaternion::rotation(). Note that rotation is always |
||||||
|
* applied after scaling and before translation. |
||||||
|
* @see @ref rotateLocal(), @ref rotateX(), @ref rotateY(), |
||||||
|
* @ref rotateZ(), @ref Math::Vector3::xAxis(), |
||||||
|
* @ref Math::Vector3::yAxis(), @ref Math::Vector3::zAxis() |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& rotate(Math::Rad<T> angle, const Math::Vector3<T>& normalizedAxis) { |
||||||
|
return rotate(Math::Quaternion<T>::rotation(angle, normalizedAxis)); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object as a local transformation |
||||||
|
* |
||||||
|
* Similar to the above, except that the rotation is applied before all |
||||||
|
* other rotations. Note that rotation is always applied after scaling |
||||||
|
* and before translation. Same as calling |
||||||
|
* @ref rotateLocal(const Math::Quaternion<T>&) with |
||||||
|
* @ref Math::Quaternion::rotation(). |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& rotateLocal(Math::Rad<T> angle, const Math::Vector3<T>& normalizedAxis) { |
||||||
|
return rotateLocal(Math::Quaternion<T>::rotation(angle, normalizedAxis)); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object around X axis |
||||||
|
* @param angle Angle (counterclockwise) |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Same as calling @ref rotate() with @ref Math::Vector3::xAxis() as an |
||||||
|
* axis. |
||||||
|
* @see @ref rotateXLocal() |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& rotateX(Math::Rad<T> angle) { |
||||||
|
return rotate(angle, Math::Vector3<T>::xAxis()); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object around X axis as a local transformation |
||||||
|
* |
||||||
|
* Similar to the above, except that the rotation is applied before all |
||||||
|
* other rotations. Note that rotation is always applied after scaling |
||||||
|
* and before translation. Same as calling @ref rotateLocal() with |
||||||
|
* @ref Math::Vector3::xAxis() as an axis. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& rotateXLocal(Math::Rad<T> angle) { |
||||||
|
return rotateLocal(angle, Math::Vector3<T>::xAxis()); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object around Y axis |
||||||
|
* @param angle Angle (counterclockwise) |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Same as calling @ref rotate() with @ref Math::Vector3::yAxis() as an |
||||||
|
* axis. |
||||||
|
* @see @ref rotateYLocal() |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& rotateY(Math::Rad<T> angle) { |
||||||
|
return rotate(angle, Math::Vector3<T>::yAxis()); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object around Y axis as a local transformation |
||||||
|
* |
||||||
|
* Similar to the above, except that the rotation is applied before all |
||||||
|
* other rotations. Note that rotation is always applied after scaling |
||||||
|
* and before translation. Same as calling @ref rotateLocal() with |
||||||
|
* @ref Math::Vector3::yAxis() as an axis. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& rotateYLocal(Math::Rad<T> angle) { |
||||||
|
return rotateLocal(angle, Math::Vector3<T>::yAxis()); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object around Z axis |
||||||
|
* @param angle Angle (counterclockwise) |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Same as calling @ref rotate() with @ref Math::Vector3::yAxis() as an |
||||||
|
* axis. |
||||||
|
* @see @ref rotateZLocal() |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& rotateZ(Math::Rad<T> angle) { |
||||||
|
return rotate(angle, Math::Vector3<T>::zAxis()); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotate object around Z axis as a local transformation |
||||||
|
* |
||||||
|
* Similar to the above, except that the rotation is applied before all |
||||||
|
* other rotations. Note that rotation is always applied after scaling |
||||||
|
* and before translation. Same as calling @ref rotateLocal() with |
||||||
|
* @ref Math::Vector3::zAxis() as an axis. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& rotateZLocal(Math::Rad<T> angle) { |
||||||
|
return rotateLocal(angle, Math::Vector3<T>::zAxis()); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scale object |
||||||
|
* @return Reference to self (for method chaining) |
||||||
|
* |
||||||
|
* Note that scaling is always applied first, before rotation and |
||||||
|
* translation. |
||||||
|
* @see @ref scaleLocal(), @ref Math::Vector3::xScale(), |
||||||
|
* @ref Math::Vector3::yScale(), @ref Math::Vector3::zScale() |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& scale(const Math::Vector3<T>& vector) { |
||||||
|
return setScaling(vector*_scaling); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scale object as a local transformation |
||||||
|
* |
||||||
|
* Equivalent to the above, as scaling is commutative. Note that |
||||||
|
* scaling is always first, before rotation and translation. |
||||||
|
*/ |
||||||
|
Object<BasicTranslationRotationScalingTransformation3D<T>>& scaleLocal(const Math::Vector3<T>& vector) { |
||||||
|
return setScaling(_scaling*vector); |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
/* Allow construction only from Object */ |
||||||
|
explicit BasicTranslationRotationScalingTransformation3D() = default; |
||||||
|
|
||||||
|
private: |
||||||
|
void doResetTransformation() override final { resetTransformation(); } |
||||||
|
|
||||||
|
void doTranslate(const Math::Vector3<T>& vector) override final { translate(vector); } |
||||||
|
void doTranslateLocal(const Math::Vector3<T>& vector) override final { translateLocal(vector); } |
||||||
|
|
||||||
|
void doRotate(Math::Rad<T> angle, const Math::Vector3<T>& normalizedAxis) override final { |
||||||
|
rotate(angle, normalizedAxis); |
||||||
|
} |
||||||
|
void doRotateLocal(Math::Rad<T> angle, const Math::Vector3<T>& normalizedAxis) override final { |
||||||
|
rotateLocal(angle, normalizedAxis); |
||||||
|
} |
||||||
|
|
||||||
|
void doRotateX(Math::Rad<T> angle) override final { rotateX(angle); } |
||||||
|
void doRotateXLocal(Math::Rad<T> angle) override final { rotateXLocal(angle); } |
||||||
|
|
||||||
|
void doRotateY(Math::Rad<T> angle) override final { rotateY(angle); } |
||||||
|
void doRotateYLocal(Math::Rad<T> angle) override final { rotateYLocal(angle); } |
||||||
|
|
||||||
|
void doRotateZ(Math::Rad<T> angle) override final { rotateZ(angle); } |
||||||
|
void doRotateZLocal(Math::Rad<T> angle) override final { rotateZLocal(angle); } |
||||||
|
|
||||||
|
void doScale(const Math::Vector3<T>& vector) override final { scale(vector); } |
||||||
|
void doScaleLocal(const Math::Vector3<T>& vector) override final { scaleLocal(vector); } |
||||||
|
|
||||||
|
Math::Vector3<T> _translation; |
||||||
|
Math::Quaternion<T> _rotation; |
||||||
|
Math::Vector3<T> _scaling{T(1)}; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Three-dimensional transformation for float scenes implemented using translation, rotation and scaling |
||||||
|
|
||||||
|
@see @ref TranslationRotationScalingTransformation2D |
||||||
|
*/ |
||||||
|
typedef BasicTranslationRotationScalingTransformation3D<Float> TranslationRotationScalingTransformation3D; |
||||||
|
|
||||||
|
template<class T> Math::Matrix4<T> BasicTranslationRotationScalingTransformation3D<T>::transformation() const { |
||||||
|
return Math::Matrix4<T>::from(_rotation.toMatrix(), _translation)* |
||||||
|
Math::Matrix4<T>::scaling(_scaling); |
||||||
|
} |
||||||
|
|
||||||
|
template<class T> Object<BasicTranslationRotationScalingTransformation3D<T>>& BasicTranslationRotationScalingTransformation3D<T>::setTransformation(const Math::Matrix4<T>& transformation) { |
||||||
|
/* Setting transformation is forbidden for the scene */ |
||||||
|
/** @todo Assert for this? */ |
||||||
|
/** @todo Do this in some common code so we don't need to include Object? */ |
||||||
|
if(!static_cast<Object<BasicTranslationRotationScalingTransformation3D<T>>*>(this)->isScene()) { |
||||||
|
_translation = transformation.translation(); |
||||||
|
_rotation = Math::Quaternion<T>::fromMatrix(transformation.rotationShear()); |
||||||
|
_scaling = transformation.scaling(); |
||||||
|
static_cast<Object<BasicTranslationRotationScalingTransformation3D<T>>*>(this)->setDirty(); |
||||||
|
} |
||||||
|
|
||||||
|
return static_cast<Object<BasicTranslationRotationScalingTransformation3D<T>>&>(*this); |
||||||
|
} |
||||||
|
|
||||||
|
template<class T> Object<BasicTranslationRotationScalingTransformation3D<T>>& BasicTranslationRotationScalingTransformation3D<T>::setTranslation(const Math::Vector3<T>& translation) { |
||||||
|
/* Setting transformation is forbidden for the scene */ |
||||||
|
/** @todo Assert for this? */ |
||||||
|
/** @todo Do this in some common code so we don't need to include Object? */ |
||||||
|
if(!static_cast<Object<BasicTranslationRotationScalingTransformation3D<T>>*>(this)->isScene()) { |
||||||
|
_translation = translation; |
||||||
|
static_cast<Object<BasicTranslationRotationScalingTransformation3D<T>>*>(this)->setDirty(); |
||||||
|
} |
||||||
|
|
||||||
|
return static_cast<Object<BasicTranslationRotationScalingTransformation3D<T>>&>(*this); |
||||||
|
} |
||||||
|
|
||||||
|
template<class T> Object<BasicTranslationRotationScalingTransformation3D<T>>& BasicTranslationRotationScalingTransformation3D<T>::setRotation(const Math::Quaternion<T>& rotation) { |
||||||
|
/* Setting transformation is forbidden for the scene */ |
||||||
|
/** @todo Assert for this? */ |
||||||
|
/** @todo Do this in some common code so we don't need to include Object? */ |
||||||
|
if(!static_cast<Object<BasicTranslationRotationScalingTransformation3D<T>>*>(this)->isScene()) { |
||||||
|
_rotation = rotation; |
||||||
|
static_cast<Object<BasicTranslationRotationScalingTransformation3D<T>>*>(this)->setDirty(); |
||||||
|
} |
||||||
|
|
||||||
|
return static_cast<Object<BasicTranslationRotationScalingTransformation3D<T>>&>(*this); |
||||||
|
} |
||||||
|
|
||||||
|
template<class T> Object<BasicTranslationRotationScalingTransformation3D<T>>& BasicTranslationRotationScalingTransformation3D<T>::setScaling(const Math::Vector3<T>& scaling) { |
||||||
|
/* Setting transformation is forbidden for the scene */ |
||||||
|
/** @todo Assert for this? */ |
||||||
|
/** @todo Do this in some common code so we don't need to include Object? */ |
||||||
|
if(!static_cast<Object<BasicTranslationRotationScalingTransformation3D<T>>*>(this)->isScene()) { |
||||||
|
_scaling = scaling; |
||||||
|
static_cast<Object<BasicTranslationRotationScalingTransformation3D<T>>*>(this)->setDirty(); |
||||||
|
} |
||||||
|
|
||||||
|
return static_cast<Object<BasicTranslationRotationScalingTransformation3D<T>>&>(*this); |
||||||
|
} |
||||||
|
|
||||||
|
namespace Implementation { |
||||||
|
|
||||||
|
template<class T> struct Transformation<BasicTranslationRotationScalingTransformation3D<T>> { |
||||||
|
constexpr static Math::Matrix4<T> fromMatrix(const Math::Matrix4<T>& matrix) { |
||||||
|
return matrix; |
||||||
|
} |
||||||
|
|
||||||
|
constexpr static Math::Matrix4<T> toMatrix(const Math::Matrix4<T>& transformation) { |
||||||
|
return transformation; |
||||||
|
} |
||||||
|
|
||||||
|
static Math::Matrix4<T> compose(const Math::Matrix4<T>& parent, const Math::Matrix4<T>& child) { |
||||||
|
return parent*child; |
||||||
|
} |
||||||
|
|
||||||
|
static Math::Matrix4<T> inverted(const Math::Matrix4<T>& transformation) { |
||||||
|
return transformation.inverted(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
#if defined(CORRADE_TARGET_WINDOWS) && !defined(__MINGW32__) |
||||||
|
extern template class MAGNUM_SCENEGRAPH_EXPORT Object<BasicTranslationRotationScalingTransformation3D<Float>>; |
||||||
|
#endif |
||||||
|
|
||||||
|
}} |
||||||
|
|
||||||
|
#endif |
||||||
Loading…
Reference in new issue