From 10f9c1b329d43157439df474a355584c61f98003 Mon Sep 17 00:00:00 2001 From: brunoherbelin Date: Wed, 10 Mar 2021 23:38:09 +0100 Subject: [PATCH] Work in progress Implementation of mixing group link and unlink methods, integration in MixingView, update groups on source change, undo-redo improved. --- ActionManager.cpp | 5 +- Mixer.cpp | 4 +- MixingGroup.cpp | 38 +++++++++++- MixingGroup.h | 9 +-- MixingView.cpp | 34 ++++++++--- Session.cpp | 147 ++++++++++++++++++++++++++++++--------------- Session.h | 18 ++++-- SessionCreator.cpp | 2 +- Source.cpp | 7 +++ Source.h | 2 +- 10 files changed, 190 insertions(+), 76 deletions(-) diff --git a/ActionManager.cpp b/ActionManager.cpp index 8a304dc..14687aa 100644 --- a/ActionManager.cpp +++ b/ActionManager.cpp @@ -237,11 +237,12 @@ void Action::restore(uint target, uint64_t id) // apply all changes creating or modifying groups in the session // (after this, new groups are created and existing groups are adjusted) for (auto group_loader_it = loadergroups.begin(); group_loader_it != loadergroups.end(); group_loader_it++) { - se->updateMixingGroup( *group_loader_it ); + se->link( *group_loader_it ); } // Get the updated list of mixing groups in the session std::list< SourceList > sessiongroups = se->getMixingGroups(); + // the remaining case is if session has groups that are not in the loaded xml // (that should therefore be deleted) if ( sessiongroups.size() > loadergroups.size() ) @@ -270,7 +271,7 @@ void Action::restore(uint target, uint64_t id) // the remaining groups in sessiongroups do not have an EQUAL match in the loader groups for (auto group_se_it = sessiongroups.begin(); group_se_it != sessiongroups.end(); ) { // remove that group from the session - se->removeMixingGroup( *group_se_it ); + se->unlink( *group_se_it ); group_se_it = sessiongroups.erase(group_se_it); } } diff --git a/Mixer.cpp b/Mixer.cpp index ece33a2..5d7bd6b 100644 --- a/Mixer.cpp +++ b/Mixer.cpp @@ -1042,7 +1042,7 @@ void Mixer::merge(Session *session) // import and attach session's mixing groups auto group_iter = session->beginMixingGroup(); while ( group_iter != session->endMixingGroup() ){ - session_->updateMixingGroup((*group_iter)->getCopy(), mixing_.scene.fg()); + session_->link((*group_iter)->getCopy(), mixing_.scene.fg()); group_iter = session->deleteMixingGroup(group_iter); } @@ -1124,7 +1124,7 @@ void Mixer::merge(SessionSource *source) // import and attach session's mixing groups auto group_iter = session->beginMixingGroup(); while ( group_iter != session->endMixingGroup() ){ - session_->updateMixingGroup((*group_iter)->getCopy(), mixing_.scene.fg()); + session_->link((*group_iter)->getCopy(), mixing_.scene.fg()); group_iter = session->deleteMixingGroup(group_iter); } diff --git a/MixingGroup.cpp b/MixingGroup.cpp index 6bd2972..bf99686 100644 --- a/MixingGroup.cpp +++ b/MixingGroup.cpp @@ -82,6 +82,16 @@ void MixingGroup::recenter() center_->translation_ = glm::vec3(center_pos_, 0.f); } +void MixingGroup::setAction (Action a) +{ + if (a == ACTION_UPDATE) { + if (update_action_ == ACTION_NONE) + update_action_ = ACTION_UPDATE; + } + else + update_action_ = a; +} + void MixingGroup::update (float dt) { // active if current source in the group @@ -89,7 +99,31 @@ void MixingGroup::update (float dt) setActive(currentsource != sources_.end()); // perform action - if (update_action_ != ACTION_NONE && updated_source_ != nullptr) { + if (update_action_ == ACTION_UPDATE ) { + + std::vector p = lines_->path(); + + // compute barycenter (0) + center_pos_ = glm::vec2(0.f, 0.f); + auto it = sources_.begin(); + for (; it != sources_.end(); it++){ + // update point + p[ index_points_[*it] ] = glm::vec2((*it)->group(View::MIXING)->translation_); + + // compute barycenter (1) + center_pos_ += glm::vec2((*it)->group(View::MIXING)->translation_); + } + // compute barycenter (2) + center_pos_ /= sources_.size(); + center_->translation_ = glm::vec3(center_pos_, 0.f); + + // update path + lines_->changePath(p); + + update_action_ = ACTION_NONE; + } + + else if (update_action_ != ACTION_NONE && updated_source_ != nullptr) { if (update_action_ == ACTION_GRAB_ONE ) { @@ -175,7 +209,7 @@ void MixingGroup::setActive (bool on) // overlays lines_->shader()->color.a = active_ ? 0.96f : 0.5f; - center_->visible_ = update_action_ != ACTION_NONE; + center_->visible_ = update_action_ > ACTION_UPDATE; } void MixingGroup::detach (Source *s) diff --git a/MixingGroup.h b/MixingGroup.h index 7e23e3e..a4e6b1c 100644 --- a/MixingGroup.h +++ b/MixingGroup.h @@ -22,11 +22,12 @@ public: // actions for update typedef enum { ACTION_NONE = 0, - ACTION_GRAB_ONE = 1, - ACTION_GRAB_ALL = 2, - ACTION_ROTATE_ALL = 3 + ACTION_UPDATE = 1, + ACTION_GRAB_ONE = 2, + ACTION_GRAB_ALL = 3, + ACTION_ROTATE_ALL = 4 } Action; - inline void setAction (Action a) { update_action_ = a; } + void setAction (Action a) ; inline Action action () { return update_action_; } inline void follow (Source *s) { updated_source_ = s; } diff --git a/MixingView.cpp b/MixingView.cpp index c970c24..84b0b86 100644 --- a/MixingView.cpp +++ b/MixingView.cpp @@ -127,18 +127,32 @@ void MixingView::draw() ImGui::PushStyleColor(ImGuiCol_Text, ImGuiToolkit::HighlightColor()); ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.36f, 0.36f, 0.36f, 0.44f)); - // special action of Mixing view - if (ImGui::Selectable( ICON_FA_DRAW_POLYGON " Link" )){ - // create MixingGroup - if (Mixer::manager().session()->updateMixingGroup(Mixer::selection().getCopy(), scene.fg() ) ) { - Action::manager().store(std::string("Sources linked."), Mixer::manager().session()->lastMixingGroup()->id()); - // clear selection and select one of the sources of the group - Source *cur = Mixer::selection().front(); - Mixer::manager().unsetCurrentSource(); - Mixer::selection().clear(); - Mixer::manager().setCurrentSource( cur ); + // special action of Mixing view: link or unlink + SourceList selected = Mixer::selection().getCopy(); + if ( Mixer::manager().session()->canlink( selected )) { + // the selected sources can be linked + if (ImGui::Selectable( ICON_FA_LINK " Link" )){ + // assemble a MixingGroup + if (Mixer::manager().session()->link(selected, scene.fg() ) ) { + Action::manager().store(std::string("Sources linked."), selected.front()->id()); + // clear selection and select one of the sources of the group + Source *cur = Mixer::selection().front(); + Mixer::manager().unsetCurrentSource(); + Mixer::selection().clear(); + Mixer::manager().setCurrentSource( cur ); + } } } + else { + // the selected sources cannot be linked: offer to unlink! + if (ImGui::Selectable( ICON_FA_UNLINK " Unlink" )){ + // dismantle MixingGroup(s) + if (Mixer::manager().session()->unlink(selected) ) { + Action::manager().store(std::string("Sources unlinked."), selected.front()->id()); + } + } + } + ImGui::Separator(); // manipulation of sources in Mixing view diff --git a/Session.cpp b/Session.cpp index 0d659b5..cc4d3b9 100644 --- a/Session.cpp +++ b/Session.cpp @@ -332,55 +332,83 @@ void Session::move(int current_index, int target_index) sources_.insert(to, s); } -bool Session::updateMixingGroup(SourceList sources, Group *parent) +bool Session::canlink (SourceList sources) +{ + bool canlink = false; + + // verify that all sources given are valid in the sesion + validate(sources); + + // we need at least 2 sources to make a group + if (sources.size() > 1) { + // does this interfere with existing groups? + SourceListCompare c = SOURCELIST_DISTINCT; + for (auto group_it = mixing_groups_.begin(); group_it!= mixing_groups_.end(); group_it++) { + // get the list of sources in the group + SourceList group_sources = (*group_it)->getCopy(); + c = compare( group_sources, sources ); + if ( c != SOURCELIST_DISTINCT ){ + // in case the source list containing entirely the group + // then extend the group with extra sources + if ( c == SOURCELIST_FIRST_IN_SECOND ) { + canlink = true; + } + // stop the loop + // (ignore INTERSECT and EQUAL cases) + break; + } + } + canlink |= ( c == SOURCELIST_DISTINCT ); + } + + return canlink; +} + +bool Session::link(SourceList sources, Group *parent) { // pointer to created MixingGroup MixingGroup *g = nullptr; // verify that all sources given are valid in the sesion - SourceList valid_sources; - for (auto _it = sources.begin(); _it != sources.end(); _it++) { - SourceList::iterator found = std::find(sources_.begin(), sources_.end(), *_it); - if ( found != sources_.end()) - valid_sources.push_back(*found); - } + validate(sources); - // try to create mixing group with valid sources (if it is a group of at least 2 sources) - if (valid_sources.size() > 1) { + // we need at least 2 sources to make a group + if (sources.size() > 1) { // does this interfere with existing groups? SourceListCompare c = SOURCELIST_DISTINCT; for (auto group_it = mixing_groups_.begin(); group_it!= mixing_groups_.end(); group_it++) { + // get the list of sources in the group SourceList group_sources = (*group_it)->getCopy(); c = compare( group_sources, sources ); if ( c != SOURCELIST_DISTINCT ){ - // case of source list containing entirely the group + // in case the source list containing entirely the group + // then extend the group with extra sources if ( c == SOURCELIST_FIRST_IN_SECOND ) { - // extend the group with extra sources g = *group_it; // add all sources (will ignore already linked sources) g->attach( sources ); } - // case of mixing group containing entirely the source list + // in case the mixing group contains entirely the source list + // then reduce the group to the source list else if ( c == SOURCELIST_SECOND_IN_FIRST ) { - // reduce the group from the extra sources g = *group_it; - // keep only elements of group_sources that are not in sources + // i.e. keep elements of group_sources that are not in list for (auto it = sources.begin(); it != sources.end(); it++) group_sources.remove(*it); // remove all extra sources g->detach( group_sources ); } // stop the loop + // (we ignore INTERSECT and EQUAL cases) break; } } - // the sourcelist is distinct from all existing groups - // (NB: ignore INTERSECT and EQUAL cases) + // remaining case: the sourcelist is distinct from all existing groups if ( c == SOURCELIST_DISTINCT ) { // create and add a new mixing group - g = new MixingGroup(valid_sources); + g = new MixingGroup(sources); mixing_groups_.push_back(g); } @@ -393,40 +421,52 @@ bool Session::updateMixingGroup(SourceList sources, Group *parent) return ( g != nullptr ); } -bool Session::removeMixingGroup (SourceList sources) +bool Session::unlink (SourceList sources) { bool ret = false; // verify that all sources given are valid in the sesion - SourceList valid_sources; - for (auto _it = sources.begin(); _it != sources.end(); _it++) { - SourceList::iterator found = std::find(sources_.begin(), sources_.end(), *_it); - if ( found != sources_.end()) - valid_sources.push_back(*found); - } + validate(sources); - // does this intersect with existing groups? - SourceListCompare c = SOURCELIST_DISTINCT; + // we need at least 1 sources to make a decision + if (sources.size() > 0) { - for (auto group_it = mixing_groups_.begin(); group_it!= mixing_groups_.end(); group_it++) { - SourceList group_sources = (*group_it)->getCopy(); - c = compare( group_sources, sources ); - if ( c == SOURCELIST_EQUAL ) { - delete (*group_it); - mixing_groups_.erase(group_it); - // stop the loop - ret = true; - break; - } - // case of mixing group containing entirely the source list - else if ( c == SOURCELIST_INTERSECT ) { - group_sources = intersect( group_sources, sources); - // remove all extra sources - (*group_it)->detach( group_sources ); - // stop the loop - ret = true; - break; + // does this list intersect with existing groups? + SourceListCompare c = SOURCELIST_DISTINCT; + for (auto group_it = mixing_groups_.begin(); group_it!= mixing_groups_.end(); group_it++) { + // get the list of sources in the group + SourceList group_sources = (*group_it)->getCopy(); + c = compare( group_sources, sources ); + // in case the group source is the same or inside the list, + // then delete the group entirely + if ( c == SOURCELIST_EQUAL || c == SOURCELIST_FIRST_IN_SECOND) { + delete (*group_it); + mixing_groups_.erase(group_it); + // did something + ret = true; + // stop the loop + break; + } + // in case of the source list is inside the group, + // then remove the listed sources from the group + else if ( c == SOURCELIST_SECOND_IN_FIRST ) { + // remove all extra sources + (*group_it)->detach( sources ); + // did something + ret = true; + } + // in case of the group source intersects with the list, + // then remove the listed sources from the group + else if ( c == SOURCELIST_INTERSECT ) { + group_sources = intersect( group_sources, sources); + // remove all extra sources + (*group_it)->detach( group_sources ); + // did something + ret = true; + } + // (NB: we ignore DISTINCT case) } + } return ret; @@ -442,12 +482,6 @@ std::list Session::getMixingGroups () const return lmg; } -MixingGroup *Session::lastMixingGroup () -{ - if (mixing_groups_.empty()) - return nullptr; - return mixing_groups_.back(); -} std::list::iterator Session::deleteMixingGroup (std::list::iterator g) { @@ -479,6 +513,19 @@ void Session::unlock() } +void Session::validate (SourceList &sources) +{ + // verify that all sources given are valid in the sesion + // and remove the invalid sources + for (auto _it = sources.begin(); _it != sources.end(); ) { + SourceList::iterator found = std::find(sources_.begin(), sources_.end(), *_it); + if ( found == sources_.end() ) + _it = sources.erase(_it); + else + _it++; + } +} + Session *Session::load(const std::string& filename, uint recursion) { // create session diff --git a/Session.h b/Session.h index 392cd7b..256dc3d 100644 --- a/Session.h +++ b/Session.h @@ -76,11 +76,19 @@ public: void setFilename (const std::string &filename) { filename_ = filename; } std::string filename () const { return filename_; } - // mixing group - bool updateMixingGroup (SourceList sources, Group *parent = nullptr); - bool removeMixingGroup (SourceList sources); - MixingGroup *lastMixingGroup (); + // get the list of sources in mixing groups std::list getMixingGroups () const; + // returns true if something can be done to create a mixing group + bool canlink (SourceList sources); + // try to link sources of the given list: + // can either create a new mixing group or extend an existing group + // returns true if something was done + bool link (SourceList sources, Group *parent = nullptr); + // try to unlink sources of the given list: + // can either delete an entire mixing group, or remove the given sources from existing groups + // returns true if something was done + bool unlink (SourceList sources); + // iterators for looping over mixing groups std::list::iterator beginMixingGroup (); std::list::iterator endMixingGroup (); std::list::iterator deleteMixingGroup (std::list::iterator g); @@ -94,6 +102,8 @@ protected: std::string filename_; Source *failedSource_; SourceList sources_; + void validate(SourceList &sources); + std::list mixing_groups_; std::map config_; bool active_; diff --git a/SessionCreator.cpp b/SessionCreator.cpp index 985c9cb..08ccd04 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -94,7 +94,7 @@ void SessionCreator::load(const std::string& filename) // create groups std::list< SourceList > groups = getMixingGroups(); for (auto group_it = groups.begin(); group_it != groups.end(); group_it++) - session_->updateMixingGroup( *group_it ); + session_->link( *group_it ); // all good session_->setFilename(filename); diff --git a/Source.cpp b/Source.cpp index 9c66307..e216a7b 100644 --- a/Source.cpp +++ b/Source.cpp @@ -516,6 +516,13 @@ void Source::setAlpha(float a) touch(); } +void Source::touch () +{ + need_update_ = true; + if (mixinggroup_) + mixinggroup_->setAction(MixingGroup::ACTION_UPDATE); +} + void Source::update(float dt) { // keep delta-t diff --git a/Source.h b/Source.h index 41f86aa..cc23bbf 100644 --- a/Source.h +++ b/Source.h @@ -90,7 +90,7 @@ public: inline MaskShader *maskShader () const { return maskshader_; } // touch to request update - inline void touch () { need_update_ = true; } + void touch (); // informs if its ready (i.e. initialized) inline bool ready () const { return initialized_; }