/***
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 .
***/
#include "input.h"
#include "common/lerp.h"
#include "node.h"
#include "output.h"
#include "inputarray.h"
NodeInput::NodeInput(const QString& id, const DataType &type, const QVariant &default_value) :
NodeParam(id),
data_type_(type),
keyframable_(true),
keyframing_(false),
dependent_(true),
has_minimum_(false),
has_maximum_(false)
{
// Have at least one keyframe/value active at any time
insert_keyframe(std::make_shared(0, default_value, NodeKeyframe::kLinear));
}
bool NodeInput::IsArray()
{
return false;
}
NodeParam::Type NodeInput::type()
{
return kInput;
}
QString NodeInput::name()
{
if (name_.isEmpty()) {
return tr("Input");
}
return NodeParam::name();
}
const NodeParam::DataType &NodeInput::data_type() const
{
return data_type_;
}
NodeOutput *NodeInput::get_connected_output() const
{
if (!edges_.isEmpty()) {
return edges_.first()->output();
}
return nullptr;
}
Node *NodeInput::get_connected_node() const
{
NodeOutput* output = get_connected_output();
if (output != nullptr) {
return output->parentNode();
}
return nullptr;
}
QVariant NodeInput::get_value_at_time(const rational &time) const
{
if (is_keyframing()) {
if (keyframes_.first()->time() >= time) {
// This time precedes any keyframe, so we just return the first value
return keyframes_.first()->value();
}
if (keyframes_.last()->time() <= time) {
// This time is after any keyframes so we return the last value
return keyframes_.last()->value();
}
// If we're here, the time must be somewhere in between the keyframes
for (int i=0;itime() == time
|| data_type() != kFloat // FIXME: Expand this to other types that can be interpolated
|| (before->time() < time && before->type() == NodeKeyframe::kHold)) {
// Time == keyframe time, so value is precise
return before->value();
} else if (before->time() < time && after->time() > time) {
// We must interpolate between these keyframes
if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) {
// FIXME: Perform a cubic bezier interpolation
} else if (before->type() == NodeKeyframe::kLinear && after->type() == NodeKeyframe::kBezier) {
// FIXME: Perform a quadratic bezier interpolation with anchors from the AFTER keyframe
} else if (before->type() == NodeKeyframe::kLinear && after->type() == NodeKeyframe::kBezier) {
// FIXME: Perform a quadratic bezier interpolation with anchors from the BEFORE keyframe
} else {
// To have arrived here, the keyframes must both be linear
qreal period_progress = (time.toDouble() - before->time().toDouble()) / (after->time().toDouble() - before->time().toDouble());
qreal interpolated_value = lerp(before->value().toDouble(), after->value().toDouble(), period_progress);
return interpolated_value;
}
}
}
}
return keyframes_.first()->value();
}
NodeKeyframePtr NodeInput::get_keyframe_at_time(const rational &time) const
{
if (!is_keyframing()) {
return nullptr;
}
foreach (NodeKeyframePtr key, keyframes_) {
if (key->time() == time) {
return key;
}
}
return nullptr;
}
NodeKeyframePtr NodeInput::get_closest_keyframe_to_time(const rational &time) const
{
if (!is_keyframing()) {
return nullptr;
}
if (time <= keyframes_.first()->time()) {
return keyframes_.first();
}
if (time >= keyframes_.last()->time()) {
return keyframes_.last();
}
for (int i=1;itime() <= time && next_key->time() >= time) {
// Return whichever is closer
rational prev_diff = time - prev_key->time();
rational next_diff = next_key->time() - time;
if (next_diff < prev_diff) {
return next_key;
} else {
return prev_key;
}
}
}
return nullptr;
}
void NodeInput::insert_keyframe(NodeKeyframePtr key)
{
insert_keyframe_internal(key);
connect(key.get(), &NodeKeyframe::TimeChanged, this, &NodeInput::KeyframeTimeChanged);
connect(key.get(), &NodeKeyframe::ValueChanged, this, &NodeInput::KeyframeValueChanged);
emit KeyframeAdded(key);
}
void NodeInput::remove_keyframe(NodeKeyframePtr key)
{
Q_ASSERT(keyframes_.size() > 1);
disconnect(key.get(), &NodeKeyframe::TimeChanged, this, &NodeInput::KeyframeTimeChanged);
disconnect(key.get(), &NodeKeyframe::ValueChanged, this, &NodeInput::KeyframeValueChanged);
keyframes_.removeOne(key);
emit KeyframeRemoved(key);
}
void NodeInput::KeyframeTimeChanged()
{
NodeKeyframe* key = static_cast(sender());
int keyframe_index = FindIndexOfKeyframeFromRawPtr(key);
Q_ASSERT(keyframe_index > -1);
if ((keyframe_index > 0 && keyframes_.at(keyframe_index - 1)->time() > key->time())
|| (keyframe_index < keyframes_.size() - 1 && keyframes_.at(keyframe_index + 1)->time() < key->time())) {
// This keyframe needs resorting, store it and remove it from the list
NodeKeyframePtr key_shared_ptr = keyframes_.at(keyframe_index);
keyframes_.removeAt(keyframe_index);
// Automatically insertion sort
insert_keyframe_internal(key_shared_ptr);
}
}
void NodeInput::KeyframeValueChanged()
{
//NodeKeyframe* key = static_cast(sender());
//int keyframe_index = FindIndexOfKeyframeFromRawPtr(key);
// FIXME: Finish this partial stub
emit ValueChanged(RATIONAL_MIN, RATIONAL_MAX);
}
int NodeInput::FindIndexOfKeyframeFromRawPtr(NodeKeyframe *raw_ptr) const
{
for (int i=0;itime() != key->time());
if (compare->time() > key->time()) {
keyframes_.insert(i, key);
return;
}
}
keyframes_.append(key);
}
bool NodeInput::has_keyframe_at_time(const rational &time) const
{
// If we aren't keyframing, there definitely isn't a keyframe at a given time
if (!is_keyframing()) {
return false;
}
// Loop through keyframes to see if any match
foreach (NodeKeyframePtr key, keyframes_) {
if (key->time() == time) {
return true;
}
}
// None match
return false;
}
/*void NodeInput::set_default_value(const QVariant &value)
{
if (parentNode() != nullptr)
parentNode()->LockUserInput();
set_is_keyframing(false);
keyframes_.clear();
keyframes_.append(NodeKeyframe(0, value, NodeKeyframe::kLinear));
if (parentNode() != nullptr)
parentNode()->UnlockUserInput();
emit ValueChanged(RATIONAL_MIN, RATIONAL_MAX);
// We set up these variables in advance (see end of function)
bool signal_vc = false;
TimeRange signal_vc_range;
if (is_keyframing()) {
// Insert value into the keyframe list chronologically
if (keyframes_.first().time() > time) {
// Store this away for the ValueChanged signal we emit later
rational existing_first_key = keyframes_.first().time();
// Insert at the beginning
keyframes_.prepend(NodeKeyframe(time, value, keyframes_.first().type()));
// Value has changed since the earliest point up until the ex-first keyframe (since the frames
// interpolating between the key we're adding and the key that existed are changing too)
signal_vc = true;
signal_vc_range = TimeRange(RATIONAL_MIN, existing_first_key);
} else if (keyframes_.first().time() == time) {
// Replace first value
keyframes_.first().set_value(value);
// Value has changed since the earliest point up until the keyframe we just changed
signal_vc = true;
signal_vc_range = TimeRange(RATIONAL_MIN, time);
} else if (keyframes_.last().time() < time) {
// Store this away for the ValueChanged signal we emit later
rational existing_last_key = keyframes_.last().time();
// Append at the end
keyframes_.append(NodeKeyframe(time, value, keyframes_.last().type()));
// Value has changed since the ex-last point up until the latest possible point (since the frames
// interpolating between the key we're adding and the key that existed are changing too)
signal_vc = true;
signal_vc_range = TimeRange(existing_last_key, RATIONAL_MAX);
} else if (keyframes_.last().time() == time) {
// Replace last value
keyframes_.last().set_value(value);
// Value has changed from this point until the latest possible point
signal_vc = true;
signal_vc_range = TimeRange(time, RATIONAL_MAX);
} else {
for (int i=0;i time) {
// Insert value in between these two keyframes
keyframes_.insert(i+1, NodeKeyframe(time, value, before.type()));
// Values have changed since the last keyframe and the next one
signal_vc = true;
signal_vc_range = TimeRange(before.time(), after.time());
break;
}
}
}
} else {
keyframes_.first().set_value(value);
// Values have changed for all times since the value is static
signal_vc = true;
signal_vc_range = TimeRange(RATIONAL_MIN, RATIONAL_MAX);
}
if (parentNode() != nullptr)
parentNode()->UnlockUserInput();
// We make sure this signal is emitted AFTER we've unlocked the node in case this leads to a tangent where something
// tries to relock this node before it's unlocked here
if (signal_vc)
emit ValueChanged(signal_vc_range.in(), signal_vc_range.out());
}*/
/*void NodeInput::set_keyframe_data(const QList keyframes)
{
keyframes_ = keyframes;
}
void NodeInput::insert_keyframe(const NodeKeyframe &key)
{
Q_ASSERT(is_keyframing());
for (int i=0;i key.time()) {
keyframes_.insert(i, key);
emit KeyframeAdded(key);
return;
}
}
keyframes_.append(key);
emit KeyframeAdded(key);
}
void NodeInput::remove_keyframe_at_time(const rational &time)
{
for (int i=0;iset_value(value);
}
const QList &NodeInput::keyframes() const
{
return keyframes_;
}
void NodeInput::set_is_keyframable(bool k)
{
keyframable_ = k;
}
const QVariant &NodeInput::minimum() const
{
return minimum_;
}
bool NodeInput::has_minimum() const
{
return has_minimum_;
}
void NodeInput::set_minimum(const QVariant &min)
{
minimum_ = min;
has_minimum_ = true;
}
const QVariant &NodeInput::maximum() const
{
return maximum_;
}
bool NodeInput::has_maximum() const
{
return has_maximum_;
}
void NodeInput::set_maximum(const QVariant &max)
{
maximum_ = max;
has_maximum_ = true;
}
void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_connections, bool lock_connections)
{
Q_ASSERT(source->id() == dest->id());
// Copy values
dest->keyframes_ = source->keyframes_;
// Copy keyframing state
dest->set_is_keyframing(source->is_keyframing());
// Copy connections
if (include_connections && source->get_connected_output() != nullptr) {
ConnectEdge(source->get_connected_output(), dest, lock_connections);
}
// If these inputs are an array, copy the subparams too
if (dest->IsArray()) {
NodeInputArray* src_array = static_cast(source);
NodeInputArray* dst_array = static_cast(dest);
dst_array->SetSize(src_array->GetSize(), lock_connections);
for (int i=0;iGetSize();i++) {
CopyValues(src_array->At(i), dst_array->At(i), include_connections);
}
}
emit dest->ValueChanged(RATIONAL_MIN, RATIONAL_MAX);
}