- oakengine_sequence_move_clip implemented for real (oaktimeline TrackMoveBlockCommand; fixes the graph-ownership/gap-anchor/ripple trim bugs the stub was hiding); same-track via the frozen C ABI, cross-track supported by the module command - oaknode clip blocks now declare a tex_in texture input and set effect_input to it, so timeline clips can host effect chains; facade test covers effect insert/remove on a real clip - oakffmpeg-link: FFMPEG_DIR is now mandatory with a clear panic (a Homebrew upgrade left the system ffmpeg .pc pointing at a deleted dav1d Cellar path, breaking links); reads a git-ignored workspace .env for IDEs that cannot inject env vars (RustRover); links the C++ stdlib for C++ codec libs (svt-av1) - oakengine re-exports oaknode so tests share one crate instance; it_node uses the direct instance's value type where it calls the module FFI (the --workspace dev-dependency feature split builds oaknode twice)
142 lines
5.7 KiB
Rust
142 lines
5.7 KiB
Rust
// 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/>.
|
|
|
|
//! Shared shape-node base (C++
|
|
//! `src/node/src/generator/shape/shapenodebase.{h,cpp}`,
|
|
//! `olive::ShapeNodeBase`).
|
|
//!
|
|
//! Abstract C++ base (extends `GeneratorWithMerge`, see
|
|
//! [`super::generatorwithmerge`]) for shape generators with position,
|
|
//! size, color and rect-scaling gizmos. Not instantiable, so this is a
|
|
//! helper module, not a [`NodeBehavior`] implementation.
|
|
|
|
/// Position input id (C++ `k_position_input`). Type: vec2; default
|
|
/// `(0, 0)`.
|
|
pub const POSITION_INPUT: &str = "pos_in";
|
|
|
|
/// Size input id (C++ `k_size_input`). Type: vec2; default
|
|
/// `(100, 100)`; properties: `min = (0, 0)`.
|
|
pub const SIZE_INPUT: &str = "size_in";
|
|
|
|
/// Color input id (C++ `k_color_input`). Type: color; default
|
|
/// `(1.0, 0.0, 0.0, 1.0)`; only added when the base is constructed with
|
|
/// `create_color_input = true`.
|
|
pub const COLOR_INPUT: &str = "color_in";
|
|
|
|
/// Helper mirroring the C++ `ShapeNodeBase` base.
|
|
///
|
|
/// The C++ members `point_gizmo_[k_gizmo_scale_count]` (8 rect-scale
|
|
/// point gizmos) and `poly_gizmo_` (whole-rect draggable polygon
|
|
/// gizmo) are GUI gizmo pointers with no Rust equivalent here; gizmos
|
|
/// are tracked in `NodeCore::gizmos`, so they are omitted. The
|
|
/// `k_gizmo_scale_*` index constants come from the C++ gizmo layer.
|
|
pub struct ShapeNodeBase;
|
|
|
|
impl ShapeNodeBase {
|
|
/// Localized base input names (C++ `retranslate()` on top of the
|
|
/// merge base): `pos_in` -> "Position", `size_in` -> "Size", and
|
|
/// `color_in` -> "Color" when the color input exists.
|
|
pub fn input_name(id: &str) -> &str {
|
|
match id {
|
|
POSITION_INPUT => "Position",
|
|
SIZE_INPUT => "Size",
|
|
COLOR_INPUT => "Color",
|
|
_ => id,
|
|
}
|
|
}
|
|
|
|
/// Gizmo layout (C++ `update_gizmo_positions()`): centers the rect
|
|
/// around the square-resolution midpoint (also stored as the
|
|
/// `offset` property of `pos_in` so values appear top-left
|
|
/// anchored), then places the 8 scale-point gizmos at the rect's
|
|
/// corners/edge centers and the polygon gizmo on the four corners
|
|
/// in top-left, top-right, bottom-right, bottom-left order.
|
|
///
|
|
/// The C++ writes the resulting points into its `PointGizmo` /
|
|
/// `PolygonGizmo` objects; the Rust `NodeCore::gizmos` records only
|
|
/// each gizmo's tracked input references and drag position, so the
|
|
/// only persistable half is the `offset` property — which requires
|
|
/// the square resolution from `NodeGlobals`, not carried by this
|
|
/// signature. The property write and the gizmo point placements are
|
|
/// therefore not representable here (`// CPP-PARITY:
|
|
/// shapenodebase.cpp` `update_gizmo_positions`).
|
|
pub fn update_gizmo_positions(
|
|
core: &mut crate::node::NodeCore,
|
|
row: &crate::value::NodeValueRow,
|
|
) {
|
|
let _ = (core, row);
|
|
}
|
|
|
|
/// Undoable rect assignment (C++ `set_rect()`): normalizes the rect
|
|
/// around the sequence center, then pushes undo children setting
|
|
/// `size_in` x/y and `pos_in` x/y standard values.
|
|
///
|
|
/// The C++ normalization needs the sequence resolution and the undo
|
|
/// command stack; neither is carried by this signature or this
|
|
/// crate's data model, so the writes are not representable here
|
|
/// (`// CPP-PARITY: shapenodebase.cpp` `set_rect`).
|
|
pub fn set_rect(core: &mut crate::node::NodeCore, rect: (f64, f64, f64, f64)) {
|
|
let _ = (core, rect);
|
|
}
|
|
|
|
/// Gizmo drag (C++ `gizmo_drag_move()`): dragging the polygon
|
|
/// gizmo offsets `pos_in` x/y directly; dragging a scale-point
|
|
/// gizmo resizes with anchor-at-opposite-point semantics —
|
|
/// Alt drags from center, Shift keeps the original aspect ratio
|
|
/// (center-edge gizmos derive the other axis from the ratio,
|
|
/// corner gizmos reconstruct both axes from the original angle and
|
|
/// the new hypotenuse) — and writes the new position/size through
|
|
/// the gizmo's four input draggers.
|
|
///
|
|
/// The C++ logic operates on its `DraggableGizmo`/dragger objects
|
|
/// with per-gizmo start values and keyframe-track references; the
|
|
/// Rust `NodeCore::gizmos` has no dragger state, so the drag is not
|
|
/// representable here (`// CPP-PARITY: shapenodebase.cpp`
|
|
/// `gizmo_drag_move`).
|
|
pub fn gizmo_drag_move(core: &mut crate::node::NodeCore, x: f64, y: f64, modifiers: u32) {
|
|
let _ = (core, x, y, modifiers);
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::node::NodeCore;
|
|
|
|
#[test]
|
|
fn input_names() {
|
|
assert_eq!(ShapeNodeBase::input_name(POSITION_INPUT), "Position");
|
|
assert_eq!(ShapeNodeBase::input_name(SIZE_INPUT), "Size");
|
|
assert_eq!(ShapeNodeBase::input_name(COLOR_INPUT), "Color");
|
|
assert_eq!(ShapeNodeBase::input_name("other_in"), "other_in");
|
|
}
|
|
|
|
#[test]
|
|
fn gizmo_helpers_are_documented_noops() {
|
|
// The gizmo data model is not representable in NodeCore (see the
|
|
// method docs); the calls must be safe no-ops.
|
|
let mut core = NodeCore::new();
|
|
let row = crate::value::NodeValueRow::default();
|
|
ShapeNodeBase::update_gizmo_positions(&mut core, &row);
|
|
ShapeNodeBase::set_rect(&mut core, (0.0, 0.0, 100.0, 100.0));
|
|
ShapeNodeBase::gizmo_drag_move(&mut core, 10.0, 20.0, 0);
|
|
assert!(
|
|
core.get_input(POSITION_INPUT).is_none(),
|
|
"no inputs are added"
|
|
);
|
|
}
|
|
}
|