updated NodeInputs to use a standard value as well as keyframed values
Previously the non-keyframed value was stored as a static keyframe but this introduced issues when an input was in a state of keyframes being enabled but 0 keyframes existing. Having a standard value makes much more sense.
This commit is contained in:
@@ -75,7 +75,7 @@ void Block::set_out(const rational &out)
|
||||
|
||||
rational Block::length() const
|
||||
{
|
||||
return length_input_->get_value_at_time(0).value<rational>();
|
||||
return length_input_->get_standard_value().value<rational>();
|
||||
}
|
||||
|
||||
void Block::set_length(const rational &length)
|
||||
@@ -86,7 +86,7 @@ void Block::set_length(const rational &length)
|
||||
return;
|
||||
}
|
||||
|
||||
length_input_->set_override_value(QVariant::fromValue(length));
|
||||
length_input_->set_standard_value(QVariant::fromValue(length));
|
||||
}
|
||||
|
||||
void Block::set_length_and_media_out(const rational &length)
|
||||
@@ -153,22 +153,22 @@ void Block::set_next(Block *next)
|
||||
|
||||
rational Block::media_in() const
|
||||
{
|
||||
return media_in_input_->get_value_at_time(0).value<rational>();
|
||||
return media_in_input_->get_standard_value().value<rational>();
|
||||
}
|
||||
|
||||
void Block::set_media_in(const rational &media_in)
|
||||
{
|
||||
media_in_input_->set_override_value(QVariant::fromValue(media_in));
|
||||
media_in_input_->set_standard_value(QVariant::fromValue(media_in));
|
||||
}
|
||||
|
||||
rational Block::media_out() const
|
||||
{
|
||||
return media_out_input_->get_value_at_time(0).value<rational>();
|
||||
return media_out_input_->get_standard_value().value<rational>();
|
||||
}
|
||||
|
||||
void Block::set_media_out(const rational &media_out)
|
||||
{
|
||||
media_out_input_->set_override_value(QVariant::fromValue(media_out));
|
||||
media_out_input_->set_standard_value(QVariant::fromValue(media_out));
|
||||
}
|
||||
|
||||
rational Block::media_length() const
|
||||
|
||||
+62
-40
@@ -29,13 +29,12 @@ NodeInput::NodeInput(const QString& id, const DataType &type, const QVariant &de
|
||||
NodeParam(id),
|
||||
data_type_(type),
|
||||
keyframable_(true),
|
||||
standard_value_(default_value),
|
||||
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<NodeKeyframe>(0, default_value, NodeKeyframe::kLinear));
|
||||
}
|
||||
|
||||
bool NodeInput::IsArray()
|
||||
@@ -84,50 +83,52 @@ Node *NodeInput::get_connected_node() const
|
||||
|
||||
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 (!is_keyframing() || keyframes_.isEmpty()) {
|
||||
return standard_value_;
|
||||
}
|
||||
|
||||
if (keyframes_.last()->time() <= time) {
|
||||
// This time is after any keyframes so we return the last value
|
||||
return keyframes_.last()->value();
|
||||
}
|
||||
if (keyframes_.first()->time() >= time) {
|
||||
// This time precedes any keyframe, so we just return the first value
|
||||
return keyframes_.first()->value();
|
||||
}
|
||||
|
||||
// If we're here, the time must be somewhere in between the keyframes
|
||||
for (int i=0;i<keyframes_.size()-1;i++) {
|
||||
NodeKeyframePtr before = keyframes_.at(i);
|
||||
NodeKeyframePtr after = keyframes_.at(i+1);
|
||||
if (keyframes_.last()->time() <= time) {
|
||||
// This time is after any keyframes so we return the last value
|
||||
return keyframes_.last()->value();
|
||||
}
|
||||
|
||||
if (before->time() == time
|
||||
|| data_type() != kFloat // FIXME: Expand this to other types that can be interpolated
|
||||
|| (before->time() < time && before->type() == NodeKeyframe::kHold)) {
|
||||
// If we're here, the time must be somewhere in between the keyframes
|
||||
for (int i=0;i<keyframes_.size()-1;i++) {
|
||||
NodeKeyframePtr before = keyframes_.at(i);
|
||||
NodeKeyframePtr after = keyframes_.at(i+1);
|
||||
|
||||
// Time == keyframe time, so value is precise
|
||||
return before->value();
|
||||
if (before->time() == time
|
||||
|| data_type() != kFloat // FIXME: Expand this to other types that can be interpolated
|
||||
|| (before->time() < time && before->type() == NodeKeyframe::kHold)) {
|
||||
|
||||
} else if (before->time() < time && after->time() > time) {
|
||||
// We must interpolate between these keyframes
|
||||
// Time == keyframe time, so value is precise
|
||||
return before->value();
|
||||
|
||||
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);
|
||||
} else if (before->time() < time && after->time() > time) {
|
||||
// We must interpolate between these keyframes
|
||||
|
||||
return interpolated_value;
|
||||
}
|
||||
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();
|
||||
return standard_value_;
|
||||
}
|
||||
|
||||
NodeKeyframePtr NodeInput::get_keyframe_at_time(const rational &time) const
|
||||
@@ -147,7 +148,7 @@ NodeKeyframePtr NodeInput::get_keyframe_at_time(const rational &time) const
|
||||
|
||||
NodeKeyframePtr NodeInput::get_closest_keyframe_to_time(const rational &time) const
|
||||
{
|
||||
if (!is_keyframing()) {
|
||||
if (!is_keyframing() || keyframes_.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -179,6 +180,17 @@ NodeKeyframePtr NodeInput::get_closest_keyframe_to_time(const rational &time) co
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NodeKeyframe::Type NodeInput::get_best_keyframe_type_for_time(const rational &time) const
|
||||
{
|
||||
NodeKeyframePtr closest_key = get_closest_keyframe_to_time(time);
|
||||
|
||||
if (closest_key) {
|
||||
return closest_key->type();
|
||||
}
|
||||
|
||||
return NodeKeyframe::kDefaultType;
|
||||
}
|
||||
|
||||
void NodeInput::insert_keyframe(NodeKeyframePtr key)
|
||||
{
|
||||
Q_ASSERT(is_keyframable() || keyframes_.isEmpty());
|
||||
@@ -293,11 +305,18 @@ bool NodeInput::is_keyframable() const
|
||||
return keyframable_;
|
||||
}
|
||||
|
||||
void NodeInput::set_override_value(const QVariant &value)
|
||||
const QVariant &NodeInput::get_standard_value() const
|
||||
{
|
||||
Q_ASSERT(!is_keyframable());
|
||||
return standard_value_;
|
||||
}
|
||||
|
||||
keyframes_.first()->set_value(value);
|
||||
void NodeInput::set_standard_value(const QVariant &value)
|
||||
{
|
||||
standard_value_ = value;
|
||||
|
||||
if (!is_keyframing()) {
|
||||
emit ValueChanged(RATIONAL_MIN, RATIONAL_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
const QList<NodeKeyframePtr> &NodeInput::keyframes() const
|
||||
@@ -346,7 +365,10 @@ void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_conn
|
||||
{
|
||||
Q_ASSERT(source->id() == dest->id());
|
||||
|
||||
// Copy values
|
||||
// Copy standard value
|
||||
dest->standard_value_ = source->standard_value_;
|
||||
|
||||
// Copy keyframes
|
||||
dest->keyframes_.clear();
|
||||
foreach (NodeKeyframePtr key, source->keyframes_) {
|
||||
NodeKeyframePtr copy = std::make_shared<NodeKeyframe>(key->time(), key->value(), key->type());
|
||||
|
||||
+19
-7
@@ -94,10 +94,15 @@ public:
|
||||
/**
|
||||
* @brief Gets the closest keyframe to a time
|
||||
*
|
||||
* If is_keyframing() is false, this will return nullptr. Otherwise, this is guaranteed to return a keyframe.
|
||||
* If is_keyframing() is false or keyframes_ is empty, this will return nullptr.
|
||||
*/
|
||||
NodeKeyframePtr get_closest_keyframe_to_time(const rational& time) const;
|
||||
|
||||
/**
|
||||
* @brief A heuristic to determine what type a keyframe should be if it's inserted at a certain time (between keyframes)
|
||||
*/
|
||||
NodeKeyframe::Type get_best_keyframe_type_for_time(const rational& time) const;
|
||||
|
||||
/**
|
||||
* @brief Inserts a keyframe at the given time and returns a reference to it
|
||||
*/
|
||||
@@ -131,11 +136,14 @@ public:
|
||||
bool is_keyframable() const;
|
||||
|
||||
/**
|
||||
* @brief Replaces all values with this value
|
||||
*
|
||||
* This can only be used on inputs where keyframing is disabled.
|
||||
* @brief Get non-keyframed value
|
||||
*/
|
||||
void set_override_value(const QVariant& value);
|
||||
const QVariant& get_standard_value() const;
|
||||
|
||||
/**
|
||||
* @brief Set non-keyframed value
|
||||
*/
|
||||
void set_standard_value(const QVariant& value);
|
||||
|
||||
/**
|
||||
* @brief Return list of keyframes in this parameter
|
||||
@@ -193,11 +201,15 @@ private:
|
||||
*/
|
||||
bool keyframable_;
|
||||
|
||||
/**
|
||||
* @brief Non-keyframed value
|
||||
*/
|
||||
QVariant standard_value_;
|
||||
|
||||
/**
|
||||
* @brief Internal keyframe array
|
||||
*
|
||||
* All internal/user-defined data is stored in this array. Even if keyframing is not enabled, this array will contain
|
||||
* one entry which will be used, and its time value will be ignored.
|
||||
* If keyframing is enabled, this data is used instead of standard_value.
|
||||
*/
|
||||
QList<NodeKeyframePtr> keyframes_;
|
||||
|
||||
|
||||
@@ -32,12 +32,12 @@ MediaInput::MediaInput() :
|
||||
|
||||
StreamPtr MediaInput::footage()
|
||||
{
|
||||
return footage_input_->get_value_at_time(0).value<StreamPtr>();
|
||||
return footage_input_->get_standard_value().value<StreamPtr>();
|
||||
}
|
||||
|
||||
void MediaInput::SetFootage(StreamPtr f)
|
||||
{
|
||||
footage_input_->set_override_value(QVariant::fromValue(f));
|
||||
footage_input_->set_standard_value(QVariant::fromValue(f));
|
||||
}
|
||||
|
||||
void MediaInput::Retranslate()
|
||||
@@ -47,7 +47,7 @@ void MediaInput::Retranslate()
|
||||
|
||||
void MediaInput::FootageChanged()
|
||||
{
|
||||
StreamPtr new_footage = footage_input_->get_value_at_time(0).value<StreamPtr>();
|
||||
StreamPtr new_footage = footage_input_->get_standard_value().value<StreamPtr>();
|
||||
|
||||
if (new_footage == connected_footage_) {
|
||||
return;
|
||||
|
||||
@@ -350,7 +350,7 @@ QString TrackOutput::GetDefaultTrackName(TrackType type, int index)
|
||||
|
||||
bool TrackOutput::IsMuted() const
|
||||
{
|
||||
return muted_input_->get_value_at_time(0).toBool();
|
||||
return muted_input_->get_standard_value().toBool();
|
||||
}
|
||||
|
||||
bool TrackOutput::IsLocked() const
|
||||
@@ -365,7 +365,7 @@ void TrackOutput::SetTrackName(const QString &name)
|
||||
|
||||
void TrackOutput::SetMuted(bool e)
|
||||
{
|
||||
muted_input_->set_override_value(e);
|
||||
muted_input_->set_standard_value(e);
|
||||
InvalidateCache(0, track_length());
|
||||
}
|
||||
|
||||
|
||||
@@ -108,6 +108,9 @@ void NodeParamViewItem::InputAddedKeyframeInternal(NodeInput *input, NodeKeyfram
|
||||
// Find global position
|
||||
lbl_center = lbl->mapToGlobal(lbl_center);
|
||||
|
||||
// Update keyframe control widget
|
||||
UpdateKeyframeControl(KeyframeControlFromInput(input));
|
||||
|
||||
emit KeyframeAdded(keyframe, lbl_center.y());
|
||||
}
|
||||
|
||||
@@ -186,8 +189,8 @@ void NodeParamViewItem::UpdateKeyframeControl(NodeParamViewKeyframeControl *key_
|
||||
NodeInput* input = key_control->GetConnectedInput();
|
||||
|
||||
// Update UI based on time
|
||||
key_control->SetPreviousButtonEnabled(time_ > input->keyframes().first()->time());
|
||||
key_control->SetNextButtonEnabled(time_ < input->keyframes().last()->time());
|
||||
key_control->SetPreviousButtonEnabled(!input->keyframes().isEmpty() && time_ > input->keyframes().first()->time());
|
||||
key_control->SetNextButtonEnabled(!input->keyframes().isEmpty() && time_ < input->keyframes().last()->time());
|
||||
key_control->SetToggleButtonChecked(input->has_keyframe_at_time(time_));
|
||||
}
|
||||
|
||||
@@ -232,7 +235,8 @@ void NodeParamViewItem::UserChangedKeyframeEnable(bool e)
|
||||
new NodeParamSetKeyframingCommand(input, true, command);
|
||||
|
||||
// NodeInputs already have one keyframe by default, we move it to the current time here
|
||||
new NodeParamSetKeyframeTimeCommand(input->keyframes().first(), time_, command);
|
||||
NodeKeyframePtr key = std::make_shared<NodeKeyframe>(time_, input->get_standard_value(), NodeKeyframe::kDefaultType);
|
||||
new NodeParamInsertKeyframeCommand(input, key, command);
|
||||
} else {
|
||||
// Confirm the user wants to clear all keyframes
|
||||
if (QMessageBox::warning(this,
|
||||
@@ -243,13 +247,13 @@ void NodeParamViewItem::UserChangedKeyframeEnable(bool e)
|
||||
// Store value at this time, we'll set this as the persistent value later
|
||||
QVariant stored_val = input->get_value_at_time(time_);
|
||||
|
||||
// Delete all keyframes EXCEPT ONE
|
||||
for (int i=input->keyframes().size()-1;i>0;i--) {
|
||||
// Delete all keyframes
|
||||
for (int i=input->keyframes().size()-1;i>=0;i--) {
|
||||
new NodeParamRemoveKeyframeCommand(input, input->keyframes().at(i), command);
|
||||
}
|
||||
|
||||
// Update value with this one
|
||||
new NodeParamSetKeyframeValueCommand(input->keyframes().first(), stored_val, command);
|
||||
// Update standard value
|
||||
new NodeParamSetStandardValueCommand(input, stored_val, command);
|
||||
|
||||
// Disable keyframing
|
||||
new NodeParamSetKeyframingCommand(input, false, command);
|
||||
@@ -273,9 +277,9 @@ void NodeParamViewItem::UserToggledKeyframe(bool e)
|
||||
|
||||
if (e && !key) {
|
||||
// Add a keyframe here
|
||||
NodeKeyframePtr closest_key = input->get_closest_keyframe_to_time(time_);
|
||||
|
||||
key = std::make_shared<NodeKeyframe>(time_, input->get_value_at_time(time_), closest_key->type());
|
||||
key = std::make_shared<NodeKeyframe>(time_,
|
||||
input->get_value_at_time(time_),
|
||||
input->get_best_keyframe_type_for_time(time_));
|
||||
|
||||
new NodeParamInsertKeyframeCommand(input, key, command);
|
||||
} else if (!e && key) {
|
||||
@@ -307,8 +311,6 @@ void NodeParamViewItem::InputAddedKeyframe(NodeKeyframePtr key)
|
||||
NodeInput* input = static_cast<NodeInput*>(sender());
|
||||
|
||||
InputAddedKeyframeInternal(input, key);
|
||||
|
||||
UpdateKeyframeControl(KeyframeControlFromInput(input));
|
||||
}
|
||||
|
||||
void NodeParamViewItem::GoToPreviousKey()
|
||||
|
||||
@@ -87,3 +87,21 @@ void NodeParamSetKeyframeTimeCommand::undo()
|
||||
{
|
||||
key_->set_time(old_time_);
|
||||
}
|
||||
|
||||
NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, const QVariant &value, QUndoCommand *parent) :
|
||||
QUndoCommand(parent),
|
||||
input_(input),
|
||||
old_value_(input_->get_standard_value()),
|
||||
new_value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeParamSetStandardValueCommand::redo()
|
||||
{
|
||||
input_->set_standard_value(new_value_);
|
||||
}
|
||||
|
||||
void NodeParamSetStandardValueCommand::undo()
|
||||
{
|
||||
input_->set_standard_value(old_value_);
|
||||
}
|
||||
|
||||
@@ -75,4 +75,18 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class NodeParamSetStandardValueCommand : public QUndoCommand {
|
||||
public:
|
||||
NodeParamSetStandardValueCommand(NodeInput* input, const QVariant& value, QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
NodeInput* input_;
|
||||
|
||||
QVariant old_value_;
|
||||
QVariant new_value_;
|
||||
};
|
||||
|
||||
#endif // NODEPARAMVIEWUNDO_H
|
||||
|
||||
@@ -254,13 +254,14 @@ void NodeParamViewWidgetBridge::SetInputValue(const QVariant &value)
|
||||
new NodeParamSetKeyframeValueCommand(existing_key, value, command);
|
||||
} else {
|
||||
// No existing key, create a new one
|
||||
NodeKeyframePtr closest_key = input_->get_closest_keyframe_to_time(time_);
|
||||
NodeKeyframePtr new_key = std::make_shared<NodeKeyframe>(time_, value, closest_key->type());
|
||||
NodeKeyframePtr new_key = std::make_shared<NodeKeyframe>(time_,
|
||||
value,
|
||||
input_->get_best_keyframe_type_for_time(time_));
|
||||
|
||||
new NodeParamInsertKeyframeCommand(input_, new_key, command);
|
||||
}
|
||||
} else {
|
||||
new NodeParamSetKeyframeValueCommand(input_->keyframes().first(), value, command);
|
||||
new NodeParamSetStandardValueCommand(input_, value, command);
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
|
||||
Reference in New Issue
Block a user