/* * This file is part of vimix - video live mixer * * **Copyright** (C) 2019-2022 Bruno Herbelin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . **/ #include #include #include #include "Source.h" #include "Session.h" #include "SourceList.h" // utility to sort Sources by depth bool compare_depth (Source * first, Source * second) { return ( first->depth() < second->depth() ); } bool notplayable (const Source *s) { return !s->playable(); } SourceList playable_only (const SourceList &list) { SourceList pl = list; pl.remove_if(notplayable); return pl; } SourceList depth_sorted(const SourceList &list) { SourceList sl = list; sl.sort(compare_depth); return sl; } // utility to sort Sources in MixingView in a clockwise order // in reference to a center point struct clockwise_centered { explicit clockwise_centered(glm::vec2 c) : center(c) { } bool operator() (Source * first, Source * second) { glm::vec2 pos_first = glm::vec2(first->group(View::MIXING)->translation_)-center; float angle_first = glm::orientedAngle( glm::normalize(pos_first), glm::vec2(1.f, 0.f) ); glm::vec2 pos_second = glm::vec2(second->group(View::MIXING)->translation_)-center; float angle_second = glm::orientedAngle( glm::normalize(pos_second), glm::vec2(1.f, 0.f) ); return (angle_first < angle_second); } glm::vec2 center; }; SourceList mixing_sorted(const SourceList &list, glm::vec2 center) { SourceList sl = list; sl.sort(clockwise_centered(center)); return sl; } SourceIdList ids (const SourceList &list) { SourceIdList idlist; for( auto sit = list.begin(); sit != list.end(); ++sit) idlist.push_back( (*sit)->id() ); // make sure no duplicate idlist.unique(); return idlist; } SourceListCompare compare (const SourceList &first, const SourceList &second) { SourceListCompare ret = SOURCELIST_DISTINCT; if (first.empty() || second.empty()) return ret; // a new test list: start with the second list and remove all commons with first list SourceList test = second; for (auto it = first.begin(); it != first.end(); ++it){ test.remove(*it); } // all sources of the second list were in the first list if (test.empty()) { // same size, therefore they are the same! if (first.size() == second.size()) ret = SOURCELIST_EQUAL; // otherwise, first list contains all sources of the second list. else ret = SOURCELIST_SECOND_IN_FIRST; } // some sources of the second list were in the first else if ( second.size() != test.size() ){ // if the number of sources removed from second is the number of sources in the first if (second.size() - test.size() == first.size()) ret = SOURCELIST_FIRST_IN_SECOND; // else, there is a patrial intersection else ret = SOURCELIST_INTERSECT; } // else no intersection, lists are distinct (return detault) return ret; } SourceList intersect (const SourceList &first, const SourceList &second) { // take second list and remove all elements also in first list // -> builds the list of what remains in second list SourceList l1 = second; for (auto it = first.begin(); it != first.end(); ++it) l1.remove(*it); // take second list and remove all elements in the remainer list // -> builds the list of what is in second list and was part of the first list SourceList l2 = second; for (auto it = l1.begin(); it != l1.end(); ++it) l2.remove(*it); return l2; } SourceList join (const SourceList &first, const SourceList &second) { SourceList l = second; for (auto it = first.begin(); it != first.end(); ++it) l.push_back(*it); l.unique(); return l; } SourceLink::SourceLink(uint64_t id, Session *se): host_(nullptr), target_(nullptr), id_(0) { connect(id, se); } SourceLink::SourceLink(Source *s): host_(nullptr), target_(nullptr), id_(0) { connect(s); } SourceLink::SourceLink(const SourceLink &l) { if (l.target_ != nullptr) connect(l.target_); else host_ = l.host_; id_ = l.id_; } void SourceLink::connect(uint64_t id, Session *se) { if (connected()) disconnect(); id_ = id; host_ = se; } void SourceLink::connect(Source *s) { if (connected()) disconnect(); target_ = s; target_->links_.push_back(this); id_ = s->id(); // TODO veryfy circular dependency recursively ? } void SourceLink::disconnect() { if (target_) target_->links_.remove(this); target_ = nullptr; id_ = 0; host_ = nullptr; } SourceLink::~SourceLink() { disconnect(); } Source *SourceLink::source() { // no link to pointer yet? if ( target_ == nullptr ) { // to find a source, we need a host and an id if ( id_ > 0 && host_ != nullptr) { // find target in session SourceList::iterator it = host_->find(id_); // found: keep pointer if (it != host_->end()) { target_ = *it; target_->links_.push_back(this); } // // not found: invalidate link // else // disconnect(); } // no host: invalidate link else disconnect(); } return target_; } SourceList validateLinkList (const SourceLinkList &list) { SourceList sourcelist; for( auto sit = list.begin(); sit != list.end(); ++sit) { Source *s = (*sit)->source(); if (s) sourcelist.push_back( s ); } // make sure no duplicate sourcelist.unique(); return sourcelist; } SourceLinkList getLinkList (const SourceList &list) { SourceLinkList linklist; for( auto sit = list.begin(); sit != list.end(); ++sit) linklist.push_back( new SourceLink(*sit) ); return linklist; } void clearLinkList (SourceLinkList list) { for( auto sit = list.begin(); sit != list.end(); ) { delete (*sit); sit = list.erase(sit); } }