From 5da6356df64d38b50b5bd66b6daad3d7189e3d7b Mon Sep 17 00:00:00 2001 From: Seb James Date: Wed, 16 Jul 2025 16:46:13 +0100 Subject: [PATCH 01/25] Rather than losing scenetrans and rotation, the scene matrix doesn't need to be a member --- mplot/VisualBase.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 5e7c9ed9..708ea49e 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -724,15 +724,13 @@ namespace mplot { //! A rotation that is saved between mouse button callbacks sm::quaternion savedRotation; - //! The projection matrix is a member of this class + //! The projection matrix is a member of this class. Value is set during setPerspective() or setOrthographic() sm::mat44 projection; - //! The inverse of the projection + //! The inverse of the projection. Value is set during setPerspective() or setOrthographic() sm::mat44 invproj; - //! A scene transformation - sm::mat44 scene; - //! Scene transformation inverse + //! The sceneview transformation inverse has to be held as state during a mouse movement sm::mat44 invscene; public: @@ -1102,9 +1100,9 @@ namespace mplot { // Save the rotation at the start of the mouse movement this->savedRotation = this->rotation; // Get the scene's rotation at the start of the mouse movement: - this->scene.setToIdentity(); - this->scene.rotate (this->savedRotation); - this->invscene = this->scene.inverse(); + sm::mat44 sceneview; + sceneview.rotate (this->savedRotation); + this->invscene = sceneview.inverse(); } if (button == mplot::mousebutton::left) { // Primary button means rotate From 84df36e78e1ae9be77f6c30083a5252f38a4da8f Mon Sep 17 00:00:00 2001 From: Seb James Date: Wed, 16 Jul 2025 16:49:42 +0100 Subject: [PATCH 02/25] Prefer this order of operations --- mplot/VisualBase.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 708ea49e..e606512d 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -1097,12 +1097,12 @@ namespace mplot { // Record the position at which the button was pressed if (action == keyaction::press) { // Button down this->mousePressPosition = this->cursorpos; - // Save the rotation at the start of the mouse movement - this->savedRotation = this->rotation; // Get the scene's rotation at the start of the mouse movement: sm::mat44 sceneview; - sceneview.rotate (this->savedRotation); + sceneview.rotate (this->rotation); this->invscene = sceneview.inverse(); + // Save the rotation at the start of the mouse movement + this->savedRotation = this->rotation; } if (button == mplot::mousebutton::left) { // Primary button means rotate From 9d960b916a006060925022ef2c2122bb0c03b815 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 17 Jul 2025 09:42:53 +0100 Subject: [PATCH 03/25] Remove need for invscene member again --- mplot/VisualBase.h | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index e606512d..7ee2ef0a 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -730,9 +730,6 @@ namespace mplot { //! The inverse of the projection. Value is set during setPerspective() or setOrthographic() sm::mat44 invproj; - //! The sceneview transformation inverse has to be held as state during a mouse movement - sm::mat44 invscene; - public: /* @@ -1037,8 +1034,9 @@ namespace mplot { // Now inverse apply the rotation of the scene to the rotation axis (vec), // so that we rotate the model the right way. - sm::vec tmp_4D = this->invscene * this->rotationAxis; - this->rotationAxis.set_from (tmp_4D); // Set rotationAxis from 4D result + sm::mat44 savedr; + savedr.rotate (this->savedRotation); + this->rotationAxis.set_from (savedr.inverse() * this->rotationAxis); // Update rotation from the saved position. this->rotation = this->savedRotation; @@ -1094,14 +1092,9 @@ namespace mplot { // If the scene is locked, then ignore the mouse movements if (this->state.test (visual_state::sceneLocked)) { return; } - // Record the position at which the button was pressed + // Record the position and rotation at which the button was pressed if (action == keyaction::press) { // Button down this->mousePressPosition = this->cursorpos; - // Get the scene's rotation at the start of the mouse movement: - sm::mat44 sceneview; - sceneview.rotate (this->rotation); - this->invscene = sceneview.inverse(); - // Save the rotation at the start of the mouse movement this->savedRotation = this->rotation; } From bea845f5b78272a7590970cc9043fe48f3aea0d3 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 17 Jul 2025 10:30:18 +0100 Subject: [PATCH 04/25] A nice improvement to rotations, now about centre of the window/workspace --- mplot/VisualOwnableMX.h | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index 466c75f8..e91460ac 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -203,13 +203,21 @@ namespace mplot { } // Calculate model view transformation - transforming from "model space" to "worldspace". - sm::mat44 sceneview; + sm::mat44 sv1; if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { - // This line translates from model space to world space. Avoid in cyl? - sceneview.translate (this->scenetrans); // send backwards into distance + // send backwards (ONLY) into distance (Avoid in cyl) + sv1.translate (sm::vec<>{0.0f, 0.0f, this->scenetrans.z()}); } - // And this rotation completes the transition from model to world - sceneview.prerotate (this->rotation); + // Now rotate (about centre of window, rather than origin of models) + sm::mat44 sv2; + sv2.rotate (this->rotation); + + // Finish the translation + sm::mat44 sv3; + sv3.translate (sm::vec<>{this->scenetrans.x(), this->scenetrans.y(), 0.0f}); + + // Combine into a sceneview matrix + sm::mat44 sceneview = sv1 * sv2 * sv3; // Clear color buffer and **also depth buffer** this->glfn->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); From 0661e03bcc605ac10edd05990016a7a5914f93b2 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 17 Jul 2025 10:47:39 +0100 Subject: [PATCH 05/25] Brings sceneview computations into base class method and applies in both OwnableNX and OwnableNoMX --- mplot/VisualBase.h | 23 +++++++++++++++++++++++ mplot/VisualOwnableMX.h | 16 +--------------- mplot/VisualOwnableNoMX.h | 8 +------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 7ee2ef0a..c7c8f698 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -654,6 +654,29 @@ namespace mplot { this->invproj = this->projection.inverse(); } + // Compute the sceneview matrix + sm::mat44 computeSceneview() + { + // Calculate model view transformation - transforming from "model space" to "worldspace". + sm::mat44 sv1; + if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { + // send backwards (ONLY) into distance (Avoid in cyl) + sv1.translate (sm::vec<>{0.0f, 0.0f, this->scenetrans.z()}); + } + // A rotation + sm::mat44 sv2; + sv2.rotate (this->rotation); + + // The 'in-page' translation + sm::mat44 sv3; + sv3.translate (sm::vec<>{this->scenetrans.x(), this->scenetrans.y(), 0.0f}); + + // Combine into a sceneview matrix - order is in-page translation, then rotation, then send-backwards + sm::mat44 sceneview = sv1 * sv2 * sv3; + + return sceneview; + } + //! A vector of pointers to all the mplot::VisualModels (HexGridVisual, //! ScatterVisual, etc) which are going to be rendered in the scene. std::vector>> vm; diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index e91460ac..1d0c1603 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -203,21 +203,7 @@ namespace mplot { } // Calculate model view transformation - transforming from "model space" to "worldspace". - sm::mat44 sv1; - if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { - // send backwards (ONLY) into distance (Avoid in cyl) - sv1.translate (sm::vec<>{0.0f, 0.0f, this->scenetrans.z()}); - } - // Now rotate (about centre of window, rather than origin of models) - sm::mat44 sv2; - sv2.rotate (this->rotation); - - // Finish the translation - sm::mat44 sv3; - sv3.translate (sm::vec<>{this->scenetrans.x(), this->scenetrans.y(), 0.0f}); - - // Combine into a sceneview matrix - sm::mat44 sceneview = sv1 * sv2 * sv3; + sm::mat44 sceneview = this->computeSceneview(); // Clear color buffer and **also depth buffer** this->glfn->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); diff --git a/mplot/VisualOwnableNoMX.h b/mplot/VisualOwnableNoMX.h index 3165b15f..6e45ffb6 100644 --- a/mplot/VisualOwnableNoMX.h +++ b/mplot/VisualOwnableNoMX.h @@ -197,13 +197,7 @@ namespace mplot { } // Calculate model view transformation - transforming from "model space" to "worldspace". - sm::mat44 sceneview; - if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { - // This line translates from model space to world space. Avoid in cyl? - sceneview.translate (this->scenetrans); // send backwards into distance - } - // And this rotation completes the transition from model to world - sceneview.prerotate (this->rotation); + sm::mat44 sceneview = this->computeSceneview(); // Clear color buffer and **also depth buffer** glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); From 459196eb31f91ac9890fd96cd6067f2742f44daa Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 18 Jul 2025 07:43:52 +0100 Subject: [PATCH 06/25] An example useful for sceneview debugging --- examples/CMakeLists.txt | 3 ++ examples/rhombo_scene2.cpp | 68 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 examples/rhombo_scene2.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index ad758cbd..076e427c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -394,6 +394,9 @@ target_link_libraries(rhombo OpenGL::GL glfw Freetype::Freetype) add_executable(rhombo_scene rhombo_scene.cpp) target_link_libraries(rhombo_scene OpenGL::GL glfw Freetype::Freetype) +add_executable(rhombo_scene2 rhombo_scene2.cpp) +target_link_libraries(rhombo_scene2 OpenGL::GL glfw Freetype::Freetype) + add_executable(quads quads.cpp) target_link_libraries(quads OpenGL::GL glfw Freetype::Freetype) diff --git a/examples/rhombo_scene2.cpp b/examples/rhombo_scene2.cpp new file mode 100644 index 00000000..4e32137b --- /dev/null +++ b/examples/rhombo_scene2.cpp @@ -0,0 +1,68 @@ +// A scene of rhombohedrons useful for developing scene rotation by user control + +#include +#include +#include +#include + +int main() +{ + // Create a scene + mplot::Visual v(1024, 768, "Rhombohedrons"); + v.lightingEffects (true); + + // Parameters of the model + sm::vec offset = { -1, 0, 0 }; // a within-scene offset + sm::vec e1 = { 0.25, 0, 0 }; + sm::vec e2 = { 0.1, 0.25, 0 }; + sm::vec e3 = { 0, 0.0, 0.25 }; + mplot::ColourMap cmap(mplot::ColourMapType::Rainbow); + + // 0 + offset = { 0, 0, 0 }; + auto rv = std::make_unique> (offset, e1/3, e2/3, e3/3, cmap.convert(0.1f)); + v.bindmodel (rv); + rv->finalize(); + v.addVisualModel (rv); + + offset = { -2, 0, 0 }; + rv = std::make_unique> (offset, e1, e2, e3, cmap.convert(1.0f)); + v.bindmodel (rv); + rv->finalize(); + v.addVisualModel (rv); + + offset = { 2, 0, 0 }; + rv = std::make_unique> (offset, e1, e2, e3, cmap.convert(0.5f)); + v.bindmodel (rv); + rv->finalize(); + v.addVisualModel (rv); + + offset = { 0, 2, 0 }; + rv = std::make_unique> (offset, e1, e2, e3, cmap.convert(0.3333f)); + v.bindmodel (rv); + rv->finalize(); + v.addVisualModel (rv); + + offset = { 0, -2, 0 }; + rv = std::make_unique> (offset, e1, e2, e3, cmap.convert(0.25f)); + v.bindmodel (rv); + rv->finalize(); + v.addVisualModel (rv); + + offset = { 0, 0, 2 }; + rv = std::make_unique> (offset, e1, e2, e3, cmap.convert(0.2f)); + v.bindmodel (rv); + rv->finalize(); + v.addVisualModel (rv); + + offset = { 0, 0, -2 }; + rv = std::make_unique> (offset, e1, e2, e3, cmap.convert(0.1f)); + v.bindmodel (rv); + rv->finalize(); + v.addVisualModel (rv); + v.render(); + + v.keepOpen(); + + return 0; +} From 2bf0e27f074d028c0b4b992f14e7a1d3845867de Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 18 Jul 2025 07:47:33 +0100 Subject: [PATCH 07/25] Progress to point where I realise that I think I do need to retain sceneview matrix --- mplot/VisualBase.h | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index c7c8f698..7f6cf534 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -654,29 +654,61 @@ namespace mplot { this->invproj = this->projection.inverse(); } + // Compute the sceneview matrix, always rotating about scene origin + sm::mat44 computeSceneview0() + { + sm::mat44 sceneview; + if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { + // This line translates from model space to world space. Avoid in cyl? + sceneview.translate (this->scenetrans); // send backwards into distance + } + // And this rotation completes the transition from model to world + sceneview.prerotate (this->rotation); + + // equiv: sv1(translate) * sv2(rotate) + return sceneview; + } + // Compute the sceneview matrix - sm::mat44 computeSceneview() + sm::mat44 computeSceneview1() { // Calculate model view transformation - transforming from "model space" to "worldspace". sm::mat44 sv1; if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { // send backwards (ONLY) into distance (Avoid in cyl) - sv1.translate (sm::vec<>{0.0f, 0.0f, this->scenetrans.z()}); + sv1.translate (sm::vec{ 0.0f, 0.0f, this->scenetrans.z() }); } // A rotation sm::mat44 sv2; sv2.rotate (this->rotation); - // The 'in-page' translation sm::mat44 sv3; - sv3.translate (sm::vec<>{this->scenetrans.x(), this->scenetrans.y(), 0.0f}); - + sv3.translate (sm::vec{ this->scenetrans.x(), this->scenetrans.y(), 0.0f }); // Combine into a sceneview matrix - order is in-page translation, then rotation, then send-backwards sm::mat44 sceneview = sv1 * sv2 * sv3; return sceneview; } + sm::mat44 computeSceneview2() + { + // Calculate model view transformation - transforming from "model space" to "worldspace". + sm::mat44 sv1; + if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { + // send backwards (ONLY) into distance (Avoid in cyl) + sv1.translate (this->scenetrans); + } + // A rotation + sm::mat44 sv2; + sv2.rotate (this->rotation); + + sm::mat44 sceneview = sv2 * sv1; + + return sceneview; + } + + sm::mat44 computeSceneview() { return this->computeSceneview0(); } + //! A vector of pointers to all the mplot::VisualModels (HexGridVisual, //! ScatterVisual, etc) which are going to be rendered in the scene. std::vector>> vm; From fdbdc5b070eb23a48ab94a80f2d37ae03cc75e57 Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 18 Jul 2025 09:25:13 +0100 Subject: [PATCH 08/25] Sceneview translation now goes via member mat44 sceneview --- mplot/VisualBase.h | 125 +++++++++++++++++++++++++++++++++------------ 1 file changed, 91 insertions(+), 34 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 7f6cf534..39b476f7 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -125,7 +125,7 @@ namespace mplot { * such as a QWidget. We have to wait on calling init functions until an OpenGL * environment is guaranteed to exist. */ - VisualBase() { } + VisualBase() { this->sceneview.translate (this->scenetrans_default); } /*! * Construct a new visualiser. The rule is 1 window to one Visual object. So, this creates a @@ -136,6 +136,7 @@ namespace mplot { , window_h(_height) , title(_title) { + this->sceneview.translate (this->scenetrans_default); this->options.set (visual_options::versionStdout, _version_stdout); this->init_gl(); // abstract } @@ -431,15 +432,25 @@ namespace mplot { //! Set the scene's x and y values at the same time. void setSceneTransXY (const float _x, const float _y) { - this->scenetrans[0] = _x; - this->scenetrans[1] = _y; this->scenetrans_default[0] = _x; this->scenetrans_default[1] = _y; + this->sceneview.setToIdentity(); + this->sceneview.translate (this->scenetrans_default); } //! Set the scene's y value. Use this to shift your scene objects left or right - void setSceneTransX (const float _x) { this->scenetrans[0] = _x; this->scenetrans_default[0] = _x; } + void setSceneTransX (const float _x) + { + this->scenetrans_default[0] = _x; + this->sceneview.setToIdentity(); + this->sceneview.translate (this->scenetrans_default); + } //! Set the scene's y value. Use this to shift your scene objects up and down - void setSceneTransY (const float _y) { this->scenetrans[1] = _y; this->scenetrans_default[1] = _y; } + void setSceneTransY (const float _y) + { + this->scenetrans_default[1] = _y; + this->sceneview.setToIdentity(); + this->sceneview.translate (this->scenetrans_default); + } //! Set the scene's z value. Use this to bring the 'camera' closer to your scene //! objects (that is, your mplot::VisualModel objects). void setSceneTransZ (const float _z) @@ -447,34 +458,36 @@ namespace mplot { if (_z > 0.0f) { std::cerr << "WARNING setSceneTransZ(): Normally, the default z value is negative.\n"; } - this->scenetrans[2] = _z; this->scenetrans_default[2] = _z; + this->sceneview.setToIdentity(); + this->sceneview.translate (this->scenetrans_default); } void setSceneTrans (float _x, float _y, float _z) { if (_z > 0.0f) { std::cerr << "WARNING setSceneTrans(): Normally, the default z value is negative.\n"; } - this->scenetrans[0] = _x; + this->scenetrans_default[0] = _x; - this->scenetrans[1] = _y; this->scenetrans_default[1] = _y; - this->scenetrans[2] = _z; this->scenetrans_default[2] = _z; + this->sceneview.setToIdentity(); + this->sceneview.translate (this->scenetrans_default); } void setSceneTrans (const sm::vec& _xyz) { if (_xyz[2] > 0.0f) { std::cerr << "WARNING setSceneTrans(vec<>&): Normally, the default z value is negative.\n"; } - this->scenetrans = _xyz; this->scenetrans_default = _xyz; + this->sceneview.setToIdentity(); + this->sceneview.translate (this->scenetrans_default); } void setSceneRotation (const sm::quaternion& _rotn) { - this->rotation = _rotn; this->rotation_default = _rotn; + this->sceneview.prerotate (_rotn); } void lightingEffects (const bool effects_on = true) @@ -657,16 +670,16 @@ namespace mplot { // Compute the sceneview matrix, always rotating about scene origin sm::mat44 computeSceneview0() { - sm::mat44 sceneview; + sm::mat44 _sceneview; if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { // This line translates from model space to world space. Avoid in cyl? - sceneview.translate (this->scenetrans); // send backwards into distance + _sceneview.translate (this->scenetrans); // send backwards into distance } // And this rotation completes the transition from model to world - sceneview.prerotate (this->rotation); + _sceneview.prerotate (this->rotation); // equiv: sv1(translate) * sv2(rotate) - return sceneview; + return _sceneview; } // Compute the sceneview matrix @@ -685,9 +698,9 @@ namespace mplot { sm::mat44 sv3; sv3.translate (sm::vec{ this->scenetrans.x(), this->scenetrans.y(), 0.0f }); // Combine into a sceneview matrix - order is in-page translation, then rotation, then send-backwards - sm::mat44 sceneview = sv1 * sv2 * sv3; + sm::mat44 _sceneview = sv1 * sv2 * sv3; - return sceneview; + return _sceneview; } sm::mat44 computeSceneview2() @@ -702,12 +715,36 @@ namespace mplot { sm::mat44 sv2; sv2.rotate (this->rotation); - sm::mat44 sceneview = sv2 * sv1; + sm::mat44 _sceneview = sv2 * sv1; - return sceneview; + return _sceneview; } - sm::mat44 computeSceneview() { return this->computeSceneview0(); } + sm::mat44 computeSceneview3() + { + // Calculate model view transformation - transforming from "model space" to "worldspace". + sm::mat44 sv_tr; + if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { + // send backwards (ONLY) into distance (Avoid in cyl) + sv_tr.translate (this->scenetrans_delta); + this->scenetrans_delta.zero(); + } + // A rotation + sm::mat44 sv_rot; + //sv_rot.rotate (this->rotation_delta); + // this->rotation_delta.zero(); + + // Original behaviour + sm::mat44 _sceneview = this->sceneview * sv_tr * sv_rot; + + return _sceneview; + } + + sm::mat44 computeSceneview() + { + this->sceneview = this->computeSceneview3(); + return this->sceneview; + } //! A vector of pointers to all the mplot::VisualModels (HexGridVisual, //! ScatterVisual, etc) which are going to be rendered in the scene. @@ -755,7 +792,10 @@ namespace mplot { constexpr static float zDefault = -5.0f; //! Holds the translation coordinates for the current location of the entire scene - sm::vec scenetrans = {0.0f, 0.0f, zDefault}; + sm::vec scenetrans = { 0.0f, 0.0f, zDefault }; + + //! A delta scene translations + sm::vec scenetrans_delta = { 0.0f, 0.0f, 0.0f }; //! Default for scenetrans. This is a scene position that can be reverted to, to //! 'reset the view'. This is copied into scenetrans when user presses Ctrl-a. @@ -767,13 +807,16 @@ namespace mplot { //! Screen coordinates of the position of the last mouse press sm::vec mousePressPosition = { 0.0f, 0.0f }; - //! The current rotation axis. World frame? + //! The current rotation axis. World frame. sm::vec rotationAxis = { 0.0f, 0.0f, 0.0f }; //! A rotation quaternion. You could have guessed that, right? sm::quaternion rotation; - //! The default rotation of the scene + //! Add additional rotation to the scene + sm::quaternion rotation_delta; + + //! The default rotation of the scene, to reconstruct the default sceneview matrix sm::quaternion rotation_default; //! A rotation that is saved between mouse button callbacks @@ -785,6 +828,13 @@ namespace mplot { //! The inverse of the projection. Value is set during setPerspective() or setOrthographic() sm::mat44 invproj; + //! The sceneview matrix, which changes as the user moves the view with mouse + //! movements. Initialized in VisualOwnable(No)MX constructor. + sm::mat44 sceneview; + + //! Saved sceneview at mouse button down + sm::mat44 savedSceneview; + public: /* @@ -1057,7 +1107,9 @@ namespace mplot { // Add the depth at which the object lies. Use forward projection to determine the // correct z coordinate for the inverse projection. This assumes only one object. - sm::vec point = { 0.0f, 0.0f, this->scenetrans.z(), 1.0f }; + sm::vec st = this->sceneview.translation(); + //sm::vec point = { 0.0f, 0.0f, this->scenetrans.z(), 1.0f }; + sm::vec point = { 0.0f, 0.0f, st.z(), 1.0f }; sm::vec pp = this->projection * point; float coord_z = pp[2]/pp[3]; // divide by pp[3] is divide by/normalise by 'w'. @@ -1094,9 +1146,12 @@ namespace mplot { this->rotationAxis.set_from (savedr.inverse() * this->rotationAxis); // Update rotation from the saved position. - this->rotation = this->savedRotation; - sm::quaternion rotnQuat (this->rotationAxis, -rotamount * sm::mathconst::deg2rad); - this->rotation.postmultiply (rotnQuat); // combines rotations + //this->rotation = this->savedRotation; + //sm::quaternion rotnQuat (this->rotationAxis, -rotamount * sm::mathconst::deg2rad); + //this->rotation.postmultiply (rotnQuat); // combines rotations + + this->rotation_delta.set_rotation (this->rotationAxis, -rotamount * sm::mathconst::deg2rad); + needs_render = true; } else if (this->state.test (visual_state::translateMode)) { // allow only rotate OR translate for a single mouse movement @@ -1113,7 +1168,9 @@ namespace mplot { // Add the depth at which the object lies. Use forward projection to determine the // correct z coordinate for the inverse projection. This assumes only one object. - sm::vec point = { 0.0f, 0.0f, this->scenetrans.z(), 1.0f }; + sm::vec st = this->savedSceneview.translation(); + //sm::vec point = { 0.0f, 0.0f, this->scenetrans.z(), 1.0f }; + sm::vec point = { 0.0f, 0.0f, st.z(), 1.0f }; sm::vec pp = this->projection * point; float coord_z = pp[2]/pp[3]; // divide by pp[3] is divide by/normalise by 'w'. @@ -1129,8 +1186,8 @@ namespace mplot { // Note: mouseMoveWorld[2] is unmodified // We "translate the whole scene" - used by 2D projection shaders (ignored by cyl shader) - this->scenetrans[0] += mouseMoveWorld[0]; - this->scenetrans[1] -= mouseMoveWorld[1]; + this->scenetrans_delta[0] = mouseMoveWorld[0]; + this->scenetrans_delta[1] = -mouseMoveWorld[1]; // Also translate our cylindrical camera position (used in cyl shader, ignored in proj. shader) this->cyl_cam_pos[0] -= mouseMoveWorld[0]; @@ -1150,7 +1207,7 @@ namespace mplot { // Record the position and rotation at which the button was pressed if (action == keyaction::press) { // Button down this->mousePressPosition = this->cursorpos; - this->savedRotation = this->rotation; + this->savedSceneview = this->sceneview; } if (button == mplot::mousebutton::left) { // Primary button means rotate @@ -1202,15 +1259,15 @@ namespace mplot { } else { // perspective_type::perspective or perspective_type::cylindrical // xoffset does what mouse drag left/right in rotateModMode does (L/R scene trans) - this->scenetrans[0] -= xoffset * this->scenetrans_stepsize; + this->scenetrans_delta[0] = -xoffset * this->scenetrans_stepsize; this->cyl_cam_pos[0] += xoffset * this->scenetrans_stepsize; // yoffset does the 'in-out zooming' sm::vec scroll_move_y = { 0.0f, static_cast(yoffset) * this->scenetrans_stepsize, 0.0f, 1.0f }; - this->scenetrans[2] += scroll_move_y[1]; + this->scenetrans_delta[2] = scroll_move_y[1]; // Translate scroll_move_y then add it to cyl_cam_pos here sm::mat44 sceneview_rotn; - sceneview_rotn.rotate (this->rotation); + sceneview_rotn.rotate (this->rotation); // FIXME for cyl_cam_pos this->cyl_cam_pos += sceneview_rotn * scroll_move_y; } return true; // needs_render From 48a4f2309ef390f75b01879bd88cc05f86246844 Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 18 Jul 2025 10:36:59 +0100 Subject: [PATCH 09/25] Current (lack of) progres --- maths | 2 +- mplot/VisualBase.h | 27 +++++++++++++++++++-------- mplot/VisualOwnableMX.h | 2 +- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/maths b/maths index c49a8474..aa2d6299 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit c49a8474f5bf25a4bba9d7e0ded35218d8f85be5 +Subproject commit aa2d629916753cc77147208b3f1c3558260eae64 diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 39b476f7..dac9207c 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -726,16 +726,22 @@ namespace mplot { sm::mat44 sv_tr; if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { // send backwards (ONLY) into distance (Avoid in cyl) + + // scenetrans_delta is in world frame, we want to convert to model frame + //std::cout << "scenetrans_delta = " << scenetrans_delta << std::endl; + //sm::quaternion curr_rotn = this->sceneview.rotation(); // NO GOOD + //sm::vec model_delta = curr_rotn * this->scenetrans_delta; + //std::cout << "sceneview.rotation() * scenetrans_delta = " << model_delta << std::endl; sv_tr.translate (this->scenetrans_delta); this->scenetrans_delta.zero(); } - // A rotation + // A rotation delta in world frame sm::mat44 sv_rot; - //sv_rot.rotate (this->rotation_delta); - // this->rotation_delta.zero(); + sv_rot.rotate (this->rotation_delta); + this->rotation_delta.reset(); // Original behaviour - sm::mat44 _sceneview = this->sceneview * sv_tr * sv_rot; + sm::mat44 _sceneview = this->sceneview * (sv_tr * sv_rot); return _sceneview; } @@ -1026,10 +1032,14 @@ namespace mplot { && _key == key::a && (mods & keymod::control) && action == keyaction::press) { std::cout << "Reset to default view\n"; // Reset translation - this->scenetrans = this->scenetrans_default; + this->scenetrans = this->scenetrans_default; // FIXME this->cyl_cam_pos = this->cyl_cam_pos_default; // Reset rotation - this->rotation = this->rotation_default; + this->rotation = this->rotation_default; // FIXME + + this->sceneview.setToIdentity(); + this->sceneview.translate (this->scenetrans_default); + this->sceneview.prerotate (this->rotation_default); needs_render = true; } @@ -1136,15 +1146,16 @@ namespace mplot { // have to project into the model frame to determine how to rotate the model! float rotamount = mouseMoveWorld.length() * 40.0f; // chosen in degrees // Calculate new rotation axis as weighted sum - this->rotationAxis = (mouseMoveWorld * rotamount); + this->rotationAxis = (mouseMoveWorld * rotamount); // rotationAxis is in world frame this->rotationAxis.renormalize(); // Now inverse apply the rotation of the scene to the rotation axis (vec), // so that we rotate the model the right way. +#if 0 sm::mat44 savedr; savedr.rotate (this->savedRotation); this->rotationAxis.set_from (savedr.inverse() * this->rotationAxis); - +#endif // Update rotation from the saved position. //this->rotation = this->savedRotation; //sm::quaternion rotnQuat (this->rotationAxis, -rotamount * sm::mathconst::deg2rad); diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index 1d0c1603..500e897b 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -252,7 +252,7 @@ namespace mplot { this->coordArrows->render(); } - sm::mat44 scenetransonly; + sm::mat44 scenetransonly; // Maybe generate from sceneview.translation()? scenetransonly.translate (this->scenetrans); auto vmi = this->vm.begin(); From 68ba50071c608bd2d8ba4724b2b1824cc9ad0d22 Mon Sep 17 00:00:00 2001 From: Seb James Date: Fri, 18 Jul 2025 16:39:33 +0100 Subject: [PATCH 10/25] More non progress --- maths | 2 +- mplot/VisualBase.h | 87 ++++++++++++++++++++--------------------- mplot/VisualOwnableMX.h | 6 +-- 3 files changed, 46 insertions(+), 49 deletions(-) diff --git a/maths b/maths index aa2d6299..e092fece 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit aa2d629916753cc77147208b3f1c3558260eae64 +Subproject commit e092fece182813698ef4c1a588f1f259fbabecb7 diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index dac9207c..2dae029b 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -725,30 +725,35 @@ namespace mplot { // Calculate model view transformation - transforming from "model space" to "worldspace". sm::mat44 sv_tr; if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { - // send backwards (ONLY) into distance (Avoid in cyl) - - // scenetrans_delta is in world frame, we want to convert to model frame - //std::cout << "scenetrans_delta = " << scenetrans_delta << std::endl; - //sm::quaternion curr_rotn = this->sceneview.rotation(); // NO GOOD - //sm::vec model_delta = curr_rotn * this->scenetrans_delta; - //std::cout << "sceneview.rotation() * scenetrans_delta = " << model_delta << std::endl; + // send backwards into distance (Avoid in cyl) +#if 0 + // scenetrans_delta is in world frame, we want to convert to model frame. + // Specifically, we want the cumulative rotation of the sceneview... + sm::mat33 r = this->savedSceneview.linear(); + sm::vec model_delta = r * -this->scenetrans_delta; + sv_tr.translate (model_delta); +#endif sv_tr.translate (this->scenetrans_delta); - this->scenetrans_delta.zero(); + + //sv_tr.translate (this->savedSceneview * this->scenetrans_delta); + //this->scenetrans_delta.zero(); } - // A rotation delta in world frame + // A rotation delta in world frame. Do we also have a cumulative rotation? sm::mat44 sv_rot; sv_rot.rotate (this->rotation_delta); - this->rotation_delta.reset(); + //this->rotation_delta.reset(); // Original behaviour - sm::mat44 _sceneview = this->sceneview * (sv_tr * sv_rot); + sm::mat44 _sceneview = (sv_tr * sv_rot) * this->savedSceneview; return _sceneview; } sm::mat44 computeSceneview() { - this->sceneview = this->computeSceneview3(); + if (std::abs(this->scenetrans_delta.sum()) > 0.0f || this->rotation_delta.is_zero_rotation() == false) { + this->sceneview = this->computeSceneview3(); + } return this->sceneview; } @@ -1040,6 +1045,8 @@ namespace mplot { this->sceneview.setToIdentity(); this->sceneview.translate (this->scenetrans_default); this->sceneview.prerotate (this->rotation_default); + this->scenetrans_delta.zero(); + this->rotation_delta.reset(); needs_render = true; } @@ -1047,17 +1054,13 @@ namespace mplot { if (this->state.test (visual_state::sceneLocked) == false && _key == key::o && (mods & keymod::control) && action == keyaction::press) { this->fov -= 2; - if (this->fov < 1.0) { - this->fov = 2.0; - } + if (this->fov < 1.0) { this->fov = 2.0; } std::cout << "FOV reduced to " << this->fov << std::endl; } if (this->state.test (visual_state::sceneLocked) == false && _key == key::p && (mods & keymod::control) && action == keyaction::press) { this->fov += 2; - if (this->fov > 179.0) { - this->fov = 178.0; - } + if (this->fov > 179.0) { this->fov = 178.0; } std::cout << "FOV increased to " << this->fov << std::endl; } if (this->state.test (visual_state::sceneLocked) == false @@ -1117,11 +1120,10 @@ namespace mplot { // Add the depth at which the object lies. Use forward projection to determine the // correct z coordinate for the inverse projection. This assumes only one object. - sm::vec st = this->sceneview.translation(); - //sm::vec point = { 0.0f, 0.0f, this->scenetrans.z(), 1.0f }; + sm::vec st = this->savedSceneview.translation(); sm::vec point = { 0.0f, 0.0f, st.z(), 1.0f }; sm::vec pp = this->projection * point; - float coord_z = pp[2]/pp[3]; // divide by pp[3] is divide by/normalise by 'w'. + float coord_z = pp[2] / pp[3]; // divide by pp[3] is divide by/normalise by 'w'. // Construct two points for the start and end of the mouse movement sm::vec p0 = { p0_coord[0], p0_coord[1], coord_z, 1.0f }; @@ -1132,14 +1134,14 @@ namespace mplot { sm::vec v0 = this->invproj * p0; sm::vec v1 = this->invproj * p1; - // This computes the difference betwen v0 and v1, the 2 mouse positions in the world + // This computes the difference between v0 and v1, the 2 mouse positions in the world // space. Note the swap between x and y if (this->state.test (visual_state::rotateModMode)) { // Sort of "rotate the page" mode. - mouseMoveWorld[2] = -((v1[1]/v1[3]) - (v0[1]/v0[3])) + ((v1[0]/v1[3]) - (v0[0]/v0[3])); + mouseMoveWorld[2] = -((v1[1] / v1[3]) - (v0[1] / v0[3])) + ((v1[0] / v1[3]) - (v0[0] / v0[3])); } else { - mouseMoveWorld[1] = -((v1[0]/v1[3]) - (v0[0]/v0[3])); - mouseMoveWorld[0] = -((v1[1]/v1[3]) - (v0[1]/v0[3])); + mouseMoveWorld[1] = -((v1[0] / v1[3]) - (v0[0] / v0[3])); + mouseMoveWorld[0] = -((v1[1] / v1[3]) - (v0[1] / v0[3])); } // Rotation axis is perpendicular to the mouse position difference vector BUT we @@ -1149,18 +1151,7 @@ namespace mplot { this->rotationAxis = (mouseMoveWorld * rotamount); // rotationAxis is in world frame this->rotationAxis.renormalize(); - // Now inverse apply the rotation of the scene to the rotation axis (vec), - // so that we rotate the model the right way. -#if 0 - sm::mat44 savedr; - savedr.rotate (this->savedRotation); - this->rotationAxis.set_from (savedr.inverse() * this->rotationAxis); -#endif - // Update rotation from the saved position. - //this->rotation = this->savedRotation; - //sm::quaternion rotnQuat (this->rotationAxis, -rotamount * sm::mathconst::deg2rad); - //this->rotation.postmultiply (rotnQuat); // combines rotations - + // rotation_delta is a rotation in the world frame of reference this->rotation_delta.set_rotation (this->rotationAxis, -rotamount * sm::mathconst::deg2rad); needs_render = true; @@ -1180,10 +1171,9 @@ namespace mplot { // Add the depth at which the object lies. Use forward projection to determine the // correct z coordinate for the inverse projection. This assumes only one object. sm::vec st = this->savedSceneview.translation(); - //sm::vec point = { 0.0f, 0.0f, this->scenetrans.z(), 1.0f }; sm::vec point = { 0.0f, 0.0f, st.z(), 1.0f }; sm::vec pp = this->projection * point; - float coord_z = pp[2]/pp[3]; // divide by pp[3] is divide by/normalise by 'w'. + float coord_z = pp[2] / pp[3]; // divide by pp[3] is divide by/normalise by 'w'. // Construct two points for the start and end of the mouse movement sm::vec p0 = { p0_coord[0], p0_coord[1], coord_z, 1.0f }; @@ -1192,13 +1182,13 @@ namespace mplot { sm::vec v0 = this->invproj * p0; sm::vec v1 = this->invproj * p1; // This computes the difference betwen v0 and v1, the 2 mouse positions in the world - mouseMoveWorld[0] = (v1[0]/v1[3]) - (v0[0]/v0[3]); - mouseMoveWorld[1] = (v1[1]/v1[3]) - (v0[1]/v0[3]); + mouseMoveWorld[0] = (v1[0] / v1[3]) - (v0[0] / v0[3]); + mouseMoveWorld[1] = (v1[1] / v1[3]) - (v0[1] / v0[3]); // Note: mouseMoveWorld[2] is unmodified // We "translate the whole scene" - used by 2D projection shaders (ignored by cyl shader) - this->scenetrans_delta[0] = mouseMoveWorld[0]; - this->scenetrans_delta[1] = -mouseMoveWorld[1]; + this->scenetrans_delta[0] += mouseMoveWorld[0]; + this->scenetrans_delta[1] -= mouseMoveWorld[1]; // Also translate our cylindrical camera position (used in cyl shader, ignored in proj. shader) this->cyl_cam_pos[0] -= mouseMoveWorld[0]; @@ -1217,8 +1207,15 @@ namespace mplot { // Record the position and rotation at which the button was pressed if (action == keyaction::press) { // Button down + std::cout << "mouse keyaction::press\n"; this->mousePressPosition = this->cursorpos; this->savedSceneview = this->sceneview; + this->scenetrans_delta.zero(); + this->rotation_delta.reset(); + } else if (action == keyaction::repeat) { + std::cout << "mouse keyaction::repeat\n"; + } else if (action == keyaction::release) { + std::cout << "mouse keyaction::release\n"; } if (button == mplot::mousebutton::left) { // Primary button means rotate @@ -1270,12 +1267,12 @@ namespace mplot { } else { // perspective_type::perspective or perspective_type::cylindrical // xoffset does what mouse drag left/right in rotateModMode does (L/R scene trans) - this->scenetrans_delta[0] = -xoffset * this->scenetrans_stepsize; + this->scenetrans_delta[0] -= xoffset * this->scenetrans_stepsize; this->cyl_cam_pos[0] += xoffset * this->scenetrans_stepsize; // yoffset does the 'in-out zooming' sm::vec scroll_move_y = { 0.0f, static_cast(yoffset) * this->scenetrans_stepsize, 0.0f, 1.0f }; - this->scenetrans_delta[2] = scroll_move_y[1]; + this->scenetrans_delta[2] += scroll_move_y[1]; // Translate scroll_move_y then add it to cyl_cam_pos here sm::mat44 sceneview_rotn; sceneview_rotn.rotate (this->rotation); // FIXME for cyl_cam_pos diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index 500e897b..1baff4b0 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -203,7 +203,7 @@ namespace mplot { } // Calculate model view transformation - transforming from "model space" to "worldspace". - sm::mat44 sceneview = this->computeSceneview(); + this->computeSceneview(); // Clear color buffer and **also depth buffer** this->glfn->Clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -245,7 +245,7 @@ namespace mplot { this->setContext(); // ...so re-acquire if we're managing it if (this->options.test (visual_options::coordArrowsInScene) == true) { - this->coordArrows->setSceneMatrix (sceneview); + this->coordArrows->setSceneMatrix (this->sceneview); } else { this->positionCoordArrows(); } @@ -261,7 +261,7 @@ namespace mplot { // It's a two-d thing. Now what? (*vmi)->setSceneMatrix (scenetransonly); } else { - (*vmi)->setSceneMatrix (sceneview); + (*vmi)->setSceneMatrix (this->sceneview); } (*vmi)->render(); ++vmi; From fdfe155b95ba975b65747f406ed2b35a79aac353 Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 21 Jul 2025 12:38:07 +0100 Subject: [PATCH 11/25] Some progress. Rotate about screen centre is sort of working. --- mplot/VisualBase.h | 104 +++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 66 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 2dae029b..bc66897d 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -668,82 +668,45 @@ namespace mplot { } // Compute the sceneview matrix, always rotating about scene origin - sm::mat44 computeSceneview0() + sm::mat44 computeSceneview_about_scene_origin() { - sm::mat44 _sceneview; - if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { - // This line translates from model space to world space. Avoid in cyl? - _sceneview.translate (this->scenetrans); // send backwards into distance - } - // And this rotation completes the transition from model to world - _sceneview.prerotate (this->rotation); - - // equiv: sv1(translate) * sv2(rotate) - return _sceneview; - } - - // Compute the sceneview matrix - sm::mat44 computeSceneview1() - { - // Calculate model view transformation - transforming from "model space" to "worldspace". - sm::mat44 sv1; - if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { - // send backwards (ONLY) into distance (Avoid in cyl) - sv1.translate (sm::vec{ 0.0f, 0.0f, this->scenetrans.z() }); - } - // A rotation - sm::mat44 sv2; - sv2.rotate (this->rotation); - // The 'in-page' translation - sm::mat44 sv3; - sv3.translate (sm::vec{ this->scenetrans.x(), this->scenetrans.y(), 0.0f }); - // Combine into a sceneview matrix - order is in-page translation, then rotation, then send-backwards - sm::mat44 _sceneview = sv1 * sv2 * sv3; - - return _sceneview; - } - - sm::mat44 computeSceneview2() - { - // Calculate model view transformation - transforming from "model space" to "worldspace". - sm::mat44 sv1; + std::cout << "savedSceneview:\n" << this->savedSceneview << std::endl; + sm::mat44 sv_tr; + sm::mat44 sv_rot; if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { - // send backwards (ONLY) into distance (Avoid in cyl) - sv1.translate (this->scenetrans); + sv_tr.translate (this->scenetrans_delta); + // A rotation delta in world frame about the 'screen centre' + std::cout << "savedSceneview.translation: " << this->savedSceneview.translation() << std::endl; + sv_rot.pretranslate (this->savedSceneview.translation()); + sv_rot.rotate (this->rotation_delta); + sv_rot.pretranslate (-this->savedSceneview.translation()); + } else { + // Only rotate in cyl view + sv_rot.rotate (this->rotation_delta); } - // A rotation - sm::mat44 sv2; - sv2.rotate (this->rotation); - sm::mat44 _sceneview = sv2 * sv1; + sm::mat44 _sceneview = this->savedSceneview * (sv_tr * sv_rot); return _sceneview; } - sm::mat44 computeSceneview3() + // Rotate about screen centre + sm::mat44 computeSceneview_about_screen_centre() { - // Calculate model view transformation - transforming from "model space" to "worldspace". sm::mat44 sv_tr; + sm::mat44 sv_rot; if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { - // send backwards into distance (Avoid in cyl) -#if 0 - // scenetrans_delta is in world frame, we want to convert to model frame. - // Specifically, we want the cumulative rotation of the sceneview... - sm::mat33 r = this->savedSceneview.linear(); - sm::vec model_delta = r * -this->scenetrans_delta; - sv_tr.translate (model_delta); -#endif sv_tr.translate (this->scenetrans_delta); - - //sv_tr.translate (this->savedSceneview * this->scenetrans_delta); - //this->scenetrans_delta.zero(); + // A rotation delta in world frame about the 'screen centre' + sm::vec screencentre = {0, 0, this->savedSceneview.translation().z()}; + sv_rot.pretranslate (-screencentre); + sv_rot.rotate (this->rotation_delta); + sv_rot.translate (screencentre); + } else { + // Only rotate in cyl view + sv_rot.rotate (this->rotation_delta); } - // A rotation delta in world frame. Do we also have a cumulative rotation? - sm::mat44 sv_rot; - sv_rot.rotate (this->rotation_delta); - //this->rotation_delta.reset(); - // Original behaviour sm::mat44 _sceneview = (sv_tr * sv_rot) * this->savedSceneview; return _sceneview; @@ -752,7 +715,8 @@ namespace mplot { sm::mat44 computeSceneview() { if (std::abs(this->scenetrans_delta.sum()) > 0.0f || this->rotation_delta.is_zero_rotation() == false) { - this->sceneview = this->computeSceneview3(); + // Calculate model view transformation - transforming from "model space" to "worldspace". + this->sceneview = this->computeSceneview_about_screen_centre(); } return this->sceneview; } @@ -808,6 +772,10 @@ namespace mplot { //! A delta scene translations sm::vec scenetrans_delta = { 0.0f, 0.0f, 0.0f }; + //! A translation to the current 'centre of the view', which is initially the centre of the + //! scene, but may change from this. + sm::vec scenetrans_centre = { 0.0f, 0.0f, zDefault }; + //! Default for scenetrans. This is a scene position that can be reverted to, to //! 'reset the view'. This is copied into scenetrans when user presses Ctrl-a. sm::vec scenetrans_default = { 0.0f, 0.0f, zDefault }; @@ -1038,6 +1006,7 @@ namespace mplot { std::cout << "Reset to default view\n"; // Reset translation this->scenetrans = this->scenetrans_default; // FIXME + this->scenetrans_centre = this->scenetrans_default; // FIXME this->cyl_cam_pos = this->cyl_cam_pos_default; // Reset rotation this->rotation = this->rotation_default; // FIXME @@ -1207,15 +1176,17 @@ namespace mplot { // Record the position and rotation at which the button was pressed if (action == keyaction::press) { // Button down - std::cout << "mouse keyaction::press\n"; + //std::cout << "mouse keyaction::press\n"; this->mousePressPosition = this->cursorpos; this->savedSceneview = this->sceneview; this->scenetrans_delta.zero(); this->rotation_delta.reset(); } else if (action == keyaction::repeat) { - std::cout << "mouse keyaction::repeat\n"; + //std::cout << "mouse keyaction::repeat\n"; } else if (action == keyaction::release) { - std::cout << "mouse keyaction::release\n"; + //std::cout << "mouse keyaction::release\n"; + this->scenetrans_delta.zero(); + this->rotation_delta.reset(); } if (button == mplot::mousebutton::left) { // Primary button means rotate @@ -1273,6 +1244,7 @@ namespace mplot { // yoffset does the 'in-out zooming' sm::vec scroll_move_y = { 0.0f, static_cast(yoffset) * this->scenetrans_stepsize, 0.0f, 1.0f }; this->scenetrans_delta[2] += scroll_move_y[1]; + this->scenetrans_centre[2] += scroll_move_y[1]; // Centre is always (0,0,z) // Translate scroll_move_y then add it to cyl_cam_pos here sm::mat44 sceneview_rotn; sceneview_rotn.rotate (this->rotation); // FIXME for cyl_cam_pos From 587910082c3ae3cc93fe9cc0ba2400ec7a44c9fc Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 21 Jul 2025 15:23:51 +0100 Subject: [PATCH 12/25] More progress --- maths | 2 +- mplot/VisualBase.h | 83 ++++++++++++++++++++++++++------------- mplot/VisualOwnableMX.h | 21 +--------- mplot/VisualOwnableNoMX.h | 21 +--------- 4 files changed, 59 insertions(+), 68 deletions(-) diff --git a/maths b/maths index e092fece..f43139c7 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit e092fece182813698ef4c1a588f1f259fbabecb7 +Subproject commit f43139c72089562cfc38c5c8f9f31563e6356c36 diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index bc66897d..0337bcf8 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -76,11 +76,13 @@ namespace mplot { //! If true, output mplot version to stdout versionStdout, //! If true (the default), then call swapBuffers() at the end of render() - renderSwapsBuffers + renderSwapsBuffers, + //! If true, rotation is about scene origin, rather than screen centre + rotateAboutSceneOrigin }; //! Whether to render with perspective or orthographic (or even a cylindrical projection) - enum class perspective_type + enum class perspective_type : uint32_t { perspective, orthographic, @@ -336,7 +338,7 @@ namespace mplot { // the screen this->coordArrows->setSceneTranslation (v0); // Apply rotation to the coordArrows model - this->coordArrows->setViewRotation (this->rotation); + this->coordArrows->setViewRotation (this->sceneview.rotation()); } // Update the coordinate axes labels @@ -716,7 +718,11 @@ namespace mplot { { if (std::abs(this->scenetrans_delta.sum()) > 0.0f || this->rotation_delta.is_zero_rotation() == false) { // Calculate model view transformation - transforming from "model space" to "worldspace". - this->sceneview = this->computeSceneview_about_screen_centre(); + if (this->options.test (visual_options::rotateAboutSceneOrigin) == false) { + this->sceneview = this->computeSceneview_about_screen_centre(); + } else { + this->sceneview = this->computeSceneview_about_scene_origin(); + } } return this->sceneview; } @@ -730,6 +736,35 @@ namespace mplot { // required to render the Visual. virtual void init_gl() = 0; + // Read-from-json code that is called from init_gl in all implementations: + void read_scenetrans_from_json() + { + // If possible, read in scenetrans and rotation state from a special config file + try { + nlohmann::json vconf; + std::ifstream fi; + fi.open ("/tmp/Visual.json", std::ios::in); + fi >> vconf; + this->scenetrans_default[0] = vconf.contains("scenetrans_x") ? vconf["scenetrans_x"].get() : this->scenetrans_default[0]; + this->scenetrans_default[1] = vconf.contains("scenetrans_y") ? vconf["scenetrans_y"].get() : this->scenetrans_default[1]; + this->scenetrans_default[2] = vconf.contains("scenetrans_z") ? vconf["scenetrans_z"].get() : this->scenetrans_default[2]; + + this->rotation_default.w = vconf.contains("scenerotn_w") ? vconf["scenerotn_w"].get() : this->rotation_default.w; + this->rotation_default.x = vconf.contains("scenerotn_x") ? vconf["scenerotn_x"].get() : this->rotation_default.x; + this->rotation_default.y = vconf.contains("scenerotn_y") ? vconf["scenerotn_y"].get() : this->rotation_default.y; + this->rotation_default.z = vconf.contains("scenerotn_z") ? vconf["scenerotn_z"].get() : this->rotation_default.z; + + this->sceneview.setToIdentity(); + this->sceneview.translate (this->scenetrans_default); + this->sceneview.prerotate (this->rotation_default); + this->scenetrans_delta.zero(); + this->rotation_delta.reset(); + + } catch (...) { + // No problem if we couldn't read /tmp/Visual.json + } + } + //! The window (and OpenGL context) for this Visual mplot::win_t* window = nullptr; @@ -772,10 +807,6 @@ namespace mplot { //! A delta scene translations sm::vec scenetrans_delta = { 0.0f, 0.0f, 0.0f }; - //! A translation to the current 'centre of the view', which is initially the centre of the - //! scene, but may change from this. - sm::vec scenetrans_centre = { 0.0f, 0.0f, zDefault }; - //! Default for scenetrans. This is a scene position that can be reverted to, to //! 'reset the view'. This is copied into scenetrans when user presses Ctrl-a. sm::vec scenetrans_default = { 0.0f, 0.0f, zDefault }; @@ -789,18 +820,12 @@ namespace mplot { //! The current rotation axis. World frame. sm::vec rotationAxis = { 0.0f, 0.0f, 0.0f }; - //! A rotation quaternion. You could have guessed that, right? - sm::quaternion rotation; - //! Add additional rotation to the scene sm::quaternion rotation_delta; - //! The default rotation of the scene, to reconstruct the default sceneview matrix + //! The default rotation of the scene, to reconstruct the default sceneview matrix/reset rotation. sm::quaternion rotation_default; - //! A rotation that is saved between mouse button callbacks - sm::quaternion savedRotation; - //! The projection matrix is a member of this class. Value is set during setPerspective() or setOrthographic() sm::mat44 projection; @@ -859,6 +884,7 @@ namespace mplot { << "Ctrl-o: Reduce field of view\n" << "Ctrl-p: Increase field of view\n" << "Ctrl-y: Cycle perspective\n" + << "Ctrl-k: Toggle rotate about screen centre or scene origin\n" << "Ctrl-z: Show the current scenetrans/rotation and save to /tmp/Visual.json\n" << "Ctrl-u: Reduce zNear cutoff plane\n" << "Ctrl-i: Increase zNear cutoff plane\n" @@ -905,14 +931,15 @@ namespace mplot { } if (_key == key::z && (mods & keymod::control) && action == keyaction::press) { + sm::quaternion rotn = this->sceneview.rotation(); std::cout << "Scenetrans setup code:\n v.setSceneTrans (sm::vec{ float{" << this->scenetrans.x() << "}, float{" << this->scenetrans.y() << "}, float{" << this->scenetrans.z() << "} });" << "\n v.setSceneRotation (sm::quaternion{ float{" - << this->rotation.w << "}, float{" << this->rotation.x << "}, float{" - << this->rotation.y << "}, float{" << this->rotation.z << "} });\n"; + << rotn.w << "}, float{" << rotn.x << "}, float{" + << rotn.y << "}, float{" << rotn.z << "} });\n"; std::cout << "Writing scene trans/rotation into /tmp/Visual.json... "; std::ofstream fout; fout.open ("/tmp/Visual.json", std::ios::out|std::ios::trunc); @@ -920,10 +947,10 @@ namespace mplot { fout << "{\"scenetrans_x\":" << this->scenetrans.x() << ", \"scenetrans_y\":" << this->scenetrans.y() << ", \"scenetrans_z\":" << this->scenetrans.z() - << ",\n \"scenerotn_w\":" << this->rotation.w - << ", \"scenerotn_x\":" << this->rotation.x - << ", \"scenerotn_y\":" << this->rotation.y - << ", \"scenerotn_z\":" << this->rotation.z << "}\n"; + << ",\n \"scenerotn_w\":" << rotn.w + << ", \"scenerotn_x\":" << rotn.x + << ", \"scenerotn_y\":" << rotn.y + << ", \"scenerotn_z\":" << rotn.z << "}\n"; fout.close(); std::cout << "Success.\n"; } else { @@ -1006,10 +1033,7 @@ namespace mplot { std::cout << "Reset to default view\n"; // Reset translation this->scenetrans = this->scenetrans_default; // FIXME - this->scenetrans_centre = this->scenetrans_default; // FIXME this->cyl_cam_pos = this->cyl_cam_pos_default; - // Reset rotation - this->rotation = this->rotation_default; // FIXME this->sceneview.setToIdentity(); this->sceneview.translate (this->scenetrans_default); @@ -1020,6 +1044,13 @@ namespace mplot { needs_render = true; } + if (_key == key::k && (action == keyaction::press || action == keyaction::repeat) && (mods & keymod::control)) { + this->options.flip (visual_options::rotateAboutSceneOrigin); + std::cout << "Rotating about " + << (this->options.test (visual_options::rotateAboutSceneOrigin) ? "scene origin" : "screen centre") + << std::endl; + } + if (this->state.test (visual_state::sceneLocked) == false && _key == key::o && (mods & keymod::control) && action == keyaction::press) { this->fov -= 2; @@ -1244,10 +1275,8 @@ namespace mplot { // yoffset does the 'in-out zooming' sm::vec scroll_move_y = { 0.0f, static_cast(yoffset) * this->scenetrans_stepsize, 0.0f, 1.0f }; this->scenetrans_delta[2] += scroll_move_y[1]; - this->scenetrans_centre[2] += scroll_move_y[1]; // Centre is always (0,0,z) // Translate scroll_move_y then add it to cyl_cam_pos here - sm::mat44 sceneview_rotn; - sceneview_rotn.rotate (this->rotation); // FIXME for cyl_cam_pos + sm::mat44 sceneview_rotn (this->sceneview.linear()); this->cyl_cam_pos += sceneview_rotn * scroll_move_y; } return true; // needs_render diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index 1baff4b0..6e1d90ca 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -428,26 +428,7 @@ namespace mplot { this->glfn->Disable (GL_CULL_FACE); mplot::gl::Util::checkError (__FILE__, __LINE__, this->glfn); - // If possible, read in scenetrans and rotation state from a special config file - try { - nlohmann::json vconf; - std::ifstream fi; - fi.open ("/tmp/Visual.json", std::ios::in); - fi >> vconf; - this->scenetrans[0] = vconf.contains("scenetrans_x") ? vconf["scenetrans_x"].get() : this->scenetrans[0]; - this->scenetrans[1] = vconf.contains("scenetrans_y") ? vconf["scenetrans_y"].get() : this->scenetrans[1]; - this->scenetrans[2] = vconf.contains("scenetrans_z") ? vconf["scenetrans_z"].get() : this->scenetrans[2]; - // Place the same numbers into scenetrans_default, too. - this->scenetrans_default[0] = this->scenetrans[0]; - this->scenetrans_default[1] = this->scenetrans[1]; - this->scenetrans_default[2] = this->scenetrans[2]; - this->rotation.w = vconf.contains("scenerotn_w") ? vconf["scenerotn_w"].get() : this->rotation.w; - this->rotation.x = vconf.contains("scenerotn_x") ? vconf["scenerotn_x"].get() : this->rotation.x; - this->rotation.y = vconf.contains("scenerotn_y") ? vconf["scenerotn_y"].get() : this->rotation.y; - this->rotation.z = vconf.contains("scenerotn_z") ? vconf["scenerotn_z"].get() : this->rotation.z; - } catch (...) { - // No problem if we couldn't read /tmp/Visual.json - } + this->read_scenetrans_from_json(); // Use coordArrowsOffset to set the location of the CoordArrows *scene* this->coordArrows = std::make_unique>(); diff --git a/mplot/VisualOwnableNoMX.h b/mplot/VisualOwnableNoMX.h index 6e45ffb6..6288a027 100644 --- a/mplot/VisualOwnableNoMX.h +++ b/mplot/VisualOwnableNoMX.h @@ -388,26 +388,7 @@ namespace mplot { glDisable (GL_CULL_FACE); mplot::gl::Util::checkError (__FILE__, __LINE__); - // If possible, read in scenetrans and rotation state from a special config file - try { - nlohmann::json vconf; - std::ifstream fi; - fi.open ("/tmp/Visual.json", std::ios::in); - fi >> vconf; - this->scenetrans[0] = vconf.contains("scenetrans_x") ? vconf["scenetrans_x"].get() : this->scenetrans[0]; - this->scenetrans[1] = vconf.contains("scenetrans_y") ? vconf["scenetrans_y"].get() : this->scenetrans[1]; - this->scenetrans[2] = vconf.contains("scenetrans_z") ? vconf["scenetrans_z"].get() : this->scenetrans[2]; - // Place the same numbers into scenetrans_default, too. - this->scenetrans_default[0] = this->scenetrans[0]; - this->scenetrans_default[1] = this->scenetrans[1]; - this->scenetrans_default[2] = this->scenetrans[2]; - this->rotation.w = vconf.contains("scenerotn_w") ? vconf["scenerotn_w"].get() : this->rotation.w; - this->rotation.x = vconf.contains("scenerotn_x") ? vconf["scenerotn_x"].get() : this->rotation.x; - this->rotation.y = vconf.contains("scenerotn_y") ? vconf["scenerotn_y"].get() : this->rotation.y; - this->rotation.z = vconf.contains("scenerotn_z") ? vconf["scenerotn_z"].get() : this->rotation.z; - } catch (...) { - // No problem if we couldn't read /tmp/Visual.json - } + this->read_scenetrans_from_json(); // Use coordArrowsOffset to set the location of the CoordArrows *scene* this->coordArrows = std::make_unique>(); From 91c5a90cb637c44b47711078dae1ad43e79faa8e Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 21 Jul 2025 15:29:12 +0100 Subject: [PATCH 13/25] Fix rod_pan --- mplot/VisualBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 0337bcf8..e7a019a4 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -1095,7 +1095,7 @@ namespace mplot { { this->rotationAxis = axis; sm::quaternion rotnQuat (this->rotationAxis, -angle); - this->rotation.postmultiply (rotnQuat); + this->sceneview.prerotate (rotnQuat); } virtual bool cursor_position_callback (double x, double y) From 47a569d8f16728be174e7547df0e94eb414b282e Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 21 Jul 2025 15:35:53 +0100 Subject: [PATCH 14/25] Unify NoMX/MX --- mplot/VisualOwnableNoMX.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mplot/VisualOwnableNoMX.h b/mplot/VisualOwnableNoMX.h index 6288a027..b82c82b1 100644 --- a/mplot/VisualOwnableNoMX.h +++ b/mplot/VisualOwnableNoMX.h @@ -197,7 +197,7 @@ namespace mplot { } // Calculate model view transformation - transforming from "model space" to "worldspace". - sm::mat44 sceneview = this->computeSceneview(); + this->computeSceneview(); // Clear color buffer and **also depth buffer** glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -239,14 +239,14 @@ namespace mplot { this->setContext(); // ...so re-acquire if we're managing it if (this->options.test (visual_options::coordArrowsInScene) == true) { - this->coordArrows->setSceneMatrix (sceneview); + this->coordArrows->setSceneMatrix (this->sceneview); } else { this->positionCoordArrows(); } this->coordArrows->render(); } - sm::mat44 scenetransonly; + sm::mat44 scenetransonly; // Maybe generate from sceneview.translation()? scenetransonly.translate (this->scenetrans); auto vmi = this->vm.begin(); @@ -255,7 +255,7 @@ namespace mplot { // It's a two-d thing. Now what? (*vmi)->setSceneMatrix (scenetransonly); } else { - (*vmi)->setSceneMatrix (sceneview); + (*vmi)->setSceneMatrix (this->sceneview); } (*vmi)->render(); ++vmi; From 14d3c35030de33596232b389c209151b033b05cb Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 21 Jul 2025 16:23:19 +0100 Subject: [PATCH 15/25] cancels scrolling --- mplot/VisualBase.h | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index e7a019a4..ed6cf327 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -57,7 +57,9 @@ namespace mplot { //! When true, rotations about the third axis are possible. rotateModMode, //! When true, cursor movements induce translation of scene - translateMode + translateMode, + //! We are scrolling (and so we will need to zero scenetrans_delta after enacting the change) + scrolling }; //! Boolean options - similar to state, but more likely to be modified by client code @@ -672,13 +674,11 @@ namespace mplot { // Compute the sceneview matrix, always rotating about scene origin sm::mat44 computeSceneview_about_scene_origin() { - std::cout << "savedSceneview:\n" << this->savedSceneview << std::endl; sm::mat44 sv_tr; sm::mat44 sv_rot; if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { sv_tr.translate (this->scenetrans_delta); // A rotation delta in world frame about the 'screen centre' - std::cout << "savedSceneview.translation: " << this->savedSceneview.translation() << std::endl; sv_rot.pretranslate (this->savedSceneview.translation()); sv_rot.rotate (this->rotation_delta); sv_rot.pretranslate (-this->savedSceneview.translation()); @@ -700,7 +700,7 @@ namespace mplot { if (this->ptype == perspective_type::orthographic || this->ptype == perspective_type::perspective) { sv_tr.translate (this->scenetrans_delta); // A rotation delta in world frame about the 'screen centre' - sm::vec screencentre = {0, 0, this->savedSceneview.translation().z()}; + sm::vec screencentre = { 0.0f, 0.0f, this->savedSceneview.translation().z() + this->scenetrans_delta.z() }; sv_rot.pretranslate (-screencentre); sv_rot.rotate (this->rotation_delta); sv_rot.translate (screencentre); @@ -717,6 +717,14 @@ namespace mplot { sm::mat44 computeSceneview() { if (std::abs(this->scenetrans_delta.sum()) > 0.0f || this->rotation_delta.is_zero_rotation() == false) { +#if 0 + if (std::abs(this->scenetrans_delta.sum()) > 0.0f) { + std::cout << "|scenetrans_delta.sum()| > 0" << std::endl; + } + if (this->rotation_delta.is_zero_rotation() == false) { + std::cout << "rotation_delta.is_zero_rotation() == false" << std::endl; + } +#endif // Calculate model view transformation - transforming from "model space" to "worldspace". if (this->options.test (visual_options::rotateAboutSceneOrigin) == false) { this->sceneview = this->computeSceneview_about_screen_centre(); @@ -724,6 +732,12 @@ namespace mplot { this->sceneview = this->computeSceneview_about_scene_origin(); } } + + if (this->state.test (visual_state::scrolling)) { + this->scenetrans_delta.zero(); + this->state.reset (visual_state::scrolling); + } + return this->sceneview; } @@ -1207,15 +1221,12 @@ namespace mplot { // Record the position and rotation at which the button was pressed if (action == keyaction::press) { // Button down - //std::cout << "mouse keyaction::press\n"; this->mousePressPosition = this->cursorpos; this->savedSceneview = this->sceneview; this->scenetrans_delta.zero(); this->rotation_delta.reset(); - } else if (action == keyaction::repeat) { - //std::cout << "mouse keyaction::repeat\n"; } else if (action == keyaction::release) { - //std::cout << "mouse keyaction::release\n"; + // On mouse button release, zero the deltas: this->scenetrans_delta.zero(); this->rotation_delta.reset(); } @@ -1257,6 +1268,11 @@ namespace mplot { if (this->state.test (visual_state::sceneLocked)) { return false; } + this->savedSceneview = this->sceneview; + this->scenetrans_delta.zero(); + this->rotation_delta.reset(); + this->state.set (visual_state::scrolling); + if (this->ptype == perspective_type::orthographic) { // In orthographic, the wheel should scale ortho_lb and ortho_rt sm::vec _lb = this->ortho_lb + (yoffset * this->scenetrans_stepsize); From a6813346378e49b70b452a9217e61aa0a99dc1e7 Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 21 Jul 2025 16:29:21 +0100 Subject: [PATCH 16/25] Sorts translation AND rotation in rotate about origin mode --- mplot/VisualBase.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index ed6cf327..dc84ce82 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -687,7 +687,7 @@ namespace mplot { sv_rot.rotate (this->rotation_delta); } - sm::mat44 _sceneview = this->savedSceneview * (sv_tr * sv_rot); + sm::mat44 _sceneview = sv_tr * this->savedSceneview * sv_rot; return _sceneview; } @@ -709,7 +709,7 @@ namespace mplot { sv_rot.rotate (this->rotation_delta); } - sm::mat44 _sceneview = (sv_tr * sv_rot) * this->savedSceneview; + sm::mat44 _sceneview = sv_tr * sv_rot * this->savedSceneview; return _sceneview; } From 75c41cab653b8aedbd6d8a070dbef70451986278 Mon Sep 17 00:00:00 2001 From: Seb James Date: Mon, 21 Jul 2025 16:42:42 +0100 Subject: [PATCH 17/25] Pulls in latest maths --- maths | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths b/maths index f43139c7..76c3cbdf 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit f43139c72089562cfc38c5c8f9f31563e6356c36 +Subproject commit 76c3cbdff535340c65fcade138e16e943a7b196f From c035996e245643f66e342971ce04fcca0164d7ca Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 22 Jul 2025 09:46:40 +0100 Subject: [PATCH 18/25] Move to maths/main --- maths | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths b/maths index 76c3cbdf..1274c1e6 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 76c3cbdff535340c65fcade138e16e943a7b196f +Subproject commit 1274c1e64e1c323a42f6cefe1d5518be81b42317 From 9184974a68087f1d4be616625e0fcbec859a69a3 Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 22 Jul 2025 09:51:56 +0100 Subject: [PATCH 19/25] No need for these functions to return a mat44 --- mplot/VisualBase.h | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index dc84ce82..47f0b63c 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -672,7 +672,7 @@ namespace mplot { } // Compute the sceneview matrix, always rotating about scene origin - sm::mat44 computeSceneview_about_scene_origin() + void computeSceneview_about_scene_origin() { sm::mat44 sv_tr; sm::mat44 sv_rot; @@ -687,13 +687,11 @@ namespace mplot { sv_rot.rotate (this->rotation_delta); } - sm::mat44 _sceneview = sv_tr * this->savedSceneview * sv_rot; - - return _sceneview; + this->sceneview = sv_tr * this->savedSceneview * sv_rot; } // Rotate about screen centre - sm::mat44 computeSceneview_about_screen_centre() + void computeSceneview_about_screen_centre() { sm::mat44 sv_tr; sm::mat44 sv_rot; @@ -709,36 +707,24 @@ namespace mplot { sv_rot.rotate (this->rotation_delta); } - sm::mat44 _sceneview = sv_tr * sv_rot * this->savedSceneview; - - return _sceneview; + this->sceneview = sv_tr * sv_rot * this->savedSceneview; } - sm::mat44 computeSceneview() + void computeSceneview() { if (std::abs(this->scenetrans_delta.sum()) > 0.0f || this->rotation_delta.is_zero_rotation() == false) { -#if 0 - if (std::abs(this->scenetrans_delta.sum()) > 0.0f) { - std::cout << "|scenetrans_delta.sum()| > 0" << std::endl; - } - if (this->rotation_delta.is_zero_rotation() == false) { - std::cout << "rotation_delta.is_zero_rotation() == false" << std::endl; - } -#endif // Calculate model view transformation - transforming from "model space" to "worldspace". if (this->options.test (visual_options::rotateAboutSceneOrigin) == false) { - this->sceneview = this->computeSceneview_about_screen_centre(); + this->computeSceneview_about_screen_centre(); } else { - this->sceneview = this->computeSceneview_about_scene_origin(); + this->computeSceneview_about_scene_origin(); } - } + } // else don't change sceneview if (this->state.test (visual_state::scrolling)) { this->scenetrans_delta.zero(); this->state.reset (visual_state::scrolling); } - - return this->sceneview; } //! A vector of pointers to all the mplot::VisualModels (HexGridVisual, From 798192cc7bfba8eb7d9c98bc8eecb6cf80fe499c Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 22 Jul 2025 12:00:52 +0100 Subject: [PATCH 20/25] Sort 2D movements --- mplot/VisualBase.h | 18 +++++++++++++++++- mplot/VisualOwnableMX.h | 7 ++----- mplot/VisualOwnableNoMX.h | 5 +---- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 47f0b63c..517708e6 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -129,7 +129,10 @@ namespace mplot { * such as a QWidget. We have to wait on calling init functions until an OpenGL * environment is guaranteed to exist. */ - VisualBase() { this->sceneview.translate (this->scenetrans_default); } + VisualBase() { + this->sceneview.translate (this->scenetrans_default); + this->sceneview_tr.translate (this->scenetrans_default); + } /*! * Construct a new visualiser. The rule is 1 window to one Visual object. So, this creates a @@ -141,6 +144,7 @@ namespace mplot { , title(_title) { this->sceneview.translate (this->scenetrans_default); + this->sceneview_tr.translate (this->scenetrans_default); this->options.set (visual_options::versionStdout, _version_stdout); this->init_gl(); // abstract } @@ -688,6 +692,7 @@ namespace mplot { } this->sceneview = sv_tr * this->savedSceneview * sv_rot; + this->sceneview_tr = sv_tr * this->savedSceneview_tr; } // Rotate about screen centre @@ -708,6 +713,7 @@ namespace mplot { } this->sceneview = sv_tr * sv_rot * this->savedSceneview; + this->sceneview_tr = sv_tr * this->savedSceneview_tr; } void computeSceneview() @@ -836,9 +842,15 @@ namespace mplot { //! movements. Initialized in VisualOwnable(No)MX constructor. sm::mat44 sceneview; + //! The non-rotating sceneview matrix, updated only from mouse translations (avoiding rotations) + sm::mat44 sceneview_tr; + //! Saved sceneview at mouse button down sm::mat44 savedSceneview; + //! Saved sceneview_tr + sm::mat44 savedSceneview_tr; + public: /* @@ -1036,8 +1048,10 @@ namespace mplot { this->cyl_cam_pos = this->cyl_cam_pos_default; this->sceneview.setToIdentity(); + this->sceneview_tr.setToIdentity(); this->sceneview.translate (this->scenetrans_default); this->sceneview.prerotate (this->rotation_default); + this->sceneview_tr.translate (this->scenetrans_default); this->scenetrans_delta.zero(); this->rotation_delta.reset(); @@ -1209,6 +1223,7 @@ namespace mplot { if (action == keyaction::press) { // Button down this->mousePressPosition = this->cursorpos; this->savedSceneview = this->sceneview; + this->savedSceneview_tr = this->sceneview_tr; this->scenetrans_delta.zero(); this->rotation_delta.reset(); } else if (action == keyaction::release) { @@ -1255,6 +1270,7 @@ namespace mplot { if (this->state.test (visual_state::sceneLocked)) { return false; } this->savedSceneview = this->sceneview; + this->savedSceneview_tr = this->sceneview_tr; this->scenetrans_delta.zero(); this->rotation_delta.reset(); this->state.set (visual_state::scrolling); diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index 6e1d90ca..25a5aa7a 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -252,14 +252,11 @@ namespace mplot { this->coordArrows->render(); } - sm::mat44 scenetransonly; // Maybe generate from sceneview.translation()? - scenetransonly.translate (this->scenetrans); - auto vmi = this->vm.begin(); while (vmi != this->vm.end()) { if ((*vmi)->twodimensional == true) { - // It's a two-d thing. Now what? - (*vmi)->setSceneMatrix (scenetransonly); + // It's a two-d thing. Use the companion 'scene trans only' matrix, which avoids any rotations + (*vmi)->setSceneMatrix (this->sceneview_tr); } else { (*vmi)->setSceneMatrix (this->sceneview); } diff --git a/mplot/VisualOwnableNoMX.h b/mplot/VisualOwnableNoMX.h index b82c82b1..edb85eaa 100644 --- a/mplot/VisualOwnableNoMX.h +++ b/mplot/VisualOwnableNoMX.h @@ -246,14 +246,11 @@ namespace mplot { this->coordArrows->render(); } - sm::mat44 scenetransonly; // Maybe generate from sceneview.translation()? - scenetransonly.translate (this->scenetrans); - auto vmi = this->vm.begin(); while (vmi != this->vm.end()) { if ((*vmi)->twodimensional == true) { // It's a two-d thing. Now what? - (*vmi)->setSceneMatrix (scenetransonly); + (*vmi)->setSceneMatrix (this->sceneview_tr); } else { (*vmi)->setSceneMatrix (this->sceneview); } From b21be78d7215c4299625baae866c6694c6c8e5fc Mon Sep 17 00:00:00 2001 From: Seb James Date: Tue, 22 Jul 2025 13:15:28 +0100 Subject: [PATCH 21/25] Updated maths --- maths | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths b/maths index 1274c1e6..970f816e 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 1274c1e64e1c323a42f6cefe1d5518be81b42317 +Subproject commit 970f816e48138dfa094850b95226cb490b9a6025 From d7aebb47e7113d8a3b888fca66c3b0b9f4fd5de3 Mon Sep 17 00:00:00 2001 From: Seb James Date: Wed, 23 Jul 2025 10:18:46 +0100 Subject: [PATCH 22/25] Update maths --- maths | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maths b/maths index 970f816e..bbd3a81c 160000 --- a/maths +++ b/maths @@ -1 +1 @@ -Subproject commit 970f816e48138dfa094850b95226cb490b9a6025 +Subproject commit bbd3a81c66d23057bc710c03aeed76aa3cb14a52 From 5d2925ea2bb13837ed4698564bc175cf6bb22462 Mon Sep 17 00:00:00 2001 From: Seb James Date: Wed, 30 Jul 2025 15:15:00 +0100 Subject: [PATCH 23/25] Correct setting of sceneview and sceneview_tr for initial scene setup --- mplot/VisualBase.h | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 517708e6..7d65714e 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -437,27 +437,33 @@ namespace mplot { //! Set a black background colour for the Visual scene void backgroundBlack() { this->bgcolour = { 0.0f, 0.0f, 0.0f, 0.0f }; } + //! Set sceneview and sceneview_tr back to scenetrans_default + void reset_sceneviews_to_scenetrans_default() + { + this->sceneview.setToIdentity(); + this->sceneview.translate (this->scenetrans_default); + this->sceneview_tr.setToIdentity(); + this->sceneview_tr.translate (this->scenetrans_default); + } + //! Set the scene's x and y values at the same time. void setSceneTransXY (const float _x, const float _y) { this->scenetrans_default[0] = _x; this->scenetrans_default[1] = _y; - this->sceneview.setToIdentity(); - this->sceneview.translate (this->scenetrans_default); + this->reset_sceneviews_to_scenetrans_default(); } //! Set the scene's y value. Use this to shift your scene objects left or right void setSceneTransX (const float _x) { this->scenetrans_default[0] = _x; - this->sceneview.setToIdentity(); - this->sceneview.translate (this->scenetrans_default); + this->reset_sceneviews_to_scenetrans_default(); } //! Set the scene's y value. Use this to shift your scene objects up and down void setSceneTransY (const float _y) { this->scenetrans_default[1] = _y; - this->sceneview.setToIdentity(); - this->sceneview.translate (this->scenetrans_default); + this->reset_sceneviews_to_scenetrans_default(); } //! Set the scene's z value. Use this to bring the 'camera' closer to your scene //! objects (that is, your mplot::VisualModel objects). @@ -467,8 +473,7 @@ namespace mplot { std::cerr << "WARNING setSceneTransZ(): Normally, the default z value is negative.\n"; } this->scenetrans_default[2] = _z; - this->sceneview.setToIdentity(); - this->sceneview.translate (this->scenetrans_default); + this->reset_sceneviews_to_scenetrans_default(); } void setSceneTrans (float _x, float _y, float _z) { @@ -479,8 +484,7 @@ namespace mplot { this->scenetrans_default[0] = _x; this->scenetrans_default[1] = _y; this->scenetrans_default[2] = _z; - this->sceneview.setToIdentity(); - this->sceneview.translate (this->scenetrans_default); + this->reset_sceneviews_to_scenetrans_default(); } void setSceneTrans (const sm::vec& _xyz) { @@ -488,8 +492,7 @@ namespace mplot { std::cerr << "WARNING setSceneTrans(vec<>&): Normally, the default z value is negative.\n"; } this->scenetrans_default = _xyz; - this->sceneview.setToIdentity(); - this->sceneview.translate (this->scenetrans_default); + this->reset_sceneviews_to_scenetrans_default(); } void setSceneRotation (const sm::quaternion& _rotn) From b75287158ff88aa50dc1a78f9d1047a8c94f8bc7 Mon Sep 17 00:00:00 2001 From: Seb James Date: Wed, 30 Jul 2025 15:38:56 +0100 Subject: [PATCH 24/25] Lose scenetrans member attribute --- mplot/VisualBase.h | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 7d65714e..718a2024 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -331,7 +331,7 @@ namespace mplot { // Add the depth at which the object lies. Use forward projection to determine the // correct z coordinate for the inverse projection. This assumes only one object. - sm::vec point = { 0.0f, 0.0f, this->scenetrans.z(), 1.0f }; + sm::vec point = { 0.0f, 0.0f, this->sceneview[14], 1.0f }; // sceneview[14] is 'scenetrans.z' sm::vec pp = this->projection * point; float coord_z = pp[2]/pp[3]; // divide by pp[3] is divide by/normalise by 'w'. @@ -766,6 +766,8 @@ namespace mplot { this->sceneview.setToIdentity(); this->sceneview.translate (this->scenetrans_default); this->sceneview.prerotate (this->rotation_default); + this->sceneview_tr.setToIdentity(); + this->sceneview_tr.translate (this->scenetrans_default); this->scenetrans_delta.zero(); this->rotation_delta.reset(); @@ -810,14 +812,11 @@ namespace mplot { //! The default z position for VisualModels should be 'away from the screen' (negative) so we can see them! constexpr static float zDefault = -5.0f; - //! Holds the translation coordinates for the current location of the entire scene - sm::vec scenetrans = { 0.0f, 0.0f, zDefault }; - //! A delta scene translations sm::vec scenetrans_delta = { 0.0f, 0.0f, 0.0f }; - //! Default for scenetrans. This is a scene position that can be reverted to, to - //! 'reset the view'. This is copied into scenetrans when user presses Ctrl-a. + //! Default for scene translation. This is a scene position that can be reverted to, to + //! 'reset the view'. This is copied into sceneview when user presses Ctrl-a. sm::vec scenetrans_default = { 0.0f, 0.0f, zDefault }; //! The world depth at which text objects should be rendered @@ -947,10 +946,11 @@ namespace mplot { if (_key == key::z && (mods & keymod::control) && action == keyaction::press) { sm::quaternion rotn = this->sceneview.rotation(); + sm::vec scenetrans = this->sceneview.translation(); std::cout << "Scenetrans setup code:\n v.setSceneTrans (sm::vec{ float{" - << this->scenetrans.x() << "}, float{" - << this->scenetrans.y() << "}, float{" - << this->scenetrans.z() + << scenetrans.x() << "}, float{" + << scenetrans.y() << "}, float{" + << scenetrans.z() << "} });" << "\n v.setSceneRotation (sm::quaternion{ float{" << rotn.w << "}, float{" << rotn.x << "}, float{" @@ -959,9 +959,9 @@ namespace mplot { std::ofstream fout; fout.open ("/tmp/Visual.json", std::ios::out|std::ios::trunc); if (fout.is_open()) { - fout << "{\"scenetrans_x\":" << this->scenetrans.x() - << ", \"scenetrans_y\":" << this->scenetrans.y() - << ", \"scenetrans_z\":" << this->scenetrans.z() + fout << "{\"scenetrans_x\":" << scenetrans.x() + << ", \"scenetrans_y\":" << scenetrans.y() + << ", \"scenetrans_z\":" << scenetrans.z() << ",\n \"scenerotn_w\":" << rotn.w << ", \"scenerotn_x\":" << rotn.x << ", \"scenerotn_y\":" << rotn.y @@ -1047,7 +1047,6 @@ namespace mplot { && _key == key::a && (mods & keymod::control) && action == keyaction::press) { std::cout << "Reset to default view\n"; // Reset translation - this->scenetrans = this->scenetrans_default; // FIXME this->cyl_cam_pos = this->cyl_cam_pos_default; this->sceneview.setToIdentity(); From 0dae98b1c21812e7c412dbfd433cb2b82fbca242 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 31 Jul 2025 16:41:57 +0100 Subject: [PATCH 25/25] Keep existing rotate about origin behaviour before merge --- mplot/VisualBase.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 718a2024..6afde789 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -374,6 +374,8 @@ namespace mplot { sm::flags _options; // Only with ImGui do we manually swap buffers, so this is true by default: _options.set (visual_options::renderSwapsBuffers); + // For now, default to rotating about scene origin, as we ever did (Ctrl-k to change) + _options.set (visual_options::rotateAboutSceneOrigin); return _options; }