transform_gizmo_egui/
lib.rs

1//! Provides a 3D transformation gizmo for the Egui library.
2//!
3//! transform-gizmo-egui provides a feature-rich and configurable gizmo
4//! that can be used for 3d transformations (translation, rotation, scale).
5//!
6//! # Usage
7//!
8//! Create a new `Gizmo` instance once.
9//!
10//! ```
11//! use transform_gizmo_egui::prelude::*;
12//!
13//! let gizmo = Gizmo::default();
14//! ```
15//!
16//! Update the gizmo configuration as needed, for example, when the camera moves.
17//!
18//! ```ignore
19//! gizmo.update_config(GizmoConfig {
20//!     view_matrix: view_matrix.into(),
21//!     projection_matrix: projection_matrix.into(),
22//!     modes: GizmoMode::all(),
23//!     orientation: GizmoOrientation::Local,
24//!     ..Default::default()
25//! });
26//! ```
27//!
28//! Finally, interact with the gizmo. The function takes a slice of transforms as an
29//! input. The result is [`Some`] if the gizmo was successfully interacted with this frame.
30//! In the result you can find the modified transforms, in the same order as was given to the function
31//! as arguments.
32//!
33//! ```ignore
34//!  let mut transform = Transform::from_scale_rotation_translation(scale, rotation, translation);
35//!
36//!  if let Some((result, new_transforms)) = gizmo.interact(ui, &[transform]) {
37//!      for (new_transform, transform) in
38//!          new_transforms.iter().zip(std::iter::once(&mut transform))
39//!      {
40//!          // Apply the modified transforms
41//!          *transform = *new_transform;
42//!      }
43//!  }
44//! ```
45//!
46//!
47use egui::{Mesh, PointerButton, Pos2, Rgba, Sense, Ui, Vec2, epaint::Vertex};
48
49use transform_gizmo::math::Transform;
50pub use transform_gizmo::*;
51pub mod prelude;
52
53pub trait GizmoExt {
54    /// Interact with the gizmo and draw it to Ui.
55    ///
56    /// Returns result of the gizmo interaction.
57    fn interact(&mut self, ui: &Ui, targets: &[Transform])
58    -> Option<(GizmoResult, Vec<Transform>)>;
59}
60
61impl GizmoExt for Gizmo {
62    fn interact(
63        &mut self,
64        ui: &Ui,
65        targets: &[Transform],
66    ) -> Option<(GizmoResult, Vec<Transform>)> {
67        let cursor_pos = ui
68            .input(|input| input.pointer.hover_pos())
69            .unwrap_or_default();
70
71        let mut viewport = self.config().viewport;
72        if !viewport.is_finite() {
73            viewport = ui.clip_rect();
74        }
75
76        let egui_viewport = Rect {
77            min: Pos2::new(viewport.min.x, viewport.min.y),
78            max: Pos2::new(viewport.max.x, viewport.max.y),
79        };
80
81        self.update_config(GizmoConfig {
82            viewport,
83            pixels_per_point: ui.ctx().pixels_per_point(),
84            ..*self.config()
85        });
86
87        let interaction = ui.interact(
88            Rect::from_center_size(cursor_pos, Vec2::splat(1.0)),
89            ui.id().with("_interaction"),
90            Sense::click_and_drag(),
91        );
92        let hovered = interaction.hovered();
93
94        let gizmo_result = self.update(
95            GizmoInteraction {
96                cursor_pos: (cursor_pos.x, cursor_pos.y),
97                hovered,
98                drag_started: ui
99                    .input(|input| input.pointer.button_pressed(PointerButton::Primary)),
100                dragging: ui.input(|input| input.pointer.button_down(PointerButton::Primary)),
101            },
102            targets,
103        );
104
105        let draw_data = self.draw();
106
107        egui::Painter::new(ui.ctx().clone(), ui.layer_id(), egui_viewport).add(Mesh {
108            indices: draw_data.indices,
109            vertices: draw_data
110                .vertices
111                .into_iter()
112                .zip(draw_data.colors)
113                .map(|(pos, [r, g, b, a])| Vertex {
114                    pos: pos.into(),
115                    uv: Pos2::default(),
116                    color: Rgba::from_rgba_premultiplied(r, g, b, a).into(),
117                })
118                .collect(),
119            ..Default::default()
120        });
121
122        gizmo_result
123    }
124}