khora_data/render/gizmo.rs
1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Per-frame gizmo output slot for the
16//! [`OutputDeck`](khora_core::lane::OutputDeck).
17//!
18//! Producers (typically the editor application) push line instances
19//! into `GizmoFrame.lines`; the engine-side `GizmoLane` consumes the
20//! slot and renders the overlay. Empty by default — a frame with no
21//! gizmos triggers no GPU work.
22//!
23//! Symmetric to the [`ShadowFrame`](khora_core::renderer::api::shadow::ShadowFrame)
24//! pattern: only contract types live in `khora-data`, no GPU code.
25
26use khora_core::ui::editor::GizmoLineInstance;
27
28/// Per-frame gizmo lines published by a host application (editor,
29/// debug tooling) and consumed by the engine's `GizmoLane`.
30#[derive(Debug, Default, Clone)]
31pub struct GizmoFrame {
32 /// Line segments to draw this frame.
33 pub lines: Vec<GizmoLineInstance>,
34}
35
36impl GizmoFrame {
37 /// Returns true when no producer published any lines this frame.
38 pub fn is_empty(&self) -> bool {
39 self.lines.is_empty()
40 }
41
42 /// Returns the number of line instances queued this frame.
43 pub fn len(&self) -> usize {
44 self.lines.len()
45 }
46
47 /// Clears all queued lines.
48 pub fn clear(&mut self) {
49 self.lines.clear();
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use khora_core::lane::OutputDeck;
57 use khora_core::math::Vec3;
58
59 #[test]
60 fn gizmo_frame_deck_round_trip() {
61 let mut deck = OutputDeck::new();
62 {
63 let frame = deck.slot::<GizmoFrame>();
64 frame.lines.push(GizmoLineInstance::new(
65 Vec3::ZERO,
66 Vec3::new(1.0, 0.0, 0.0),
67 [1.0, 0.0, 0.0, 1.0],
68 ));
69 }
70 let taken: GizmoFrame = deck.take();
71 assert_eq!(taken.len(), 1);
72 }
73
74 #[test]
75 fn empty_by_default() {
76 let frame = GizmoFrame::default();
77 assert!(frame.is_empty());
78 }
79}