From dec07ff3a5ed60e90ae51df29a0860e1cde15bae Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Wed, 27 Nov 2024 19:43:41 +0100 Subject: [PATCH 1/2] BugFix Undo history Force store of first status, and limit number of undo steps (even if huge number of 1000). --- src/ActionManager.cpp | 29 ++++++++++++++++++++--------- src/ActionManager.h | 6 +++++- src/Mixer.cpp | 2 +- src/UserInterfaceManager.cpp | 9 +++++---- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/ActionManager.cpp b/src/ActionManager.cpp index 058b809..c1aede6 100644 --- a/src/ActionManager.cpp +++ b/src/ActionManager.cpp @@ -43,24 +43,25 @@ using namespace tinyxml2; -Action::Action(): history_step_(0), history_max_step_(0), +Action::Action(): history_step_(0), history_max_step_(0), history_min_step_(0), snapshot_id_(0), snapshot_node_(nullptr), interpolator_(nullptr), interpolator_node_(nullptr) { } -void Action::init() +void Action::init(const std::string &label) { // clean the history history_doc_.Clear(); history_step_ = 0; - history_max_step_ = 0; + history_min_step_ = 1; + history_max_step_ = 1; // reset snapshot snapshot_id_ = 0; snapshot_node_ = nullptr; - store("Session start"); + store(label); } // must be called in a thread running in parrallel of the rendering @@ -131,8 +132,12 @@ void captureMixerSession(Session *se, std::string node, std::string label, tinyx void Action::storeSession(Session *se, std::string label) { - // - if (!Action::manager().history_access_.try_lock()) + // force lock for creation of first step + if (Action::manager().history_step_ < 1) + Action::manager().history_access_.lock(); + // try lock for storing history in normal case + // (i.e. priority to realtime performance over storing history) + else if (!Action::manager().history_access_.try_lock()) return; // incremental naming of history nodes @@ -146,6 +151,14 @@ void Action::storeSession(Session *se, std::string label) } Action::manager().history_max_step_ = Action::manager().history_step_; + // Ensure a maximum amount of stored steps (even very big, just to ensure memory limit) + if (Action::manager().history_max_step_ - Action::manager().history_min_step_ > MAX_COUNT_HISTORY) { + XMLElement *node = Action::manager().history_doc_.FirstChildElement( HISTORY_NODE(Action::manager().history_min_step_+1).c_str() ); + if ( node ) + Action::manager().history_doc_.DeleteChild(node); + Action::manager().history_min_step_++; + } + // capture current session captureMixerSession(se, HISTORY_NODE(Action::manager().history_step_), @@ -158,9 +171,7 @@ void Action::storeSession(Session *se, std::string label) void Action::store(const std::string &label) { - // TODO: set a maximum amount of stored steps? (even very big, just to ensure memory limit) - - // ignore if locked or if no label is given + // ignore if no label is given if (label.empty()) return; diff --git a/src/ActionManager.h b/src/ActionManager.h index 5e334d5..f9895df 100644 --- a/src/ActionManager.h +++ b/src/ActionManager.h @@ -7,6 +7,8 @@ #include +#define MAX_COUNT_HISTORY 1000 + class Session; class Interpolator; class FrameBufferImage; @@ -26,7 +28,7 @@ public: static Action _instance; return _instance; } - void init (); + void init (const std::string &label); // Undo History void store (const std::string &label); @@ -36,6 +38,7 @@ public: inline uint current () const { return history_step_; } inline uint max () const { return history_max_step_; } + inline uint min () const { return history_min_step_; } std::string label (uint s) const; std::string shortlabel (uint s) const; FrameBufferImage *thumbnail (uint s) const; @@ -66,6 +69,7 @@ private: tinyxml2::XMLDocument history_doc_; uint history_step_; uint history_max_step_; + uint history_min_step_; std::mutex history_access_; static void storeSession(Session *se, std::string label); void restore(uint target); diff --git a/src/Mixer.cpp b/src/Mixer.cpp index 310ae16..ee44b7a 100644 --- a/src/Mixer.cpp +++ b/src/Mixer.cpp @@ -1555,7 +1555,7 @@ void Mixer::swap() } // reset History manager - Action::manager().init(); + Action::manager().init("Session start"); // notification if (session_->filename().empty()) diff --git a/src/UserInterfaceManager.cpp b/src/UserInterfaceManager.cpp index 37880c6..17454f8 100644 --- a/src/UserInterfaceManager.cpp +++ b/src/UserInterfaceManager.cpp @@ -1064,7 +1064,7 @@ void UserInterface::showMenuEdit() bool has_clipboard = (clipboard != nullptr && strlen(clipboard) > 0 && SessionLoader::isClipboard(clipboard)); // UNDO - if ( ImGui::MenuItem( MENU_UNDO, SHORTCUT_UNDO, false, Action::manager().current() > 1) ) + if ( ImGui::MenuItem( MENU_UNDO, SHORTCUT_UNDO, false, Action::manager().current() > Action::manager().min()) ) Action::manager().undo(); if ( ImGui::MenuItem( MENU_REDO, SHORTCUT_REDO, false, Action::manager().current() < Action::manager().max()) ) Action::manager().redo(); @@ -4898,7 +4898,7 @@ void Navigator::RenderMainPannelSession() static bool _tooltip = 0; ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y -ImGui::GetFrameHeight() )); - if ( Action::manager().current() > 1 ) { + if ( Action::manager().current() > Action::manager().min() ) { if ( ImGuiToolkit::IconButton( ICON_FA_UNDO, "Undo" ) ) Action::manager().undo(); } else @@ -4920,7 +4920,8 @@ void Navigator::RenderMainPannelSession() int count_over = 0; ImVec2 size = ImVec2( ImGui::GetContentRegionAvailWidth(), ImGui::GetTextLineHeight() ); - for (uint i = Action::manager().max(); i > 0; --i) { + for (uint i = Action::manager().max(); + i >= Action::manager().min(); --i) { if (ImGui::Selectable( Action::manager().shortlabel(i).c_str(), i == Action::manager().current(), ImGuiSelectableFlags_AllowDoubleClick, size )) { // go to on double clic @@ -4978,7 +4979,7 @@ void Navigator::RenderMainPannelSession() if ( Action::manager().max() > 1 ) { ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_top.y )); if (ImGuiToolkit::IconButton( 12, 14, "Clear history")) - Action::manager().init(); + Action::manager().init("Reset"); } ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, pos_bot.y - 2.f * ImGui::GetFrameHeightWithSpacing())); From 7c55bab17b319bb6fdcd595491fc2e08c29f70cd Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Wed, 27 Nov 2024 23:17:26 +0100 Subject: [PATCH 2/2] Change OSC Corner coordinate to be in Image reference frame --- src/ControlManager.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/ControlManager.cpp b/src/ControlManager.cpp index 99cb9fe..346997f 100644 --- a/src/ControlManager.cpp +++ b/src/ControlManager.cpp @@ -749,7 +749,11 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut arguments >> t >> osc::EndMessage; target->call( new SetGeometry( &transform, t), true ); } - /// e.g. '/vimix/current/corner ffffffff 0 0 0 0 0 0 0 0' + /// e.g. '/vimix/current/corner ffffffff -1 -1 -1 +1 +1 -1 +1 +1' + /// 1. Lower left (-1 -1) + /// 2. Upper left (-1 +1) + /// 3. Lower right (+1 -1) + /// 4. Upper right (+1 +1) else if (attribute.compare(OSC_SOURCE_CORNER) == 0) { // read 8 float values float corners[8] = {0.f}; @@ -764,14 +768,14 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut // convert to data_ matrix format Group transform; transform.copyTransform(target->group(View::GEOMETRY)); - transform.data_[0].x = corners[0]; - transform.data_[0].y = corners[1]; - transform.data_[1].x = corners[2]; - transform.data_[1].y = corners[3]; - transform.data_[2].x = corners[4]; - transform.data_[2].y = corners[5]; - transform.data_[3].x = corners[6]; - transform.data_[3].y = corners[7]; + transform.data_[0].x = corners[0] + 1.f; + transform.data_[0].y = corners[1] + 1.f; + transform.data_[1].x = corners[2] + 1.f; + transform.data_[1].y = corners[3] - 1.f; + transform.data_[2].x = corners[4] - 1.f; + transform.data_[2].y = corners[5] + 1.f; + transform.data_[3].x = corners[6] - 1.f; + transform.data_[3].y = corners[7] - 1.f; // duration argument float t = 0.f; if (arguments.Eos())