From c288a98efcaf742583b76284d07690786aab0630 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 23 Aug 2024 19:16:06 +0600 Subject: [PATCH 1/7] expose complete camera controller to python + tests --- cpp/modmesh/view/R3DWidget.cpp | 20 +-------- cpp/modmesh/view/R3DWidget.hpp | 2 - cpp/modmesh/view/RCameraController.cpp | 12 ++++++ cpp/modmesh/view/RCameraController.hpp | 44 ++++++++++++++------ cpp/modmesh/view/RManager.cpp | 2 +- cpp/modmesh/view/wrap_view.cpp | 57 ++++++++++++++++++++++---- tests/test_view.py | 45 ++++++++++++++++++-- 7 files changed, 135 insertions(+), 47 deletions(-) diff --git a/cpp/modmesh/view/R3DWidget.cpp b/cpp/modmesh/view/R3DWidget.cpp index 4471dae0..9809aaab 100644 --- a/cpp/modmesh/view/R3DWidget.cpp +++ b/cpp/modmesh/view/R3DWidget.cpp @@ -41,7 +41,8 @@ R3DWidget::R3DWidget(Qt3DExtras::Qt3DWindow * window, RScene * scene, QWidget * { m_view->setRootEntity(m_scene); - resetCamera(); + cameraController()->setCamera(m_view->camera()); + cameraController()->reset(); if (Toggle::instance().fixed().get_show_axis()) { @@ -99,23 +100,6 @@ void R3DWidget::resizeEvent(QResizeEvent * event) m_container->resize(event->size()); } -void R3DWidget::resetCamera() const -{ - Qt3DRender::QCamera * camera = m_view->camera(); - - // Set up the camera. - camera->lens()->setPerspectiveProjection(45.0f, 16.0f / 9.0f, 0.1f, 1000.0f); - camera->setPosition(QVector3D(0.0f, 0.0f, 10.0f)); - camera->setViewCenter(QVector3D(0.0f, 0.0f, 0.0f)); - camera->setUpVector(QVector3D(0.f, 1.f, 0.f)); - - // Set up the camera control. - auto * control = m_scene->controller(); - control->setCamera(camera); - control->setLinearSpeed(50.0f); - control->setLookSpeed(180.0f); -} - } /* end namespace modmesh */ // vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: diff --git a/cpp/modmesh/view/R3DWidget.hpp b/cpp/modmesh/view/R3DWidget.hpp index 8727ad4a..b1b032e1 100644 --- a/cpp/modmesh/view/R3DWidget.hpp +++ b/cpp/modmesh/view/R3DWidget.hpp @@ -100,8 +100,6 @@ class R3DWidget Qt3DExtras::QAbstractCameraController * qtCameraController() { return m_scene->controller(); } CameraController * cameraController() { return dynamic_cast(m_scene->controller()); } - void resetCamera() const; - QPixmap grabPixmap() const { return m_view->screen()->grabWindow(m_view->winId()); } void showMark(); diff --git a/cpp/modmesh/view/RCameraController.cpp b/cpp/modmesh/view/RCameraController.cpp index 77774f85..3bb7b572 100644 --- a/cpp/modmesh/view/RCameraController.cpp +++ b/cpp/modmesh/view/RCameraController.cpp @@ -212,6 +212,18 @@ void RCameraInputListener::initKeyboardListeners() const m_ty_axis->addInput(m_keyboard_ty_neg_input); } +void CameraController::reset() +{ + camera()->lens()->setPerspectiveProjection(45.0f, 16.0f / 9.0f, 0.1f, 1000.0f); + + setPosition(QVector3D(0.0f, 0.0f, 10.0f)); + setViewCenter(QVector3D(0.0f, 0.0f, 0.0f)); + setUpVector(QVector3D(0.f, 1.f, 0.f)); + + setLinearSpeed(50.0f); + setLookSpeed(180.0f); +} + RFirstPersonCameraController::RFirstPersonCameraController(QNode * parent) : QFirstPersonCameraController(parent) { diff --git a/cpp/modmesh/view/RCameraController.hpp b/cpp/modmesh/view/RCameraController.hpp index c61df707..d59abb73 100644 --- a/cpp/modmesh/view/RCameraController.hpp +++ b/cpp/modmesh/view/RCameraController.hpp @@ -135,21 +135,29 @@ class CameraController virtual void updateCameraPosition(const CameraInputState & state, float dt) = 0; - virtual Qt3DRender::QCamera * getCamera() = 0; + virtual Qt3DRender::QCamera * camera() const = 0; + virtual void setCamera(Qt3DRender::QCamera * camera) = 0; - virtual float getLinearSpeed() = 0; + virtual float linearSpeed() const = 0; + virtual void setLinearSpeed(float value) = 0; - virtual float getLookSpeed() = 0; + virtual float lookSpeed() const = 0; + virtual void setLookSpeed(float value) = 0; virtual CameraControllerType getType() = 0; - QVector3D position() { return getCamera()->position(); } + QVector3D position() const { return camera()->position(); } + void setPosition(const QVector3D & value) const { camera()->setPosition(value); } - QVector3D viewVector() { return getCamera()->viewVector(); } + QVector3D viewVector() const { return camera()->viewVector(); } - QVector3D viewCenter() { return getCamera()->viewCenter(); } + QVector3D viewCenter() const { return camera()->viewCenter(); } + void setViewCenter(const QVector3D & value) const { camera()->setViewCenter(value); } - QVector3D upVector() { return getCamera()->upVector(); } + QVector3D upVector() const { return camera()->upVector(); } + void setUpVector(const QVector3D & value) const { camera()->setUpVector(value); } + + void reset(); protected: RCameraInputListener * m_listener = nullptr; @@ -169,9 +177,14 @@ class RFirstPersonCameraController : public Qt3DExtras::QFirstPersonCameraContro public: explicit RFirstPersonCameraController(QNode * parent = nullptr); - Qt3DRender::QCamera * getCamera() override { return camera(); } - float getLinearSpeed() override { return linearSpeed(); } - float getLookSpeed() override { return lookSpeed(); } + Qt3DRender::QCamera * camera() const override { return QFirstPersonCameraController::camera(); } + void setCamera(Qt3DRender::QCamera * camera) override { QFirstPersonCameraController::setCamera(camera); } + + float linearSpeed() const override { return QFirstPersonCameraController::linearSpeed(); } + void setLinearSpeed(float value) override { QFirstPersonCameraController::setLinearSpeed(value); } + + float lookSpeed() const override { return QFirstPersonCameraController::lookSpeed(); } + void setLookSpeed(float value) override { QFirstPersonCameraController::setLookSpeed(value); } private: static constexpr auto lookSpeedFactorOnShiftPressed = 0.2f; @@ -191,9 +204,14 @@ class ROrbitCameraController : public Qt3DExtras::QOrbitCameraController public: explicit ROrbitCameraController(QNode * parent = nullptr); - Qt3DRender::QCamera * getCamera() override { return camera(); } - float getLinearSpeed() override { return linearSpeed(); } - float getLookSpeed() override { return lookSpeed(); } + Qt3DRender::QCamera * camera() const override { return QOrbitCameraController::camera(); } + void setCamera(Qt3DRender::QCamera * camera) override { QOrbitCameraController::setCamera(camera); } + + float linearSpeed() const override { return QOrbitCameraController::linearSpeed(); } + void setLinearSpeed(float value) override { QOrbitCameraController::setLinearSpeed(value); } + + float lookSpeed() const override { return QOrbitCameraController::lookSpeed(); } + void setLookSpeed(float value) override { QOrbitCameraController::setLookSpeed(value); } private: void moveCamera(const InputState & state, float dt) override {} diff --git a/cpp/modmesh/view/RManager.cpp b/cpp/modmesh/view/RManager.cpp index 2516e9d7..21177e1d 100644 --- a/cpp/modmesh/view/RManager.cpp +++ b/cpp/modmesh/view/RManager.cpp @@ -267,7 +267,7 @@ void RManager::setUpCameraMovementMenuItems() const if (viewer == nullptr || viewer->camera() == nullptr) return; - viewer->resetCamera(); + viewer->cameraController()->reset(); }); auto * move_camera_up = new RAction( diff --git a/cpp/modmesh/view/wrap_view.cpp b/cpp/modmesh/view/wrap_view.cpp index 2085b889..52b60368 100644 --- a/cpp/modmesh/view/wrap_view.cpp +++ b/cpp/modmesh/view/wrap_view.cpp @@ -196,12 +196,6 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapR3DWidget } }, py::arg("name")) - .def( - "resetCamera", - [](wrapped_type & self) - { - self.resetCamera(); - }) .def("cameraController", &wrapped_type::cameraController); #define DECL_QVECTOR3D_PROPERTY(NAME, GETTER, SETTER) \ @@ -448,6 +442,10 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRCameraController namespace py = pybind11; (*this) + .def( + "reset", + [](wrapped_type & self) + { self.reset(); }) .def( "updateCameraPosition", []( @@ -496,14 +494,37 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRCameraController const auto position = self.position(); return py::make_tuple(position.x(), position.y(), position.z()); }) + .def( + "setPosition", + [](wrapped_type & self, float x, float y, float z) + { + self.setPosition(QVector3D(x, y, z)); + }, + py::arg("x"), + py::arg("y"), + py::arg("z")) .def( "linearSpeed", [](wrapped_base_type & self) - { return self.getLinearSpeed(); }) + { return self.linearSpeed(); }) + .def( + "setLinearSpeed", + [](wrapped_base_type & self, float value) + { + self.setLinearSpeed(value); + }, + py::arg("value")) .def( "lookSpeed", [](wrapped_base_type & self) - { return self.getLookSpeed(); }) + { return self.lookSpeed(); }) + .def( + "setLookSpeed", + [](wrapped_base_type & self, float value) + { + self.setLookSpeed(value); + }, + py::arg("value")) .def( "viewCenter", [](wrapped_base_type & self) @@ -511,6 +532,15 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRCameraController const auto center = self.viewCenter(); return py::make_tuple(center.x(), center.y(), center.z()); }) + .def( + "setViewCenter", + [](wrapped_type & self, float x, float y, float z) + { + self.setViewCenter(QVector3D(x, y, z)); + }, + py::arg("x"), + py::arg("y"), + py::arg("z")) .def( "viewVector", [](wrapped_base_type & self) @@ -524,7 +554,16 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRCameraController { const auto vector = self.upVector(); return py::make_tuple(vector.x(), vector.y(), vector.z()); - }); + }) + .def( + "setUpVector", + [](wrapped_type & self, float x, float y, float z) + { + self.setUpVector(QVector3D(x, y, z)); + }, + py::arg("x"), + py::arg("y"), + py::arg("z")); } }; diff --git a/tests/test_view.py b/tests/test_view.py index 4424b4b6..27d66831 100644 --- a/tests/test_view.py +++ b/tests/test_view.py @@ -30,6 +30,7 @@ import os import modmesh + try: from modmesh import view except ImportError: @@ -75,13 +76,24 @@ def setUpClass(cls): cls.controller = widget.cameraController() cls.move = cls.controller.updateCameraPosition + cls.resetCamera = cls.controller.reset cls.pos = cls.controller.position + cls.set_pos = cls.controller.setPosition + cls.look_speed = cls.controller.lookSpeed + cls.set_look_speed = cls.controller.setLookSpeed + cls.linear_speed = cls.controller.linearSpeed + cls.set_linear_speed = cls.controller.setLinearSpeed + cls.view_vector = cls.controller.viewVector + cls.view_center = cls.controller.viewCenter + cls.set_view_center = cls.controller.setViewCenter + cls.up_vector = cls.controller.upVector + cls.set_up_vector = cls.controller.setUpVector @classmethod def tearDownClass(cls): @@ -111,12 +123,37 @@ def normalize(self, vec): return vec / np.linalg.norm(vec) +@unittest.skipIf(GITHUB_ACTIONS, "GUI is not available in GitHub Actions") +class ViewCommonCameraTC(ViewCameraTB, unittest.TestCase): + camera_type = "fps" # no difference when use orbit camera + + def setUp(self): + self.resetCamera() + + def test_value_get_set(self): + self.set_linear_speed(123.0) + self.assertEqual(self.linear_speed(), 123.0) + + self.set_look_speed(456.0) + self.assertEqual(self.look_speed(), 456.0) + + def test_vector_get_set(self): + self.set_pos(x=1, y=2, z=3) + self.assertEqual(self.pos(), (1, 2, 3)) + + self.set_view_center(x=4, y=5, z=6) + self.assertEqual(self.view_center(), (4, 5, 6)) + + self.set_up_vector(x=7, y=8, z=9) + self.assertEqual(self.up_vector(), (7, 8, 9)) + + @unittest.skipIf(GITHUB_ACTIONS, "GUI is not available in GitHub Actions") class ViewFPSCameraTC(ViewCameraTB, unittest.TestCase): camera_type = "fps" def setUp(self): - self.widget.resetCamera() + self.resetCamera() def test_reset(self): dt = 0.01 @@ -133,7 +170,7 @@ def test_reset(self): self.assertNotEqual(self.view_center(), initial_view_center) self.assertNotEqual(self.up_vector(), initial_up_vector) - self.widget.resetCamera() + self.resetCamera() self.assertEqual(self.pos(), initial_pos) self.assertEqual(self.view_vector(), initial_view_vector) @@ -212,7 +249,7 @@ class ViewOrbitCameraTC(ViewCameraTB, unittest.TestCase): camera_type = "orbit" def setUp(self): - self.widget.resetCamera() + self.resetCamera() def test_reset(self): dt = 0.01 @@ -229,7 +266,7 @@ def test_reset(self): self.assertNotEqual(self.view_center(), initial_view_center) self.assertNotEqual(self.up_vector(), initial_up_vector) - self.widget.resetCamera() + self.resetCamera() self.assertEqual(self.pos(), initial_pos) self.assertEqual(self.view_vector(), initial_view_vector) From dcf2bc8346a66a94a472ac0918a286f113d6b159 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 23 Aug 2024 19:36:12 +0600 Subject: [PATCH 2/7] camera default values api + test --- cpp/modmesh/view/RCameraController.cpp | 10 +++--- cpp/modmesh/view/RCameraController.hpp | 12 +++++++ cpp/modmesh/view/wrap_view.cpp | 43 +++++++++++++++++++++++++- tests/test_view.py | 21 +++++++++++++ 4 files changed, 80 insertions(+), 6 deletions(-) diff --git a/cpp/modmesh/view/RCameraController.cpp b/cpp/modmesh/view/RCameraController.cpp index 3bb7b572..3b7bb0ce 100644 --- a/cpp/modmesh/view/RCameraController.cpp +++ b/cpp/modmesh/view/RCameraController.cpp @@ -216,12 +216,12 @@ void CameraController::reset() { camera()->lens()->setPerspectiveProjection(45.0f, 16.0f / 9.0f, 0.1f, 1000.0f); - setPosition(QVector3D(0.0f, 0.0f, 10.0f)); - setViewCenter(QVector3D(0.0f, 0.0f, 0.0f)); - setUpVector(QVector3D(0.f, 1.f, 0.f)); + setPosition(m_default_position); + setViewCenter(m_default_view_center); + setUpVector(m_default_up_vector); - setLinearSpeed(50.0f); - setLookSpeed(180.0f); + setLinearSpeed(m_default_linear_speed); + setLookSpeed(m_default_look_speed); } RFirstPersonCameraController::RFirstPersonCameraController(QNode * parent) diff --git a/cpp/modmesh/view/RCameraController.hpp b/cpp/modmesh/view/RCameraController.hpp index d59abb73..b53790e0 100644 --- a/cpp/modmesh/view/RCameraController.hpp +++ b/cpp/modmesh/view/RCameraController.hpp @@ -159,6 +159,12 @@ class CameraController void reset(); + void setDefaultPosition(QVector3D value) { m_default_position = value; } + void setDefaultViewCenter(QVector3D value) { m_default_view_center = value; } + void setDefaultUpVector(QVector3D value) { m_default_up_vector = value; } + void setDefaultLinearSpeed(float value) { m_default_linear_speed = value; } + void setDefaultLookSpeed(float value) { m_default_look_speed = value; } + protected: RCameraInputListener * m_listener = nullptr; @@ -167,6 +173,12 @@ class CameraController { return dynamic_cast(this); } + + QVector3D m_default_position = QVector3D(0.0f, 0.0f, 10.0f); + QVector3D m_default_view_center = QVector3D(0.0f, 0.0f, 0.0f); + QVector3D m_default_up_vector = QVector3D(0.0f, 1.0f, 0.0f); + float m_default_linear_speed = 50.0f; + float m_default_look_speed = 180.0f; }; /* end class CameraController */ class RFirstPersonCameraController : public Qt3DExtras::QFirstPersonCameraController diff --git a/cpp/modmesh/view/wrap_view.cpp b/cpp/modmesh/view/wrap_view.cpp index 52b60368..fec88c0d 100644 --- a/cpp/modmesh/view/wrap_view.cpp +++ b/cpp/modmesh/view/wrap_view.cpp @@ -563,7 +563,48 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRCameraController }, py::arg("x"), py::arg("y"), - py::arg("z")); + py::arg("z")) + .def( + "setDefaultPosition", + [](wrapped_type & self, float x, float y, float z) + { + self.setDefaultPosition(QVector3D(x, y, z)); + }, + py::arg("x"), + py::arg("y"), + py::arg("z")) + .def( + "setDefaultViewCenter", + [](wrapped_type & self, float x, float y, float z) + { + self.setDefaultViewCenter(QVector3D(x, y, z)); + }, + py::arg("x"), + py::arg("y"), + py::arg("z")) + .def( + "setDefaultUpVector", + [](wrapped_type & self, float x, float y, float z) + { + self.setDefaultUpVector(QVector3D(x, y, z)); + }, + py::arg("x"), + py::arg("y"), + py::arg("z")) + .def( + "setDefaultLinearSpeed", + [](wrapped_base_type & self, float value) + { + self.setDefaultLinearSpeed(value); + }, + py::arg("value")) + .def( + "setDefaultLookSpeed", + [](wrapped_base_type & self, float value) + { + self.setDefaultLookSpeed(value); + }, + py::arg("value")); } }; diff --git a/tests/test_view.py b/tests/test_view.py index 27d66831..b4cfad27 100644 --- a/tests/test_view.py +++ b/tests/test_view.py @@ -95,6 +95,12 @@ def setUpClass(cls): cls.up_vector = cls.controller.upVector cls.set_up_vector = cls.controller.setUpVector + cls.set_default_pos = cls.controller.setDefaultPosition + cls.set_default_view_center = cls.controller.setDefaultViewCenter + cls.set_default_up_vector = cls.controller.setDefaultUpVector + cls.set_default_linear_speed = cls.controller.setDefaultLinearSpeed + cls.set_default_look_speed = cls.controller.setDefaultLookSpeed + @classmethod def tearDownClass(cls): cls.widget.close_and_destroy() @@ -147,6 +153,21 @@ def test_vector_get_set(self): self.set_up_vector(x=7, y=8, z=9) self.assertEqual(self.up_vector(), (7, 8, 9)) + def test_default_values(self): + self.set_default_pos(x=1, y=2, z=3) + self.set_default_view_center(x=4, y=5, z=6) + self.set_default_up_vector(x=7, y=8, z=9) + self.set_default_linear_speed(123.0) + self.set_default_look_speed(456.0) + + self.resetCamera() + + self.assertEqual(self.pos(), (1, 2, 3)) + self.assertEqual(self.view_center(), (4, 5, 6)) + self.assertEqual(self.up_vector(), (7, 8, 9)) + self.assertEqual(self.linear_speed(), 123.0) + self.assertEqual(self.look_speed(), 456.0) + @unittest.skipIf(GITHUB_ACTIONS, "GUI is not available in GitHub Actions") class ViewFPSCameraTC(ViewCameraTB, unittest.TestCase): From f527710e8a05a72a4d8351bbac9e25df77fbd967 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 23 Aug 2024 23:52:02 +0600 Subject: [PATCH 3/7] refactor cameraController getters --- cpp/modmesh/view/R3DWidget.hpp | 26 ++++++++++++-------------- cpp/modmesh/view/RCameraController.hpp | 8 ++++---- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/cpp/modmesh/view/R3DWidget.hpp b/cpp/modmesh/view/R3DWidget.hpp index b1b032e1..5f2b769c 100644 --- a/cpp/modmesh/view/R3DWidget.hpp +++ b/cpp/modmesh/view/R3DWidget.hpp @@ -46,24 +46,21 @@ namespace modmesh { -class RScene - : public Qt3DCore::QEntity +class RScene : public Qt3DCore::QEntity { - public: - explicit RScene(Qt3DCore::QNode * parent = nullptr) - : Qt3DCore::QEntity(parent) - , m_controller(new ROrbitCameraController(this)) + explicit RScene(QNode * parent = nullptr) + : QEntity(parent) { + m_controller = new ROrbitCameraController(this); } - Qt3DExtras::QAbstractCameraController const * controller() const { return m_controller; } - Qt3DExtras::QAbstractCameraController * controller() { return m_controller; } + CameraController * controller() const { return m_controller; } + Qt3DExtras::QAbstractCameraController * qtController() const { return m_controller->asQtCameraController(); } - void setCameraController(Qt3DExtras::QAbstractCameraController * controller) - { - m_controller->deleteLater(); + void setCameraController(CameraController * controller) { + qtController()->deleteLater(); m_controller = controller; } @@ -73,7 +70,7 @@ class RScene private: - Qt3DExtras::QAbstractCameraController * m_controller = nullptr; + CameraController* m_controller; }; /* end class RScene */ @@ -97,8 +94,9 @@ class R3DWidget Qt3DExtras::Qt3DWindow * view() { return m_view; } RScene * scene() { return m_scene; } Qt3DRender::QCamera * camera() { return m_view->camera(); } - Qt3DExtras::QAbstractCameraController * qtCameraController() { return m_scene->controller(); } - CameraController * cameraController() { return dynamic_cast(m_scene->controller()); } + + CameraController * cameraController() const { return m_scene->controller(); } + Qt3DExtras::QAbstractCameraController * qtCameraController() const { return m_scene->qtController(); } QPixmap grabPixmap() const { return m_view->screen()->grabWindow(m_view->winId()); } diff --git a/cpp/modmesh/view/RCameraController.hpp b/cpp/modmesh/view/RCameraController.hpp index b53790e0..67118692 100644 --- a/cpp/modmesh/view/RCameraController.hpp +++ b/cpp/modmesh/view/RCameraController.hpp @@ -165,15 +165,15 @@ class CameraController void setDefaultLinearSpeed(float value) { m_default_linear_speed = value; } void setDefaultLookSpeed(float value) { m_default_look_speed = value; } -protected: - RCameraInputListener * m_listener = nullptr; - -private: Qt3DExtras::QAbstractCameraController * asQtCameraController() { return dynamic_cast(this); } +protected: + RCameraInputListener * m_listener = nullptr; + +private: QVector3D m_default_position = QVector3D(0.0f, 0.0f, 10.0f); QVector3D m_default_view_center = QVector3D(0.0f, 0.0f, 0.0f); QVector3D m_default_up_vector = QVector3D(0.0f, 1.0f, 0.0f); From 24f2312c99b50d781c383279db81353854f66bb8 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 23 Aug 2024 23:53:03 +0600 Subject: [PATCH 4/7] do not change aspect ration of camera when reest --- cpp/modmesh/view/RCameraController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/modmesh/view/RCameraController.cpp b/cpp/modmesh/view/RCameraController.cpp index 3b7bb0ce..cd8e6e98 100644 --- a/cpp/modmesh/view/RCameraController.cpp +++ b/cpp/modmesh/view/RCameraController.cpp @@ -214,7 +214,7 @@ void RCameraInputListener::initKeyboardListeners() const void CameraController::reset() { - camera()->lens()->setPerspectiveProjection(45.0f, 16.0f / 9.0f, 0.1f, 1000.0f); + camera()->lens()->setPerspectiveProjection(45.0f, camera()->lens()->aspectRatio(), 0.1f, 1000.0f); setPosition(m_default_position); setViewCenter(m_default_view_center); From 09842d0eeb50181eb02573cf6d4a62eac74a7374 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Sat, 24 Aug 2024 01:50:30 +0600 Subject: [PATCH 5/7] use properties in camera python api --- cpp/modmesh/view/R3DWidget.hpp | 5 +- cpp/modmesh/view/RCameraController.hpp | 9 + cpp/modmesh/view/wrap_view.cpp | 204 +++++------------ tests/test_view.py | 289 +++++++++++-------------- 4 files changed, 202 insertions(+), 305 deletions(-) diff --git a/cpp/modmesh/view/R3DWidget.hpp b/cpp/modmesh/view/R3DWidget.hpp index 5f2b769c..cb595076 100644 --- a/cpp/modmesh/view/R3DWidget.hpp +++ b/cpp/modmesh/view/R3DWidget.hpp @@ -59,7 +59,8 @@ class RScene : public Qt3DCore::QEntity CameraController * controller() const { return m_controller; } Qt3DExtras::QAbstractCameraController * qtController() const { return m_controller->asQtCameraController(); } - void setCameraController(CameraController * controller) { + void setCameraController(CameraController * controller) + { qtController()->deleteLater(); m_controller = controller; } @@ -70,7 +71,7 @@ class RScene : public Qt3DCore::QEntity private: - CameraController* m_controller; + CameraController * m_controller; }; /* end class RScene */ diff --git a/cpp/modmesh/view/RCameraController.hpp b/cpp/modmesh/view/RCameraController.hpp index 67118692..5a199329 100644 --- a/cpp/modmesh/view/RCameraController.hpp +++ b/cpp/modmesh/view/RCameraController.hpp @@ -159,10 +159,19 @@ class CameraController void reset(); + QVector3D defaultPosition() const { return m_default_position; } void setDefaultPosition(QVector3D value) { m_default_position = value; } + + QVector3D defaultViewCenter() const { return m_default_view_center; } void setDefaultViewCenter(QVector3D value) { m_default_view_center = value; } + + QVector3D defaultUpVector() const { return m_default_up_vector; } void setDefaultUpVector(QVector3D value) { m_default_up_vector = value; } + + float defaultLinearSpeed() const { return m_default_linear_speed; } void setDefaultLinearSpeed(float value) { m_default_linear_speed = value; } + + float defaultLookSpeed() const { return m_default_look_speed; } void setDefaultLookSpeed(float value) { m_default_look_speed = value; } Qt3DExtras::QAbstractCameraController * asQtCameraController() diff --git a/cpp/modmesh/view/wrap_view.cpp b/cpp/modmesh/view/wrap_view.cpp index fec88c0d..7ca475a2 100644 --- a/cpp/modmesh/view/wrap_view.cpp +++ b/cpp/modmesh/view/wrap_view.cpp @@ -196,33 +196,7 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapR3DWidget } }, py::arg("name")) - .def("cameraController", &wrapped_type::cameraController); - -#define DECL_QVECTOR3D_PROPERTY(NAME, GETTER, SETTER) \ - .def_property( \ - #NAME, \ - [](wrapped_type & self) \ - { \ - QVector3D const v = self.camera()->GETTER(); \ - return py::make_tuple(v.x(), v.y(), v.z()); \ - }, \ - [](wrapped_type & self, std::vector const & v) \ - { \ - double const x = v.at(0); \ - double const y = v.at(1); \ - double const z = v.at(2); \ - self.camera()->SETTER(QVector3D(x, y, z)); \ - }) - - (*this) - // clang-format off - DECL_QVECTOR3D_PROPERTY(position, position, setPosition) - DECL_QVECTOR3D_PROPERTY(up_vector, upVector, setUpVector) - DECL_QVECTOR3D_PROPERTY(view_center, viewCenter, setViewCenter) - // clang-format on - ; - -#undef DECL_QVECTOR3D_PROPERTY + .def_property_readonly("camera", &wrapped_type::cameraController); } }; /* end class WrapR3DWidget */ @@ -447,7 +421,7 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRCameraController [](wrapped_type & self) { self.reset(); }) .def( - "updateCameraPosition", + "move", []( wrapped_type & self, float x, @@ -458,8 +432,7 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRCameraController bool left_mouse_button, bool right_mouse_button, bool alt_key, - bool shift_key, - float dt) + bool shift_key) { CameraInputState input{}; input.txAxisValue = x; @@ -474,6 +447,7 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRCameraController input.altKeyActive = alt_key; input.shiftKeyActive = shift_key; + constexpr float dt = 1.0f; self.updateCameraPosition(input, dt); }, @@ -485,126 +459,64 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRCameraController py::arg("left_mouse_button") = false, py::arg("right_mouse_button") = false, py::arg("alt_key") = false, - py::arg("shift_key") = false, - py::arg("dt")) - .def( - "position", - [](wrapped_type & self) - { - const auto position = self.position(); - return py::make_tuple(position.x(), position.y(), position.z()); - }) - .def( - "setPosition", - [](wrapped_type & self, float x, float y, float z) - { - self.setPosition(QVector3D(x, y, z)); - }, - py::arg("x"), - py::arg("y"), - py::arg("z")) - .def( - "linearSpeed", - [](wrapped_base_type & self) - { return self.linearSpeed(); }) - .def( - "setLinearSpeed", - [](wrapped_base_type & self, float value) - { - self.setLinearSpeed(value); - }, - py::arg("value")) - .def( - "lookSpeed", - [](wrapped_base_type & self) - { return self.lookSpeed(); }) - .def( - "setLookSpeed", - [](wrapped_base_type & self, float value) - { - self.setLookSpeed(value); - }, - py::arg("value")) - .def( - "viewCenter", - [](wrapped_base_type & self) - { - const auto center = self.viewCenter(); - return py::make_tuple(center.x(), center.y(), center.z()); - }) - .def( - "setViewCenter", - [](wrapped_type & self, float x, float y, float z) - { - self.setViewCenter(QVector3D(x, y, z)); - }, - py::arg("x"), - py::arg("y"), - py::arg("z")) - .def( - "viewVector", + py::arg("shift_key") = false) + .def_property_readonly( + "view_vector", [](wrapped_base_type & self) { const auto vector = self.viewVector(); return py::make_tuple(vector.x(), vector.y(), vector.z()); - }) - .def( - "upVector", - [](wrapped_base_type & self) - { - const auto vector = self.upVector(); - return py::make_tuple(vector.x(), vector.y(), vector.z()); - }) - .def( - "setUpVector", - [](wrapped_type & self, float x, float y, float z) - { - self.setUpVector(QVector3D(x, y, z)); - }, - py::arg("x"), - py::arg("y"), - py::arg("z")) - .def( - "setDefaultPosition", - [](wrapped_type & self, float x, float y, float z) - { - self.setDefaultPosition(QVector3D(x, y, z)); - }, - py::arg("x"), - py::arg("y"), - py::arg("z")) - .def( - "setDefaultViewCenter", - [](wrapped_type & self, float x, float y, float z) - { - self.setDefaultViewCenter(QVector3D(x, y, z)); - }, - py::arg("x"), - py::arg("y"), - py::arg("z")) - .def( - "setDefaultUpVector", - [](wrapped_type & self, float x, float y, float z) - { - self.setDefaultUpVector(QVector3D(x, y, z)); - }, - py::arg("x"), - py::arg("y"), - py::arg("z")) - .def( - "setDefaultLinearSpeed", - [](wrapped_base_type & self, float value) - { - self.setDefaultLinearSpeed(value); - }, - py::arg("value")) - .def( - "setDefaultLookSpeed", - [](wrapped_base_type & self, float value) - { - self.setDefaultLookSpeed(value); - }, - py::arg("value")); + }); + +#define DECL_QVECTOR3D_PROPERTY(NAME, GETTER, SETTER) \ + .def_property( \ + #NAME, \ + [](wrapped_type & self) \ + { \ + QVector3D const v = self.GETTER(); \ + return py::make_tuple(v.x(), v.y(), v.z()); \ + }, \ + [](wrapped_type & self, std::vector const & v) \ + { \ + double const x = v.at(0); \ + double const y = v.at(1); \ + double const z = v.at(2); \ + self.SETTER(QVector3D(x, y, z)); \ + }) + + (*this) + // clang-format off + DECL_QVECTOR3D_PROPERTY(position, position, setPosition) + DECL_QVECTOR3D_PROPERTY(up_vector, upVector, setUpVector) + DECL_QVECTOR3D_PROPERTY(view_center, viewCenter, setViewCenter) + DECL_QVECTOR3D_PROPERTY(default_position, defaultPosition, setDefaultPosition) + DECL_QVECTOR3D_PROPERTY(default_view_center, defaultViewCenter, setDefaultViewCenter) + DECL_QVECTOR3D_PROPERTY(default_up_vector, defaultUpVector, setDefaultUpVector) + // clang-format on + ; +#undef DECL_QVECTOR3D_PROPERTY + +#define DECL_FLOAT_PROPERTY(NAME, GETTER, SETTER) \ + .def_property( \ + #NAME, \ + [](wrapped_type & self) \ + { \ + return self.GETTER(); \ + }, \ + [](wrapped_type & self, float v) \ + { \ + self.SETTER(v); \ + }) + + (*this) + // clang-format off + DECL_FLOAT_PROPERTY(linear_speed, linearSpeed, setLinearSpeed) + DECL_FLOAT_PROPERTY(look_speed, lookSpeed, setLookSpeed) + DECL_FLOAT_PROPERTY(default_linear_speed, defaultLinearSpeed, setDefaultLinearSpeed) + DECL_FLOAT_PROPERTY(default_look_speed, defaultLookSpeed, setDefaultLookSpeed) + // clang-format on + ; +#undef DECL_FLOAT_PROPERTY } }; diff --git a/tests/test_view.py b/tests/test_view.py index b4cfad27..e342faaa 100644 --- a/tests/test_view.py +++ b/tests/test_view.py @@ -73,33 +73,7 @@ def setUpClass(cls): widget.setCameraType(cls.camera_type) cls.widget = widget - cls.controller = widget.cameraController() - - cls.move = cls.controller.updateCameraPosition - cls.resetCamera = cls.controller.reset - - cls.pos = cls.controller.position - cls.set_pos = cls.controller.setPosition - - cls.look_speed = cls.controller.lookSpeed - cls.set_look_speed = cls.controller.setLookSpeed - - cls.linear_speed = cls.controller.linearSpeed - cls.set_linear_speed = cls.controller.setLinearSpeed - - cls.view_vector = cls.controller.viewVector - - cls.view_center = cls.controller.viewCenter - cls.set_view_center = cls.controller.setViewCenter - - cls.up_vector = cls.controller.upVector - cls.set_up_vector = cls.controller.setUpVector - - cls.set_default_pos = cls.controller.setDefaultPosition - cls.set_default_view_center = cls.controller.setDefaultViewCenter - cls.set_default_up_vector = cls.controller.setDefaultUpVector - cls.set_default_linear_speed = cls.controller.setDefaultLinearSpeed - cls.set_default_look_speed = cls.controller.setDefaultLookSpeed + cls.camera = widget.camera @classmethod def tearDownClass(cls): @@ -134,39 +108,44 @@ class ViewCommonCameraTC(ViewCameraTB, unittest.TestCase): camera_type = "fps" # no difference when use orbit camera def setUp(self): - self.resetCamera() + self.camera.reset() def test_value_get_set(self): - self.set_linear_speed(123.0) - self.assertEqual(self.linear_speed(), 123.0) + c = self.camera - self.set_look_speed(456.0) - self.assertEqual(self.look_speed(), 456.0) + c.linear_speed = 123.0 + self.assertEqual(c.linear_speed, 123.0) + + c.look_speed = 456.0 + self.assertEqual(c.look_speed, 456.0) def test_vector_get_set(self): - self.set_pos(x=1, y=2, z=3) - self.assertEqual(self.pos(), (1, 2, 3)) + c = self.camera - self.set_view_center(x=4, y=5, z=6) - self.assertEqual(self.view_center(), (4, 5, 6)) + c.position = (1, 2, 3) + c.view_center = (4, 5, 6) + c.up_vector = (7, 8, 9) - self.set_up_vector(x=7, y=8, z=9) - self.assertEqual(self.up_vector(), (7, 8, 9)) + self.assertEqual(c.position, (1, 2, 3)) + self.assertEqual(c.view_center, (4, 5, 6)) + self.assertEqual(c.up_vector, (7, 8, 9)) def test_default_values(self): - self.set_default_pos(x=1, y=2, z=3) - self.set_default_view_center(x=4, y=5, z=6) - self.set_default_up_vector(x=7, y=8, z=9) - self.set_default_linear_speed(123.0) - self.set_default_look_speed(456.0) + c = self.camera + + c.default_position = (1, 2, 3) + c.default_view_center = (4, 5, 6) + c.default_up_vector = (7, 8, 9) + c.default_linear_speed = 123.0 + c.default_look_speed = 456.0 - self.resetCamera() + c.reset() - self.assertEqual(self.pos(), (1, 2, 3)) - self.assertEqual(self.view_center(), (4, 5, 6)) - self.assertEqual(self.up_vector(), (7, 8, 9)) - self.assertEqual(self.linear_speed(), 123.0) - self.assertEqual(self.look_speed(), 456.0) + self.assertEqual(c.position, (1, 2, 3)) + self.assertEqual(c.view_center, (4, 5, 6)) + self.assertEqual(c.up_vector, (7, 8, 9)) + self.assertEqual(c.linear_speed, 123.0) + self.assertEqual(c.look_speed, 456.0) @unittest.skipIf(GITHUB_ACTIONS, "GUI is not available in GitHub Actions") @@ -174,95 +153,92 @@ class ViewFPSCameraTC(ViewCameraTB, unittest.TestCase): camera_type = "fps" def setUp(self): - self.resetCamera() + self.camera.reset() def test_reset(self): - dt = 0.01 - initial_pos = self.pos() - initial_view_vector = self.view_vector() - initial_view_center = self.view_center() - initial_up_vector = self.up_vector() + c = self.camera - self.move(x=1, y=1, z=1, dt=dt) - self.move(yaw=1, pitch=1, dt=dt, left_mouse_button=True) + initial_position = c.position + initial_view_center = c.view_center + initial_up_vector = c.up_vector - self.assertNotEqual(self.pos(), initial_pos) - self.assertNotEqual(self.view_vector(), initial_view_vector) - self.assertNotEqual(self.view_center(), initial_view_center) - self.assertNotEqual(self.up_vector(), initial_up_vector) + c.move(x=0.1, y=0.1, z=0.1) + c.move(yaw=1, pitch=1, left_mouse_button=True) - self.resetCamera() + self.assertNotEqual(c.position, initial_position) + self.assertNotEqual(c.view_center, initial_view_center) + self.assertNotEqual(c.up_vector, initial_up_vector) - self.assertEqual(self.pos(), initial_pos) - self.assertEqual(self.view_vector(), initial_view_vector) - self.assertEqual(self.view_center(), initial_view_center) - self.assertEqual(self.up_vector(), initial_up_vector) + c.reset() + + self.assertEqual(c.position, initial_position) + self.assertEqual(c.view_center, initial_view_center) + self.assertEqual(c.up_vector, initial_up_vector) def test_translation(self): - dt = 0.01 - delta = dt * self.linear_speed() - delta_vec = np.array([delta, delta, -delta]) - new_pos = np.array(self.pos()) + delta_vec - new_view_center = np.array(self.view_center()) + delta_vec + c = self.camera + + speed = c.linear_speed + delta_vec = np.array([speed, speed, -speed]) + new_position = np.array(c.position) + delta_vec + new_view_center = np.array(c.view_center) + delta_vec - self.move(x=1, dt=dt) - self.assertEqual(self.pos()[0], new_pos[0]) + c.move(x=1) + self.assertEqual(c.position[0], new_position[0]) - self.move(y=1, dt=dt) - self.assertEqual(self.pos()[1], new_pos[1]) + c.move(y=1) + self.assertEqual(c.position[1], new_position[1]) # camera moves in negative z direction - self.move(z=1, dt=dt) - self.assertEqual(self.pos()[2], new_pos[2]) + c.move(z=1) + self.assertEqual(c.position[2], new_position[2]) # camera view center should move with camera - view_center = self.view_center() - self.assertEqual(view_center[0], new_view_center[0]) - self.assertEqual(view_center[1], new_view_center[1]) - self.assertEqual(view_center[2], new_view_center[2]) + self.assertEqual(c.view_center[0], new_view_center[0]) + self.assertEqual(c.view_center[1], new_view_center[1]) + self.assertEqual(c.view_center[2], new_view_center[2]) def test_rotation(self): - dt = 0.01 - angle = dt * self.look_speed() + c = self.camera - initial_view_center = self.view_center() - initial_view_vector = self.view_vector() + angle = c.look_speed + + initial_view_center = c.view_center + initial_view_vector = c.view_vector # test camera does not rotate when left mouse button is not pressed - self.move(yaw=1, pitch=1, dt=dt) - self.assertEqual(self.view_vector(), initial_view_vector) - self.assertEqual(self.view_center(), initial_view_center) + c.move(yaw=1, pitch=1) + self.assertEqual(c.view_vector, initial_view_vector) + self.assertEqual(c.view_center, initial_view_center) # test camera rotates around y-axis - self.move(yaw=1, dt=dt, left_mouse_button=True) + c.move(yaw=1, left_mouse_button=True) rotation_matrix = self.angle_axis(angle, (0, 1, 0)) rotated_vector = np.array(initial_view_vector) @ rotation_matrix - view_vector = self.view_vector() - self.assertAlmostEqual(rotated_vector[0], view_vector[0], delta=1e-5) - self.assertAlmostEqual(rotated_vector[1], view_vector[1], delta=1e-5) - self.assertAlmostEqual(rotated_vector[2], view_vector[2], delta=1e-5) + self.assertAlmostEqual(rotated_vector[0], c.view_vector[0], places=5) + self.assertAlmostEqual(rotated_vector[1], c.view_vector[1], places=5) + self.assertAlmostEqual(rotated_vector[2], c.view_vector[2], places=5) # test camera rotates around x-axis - self.move(pitch=1, dt=dt, left_mouse_button=True) + old_view_vector = c.view_vector + c.move(pitch=1, left_mouse_button=True) - x_basis = -self.normalize(np.cross(view_vector, self.up_vector())) + x_basis = -self.normalize(np.cross(old_view_vector, c.up_vector)) rotation_matrix = self.angle_axis(angle, x_basis) - rotated_vector = np.array(view_vector) @ rotation_matrix - view_vector = self.view_vector() + rotated_vector = np.array(old_view_vector) @ rotation_matrix - self.assertAlmostEqual(rotated_vector[0], view_vector[0], delta=1e-5) - self.assertAlmostEqual(rotated_vector[1], view_vector[1], delta=1e-5) - self.assertAlmostEqual(rotated_vector[2], view_vector[2], delta=1e-5) + self.assertAlmostEqual(rotated_vector[0], c.view_vector[0], places=5) + self.assertAlmostEqual(rotated_vector[1], c.view_vector[1], places=5) + self.assertAlmostEqual(rotated_vector[2], c.view_vector[2], places=5) # test view center moved with the camera - new_view_center = view_vector + np.array(self.pos()) - view_center = self.view_center() + new_view_center = c.view_vector + np.array(c.position) - self.assertAlmostEqual(view_center[0], new_view_center[0], delta=1e-5) - self.assertAlmostEqual(view_center[1], new_view_center[1], delta=1e-5) - self.assertAlmostEqual(view_center[2], new_view_center[2], delta=1e-5) + self.assertAlmostEqual(c.view_center[0], new_view_center[0], places=5) + self.assertAlmostEqual(c.view_center[1], new_view_center[1], places=5) + self.assertAlmostEqual(c.view_center[2], new_view_center[2], places=5) @unittest.skipIf(GITHUB_ACTIONS, "GUI is not available in GitHub Actions") @@ -270,89 +246,88 @@ class ViewOrbitCameraTC(ViewCameraTB, unittest.TestCase): camera_type = "orbit" def setUp(self): - self.resetCamera() + self.camera.reset() def test_reset(self): - dt = 0.01 - initial_pos = self.pos() - initial_view_vector = self.view_vector() - initial_view_center = self.view_center() - initial_up_vector = self.up_vector() + c = self.camera + + initial_position = c.position + initial_view_vector = c.view_vector + initial_view_center = c.view_center + initial_up_vector = c.up_vector - self.move(x=1, y=1, z=1, dt=dt) - self.move(yaw=1, pitch=1, dt=dt, right_mouse_button=True) + c.move(x=0.1, y=0.1, z=0.1) + c.move(yaw=1, pitch=1, right_mouse_button=True) - self.assertNotEqual(self.pos(), initial_pos) - self.assertNotEqual(self.view_vector(), initial_view_vector) - self.assertNotEqual(self.view_center(), initial_view_center) - self.assertNotEqual(self.up_vector(), initial_up_vector) + self.assertNotEqual(c.position, initial_position) + self.assertNotEqual(c.view_vector, initial_view_vector) + self.assertNotEqual(c.up_vector, initial_up_vector) - self.resetCamera() + c.reset() - self.assertEqual(self.pos(), initial_pos) - self.assertEqual(self.view_vector(), initial_view_vector) - self.assertEqual(self.view_center(), initial_view_center) - self.assertEqual(self.up_vector(), initial_up_vector) + self.assertEqual(c.position, initial_position) + self.assertEqual(c.view_vector, initial_view_vector) + self.assertEqual(c.view_center, initial_view_center) + self.assertEqual(c.up_vector, initial_up_vector) def test_translation(self): - dt = 0.01 - delta = dt * self.linear_speed() - delta_vec = np.array([delta, delta, -delta]) - new_pos = np.array(self.pos()) + delta_vec - new_view_center = np.array(self.view_center()) + delta_vec + c = self.camera + + speed = c.linear_speed + delta_vec = np.array([speed, speed, -speed]) + new_pos = np.array(c.position) + delta_vec + new_view_center = np.array(c.view_center) + delta_vec - self.move(x=1, dt=dt) - self.assertEqual(self.pos()[0], new_pos[0]) + c.move(x=1) + self.assertEqual(c.position[0], new_pos[0]) - self.move(y=1, dt=dt) - self.assertEqual(self.pos()[1], new_pos[1]) + c.move(y=1) + self.assertEqual(c.position[1], new_pos[1]) # camera moves in negative z direction - self.move(z=1, dt=dt) - self.assertEqual(self.pos()[2], new_pos[2]) + c.move(z=1) + self.assertEqual(c.position[2], new_pos[2]) # camera view center should move with camera - view_center = self.view_center() - self.assertEqual(view_center[0], new_view_center[0]) - self.assertEqual(view_center[1], new_view_center[1]) - self.assertEqual(view_center[2], new_view_center[2]) + self.assertEqual(c.view_center[0], new_view_center[0]) + self.assertEqual(c.view_center[1], new_view_center[1]) + self.assertEqual(c.view_center[2], new_view_center[2]) def test_rotation(self): - dt = 0.01 - angle = dt * self.look_speed() + c = self.camera - initial_view_center = self.view_center() - initial_view_vector = self.view_vector() + angle = c.look_speed + initial_view_center = c.view_center + initial_view_vector = c.view_vector # test camera does not rotate when right mouse button is not pressed - self.move(yaw=1, pitch=1, dt=dt) - self.assertEqual(self.view_vector(), initial_view_vector) - self.assertEqual(self.view_center(), initial_view_center) + c.move(yaw=1, pitch=1) + self.assertEqual(c.view_vector, initial_view_vector) + self.assertEqual(c.view_center, initial_view_center) # test camera rotates around y-axis - self.move(yaw=1, dt=dt, right_mouse_button=True) + c.move(yaw=1, right_mouse_button=True) rotation_matrix = self.angle_axis(angle, (0, -1, 0)) rotated_vector = np.array(initial_view_vector) @ rotation_matrix - view_vector = self.view_vector() - self.assertAlmostEqual(rotated_vector[0], view_vector[0], delta=1e-5) - self.assertAlmostEqual(rotated_vector[1], view_vector[1], delta=1e-5) - self.assertAlmostEqual(rotated_vector[2], view_vector[2], delta=1e-5) + self.assertAlmostEqual(rotated_vector[0], c.view_vector[0], places=5) + self.assertAlmostEqual(rotated_vector[1], c.view_vector[1], places=5) + self.assertAlmostEqual(rotated_vector[2], c.view_vector[2], places=5) # test camera rotates around x-axis - self.move(pitch=1, dt=dt, right_mouse_button=True) + old_view_vector = c.view_vector + c.move(pitch=1, right_mouse_button=True) - x_basis = self.normalize(np.cross(view_vector, self.up_vector())) + x_basis = self.normalize(np.cross(old_view_vector, c.up_vector)) rotation_matrix = self.angle_axis(angle, x_basis) - rotated_vector = np.array(view_vector) @ rotation_matrix - view_vector = self.view_vector() + rotated_vector = np.array(old_view_vector) @ rotation_matrix - self.assertAlmostEqual(rotated_vector[0], view_vector[0], delta=1e-5) - self.assertAlmostEqual(rotated_vector[1], view_vector[1], delta=1e-5) - self.assertAlmostEqual(rotated_vector[2], view_vector[2], delta=1e-5) + self.assertAlmostEqual(rotated_vector[0], c.view_vector[0], places=5) + self.assertAlmostEqual(rotated_vector[1], c.view_vector[1], places=5) + self.assertAlmostEqual(rotated_vector[2], c.view_vector[2], places=5) # camera view center should not change - self.assertEqual(self.view_center(), initial_view_center) + self.assertEqual(c.view_center, initial_view_center) # vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: From 3adeb702740c4e9e1d3fb05373893564d38225bb Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Sun, 25 Aug 2024 02:07:54 +0600 Subject: [PATCH 6/7] apply review --- cpp/modmesh/view/R3DWidget.hpp | 7 ++- cpp/modmesh/view/RCameraController.cpp | 21 +++++--- cpp/modmesh/view/RCameraController.hpp | 67 +++++++++----------------- cpp/modmesh/view/RManager.cpp | 2 +- cpp/modmesh/view/wrap_view.cpp | 50 +++++++++---------- tests/test_view.py | 61 ++++++++++++----------- 6 files changed, 97 insertions(+), 111 deletions(-) diff --git a/cpp/modmesh/view/R3DWidget.hpp b/cpp/modmesh/view/R3DWidget.hpp index cb595076..91f4c407 100644 --- a/cpp/modmesh/view/R3DWidget.hpp +++ b/cpp/modmesh/view/R3DWidget.hpp @@ -46,7 +46,8 @@ namespace modmesh { -class RScene : public Qt3DCore::QEntity +class RScene + : public Qt3DCore::QEntity { public: @@ -57,11 +58,10 @@ class RScene : public Qt3DCore::QEntity } CameraController * controller() const { return m_controller; } - Qt3DExtras::QAbstractCameraController * qtController() const { return m_controller->asQtCameraController(); } void setCameraController(CameraController * controller) { - qtController()->deleteLater(); + m_controller->deleteLater(); m_controller = controller; } @@ -97,7 +97,6 @@ class R3DWidget Qt3DRender::QCamera * camera() { return m_view->camera(); } CameraController * cameraController() const { return m_scene->controller(); } - Qt3DExtras::QAbstractCameraController * qtCameraController() const { return m_scene->qtController(); } QPixmap grabPixmap() const { return m_view->screen()->grabWindow(m_view->winId()); } diff --git a/cpp/modmesh/view/RCameraController.cpp b/cpp/modmesh/view/RCameraController.cpp index cd8e6e98..8bc41de5 100644 --- a/cpp/modmesh/view/RCameraController.cpp +++ b/cpp/modmesh/view/RCameraController.cpp @@ -214,7 +214,10 @@ void RCameraInputListener::initKeyboardListeners() const void CameraController::reset() { - camera()->lens()->setPerspectiveProjection(45.0f, camera()->lens()->aspectRatio(), 0.1f, 1000.0f); + constexpr float fov_deg = 45.0f; + constexpr float nearPlane = 0.1f; + constexpr float farPlane = 1000.0f; + camera()->lens()->setPerspectiveProjection(fov_deg, camera()->lens()->aspectRatio(), nearPlane, farPlane); setPosition(m_default_position); setViewCenter(m_default_view_center); @@ -225,17 +228,17 @@ void CameraController::reset() } RFirstPersonCameraController::RFirstPersonCameraController(QNode * parent) - : QFirstPersonCameraController(parent) + : CameraController(parent) { auto callback = [this](const CameraInputState & state, const float dt) { - updateCameraPosition(state, dt); + moveCamera(state, dt); }; m_listener = new RCameraInputListener(keyboardDevice(), mouseDevice(), callback, this); } -void RFirstPersonCameraController::updateCameraPosition(const CameraInputState & input, const float dt) +void RFirstPersonCameraController::moveCamera(const CameraInputState & input, const float dt) { constexpr auto positiveY = QVector3D(0.f, 1.f, 0.f); @@ -256,17 +259,17 @@ void RFirstPersonCameraController::updateCameraPosition(const CameraInputState & } ROrbitCameraController::ROrbitCameraController(QNode * parent) - : QOrbitCameraController(parent) + : CameraController(parent) { auto callback = [this](const CameraInputState & state, const float dt) { - updateCameraPosition(state, dt); + moveCamera(state, dt); }; m_listener = new RCameraInputListener(keyboardDevice(), mouseDevice(), callback, this); } -void ROrbitCameraController::updateCameraPosition(const CameraInputState & input, const float dt) +void ROrbitCameraController::moveCamera(const CameraInputState & input, const float dt) { if (camera() == nullptr) return; @@ -317,7 +320,9 @@ void ROrbitCameraController::updateCameraPosition(const CameraInputState & input void ROrbitCameraController::zoom(const float zoomValue) const { - const float limitSquared = zoomInLimit() * zoomInLimit(); + constexpr float zoomInLimit = 2.0f; // Default Qt zoomInLimit value + + const float limitSquared = zoomInLimit * zoomInLimit; const float distanceSquared = zoomDistanceSquared(camera()->position(), camera()->viewCenter()); const float z = distanceSquared > limitSquared ? zoomValue : -0.5f; diff --git a/cpp/modmesh/view/RCameraController.hpp b/cpp/modmesh/view/RCameraController.hpp index 5a199329..f7af79a9 100644 --- a/cpp/modmesh/view/RCameraController.hpp +++ b/cpp/modmesh/view/RCameraController.hpp @@ -29,10 +29,8 @@ */ #include // Must be the first include. - -#include -#include #include +#include #include #include @@ -128,21 +126,29 @@ class RCameraInputListener : public Qt3DCore::QEntity Qt3DInput::QButtonAxisInput * m_keyboard_tz_neg_input; }; /* end class RCameraInputListener */ -class CameraController +class CameraController : public Qt3DExtras::QAbstractCameraController { + Q_OBJECT + public: - virtual ~CameraController() = default; + explicit CameraController(Qt3DCore::QNode * parent = nullptr) + : QAbstractCameraController(parent) + { + } - virtual void updateCameraPosition(const CameraInputState & state, float dt) = 0; + // Do nothing in QAbstractCameraController's moveCamera + void moveCamera(const InputState & state, float dt) override {} + + virtual void moveCamera(const CameraInputState & state, float dt) = 0; - virtual Qt3DRender::QCamera * camera() const = 0; - virtual void setCamera(Qt3DRender::QCamera * camera) = 0; + Qt3DRender::QCamera * camera() const { return QAbstractCameraController::camera(); } + void setCamera(Qt3DRender::QCamera * camera) { QAbstractCameraController::setCamera(camera); } - virtual float linearSpeed() const = 0; - virtual void setLinearSpeed(float value) = 0; + float linearSpeed() const { return QAbstractCameraController::linearSpeed(); } + void setLinearSpeed(float value) { QAbstractCameraController::setLinearSpeed(value); } - virtual float lookSpeed() const = 0; - virtual void setLookSpeed(float value) = 0; + float lookSpeed() const { return QAbstractCameraController::lookSpeed(); } + void setLookSpeed(float value) { QAbstractCameraController::setLookSpeed(value); } virtual CameraControllerType getType() = 0; @@ -174,11 +180,6 @@ class CameraController float defaultLookSpeed() const { return m_default_look_speed; } void setDefaultLookSpeed(float value) { m_default_look_speed = value; } - Qt3DExtras::QAbstractCameraController * asQtCameraController() - { - return dynamic_cast(this); - } - protected: RCameraInputListener * m_listener = nullptr; @@ -190,54 +191,30 @@ class CameraController float m_default_look_speed = 180.0f; }; /* end class CameraController */ -class RFirstPersonCameraController : public Qt3DExtras::QFirstPersonCameraController - , public CameraController +class RFirstPersonCameraController : public CameraController { Q_OBJECT public: explicit RFirstPersonCameraController(QNode * parent = nullptr); - Qt3DRender::QCamera * camera() const override { return QFirstPersonCameraController::camera(); } - void setCamera(Qt3DRender::QCamera * camera) override { QFirstPersonCameraController::setCamera(camera); } - - float linearSpeed() const override { return QFirstPersonCameraController::linearSpeed(); } - void setLinearSpeed(float value) override { QFirstPersonCameraController::setLinearSpeed(value); } - - float lookSpeed() const override { return QFirstPersonCameraController::lookSpeed(); } - void setLookSpeed(float value) override { QFirstPersonCameraController::setLookSpeed(value); } - private: static constexpr auto lookSpeedFactorOnShiftPressed = 0.2f; - void moveCamera(const InputState & state, float dt) override {} - - void updateCameraPosition(const CameraInputState & input, float dt) override; + void moveCamera(const CameraInputState & input, float dt) override; CameraControllerType getType() override { return CameraControllerType::FirstPerson; } }; /* end class RFirstPersonCameraController */ -class ROrbitCameraController : public Qt3DExtras::QOrbitCameraController - , public CameraController +class ROrbitCameraController : public CameraController { Q_OBJECT public: explicit ROrbitCameraController(QNode * parent = nullptr); - Qt3DRender::QCamera * camera() const override { return QOrbitCameraController::camera(); } - void setCamera(Qt3DRender::QCamera * camera) override { QOrbitCameraController::setCamera(camera); } - - float linearSpeed() const override { return QOrbitCameraController::linearSpeed(); } - void setLinearSpeed(float value) override { QOrbitCameraController::setLinearSpeed(value); } - - float lookSpeed() const override { return QOrbitCameraController::lookSpeed(); } - void setLookSpeed(float value) override { QOrbitCameraController::setLookSpeed(value); } - private: - void moveCamera(const InputState & state, float dt) override {} - - void updateCameraPosition(const CameraInputState & input, float dt) override; + void moveCamera(const CameraInputState & input, float dt) override; void zoom(float zoomValue) const; diff --git a/cpp/modmesh/view/RManager.cpp b/cpp/modmesh/view/RManager.cpp index 21177e1d..002e16b0 100644 --- a/cpp/modmesh/view/RManager.cpp +++ b/cpp/modmesh/view/RManager.cpp @@ -379,7 +379,7 @@ std::function RManager::createCameraMovementItemHandler(const std::funct } } - viewer->cameraController()->updateCameraPosition(input, 0.01); + viewer->cameraController()->moveCamera(input, 0.01); }; } diff --git a/cpp/modmesh/view/wrap_view.cpp b/cpp/modmesh/view/wrap_view.cpp index 7ca475a2..bdb76fe3 100644 --- a/cpp/modmesh/view/wrap_view.cpp +++ b/cpp/modmesh/view/wrap_view.cpp @@ -448,7 +448,7 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRCameraController input.shiftKeyActive = shift_key; constexpr float dt = 1.0f; - self.updateCameraPosition(input, dt); + self.moveCamera(input, dt); }, py::arg("x") = 0.f, @@ -468,7 +468,7 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRCameraController return py::make_tuple(vector.x(), vector.y(), vector.z()); }); -#define DECL_QVECTOR3D_PROPERTY(NAME, GETTER, SETTER) \ +#define MM_DECL_QVECTOR3D_PROPERTY(NAME, GETTER, SETTER) \ .def_property( \ #NAME, \ [](wrapped_type & self) \ @@ -486,37 +486,37 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRCameraController (*this) // clang-format off - DECL_QVECTOR3D_PROPERTY(position, position, setPosition) - DECL_QVECTOR3D_PROPERTY(up_vector, upVector, setUpVector) - DECL_QVECTOR3D_PROPERTY(view_center, viewCenter, setViewCenter) - DECL_QVECTOR3D_PROPERTY(default_position, defaultPosition, setDefaultPosition) - DECL_QVECTOR3D_PROPERTY(default_view_center, defaultViewCenter, setDefaultViewCenter) - DECL_QVECTOR3D_PROPERTY(default_up_vector, defaultUpVector, setDefaultUpVector) + MM_DECL_QVECTOR3D_PROPERTY(position, position, setPosition) + MM_DECL_QVECTOR3D_PROPERTY(up_vector, upVector, setUpVector) + MM_DECL_QVECTOR3D_PROPERTY(view_center, viewCenter, setViewCenter) + MM_DECL_QVECTOR3D_PROPERTY(default_position, defaultPosition, setDefaultPosition) + MM_DECL_QVECTOR3D_PROPERTY(default_view_center, defaultViewCenter, setDefaultViewCenter) + MM_DECL_QVECTOR3D_PROPERTY(default_up_vector, defaultUpVector, setDefaultUpVector) // clang-format on ; -#undef DECL_QVECTOR3D_PROPERTY - -#define DECL_FLOAT_PROPERTY(NAME, GETTER, SETTER) \ - .def_property( \ - #NAME, \ - [](wrapped_type & self) \ - { \ - return self.GETTER(); \ - }, \ - [](wrapped_type & self, float v) \ - { \ - self.SETTER(v); \ +#undef MM_DECL_QVECTOR3D_PROPERTY + +#define MM_DECL_FLOAT_PROPERTY(NAME, GETTER, SETTER) \ + .def_property( \ + #NAME, \ + [](wrapped_type & self) \ + { \ + return self.GETTER(); \ + }, \ + [](wrapped_type & self, float v) \ + { \ + self.SETTER(v); \ }) (*this) // clang-format off - DECL_FLOAT_PROPERTY(linear_speed, linearSpeed, setLinearSpeed) - DECL_FLOAT_PROPERTY(look_speed, lookSpeed, setLookSpeed) - DECL_FLOAT_PROPERTY(default_linear_speed, defaultLinearSpeed, setDefaultLinearSpeed) - DECL_FLOAT_PROPERTY(default_look_speed, defaultLookSpeed, setDefaultLookSpeed) + MM_DECL_FLOAT_PROPERTY(linear_speed, linearSpeed, setLinearSpeed) + MM_DECL_FLOAT_PROPERTY(look_speed, lookSpeed, setLookSpeed) + MM_DECL_FLOAT_PROPERTY(default_linear_speed, defaultLinearSpeed, setDefaultLinearSpeed) + MM_DECL_FLOAT_PROPERTY(default_look_speed, defaultLookSpeed, setDefaultLookSpeed) // clang-format on ; -#undef DECL_FLOAT_PROPERTY +#undef MM_DECL_FLOAT_PROPERTY } }; diff --git a/tests/test_view.py b/tests/test_view.py index e342faaa..03713cf8 100644 --- a/tests/test_view.py +++ b/tests/test_view.py @@ -30,7 +30,6 @@ import os import modmesh - try: from modmesh import view except ImportError: @@ -70,7 +69,9 @@ class ViewCameraTB: @classmethod def setUpClass(cls): widget = view.RManager.instance.setUp().add3DWidget() - widget.setCameraType(cls.camera_type) + + if cls.camera_type is not None: + widget.setCameraType(cls.camera_type) cls.widget = widget cls.camera = widget.camera @@ -105,47 +106,51 @@ def normalize(self, vec): @unittest.skipIf(GITHUB_ACTIONS, "GUI is not available in GitHub Actions") class ViewCommonCameraTC(ViewCameraTB, unittest.TestCase): - camera_type = "fps" # no difference when use orbit camera - - def setUp(self): - self.camera.reset() - def test_value_get_set(self): c = self.camera - c.linear_speed = 123.0 - self.assertEqual(c.linear_speed, 123.0) + for camera_type in ["fps", "orbit"]: + self.widget.setCameraType(camera_type) + + c.linear_speed = 123.0 + self.assertEqual(c.linear_speed, 123.0) - c.look_speed = 456.0 - self.assertEqual(c.look_speed, 456.0) + c.look_speed = 456.0 + self.assertEqual(c.look_speed, 456.0) def test_vector_get_set(self): c = self.camera - c.position = (1, 2, 3) - c.view_center = (4, 5, 6) - c.up_vector = (7, 8, 9) + for camera_type in ["fps", "orbit"]: + self.widget.setCameraType(camera_type) + + c.position = (1, 2, 3) + c.view_center = (4, 5, 6) + c.up_vector = (7, 8, 9) - self.assertEqual(c.position, (1, 2, 3)) - self.assertEqual(c.view_center, (4, 5, 6)) - self.assertEqual(c.up_vector, (7, 8, 9)) + self.assertEqual(c.position, (1, 2, 3)) + self.assertEqual(c.view_center, (4, 5, 6)) + self.assertEqual(c.up_vector, (7, 8, 9)) def test_default_values(self): c = self.camera - c.default_position = (1, 2, 3) - c.default_view_center = (4, 5, 6) - c.default_up_vector = (7, 8, 9) - c.default_linear_speed = 123.0 - c.default_look_speed = 456.0 + for camera_type in ["fps", "orbit"]: + self.widget.setCameraType(camera_type) - c.reset() + c.default_position = (1, 2, 3) + c.default_view_center = (4, 5, 6) + c.default_up_vector = (7, 8, 9) + c.default_linear_speed = 123.0 + c.default_look_speed = 456.0 + + c.reset() - self.assertEqual(c.position, (1, 2, 3)) - self.assertEqual(c.view_center, (4, 5, 6)) - self.assertEqual(c.up_vector, (7, 8, 9)) - self.assertEqual(c.linear_speed, 123.0) - self.assertEqual(c.look_speed, 456.0) + self.assertEqual(c.position, (1, 2, 3)) + self.assertEqual(c.view_center, (4, 5, 6)) + self.assertEqual(c.up_vector, (7, 8, 9)) + self.assertEqual(c.linear_speed, 123.0) + self.assertEqual(c.look_speed, 456.0) @unittest.skipIf(GITHUB_ACTIONS, "GUI is not available in GitHub Actions") From 42161b525396b3f6d032b6b85133f18f74ebc464 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Sun, 25 Aug 2024 17:28:28 +0600 Subject: [PATCH 7/7] apply review 2 --- cpp/modmesh/view/R3DWidget.hpp | 8 +- cpp/modmesh/view/RCameraController.cpp | 6 +- cpp/modmesh/view/RCameraController.hpp | 14 +-- cpp/modmesh/view/wrap_view.cpp | 2 +- tests/test_view.py | 135 ++++++++++++------------- 5 files changed, 82 insertions(+), 83 deletions(-) diff --git a/cpp/modmesh/view/R3DWidget.hpp b/cpp/modmesh/view/R3DWidget.hpp index 91f4c407..2462f342 100644 --- a/cpp/modmesh/view/R3DWidget.hpp +++ b/cpp/modmesh/view/R3DWidget.hpp @@ -57,9 +57,9 @@ class RScene m_controller = new ROrbitCameraController(this); } - CameraController * controller() const { return m_controller; } + RCameraController * controller() const { return m_controller; } - void setCameraController(CameraController * controller) + void setCameraController(RCameraController * controller) { m_controller->deleteLater(); m_controller = controller; @@ -71,7 +71,7 @@ class RScene private: - CameraController * m_controller; + RCameraController * m_controller; }; /* end class RScene */ @@ -96,7 +96,7 @@ class R3DWidget RScene * scene() { return m_scene; } Qt3DRender::QCamera * camera() { return m_view->camera(); } - CameraController * cameraController() const { return m_scene->controller(); } + RCameraController * cameraController() const { return m_scene->controller(); } QPixmap grabPixmap() const { return m_view->screen()->grabWindow(m_view->winId()); } diff --git a/cpp/modmesh/view/RCameraController.cpp b/cpp/modmesh/view/RCameraController.cpp index 8bc41de5..c2bdac15 100644 --- a/cpp/modmesh/view/RCameraController.cpp +++ b/cpp/modmesh/view/RCameraController.cpp @@ -212,7 +212,7 @@ void RCameraInputListener::initKeyboardListeners() const m_ty_axis->addInput(m_keyboard_ty_neg_input); } -void CameraController::reset() +void RCameraController::reset() { constexpr float fov_deg = 45.0f; constexpr float nearPlane = 0.1f; @@ -228,7 +228,7 @@ void CameraController::reset() } RFirstPersonCameraController::RFirstPersonCameraController(QNode * parent) - : CameraController(parent) + : RCameraController(parent) { auto callback = [this](const CameraInputState & state, const float dt) { @@ -259,7 +259,7 @@ void RFirstPersonCameraController::moveCamera(const CameraInputState & input, co } ROrbitCameraController::ROrbitCameraController(QNode * parent) - : CameraController(parent) + : RCameraController(parent) { auto callback = [this](const CameraInputState & state, const float dt) { diff --git a/cpp/modmesh/view/RCameraController.hpp b/cpp/modmesh/view/RCameraController.hpp index f7af79a9..dbe7752a 100644 --- a/cpp/modmesh/view/RCameraController.hpp +++ b/cpp/modmesh/view/RCameraController.hpp @@ -126,18 +126,20 @@ class RCameraInputListener : public Qt3DCore::QEntity Qt3DInput::QButtonAxisInput * m_keyboard_tz_neg_input; }; /* end class RCameraInputListener */ -class CameraController : public Qt3DExtras::QAbstractCameraController +class RCameraController : public Qt3DExtras::QAbstractCameraController { Q_OBJECT public: - explicit CameraController(Qt3DCore::QNode * parent = nullptr) + explicit RCameraController(Qt3DCore::QNode * parent = nullptr) : QAbstractCameraController(parent) { } - // Do nothing in QAbstractCameraController's moveCamera - void moveCamera(const InputState & state, float dt) override {} + void moveCamera(const InputState & state, float dt) override + { + // Do nothing in QAbstractCameraController's moveCamera + } virtual void moveCamera(const CameraInputState & state, float dt) = 0; @@ -191,7 +193,7 @@ class CameraController : public Qt3DExtras::QAbstractCameraController float m_default_look_speed = 180.0f; }; /* end class CameraController */ -class RFirstPersonCameraController : public CameraController +class RFirstPersonCameraController : public RCameraController { Q_OBJECT @@ -206,7 +208,7 @@ class RFirstPersonCameraController : public CameraController CameraControllerType getType() override { return CameraControllerType::FirstPerson; } }; /* end class RFirstPersonCameraController */ -class ROrbitCameraController : public CameraController +class ROrbitCameraController : public RCameraController { Q_OBJECT diff --git a/cpp/modmesh/view/wrap_view.cpp b/cpp/modmesh/view/wrap_view.cpp index bdb76fe3..77becbfb 100644 --- a/cpp/modmesh/view/wrap_view.cpp +++ b/cpp/modmesh/view/wrap_view.cpp @@ -405,7 +405,7 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRManager }; /* end class WrapRManager */ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapRCameraController - : public WrapBase + : public WrapBase { friend root_base_type; diff --git a/tests/test_view.py b/tests/test_view.py index 03713cf8..c635fbb8 100644 --- a/tests/test_view.py +++ b/tests/test_view.py @@ -111,6 +111,7 @@ def test_value_get_set(self): for camera_type in ["fps", "orbit"]: self.widget.setCameraType(camera_type) + c.reset() c.linear_speed = 123.0 self.assertEqual(c.linear_speed, 123.0) @@ -123,6 +124,7 @@ def test_vector_get_set(self): for camera_type in ["fps", "orbit"]: self.widget.setCameraType(camera_type) + c.reset() c.position = (1, 2, 3) c.view_center = (4, 5, 6) @@ -137,6 +139,13 @@ def test_default_values(self): for camera_type in ["fps", "orbit"]: self.widget.setCameraType(camera_type) + c.reset() + + original_position = c.default_position + original_view_center = c.default_view_center + original_up_vector = c.default_up_vector + original_linear_speed = c.default_linear_speed + original_look_speed = c.default_look_speed c.default_position = (1, 2, 3) c.default_view_center = (4, 5, 6) @@ -152,6 +161,39 @@ def test_default_values(self): self.assertEqual(c.linear_speed, 123.0) self.assertEqual(c.look_speed, 456.0) + c.default_position = original_position + c.default_view_center = original_view_center + c.default_up_vector = original_up_vector + c.default_linear_speed = original_linear_speed + c.default_look_speed = original_look_speed + + def test_translation(self): + c = self.camera + + for camera_type in ["orbit"]: + self.widget.setCameraType(camera_type) + c.reset() + + speed = c.linear_speed + delta_vec = np.array([speed, speed, -speed]) + new_position = np.array(c.position) + delta_vec + new_view_center = np.array(c.view_center) + delta_vec + + c.move(x=1) + self.assertEqual(c.position[0], new_position[0]) + + c.move(y=1) + self.assertEqual(c.position[1], new_position[1]) + + # camera moves in negative z direction + c.move(z=1) + self.assertEqual(c.position[2], new_position[2]) + + # camera view center should move with camera + self.assertEqual(c.view_center[0], new_view_center[0]) + self.assertEqual(c.view_center[1], new_view_center[1]) + self.assertEqual(c.view_center[2], new_view_center[2]) + @unittest.skipIf(GITHUB_ACTIONS, "GUI is not available in GitHub Actions") class ViewFPSCameraTC(ViewCameraTB, unittest.TestCase): @@ -180,29 +222,6 @@ def test_reset(self): self.assertEqual(c.view_center, initial_view_center) self.assertEqual(c.up_vector, initial_up_vector) - def test_translation(self): - c = self.camera - - speed = c.linear_speed - delta_vec = np.array([speed, speed, -speed]) - new_position = np.array(c.position) + delta_vec - new_view_center = np.array(c.view_center) + delta_vec - - c.move(x=1) - self.assertEqual(c.position[0], new_position[0]) - - c.move(y=1) - self.assertEqual(c.position[1], new_position[1]) - - # camera moves in negative z direction - c.move(z=1) - self.assertEqual(c.position[2], new_position[2]) - - # camera view center should move with camera - self.assertEqual(c.view_center[0], new_view_center[0]) - self.assertEqual(c.view_center[1], new_view_center[1]) - self.assertEqual(c.view_center[2], new_view_center[2]) - def test_rotation(self): c = self.camera @@ -219,31 +238,32 @@ def test_rotation(self): # test camera rotates around y-axis c.move(yaw=1, left_mouse_button=True) - rotation_matrix = self.angle_axis(angle, (0, 1, 0)) - rotated_vector = np.array(initial_view_vector) @ rotation_matrix + rot_matrix = self.angle_axis(angle, (0, 1, 0)) + rot_vector = np.array(initial_view_vector) @ rot_matrix - self.assertAlmostEqual(rotated_vector[0], c.view_vector[0], places=5) - self.assertAlmostEqual(rotated_vector[1], c.view_vector[1], places=5) - self.assertAlmostEqual(rotated_vector[2], c.view_vector[2], places=5) + self.assertAlmostEqual(rot_vector[0], c.view_vector[0], delta=1e-5) + self.assertAlmostEqual(rot_vector[1], c.view_vector[1], delta=1e-5) + self.assertAlmostEqual(rot_vector[2], c.view_vector[2], delta=1e-5) # test camera rotates around x-axis old_view_vector = c.view_vector c.move(pitch=1, left_mouse_button=True) x_basis = -self.normalize(np.cross(old_view_vector, c.up_vector)) - rotation_matrix = self.angle_axis(angle, x_basis) - rotated_vector = np.array(old_view_vector) @ rotation_matrix + rot_matrix = self.angle_axis(angle, x_basis) + rot_vector = np.array(old_view_vector) @ rot_matrix - self.assertAlmostEqual(rotated_vector[0], c.view_vector[0], places=5) - self.assertAlmostEqual(rotated_vector[1], c.view_vector[1], places=5) - self.assertAlmostEqual(rotated_vector[2], c.view_vector[2], places=5) + self.assertAlmostEqual(rot_vector[0], c.view_vector[0], delta=1e-5) + self.assertAlmostEqual(rot_vector[1], c.view_vector[1], delta=1e-5) + self.assertAlmostEqual(rot_vector[2], c.view_vector[2], delta=1e-5) # test view center moved with the camera + view_center = c.view_center new_view_center = c.view_vector + np.array(c.position) - self.assertAlmostEqual(c.view_center[0], new_view_center[0], places=5) - self.assertAlmostEqual(c.view_center[1], new_view_center[1], places=5) - self.assertAlmostEqual(c.view_center[2], new_view_center[2], places=5) + self.assertAlmostEqual(view_center[0], new_view_center[0], delta=1e-5) + self.assertAlmostEqual(view_center[1], new_view_center[1], delta=1e-5) + self.assertAlmostEqual(view_center[2], new_view_center[2], delta=1e-5) @unittest.skipIf(GITHUB_ACTIONS, "GUI is not available in GitHub Actions") @@ -275,29 +295,6 @@ def test_reset(self): self.assertEqual(c.view_center, initial_view_center) self.assertEqual(c.up_vector, initial_up_vector) - def test_translation(self): - c = self.camera - - speed = c.linear_speed - delta_vec = np.array([speed, speed, -speed]) - new_pos = np.array(c.position) + delta_vec - new_view_center = np.array(c.view_center) + delta_vec - - c.move(x=1) - self.assertEqual(c.position[0], new_pos[0]) - - c.move(y=1) - self.assertEqual(c.position[1], new_pos[1]) - - # camera moves in negative z direction - c.move(z=1) - self.assertEqual(c.position[2], new_pos[2]) - - # camera view center should move with camera - self.assertEqual(c.view_center[0], new_view_center[0]) - self.assertEqual(c.view_center[1], new_view_center[1]) - self.assertEqual(c.view_center[2], new_view_center[2]) - def test_rotation(self): c = self.camera @@ -313,24 +310,24 @@ def test_rotation(self): # test camera rotates around y-axis c.move(yaw=1, right_mouse_button=True) - rotation_matrix = self.angle_axis(angle, (0, -1, 0)) - rotated_vector = np.array(initial_view_vector) @ rotation_matrix + rot_matrix = self.angle_axis(angle, (0, -1, 0)) + rot_vector = np.array(initial_view_vector) @ rot_matrix - self.assertAlmostEqual(rotated_vector[0], c.view_vector[0], places=5) - self.assertAlmostEqual(rotated_vector[1], c.view_vector[1], places=5) - self.assertAlmostEqual(rotated_vector[2], c.view_vector[2], places=5) + self.assertAlmostEqual(rot_vector[0], c.view_vector[0], delta=1e-5) + self.assertAlmostEqual(rot_vector[1], c.view_vector[1], delta=1e-5) + self.assertAlmostEqual(rot_vector[2], c.view_vector[2], delta=1e-5) # test camera rotates around x-axis old_view_vector = c.view_vector c.move(pitch=1, right_mouse_button=True) x_basis = self.normalize(np.cross(old_view_vector, c.up_vector)) - rotation_matrix = self.angle_axis(angle, x_basis) - rotated_vector = np.array(old_view_vector) @ rotation_matrix + rot_matrix = self.angle_axis(angle, x_basis) + rot_vector = np.array(old_view_vector) @ rot_matrix - self.assertAlmostEqual(rotated_vector[0], c.view_vector[0], places=5) - self.assertAlmostEqual(rotated_vector[1], c.view_vector[1], places=5) - self.assertAlmostEqual(rotated_vector[2], c.view_vector[2], places=5) + self.assertAlmostEqual(rot_vector[0], c.view_vector[0], delta=1e-5) + self.assertAlmostEqual(rot_vector[1], c.view_vector[1], delta=1e-5) + self.assertAlmostEqual(rot_vector[2], c.view_vector[2], delta=1e-5) # camera view center should not change self.assertEqual(c.view_center, initial_view_center)