Files
oak-editor/app/node/output/track/track.cpp
T
itsmattkc bc0f01d9a3 make nodes more const friendly and prepare their functions for stateless work
Since the nodes won't be holding any rendering data themselves in this system,
we may as well enforce some level of non-write access by setting all the
functions to const. They were already const-friendly, they just weren't
labelled as such.
2019-12-04 20:10:42 +11:00

452 lines
10 KiB
C++

/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 Olive Team
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 <http://www.gnu.org/licenses/>.
***/
#include "track.h"
#include <QDebug>
#include "node/block/gap/gap.h"
#include "node/graph.h"
TrackOutput::TrackOutput() :
track_type_(kTrackTypeNone),
block_invalidate_cache_stack_(0),
index_(-1)
{
block_input_ = new NodeInputArray("block_in");
AddParameter(block_input_);
connect(block_input_, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(BlockConnected(NodeEdgePtr)));
connect(block_input_, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(BlockDisconnected(NodeEdgePtr)));
connect(block_input_, SIGNAL(SizeChanged(int)), this, SLOT(BlockListSizeChanged(int)));
track_input_ = new NodeInput("track_in");
track_input_->set_dependent(false);
AddParameter(track_input_);
track_output_ = new NodeOutput("track_out");
AddParameter(track_output_);
}
void TrackOutput::set_track_type(const TrackType &track_type)
{
track_type_ = track_type;
}
const TrackType& TrackOutput::track_type()
{
return track_type_;
}
Block::Type TrackOutput::type() const
{
return kTrack;
}
Block *TrackOutput::copy() const
{
return new TrackOutput();
}
QString TrackOutput::Name() const
{
return tr("Track");
}
QString TrackOutput::id() const
{
return "org.olivevideoeditor.Olive.track";
}
QString TrackOutput::Category() const
{
return tr("Output");
}
QString TrackOutput::Description() const
{
return tr("Node for representing and processing a single array of Blocks sorted by time. Also represents the end of "
"a Sequence.");
}
const int &TrackOutput::Index()
{
return index_;
}
void TrackOutput::SetIndex(const int &index)
{
index_ = index;
}
TrackOutput *TrackOutput::next_track()
{
// FIXME: Re-do this without dynamic_cast at some point
return dynamic_cast<TrackOutput*>(track_input_->get_connected_node());
}
NodeInput *TrackOutput::track_input()
{
return track_input_;
}
NodeOutput* TrackOutput::track_output()
{
return track_output_;
}
Block *TrackOutput::BlockContainingTime(const rational &time) const
{
foreach (Block* block, block_cache_) {
if (block->in() < time && block->out() > time) {
return block;
} else if (block->out() == time) {
break;
}
}
return nullptr;
}
Block *TrackOutput::NearestBlockBefore(const rational &time) const
{
foreach (Block* block, block_cache_) {
// Blocks are sorted by time, so the first Block who's out point is at/after this time is the correct Block
if (block->out() >= time) {
return block;
}
}
return nullptr;
}
Block *TrackOutput::NearestBlockAfter(const rational &time) const
{
foreach (Block* block, block_cache_) {
// Blocks are sorted by time, so the first Block after this time is the correct Block
if (block->in() >= time) {
return block;
}
}
return nullptr;
}
const QVector<Block *> &TrackOutput::Blocks() const
{
return block_cache_;
}
void TrackOutput::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from)
{
/*// We intercept IC signals from Blocks since we may be performing several options and they may over-signal
if (from == previous_input() && block_invalidate_cache_stack_ == 0) {
Node::InvalidateCache(qMax(start_range, rational(0)), qMin(end_range, in()), from);
} else {
}*/
Node::InvalidateCache(start_range, end_range, from);
}
void TrackOutput::InsertBlockBefore(Block* block, Block* after)
{
InsertBlockAtIndex(block, block_cache_.indexOf(after));
}
void TrackOutput::InsertBlockAfter(Block *block, Block *before)
{
int before_index = block_cache_.indexOf(before);
Q_ASSERT(before_index >= 0);
if (before_index == block_cache_.size() - 1) {
AppendBlock(block);
} else {
InsertBlockAtIndex(block, before_index + 1);
}
}
void TrackOutput::PrependBlock(Block *block)
{
InsertBlockAtIndex(block, 0);
}
void TrackOutput::InsertBlockAtIndex(Block *block, int index)
{
AddBlockToGraph(block);
block_input_->InsertAt(index);
NodeParam::ConnectEdge(block->block_output(),
block_input_->ParamAt(index));
}
void TrackOutput::AppendBlock(Block *block)
{
AddBlockToGraph(block);
BlockInvalidateCache();
int last_index = block_input_->GetSize();
block_input_->Append();
NodeParam::ConnectEdge(block->block_output(),
block_input_->ParamAt(last_index));
UnblockInvalidateCache();
// Invalidate area that block was added to
InvalidateCache(block->in(), track_length());
}
void TrackOutput::AddBlockToGraph(Block *block)
{
// Find the parent graph
NodeGraph* graph = static_cast<NodeGraph*>(parent());
graph->AddNodeWithDependencies(block);
}
void TrackOutput::BlockInvalidateCache()
{
block_invalidate_cache_stack_++;
}
void TrackOutput::UnblockInvalidateCache()
{
block_invalidate_cache_stack_--;
}
void TrackOutput::RemoveBlock(Block *block)
{
GapBlock* gap = new GapBlock();
gap->set_length(block->length());
ReplaceBlock(block, gap);
}
void TrackOutput::RippleRemoveBlock(Block *block)
{
BlockInvalidateCache();
rational remove_in = block->in();
int index_of_block_to_remove = block_cache_.indexOf(block);
block_input_->RemoveAt(index_of_block_to_remove);
UnblockInvalidateCache();
InvalidateCache(remove_in, track_length());
// FIXME: Should there be removing the Blocks from the graph?
}
void TrackOutput::ReplaceBlock(Block *old, Block *replace)
{
Q_ASSERT(old->length() == replace->length());
BlockInvalidateCache();
AddBlockToGraph(replace);
int index_of_old_block = block_cache_.indexOf(old);
NodeParam::DisconnectEdge(old->block_output(),
block_input_->ParamAt(index_of_old_block));
NodeParam::ConnectEdge(replace->block_output(),
block_input_->ParamAt(index_of_old_block));
UnblockInvalidateCache();
InvalidateCache(replace->in(), replace->out());
}
TrackOutput *TrackOutput::TrackFromBlock(Block *block)
{
NodeOutput* output = block->block_output();
foreach (NodeEdgePtr edge, output->edges()) {
Node* n = edge->input()->parentNode();
if (n->IsTrack()) {
return static_cast<TrackOutput*>(n);
}
}
return nullptr;
}
const rational &TrackOutput::track_length() const
{
return track_length_;
}
bool TrackOutput::IsTrack() const
{
return true;
}
void TrackOutput::UpdateInOutFrom(int index)
{
Q_ASSERT(index >= 0);
Q_ASSERT(index < block_cache_.size());
rational new_track_length;
for (int i=index;i<block_cache_.size();i++) {
Block* b = block_cache_.at(i);
if (b) {
rational prev_out;
// Find previous block and retrieve its out point (if there isn't one, this in will be set to 0)
for (int j=i-1;j>=0;j--) {
Block* previous = block_cache_.at(j);
if (previous) {
prev_out = previous->out();
break;
}
}
rational new_out = prev_out + b->length();
b->set_in(prev_out);
b->set_out(new_out);
new_track_length = new_out;
emit b->Refreshed();
}
}
// Update track length
if (new_track_length != track_length_) {
track_length_ = new_track_length;
emit TrackLengthChanged();
}
}
void TrackOutput::UpdatePreviousAndNextOfIndex(int index)
{
Block* ref = block_cache_.at(index);
Block* previous = nullptr;
Block* next = nullptr;
// Find previous
for (int i=index-1;i>=0;i--) {
previous = block_cache_.at(i);
if (previous) {
break;
}
}
// Find next
for (int i=index+1;i<block_cache_.size();i++) {
next = block_cache_.at(i);
if (next) {
break;
}
}
if (ref) {
// Link blocks together
ref->set_previous(previous);
ref->set_next(next);
if (previous)
previous->set_next(ref);
if (next)
next->set_previous(ref);
} else {
// Link previous and next together
if (previous)
previous->set_next(next);
if (next)
next->set_previous(previous);
}
}
void TrackOutput::BlockConnected(NodeEdgePtr edge)
{
int block_index = block_input_->IndexOfSubParameter(edge->input());
Q_ASSERT(block_index >= 0);
Node* connected_node = edge->output()->parentNode();
Block* connected_block = connected_node->IsBlock() ? static_cast<Block*>(connected_node) : nullptr;
block_cache_.replace(block_index, connected_block);
UpdatePreviousAndNextOfIndex(block_index);
UpdateInOutFrom(block_index);
if (connected_block) {
connect(connected_block, SIGNAL(LengthChanged(const rational&)), this, SLOT(BlockLengthChanged()));
emit BlockAdded(connected_block);
}
}
void TrackOutput::BlockDisconnected(NodeEdgePtr edge)
{
int block_index = block_input_->IndexOfSubParameter(edge->input());
Q_ASSERT(block_index >= 0);
block_cache_.replace(block_index, nullptr);
UpdatePreviousAndNextOfIndex(block_index);
UpdateInOutFrom(block_index);
Node* connected_node = edge->output()->parentNode();
Block* connected_block = connected_node->IsBlock() ? static_cast<Block*>(connected_node) : nullptr;
if (connected_block) {
disconnect(connected_block, SIGNAL(LengthChanged(const rational&)), this, SLOT(BlockLengthChanged()));
// Update previous and next references
emit BlockRemoved(connected_block);
}
}
void TrackOutput::BlockListSizeChanged(int size)
{
int old_size = block_cache_.size();
block_cache_.resize(size);
if (size > old_size) {
// Fill new slots with nullptr
for (int i=old_size;i<size;i++) {
block_cache_.replace(i, nullptr);
}
}
}
void TrackOutput::BlockLengthChanged()
{
// Assumes sender is a Block
Block* b = static_cast<Block*>(sender());
int index = block_cache_.indexOf(b);
Q_ASSERT(index >= 0);
UpdateInOutFrom(index);
}