Files
oak-editor/crates/oaknode/src/nodes/shapenodebase.rs
T
Mike-Solar 18ff60f147 feat(engine): clip move, clip effect_input, mandatory static FFmpeg
- 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)
2026-08-11 23:04:48 +08:00

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"
);
}
}