/* * Olive Community Edition - Non-Linear Video Editor * Copyright (C) 2025 Olive CE 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 "inputimmediate.h" #include "common/lerp.h" #include "common/tohex.h" namespace olive { NodeInputImmediate::NodeInputImmediate(NodeValue::Type type, const SplitValue &default_val) : default_value_(default_val) , keyframing_(false) { set_data_type(type); } void NodeInputImmediate::set_standard_value_on_track(const QVariant &value, int track) { standard_value_.replace(track, value); } void NodeInputImmediate::set_split_standard_value(const SplitValue &value) { for (int i = 0; i < value.size() && i < standard_value_.size(); i++) { standard_value_[i] = value[i]; } } QVector NodeInputImmediate::get_keyframe_at_time(const rational &time) const { QVector keys; for (int i = 0; i < keyframe_tracks_.size(); i++) { NodeKeyframe *key_at_time = get_keyframe_at_time_on_track(time, i); if (key_at_time) { keys.append(key_at_time); } } return keys; } NodeKeyframe * NodeInputImmediate::get_keyframe_at_time_on_track(const rational &time, int track) const { if (!is_using_standard_value(track)) { foreach (NodeKeyframe *key, keyframe_tracks_.at(track)) { if (key->time() == time) { return key; } } } return nullptr; } NodeKeyframe * NodeInputImmediate::get_closest_keyframe_to_time_on_track(const rational &time, int track) const { if (is_using_standard_value(track)) { return nullptr; } const NodeKeyframeTrack &key_track = keyframe_tracks_.at(track); if (time <= key_track.first()->time()) { return key_track.first(); } if (time >= key_track.last()->time()) { return key_track.last(); } for (int i = 1; i < key_track.size(); i++) { NodeKeyframe *prev_key = key_track.at(i - 1); NodeKeyframe *next_key = key_track.at(i); if (prev_key->time() <= 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; } NodeKeyframe * NodeInputImmediate::get_closest_keyframe_before_time(const rational &time) const { NodeKeyframe *key = nullptr; foreach (const NodeKeyframeTrack &track, keyframe_tracks_) { foreach (NodeKeyframe *k, track) { if (k->time() >= time) { break; } else if (!key || k->time() > key->time()) { key = k; } } } return key; } NodeKeyframe * NodeInputImmediate::get_closest_keyframe_after_time(const rational &time) const { NodeKeyframe *key = nullptr; foreach (const NodeKeyframeTrack &track, keyframe_tracks_) { for (int i = track.size() - 1; i >= 0; i--) { NodeKeyframe *k = track.at(i); if (k->time() <= time) { break; } else if (!key || k->time() < key->time()) { key = k; } } } return key; } NodeKeyframe::Type NodeInputImmediate::get_best_keyframe_type_for_time(const rational &time, int track) const { NodeKeyframe *closest_key = get_closest_keyframe_to_time_on_track(time, track); if (closest_key) { return closest_key->type(); } return NodeKeyframe::kDefaultType; } bool NodeInputImmediate::has_keyframe_at_time(const rational &time) const { if (!is_keyframing()) { return false; } // Loop through keyframes to see if any match foreach (const NodeKeyframeTrack &track, keyframe_tracks_) { foreach (NodeKeyframe *key, track) { if (key->time() == time) { return true; } } } // None match return false; } void NodeInputImmediate::set_data_type(NodeValue::Type type) { int track_size = NodeValue::get_number_of_keyframe_tracks(type); keyframe_tracks_.resize(track_size); standard_value_.resize(track_size); set_split_standard_value(default_value_); } NodeKeyframe *NodeInputImmediate::get_earliest_keyframe() const { NodeKeyframe *earliest = nullptr; foreach (const NodeKeyframeTrack &track, keyframe_tracks_) { if (!track.isEmpty()) { NodeKeyframe *earliest_in_track = track.first(); if (!earliest || earliest_in_track->time() < earliest->time()) { earliest = earliest_in_track; } } } return earliest; } NodeKeyframe *NodeInputImmediate::get_latest_keyframe() const { NodeKeyframe *latest = nullptr; foreach (const NodeKeyframeTrack &track, keyframe_tracks_) { if (!track.isEmpty()) { NodeKeyframe *latest_in_track = track.last(); if (!latest || latest_in_track->time() > latest->time()) { latest = latest_in_track; } } } return latest; } void NodeInputImmediate::insert_keyframe(NodeKeyframe *key) { NodeKeyframeTrack &key_track = keyframe_tracks_[key->track()]; int insert_index = key_track.size(); for (int i = 0; i < key_track.size(); i++) { NodeKeyframe *compare = key_track.at(i); // Ensure we aren't trying to insert two keyframes at the same time Q_ASSERT(compare->time() != key->time()); if (compare->time() > key->time()) { insert_index = i; break; } } key_track.insert(insert_index, key); NodeKeyframe *previous = insert_index > 0 ? key_track.at(insert_index - 1) : nullptr; NodeKeyframe *next = insert_index < key_track.size() - 1 ? key_track.at(insert_index + 1) : nullptr; key->set_previous(previous); key->set_next(next); if (previous) { previous->set_next(key); } if (next) { next->set_previous(key); } } void NodeInputImmediate::remove_keyframe(NodeKeyframe *key) { if (key->previous()) { key->previous()->set_next(key->next()); } if (key->next()) { key->next()->set_previous(key->previous()); } key->set_previous(nullptr); key->set_next(nullptr); keyframe_tracks_[key->track()].removeOne(key); } void NodeInputImmediate::delete_all_keyframes(QObject *parent) { for (NodeKeyframeTrack &track : keyframe_tracks_) { while (!track.isEmpty()) { if (parent) { track.first()->setParent(parent); } else { delete track.first(); } } } } }