Files
vimix/SourceList.cpp

236 lines
5.9 KiB
C++

/*
* This file is part of vimix - video live mixer
*
* **Copyright** (C) 2020-2021 Bruno Herbelin <bruno.herbelin@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
**/
#include <algorithm>
#include <glm/gtx/vector_angle.hpp>
#include <glm/gtx/rotate_vector.hpp>
#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;
}
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 validate (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;
}