refactor: workspace layout — crates/, app at root, legacy C++ removed
Single mechanical restructure commit: - root Cargo.toml = oakapp bin + workspace; one cargo build produces oakapp, oak-cli, oak-worker, liboakengine.dylib - app/rust/src -> src/ (app at repo root, no rust/ nesting) - src/<mod>/rust -> crates/oak<mod>; src/oakcore-rs -> crates/oakcore; src/bindings/oakotio -> crates/oakotio; src/engine/rust -> crates/oakengine (keeps cdylib+staticlib+rlib) - public C headers include/<mod>/ -> crates/oakengine/include/<mod>/ - OFX SDK headers vendored into crates/oakplugin/ofx/ (HostSupport gone) - legacy deleted: old src/ C++ modules, engine/, core/, ffmpeg_bridge/, app/ (Qt), cli/worker C++, root CMakeLists, third_party/KDDockWidgets submodule, otio-install, all build-* output (~40GB) - oakstorage kept but excluded from the workspace (skeleton w/ todos); gpui excluded (own workspace) - verified: cargo build green, cargo test --workspace 1845/0 (with the documented OCIO_RS_* env override for the homebrew OCIO)
This commit is contained in:
@@ -0,0 +1,645 @@
|
||||
// Oak Video Editor - Non-Linear Video Editor
|
||||
// Copyright (C) 2026 Oak 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/>.
|
||||
|
||||
//! Node core data and the behavior trait — the C++ `Node` class,
|
||||
//! restructured for Rust. COVERAGE.md maps every C++ method to its
|
||||
//! Rust home; this file carries the virtual surface ([`NodeBehavior`])
|
||||
//! and the shared data ([`NodeCore`]).
|
||||
|
||||
use oakcore_rs::{Rational, TimeRange};
|
||||
|
||||
use crate::id::NodeId;
|
||||
use crate::input::{Input, ValueHint};
|
||||
use crate::keyframe::KeyframeTrack;
|
||||
use crate::value::{NodeValue, NodeValueRow, NodeValueTable};
|
||||
|
||||
/// Node category (mirrors C++ `Node::CategoryID` order).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Category {
|
||||
/// Output nodes.
|
||||
Output,
|
||||
/// Effects.
|
||||
Effect,
|
||||
/// Generators.
|
||||
Generator,
|
||||
/// Inputs (footage).
|
||||
Input,
|
||||
/// Math/combine.
|
||||
Math,
|
||||
/// Color.
|
||||
Color,
|
||||
/// Distort.
|
||||
Distort,
|
||||
/// Filter.
|
||||
Filter,
|
||||
/// Keying.
|
||||
Keying,
|
||||
/// OpenFX plugins.
|
||||
OpenFx,
|
||||
/// Timeline structural (tracks/blocks).
|
||||
Timeline,
|
||||
/// Groups.
|
||||
Group,
|
||||
}
|
||||
|
||||
/// A gizmo: viewport-interaction data object (C++ `NodeGizmo`). Data
|
||||
/// only — drawing and mouse handling live in facade/app.
|
||||
#[derive(Clone)]
|
||||
pub struct Gizmo {
|
||||
/// Keyframed position inputs (track references).
|
||||
pub position_inputs: Vec<(String, i32, i32)>,
|
||||
/// Current drag position.
|
||||
pub drag_point: (f64, f64),
|
||||
}
|
||||
|
||||
/// Shared per-node data (the C++ `Node` member fields). Behavior lives
|
||||
/// in [`NodeBehavior`].
|
||||
#[derive(Clone)]
|
||||
pub struct NodeCore {
|
||||
/// Inputs by id (array inputs hold multiple elements).
|
||||
pub inputs: Vec<Input>,
|
||||
/// Keyframe tracks per (input, element).
|
||||
pub keyframes: Vec<(String, i32, KeyframeTrack)>,
|
||||
/// Caches as oakrender handles (created via bridge::render).
|
||||
pub caches: NodeCaches,
|
||||
/// Node flags bitmask (hidden, dont-show-in-param-view, ...).
|
||||
pub flags: u64,
|
||||
/// Editor position (serialization only).
|
||||
pub position: (f64, f64),
|
||||
/// User label (C++ label_).
|
||||
pub label: String,
|
||||
/// Color override index (-1 = none).
|
||||
pub override_color: i32,
|
||||
/// Effect input id (C++ effect_input_).
|
||||
pub effect_input: String,
|
||||
/// Value hints per (input, element).
|
||||
pub hints: Vec<((String, i32), ValueHint)>,
|
||||
/// Group-context positions (C++ context_positions_).
|
||||
pub context_positions: Vec<(NodeId, (f64, f64), bool)>,
|
||||
/// Linked nodes (C++ links_).
|
||||
pub links: Vec<NodeId>,
|
||||
/// Bin folder membership (None = not in the bin).
|
||||
pub bin_folder: Option<NodeId>,
|
||||
/// Caches master toggle (C++ caches_enabled_).
|
||||
pub caches_enabled: bool,
|
||||
/// Gizmos owned by this node.
|
||||
pub gizmos: Vec<Gizmo>,
|
||||
/// Currently dragged gizmo index.
|
||||
pub current_gizmo: Option<usize>,
|
||||
/// Standard (non-keyframed) values keyed by (input id, element);
|
||||
/// falls back to [`Input::default`] (the C++ `NodeInputImmediate`
|
||||
/// `standard_value_` map — `// CPP-PARITY: inputimmediate.h`).
|
||||
pub standard_values: std::collections::HashMap<(String, i32), crate::value::NodeValue>,
|
||||
}
|
||||
|
||||
/// The always-present "enabled" input id (C++
|
||||
/// `Node::k_enabled_input`, `"enabled_in"`).
|
||||
pub const ENABLED_INPUT: &str = "enabled_in";
|
||||
|
||||
/// Node flag bits (C++ `Node::Flag` enum; values cross the C ABI and
|
||||
/// project XML as ints — `// CPP-PARITY: node.h:109`).
|
||||
pub mod flags {
|
||||
/// `k_dont_show_in_param_view`.
|
||||
pub const DONT_SHOW_IN_PARAM_VIEW: u64 = 0x1;
|
||||
/// `k_video_effect`.
|
||||
pub const VIDEO_EFFECT: u64 = 0x2;
|
||||
/// `k_audio_effect`.
|
||||
pub const AUDIO_EFFECT: u64 = 0x4;
|
||||
/// `k_dont_show_in_create_menu`.
|
||||
pub const DONT_SHOW_IN_CREATE_MENU: u64 = 0x8;
|
||||
}
|
||||
|
||||
impl NodeCore {
|
||||
/// Bare core with no inputs (vacant arena slots).
|
||||
pub fn empty() -> NodeCore {
|
||||
NodeCore {
|
||||
inputs: Vec::new(),
|
||||
keyframes: Vec::new(),
|
||||
caches: NodeCaches::default(),
|
||||
flags: 0,
|
||||
position: (0.0, 0.0),
|
||||
label: String::new(),
|
||||
override_color: -1,
|
||||
effect_input: String::new(),
|
||||
hints: Vec::new(),
|
||||
context_positions: Vec::new(),
|
||||
links: Vec::new(),
|
||||
bin_folder: None,
|
||||
caches_enabled: true,
|
||||
gizmos: Vec::new(),
|
||||
current_gizmo: None,
|
||||
standard_values: std::collections::HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Fresh core for a node constructor: adds the standard `enabled_in`
|
||||
/// boolean input (default true) first, exactly like the C++ `Node`
|
||||
/// constructor (`// CPP-PARITY: node.cpp:91`).
|
||||
pub fn new() -> NodeCore {
|
||||
let mut core = NodeCore::empty();
|
||||
core.add_input(Input::new(
|
||||
ENABLED_INPUT,
|
||||
crate::value::ValueType::Boolean,
|
||||
crate::value::NodeValue::Boolean(true),
|
||||
));
|
||||
core
|
||||
}
|
||||
|
||||
/// Append an input descriptor.
|
||||
pub fn add_input(&mut self, input: Input) {
|
||||
self.inputs.push(input);
|
||||
}
|
||||
|
||||
/// Look up an input by id.
|
||||
pub fn get_input(&self, id: &str) -> Option<&Input> {
|
||||
self.inputs.iter().find(|i| i.id == id)
|
||||
}
|
||||
|
||||
/// Mutable input lookup by id.
|
||||
pub fn get_input_mut(&mut self, id: &str) -> Option<&mut Input> {
|
||||
self.inputs.iter_mut().find(|i| i.id == id)
|
||||
}
|
||||
|
||||
/// Index of the input in declaration order (C++ `inputs()`).
|
||||
pub fn input_index(&self, id: &str) -> Option<usize> {
|
||||
self.inputs.iter().position(|i| i.id == id)
|
||||
}
|
||||
|
||||
/// True when the node declares `id` (C++ `has_input_with_id`).
|
||||
pub fn has_input(&self, id: &str) -> bool {
|
||||
self.inputs.iter().any(|i| i.id == id)
|
||||
}
|
||||
|
||||
/// Remove the input `id`, if present (C++ `Node::remove_input`).
|
||||
pub fn remove_input(&mut self, id: &str) -> bool {
|
||||
let before = self.inputs.len();
|
||||
self.inputs.retain(|i| i.id != id);
|
||||
self.inputs.len() != before
|
||||
}
|
||||
|
||||
/// Declared value type of `id` (C++ `get_input_data_type`).
|
||||
pub fn input_data_type(&self, id: &str) -> Option<crate::value::ValueType> {
|
||||
self.get_input(id).map(|i| i.value_type)
|
||||
}
|
||||
|
||||
/// Flag bits of `id` (C++ `get_input_flags`).
|
||||
pub fn input_flags(&self, id: &str) -> u32 {
|
||||
self.get_input(id).map(|i| i.flags).unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Display name of `id` (C++ `get_input_name`, non-virtual part).
|
||||
pub fn input_display_name(&self, id: &str) -> String {
|
||||
self.get_input(id)
|
||||
.map(|i| i.display_name.clone())
|
||||
.unwrap_or_else(|| id.to_string())
|
||||
}
|
||||
|
||||
/// Array size of `id` (0 for non-array inputs; C++ `input_array_size`).
|
||||
pub fn input_array_size(&self, id: &str) -> usize {
|
||||
self.get_input(id).map(|i| i.array_size).unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Grow/shrink an array input's element count, inserting/removing the
|
||||
/// given element index. Per-element standard values and keyframe
|
||||
/// tracks shift to keep their element mapping (C++
|
||||
/// `Node::input_array_insert`/`input_array_remove`, values half).
|
||||
pub fn input_array_insert(&mut self, id: &str, index: usize) {
|
||||
if let Some(input) = self.get_input_mut(id) {
|
||||
input.array_size += 1;
|
||||
}
|
||||
let size = self.input_array_size(id);
|
||||
// Shift standard values and keyframe tracks up one element.
|
||||
for e in (index + 1..size).rev() {
|
||||
self.move_element_value(id, e - 1, e);
|
||||
self.move_element_keyframes(id, e - 1, e);
|
||||
}
|
||||
// The freshly inserted slot carries no value or track.
|
||||
self.standard_values.remove(&(id.to_string(), index as i32));
|
||||
self.remove_element_keyframes(id, index);
|
||||
}
|
||||
|
||||
/// See [`NodeCore::input_array_insert`].
|
||||
pub fn input_array_remove(&mut self, id: &str, index: usize) {
|
||||
let size = self.input_array_size(id);
|
||||
if index >= size {
|
||||
return;
|
||||
}
|
||||
// Drop the removed element's own value and track first (the
|
||||
// shift below only overwrites targets whose source has data).
|
||||
self.standard_values.remove(&(id.to_string(), index as i32));
|
||||
self.remove_element_keyframes(id, index);
|
||||
// Shift values/keyframes down one element, then drop the tail.
|
||||
for e in index..size.saturating_sub(1) {
|
||||
self.move_element_value(id, e + 1, e);
|
||||
self.move_element_keyframes(id, e + 1, e);
|
||||
}
|
||||
self.standard_values.remove(&(id.to_string(), (size - 1) as i32));
|
||||
self.remove_element_keyframes(id, size - 1);
|
||||
if let Some(input) = self.get_input_mut(id) {
|
||||
input.array_size = input.array_size.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn move_element_value(&mut self, id: &str, from: usize, to: usize) {
|
||||
let key = |e: usize| (id.to_string(), e as i32);
|
||||
if let Some(v) = self.standard_values.remove(&key(from)) {
|
||||
self.standard_values.insert(key(to), v);
|
||||
}
|
||||
}
|
||||
|
||||
fn move_element_keyframes(&mut self, id: &str, from: usize, to: usize) {
|
||||
let track = match self
|
||||
.keyframes
|
||||
.iter()
|
||||
.position(|(i, e, _)| i == id && *e == from as i32)
|
||||
{
|
||||
Some(i) => self.keyframes.remove(i).2,
|
||||
None => return,
|
||||
};
|
||||
// Replace or insert at the target element.
|
||||
if let Some(slot) = self
|
||||
.keyframes
|
||||
.iter_mut()
|
||||
.find(|(i, e, _)| i == id && *e == to as i32)
|
||||
{
|
||||
slot.2 = track;
|
||||
} else {
|
||||
self.keyframes.push((id.to_string(), to as i32, track));
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_element_keyframes(&mut self, id: &str, element: usize) {
|
||||
self.keyframes
|
||||
.retain(|(i, e, _)| !(i == id && *e == element as i32));
|
||||
}
|
||||
|
||||
/// The keyframe track for (input, element), if any.
|
||||
pub fn keyframe_track(&self, id: &str, element: i32) -> Option<&KeyframeTrack> {
|
||||
self.keyframes
|
||||
.iter()
|
||||
.find(|(i, e, _)| i == id && *e == element)
|
||||
.map(|(_, _, t)| t)
|
||||
}
|
||||
|
||||
/// Mutable keyframe track access, creating one on demand.
|
||||
pub fn keyframe_track_mut(&mut self, id: &str, element: i32) -> &mut KeyframeTrack {
|
||||
if let Some(i) = self
|
||||
.keyframes
|
||||
.iter()
|
||||
.position(|(i, e, _)| i == id && *e == element)
|
||||
{
|
||||
return &mut self.keyframes[i].2;
|
||||
}
|
||||
self.keyframes.push((id.to_string(), element, KeyframeTrack::default()));
|
||||
let last = self.keyframes.len() - 1;
|
||||
&mut self.keyframes[last].2
|
||||
}
|
||||
|
||||
/// Standard (non-keyframed) value of (input, element): the per-element
|
||||
/// override or the input's default (C++ `get_standard_value`).
|
||||
pub fn standard_value(&self, id: &str, element: i32) -> crate::value::NodeValue {
|
||||
self.standard_values
|
||||
.get(&(id.to_string(), element))
|
||||
.cloned()
|
||||
.or_else(|| self.get_input(id).map(|i| i.default.clone()))
|
||||
.unwrap_or(crate::value::NodeValue::None)
|
||||
}
|
||||
|
||||
/// Set the standard value of (input, element) (C++ `set_standard_value`).
|
||||
pub fn set_standard_value(
|
||||
&mut self,
|
||||
id: &str,
|
||||
element: i32,
|
||||
value: crate::value::NodeValue,
|
||||
) {
|
||||
self.standard_values.insert((id.to_string(), element), value);
|
||||
}
|
||||
|
||||
/// Value of `input` at `time`: keyframes when the (input, element)
|
||||
/// track is non-empty, else the standard value (C++
|
||||
/// `get_value_at_time`; `// CPP-PARITY: node.cpp:465`).
|
||||
pub fn value_at_time(&self, id: &str, element: i32, time: oakcore_rs::Rational) -> crate::value::NodeValue {
|
||||
match self.keyframe_track(id, element) {
|
||||
Some(track) if !track.keys().is_empty() => track
|
||||
.value_at(time)
|
||||
.unwrap_or_else(|| self.standard_value(id, element)),
|
||||
_ => self.standard_value(id, element),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether (input, element) is being keyframed (C++
|
||||
/// `Node::is_input_keyframing`): the keyframe track exists and is
|
||||
/// non-empty.
|
||||
pub fn is_input_keyframing(&self, id: &str, element: i32) -> bool {
|
||||
self.keyframe_track(id, element)
|
||||
.map(|t| !t.keys().is_empty())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Whether (input, element) is static at evaluation time (C++
|
||||
/// `Node::is_input_static`): neither connected nor keyframed.
|
||||
/// `inputs` is the render-time input row — a connected input appears
|
||||
/// in the row under its id.
|
||||
pub fn is_input_static(&self, inputs: &crate::value::NodeValueRow, id: &str, element: i32) -> bool {
|
||||
!inputs.contains_key(id) && !self.is_input_keyframing(id, element)
|
||||
}
|
||||
|
||||
/// Set the value hint for (input, element) (C++ `set_value_hint_for_input`).
|
||||
pub fn set_value_hint(&mut self, id: &str, element: i32, hint: ValueHint) {
|
||||
if let Some(slot) = self.hints.iter_mut().find(|((i, e), _)| i == id && *e == element) {
|
||||
slot.1 = hint;
|
||||
} else {
|
||||
self.hints.push(((id.to_string(), element), hint));
|
||||
}
|
||||
}
|
||||
|
||||
/// The value hint for (input, element), if any.
|
||||
pub fn value_hint(&self, id: &str, element: i32) -> Option<&ValueHint> {
|
||||
self.hints
|
||||
.iter()
|
||||
.find(|((i, e), _)| i == id && *e == element)
|
||||
.map(|(_, h)| h)
|
||||
}
|
||||
|
||||
/// True when `context` appears in this node's context-position map
|
||||
/// (C++ `context_contains_node`).
|
||||
pub fn context_contains(&self, context: NodeId) -> bool {
|
||||
self.context_positions.iter().any(|(c, _, _)| *c == context)
|
||||
}
|
||||
|
||||
/// Set this node's position in `context` (C++
|
||||
/// `set_node_position_in_context`). Returns true when newly added.
|
||||
pub fn set_context_position(&mut self, context: NodeId, x: f64, y: f64, expanded: bool) -> bool {
|
||||
let added = !self.context_contains(context);
|
||||
if let Some(slot) = self.context_positions.iter_mut().find(|(c, _, _)| *c == context) {
|
||||
slot.1 = (x, y);
|
||||
slot.2 = expanded;
|
||||
} else {
|
||||
self.context_positions.push((context, (x, y), expanded));
|
||||
}
|
||||
added
|
||||
}
|
||||
|
||||
/// Remove this node from `context`; false when absent (C++
|
||||
/// `remove_node_from_context`).
|
||||
pub fn remove_from_context(&mut self, context: NodeId) -> bool {
|
||||
let before = self.context_positions.len();
|
||||
self.context_positions.retain(|(c, _, _)| *c != context);
|
||||
self.context_positions.len() != before
|
||||
}
|
||||
}
|
||||
|
||||
/// The node's oakrender caches (frame/thumbnail/audio/waveform),
|
||||
/// owned handles released with the node.
|
||||
#[derive(Clone)]
|
||||
pub struct NodeCaches {
|
||||
/// Video frame hash cache.
|
||||
pub video: crate::bridge::render::CacheHandle,
|
||||
/// Thumbnail cache.
|
||||
pub thumbnail: crate::bridge::render::CacheHandle,
|
||||
/// Audio playback cache.
|
||||
pub audio: crate::bridge::render::CacheHandle,
|
||||
/// Waveform cache.
|
||||
pub waveform: crate::bridge::render::CacheHandle,
|
||||
}
|
||||
|
||||
impl Default for NodeCaches {
|
||||
/// All empty handles (caches are created lazily through bridge::render
|
||||
/// when a node enters a project; `// CPP-PARITY: node.cpp:102`).
|
||||
fn default() -> Self {
|
||||
NodeCaches {
|
||||
video: crate::handle::CHandle::null(),
|
||||
thumbnail: crate::handle::CHandle::null(),
|
||||
audio: crate::handle::CHandle::null(),
|
||||
waveform: crate::handle::CHandle::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The polymorphic surface of a node — every C++ virtual on `Node`
|
||||
/// becomes a method here (see COVERAGE.md §1/§7/§8/§10).
|
||||
pub trait NodeBehavior: Send {
|
||||
/// Human-readable name (C++ `name()`).
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// Short menu name (C++ `short_name()`; defaults to [`Self::name`]).
|
||||
fn short_name(&self) -> &str {
|
||||
self.name()
|
||||
}
|
||||
|
||||
/// Stable type id (C++ `id()`).
|
||||
fn type_id(&self) -> &str;
|
||||
|
||||
/// Categories (C++ `category()`).
|
||||
fn categories(&self) -> &[Category] {
|
||||
&[]
|
||||
}
|
||||
|
||||
/// Sub-category (C++ `sub_category()`).
|
||||
fn sub_category(&self) -> &str {
|
||||
""
|
||||
}
|
||||
|
||||
/// Description (C++ `description()`).
|
||||
fn description(&self) -> &str {
|
||||
""
|
||||
}
|
||||
|
||||
/// Localized input name (C++ `get_input_name()` virtual).
|
||||
fn input_name<'a>(&self, id: &'a str) -> &'a str {
|
||||
// The standard enabled input displays as "Enabled" on every node
|
||||
// (`// CPP-PARITY: node.cpp:155` retranslate).
|
||||
if id == crate::node::ENABLED_INPUT {
|
||||
"Enabled"
|
||||
} else {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
/// Inputs excluded from rendering (C++ `ignore_inputs_for_rendering()`).
|
||||
fn ignore_inputs_for_rendering(&self) -> &[String] {
|
||||
&[]
|
||||
}
|
||||
|
||||
/// Array elements active at `time` (C++ `get_active_elements_at_time()`).
|
||||
fn active_elements_at_time(&self, input: &str, time: Rational) -> Vec<i32> {
|
||||
let _ = (input, time);
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
/// Cache ranges (C++ `get_video_cache_range()` /
|
||||
/// `get_audio_cache_range()`).
|
||||
fn video_cache_range(&self, core: &NodeCore) -> TimeRange {
|
||||
let _ = core;
|
||||
TimeRange::default()
|
||||
}
|
||||
|
||||
/// Audio cache range.
|
||||
fn audio_cache_range(&self, core: &NodeCore) -> TimeRange {
|
||||
let _ = core;
|
||||
TimeRange::default()
|
||||
}
|
||||
|
||||
/// Value hint for an input (C++ `get_value_hint_for_input()` virtual).
|
||||
fn value_hint_for_input(&self, input: &str) -> Option<ValueHint> {
|
||||
let _ = input;
|
||||
None
|
||||
}
|
||||
|
||||
/// Render-time connection resolution (C++
|
||||
/// `get_connected_render_output()`; Group overrides).
|
||||
fn connected_render_output(&self, core: &NodeCore, input: &str, element: i32) -> Option<NodeId> {
|
||||
let _ = (core, input, element);
|
||||
None
|
||||
}
|
||||
|
||||
/// Time adjustment through this node (C++
|
||||
/// `input_time_adjustment()`/`output_time_adjustment()`; clips
|
||||
/// override for speed/reverse).
|
||||
fn input_time_adjustment(&self, input: &str, element: i32, time: TimeRange, traverse: bool) -> TimeRange {
|
||||
let _ = (input, element, traverse);
|
||||
time
|
||||
}
|
||||
|
||||
/// Output-side time adjustment.
|
||||
fn output_time_adjustment(&self, input: &str, element: i32, time: TimeRange, traverse: bool) -> TimeRange {
|
||||
let _ = (input, element, traverse);
|
||||
time
|
||||
}
|
||||
|
||||
/// Evaluate outputs (C++ `value()`).
|
||||
fn value(&self, core: &NodeCore, inputs: &NodeValueRow, time: Rational, table: &mut NodeValueTable) {
|
||||
let _ = (core, inputs, time, table);
|
||||
}
|
||||
|
||||
/// Process a span of samples (C++ `process_samples()`).
|
||||
fn process_samples(
|
||||
&self,
|
||||
core: &NodeCore,
|
||||
inputs: &NodeValueRow,
|
||||
range: TimeRange,
|
||||
output: &mut crate::value::SampleBuffer,
|
||||
) {
|
||||
let _ = (core, inputs, range, output);
|
||||
}
|
||||
|
||||
/// Direct frame generation (C++ `generate_frame()`; CPU-render
|
||||
/// nodes).
|
||||
fn generate_frame(&self, core: &NodeCore, frame: &mut crate::bridge::render::TextureHandle, time: Rational) {
|
||||
let _ = (core, frame, time);
|
||||
}
|
||||
|
||||
/// Shader code request (C++ `get_shader_code()`; GPU nodes).
|
||||
fn shader_code(&self, request: &str) -> Option<String> {
|
||||
let _ = request;
|
||||
None
|
||||
}
|
||||
|
||||
/// Gizmo transform/positions (C++ `gizmo_transformation()` /
|
||||
/// `update_gizmo_positions()`).
|
||||
fn gizmo_update(&self, core: &NodeCore, row: &NodeValueRow) {
|
||||
let _ = (core, row);
|
||||
}
|
||||
|
||||
/// Gizmo drag callbacks (C++ `gizmo_drag_start/move`).
|
||||
fn gizmo_drag(&mut self, core: &mut NodeCore, start: bool, x: f64, y: f64, modifiers: u32) {
|
||||
let _ = (core, start, x, y, modifiers);
|
||||
}
|
||||
|
||||
/// Input value changed (C++ `InputValueChangedEvent`).
|
||||
fn input_value_changed(&mut self, core: &mut NodeCore, input: &str, element: i32) {
|
||||
let _ = (core, input, element);
|
||||
}
|
||||
|
||||
/// Edge connected to an input (C++ `InputConnectedEvent`).
|
||||
fn input_connected(&mut self, core: &mut NodeCore, input: &str, element: i32, source: NodeId) {
|
||||
let _ = (core, input, element, source);
|
||||
}
|
||||
|
||||
/// Edge disconnected from an input (C++ `InputDisconnectedEvent`).
|
||||
fn input_disconnected(&mut self, core: &mut NodeCore, input: &str, element: i32, source: NodeId) {
|
||||
let _ = (core, input, element, source);
|
||||
}
|
||||
|
||||
/// Someone connected to this node's output (C++
|
||||
/// `OutputConnectedEvent`).
|
||||
fn output_connected(&mut self, core: &mut NodeCore, target: NodeId, input: &str, element: i32) {
|
||||
let _ = (core, target, input, element);
|
||||
}
|
||||
|
||||
/// Output disconnected (C++ `OutputDisconnectedEvent`).
|
||||
fn output_disconnected(&mut self, core: &mut NodeCore, target: NodeId, input: &str, element: i32) {
|
||||
let _ = (core, target, input, element);
|
||||
}
|
||||
|
||||
/// Attached to a preview/viewer (C++ `ConnectedToPreviewEvent`).
|
||||
fn connected_to_preview(&mut self, core: &mut NodeCore) {
|
||||
let _ = core;
|
||||
}
|
||||
|
||||
/// Inserted into / removed from a project graph (C++
|
||||
/// `AddedToGraphEvent` / `RemovedFromGraphEvent`).
|
||||
fn added_to_graph(&mut self, core: &mut NodeCore) {
|
||||
let _ = core;
|
||||
}
|
||||
|
||||
/// See [`NodeBehavior::added_to_graph`].
|
||||
fn removed_from_graph(&mut self, core: &mut NodeCore) {
|
||||
let _ = core;
|
||||
}
|
||||
|
||||
/// Node links changed (C++ `LinkChangeEvent`).
|
||||
fn link_changed(&mut self, core: &mut NodeCore) {
|
||||
let _ = core;
|
||||
}
|
||||
|
||||
/// Deep copy (C++ `copy()`); None = not copiable.
|
||||
fn duplicate(&self, core: &NodeCore) -> Option<Box<dyn NodeBehavior>>;
|
||||
|
||||
/// Custom load/save (C++ `load_custom()`/`save_custom()`).
|
||||
fn load_custom(&mut self, core: &mut NodeCore, reader: &mut dyn crate::serializer::XmlRead) -> bool {
|
||||
let _ = (core, reader);
|
||||
true
|
||||
}
|
||||
|
||||
/// See [`NodeBehavior::load_custom`].
|
||||
fn save_custom(&self, core: &NodeCore, writer: &mut dyn crate::serializer::XmlWrite) {
|
||||
let _ = (core, writer);
|
||||
}
|
||||
|
||||
/// Post-load fixups (C++ `PostLoadEvent` / `LoadFinishedEvent`).
|
||||
fn post_load(&mut self, core: &mut NodeCore) {
|
||||
let _ = core;
|
||||
}
|
||||
|
||||
/// Legacy input id mapping for old project versions (C++
|
||||
/// `get_input_id_for_legacy_id()`; default identity).
|
||||
fn map_legacy_input_id<'a>(&self, id: &'a str) -> &'a str {
|
||||
id
|
||||
}
|
||||
|
||||
/// Downcast to the concrete behavior (used by the timeline families
|
||||
/// to reach `FolderBehavior`/`TrackBehavior`/`SequenceBehavior`
|
||||
/// state). Default `None`; concrete behaviors override.
|
||||
fn as_any(&self) -> Option<&dyn std::any::Any> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Mutable downcast (see [`NodeBehavior::as_any`]).
|
||||
fn as_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
|
||||
None
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user