1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
//! World-space annotation labels that project 3D positions to 2D screen coordinates.
//!
//! # Purpose
//!
//! Callers can pin text labels to arbitrary 3D world positions (peak pressure values,
//! boundary condition names, measurement callouts) that are reprojected to screen space
//! each frame. Rendering is handled by the caller via egui (or any 2D drawing API) so
//! there is no wgpu pipeline involved.
//!
//! # Typical usage
//!
//! ```rust,ignore
//! // Build a label once (or each frame if the data changes).
//! let mut label = AnnotationLabel::default();
//! label.world_pos = glam::Vec3::new(2.0, 3.0, 0.0);
//! label.text = "Peak pressure: 101.3 kPa".to_string();
//! label.leader_end = Some(glam::Vec3::new(1.0, 1.0, 0.0));
//!
//! // Each frame, project to screen.
//! let view = camera.view_matrix();
//! let proj = camera.proj_matrix();
//! if let Some(screen_pos) = world_to_screen(label.world_pos, &view, &proj, viewport_size) {
//! // Draw with your 2D API here.
//! }
//! ```
use crateFrameData;
/// A text label pinned to a 3D world position.
///
/// Labels are rendered by the caller (typically via egui's painter) after projecting
/// the world position to screen space using [`world_to_screen`] or [`world_to_screen_from_frame`].
/// No wgpu pipeline changes are required.
///
/// # Examples
///
/// ```rust
/// # use viewport_lib::annotation::AnnotationLabel;
/// let mut label = AnnotationLabel::default();
/// label.world_pos = glam::Vec3::new(0.0, 5.0, 0.0);
/// label.text = "Inlet".to_string();
/// label.color = [0.4, 0.8, 1.0, 1.0]; // light blue
/// ```
/// Projects a 3D world-space position to 2D screen-space coordinates.
///
/// Returns `None` if the point is behind the camera (`clip.w <= 0`) or
/// outside the view frustum (NDC outside `[-1, 1]` on X or Y).
///
/// # Arguments
///
/// * `pos` — World-space position to project.
/// * `view` — Camera view matrix (world → view space).
/// * `proj` — Camera projection matrix (view space → clip space).
/// * `viewport_size` — Viewport dimensions in physical pixels `[width, height]`.
///
/// # Returns
///
/// Screen-space position in physical pixels with the origin at the top-left corner,
/// or `None` if the point would not be visible on screen.
///
/// # Examples
///
/// ```rust
/// # use viewport_lib::annotation::world_to_screen;
/// # use glam::{Mat4, Vec2, Vec3};
/// let view = Mat4::look_at_rh(Vec3::new(0.0, 0.0, 5.0), Vec3::ZERO, Vec3::Y);
/// let proj = Mat4::perspective_rh(std::f32::consts::FRAC_PI_4, 1.0, 0.1, 100.0);
/// let screen = world_to_screen(Vec3::ZERO, &view, &proj, [800.0, 600.0]);
/// assert!(screen.is_some()); // origin is in front of the camera
/// ```
/// Convenience wrapper that extracts camera matrices and viewport size from a [`FrameData`].
///
/// Equivalent to calling [`world_to_screen`] with `frame.camera_view`, `frame.camera_proj`,
/// and `frame.viewport_size`.
///
/// # Arguments
///
/// * `pos` — World-space position to project.
/// * `frame` — Current frame data containing camera matrices and viewport dimensions.
///
/// # Returns
///
/// Screen-space position in physical pixels (top-left origin), or `None` if not visible.
/// Draws a slice of [`AnnotationLabel`]s using an egui `Painter`.
///
/// For each label:
///
/// 1. Projects `world_pos` to screen space; skips the label if not visible.
/// 2. Converts `color` to `egui::Color32`.
/// 3. Draws the text with an optional semi-transparent background.
/// 4. If `leader_end` is `Some` and also projects successfully, draws a 1 px leader line
/// from the leader endpoint to the label anchor.
///
/// Clipping is handled automatically by egui: the painter clips to its own rect, so
/// passing a tight `clip_rect` (the viewport rectangle) is sufficient.
///
/// # Arguments
///
/// * `painter` — An egui painter whose clip rect covers the viewport.
/// * `labels` — Slice of labels to draw.
/// * `view` — Camera view matrix.
/// * `proj` — Camera projection matrix.
/// * `viewport_size` — Viewport in physical pixels `[width, height]`.
/// * `_clip_rect` — Viewport rectangle (egui clips automatically; passed for explicitness).
///
/// # Feature
///
/// This function is only available when the `egui` feature of `viewport-lib` is enabled.