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}