1#[derive(Debug, Clone, Copy)]
8pub struct PaneRenderGeometry {
9 pub window_width: f32,
10 pub window_height: f32,
11 pub pane_origin_x: f32,
12 pub pane_origin_y: f32,
13}
14
15#[allow(clippy::too_many_arguments)]
21pub(super) fn compute_graphic_geometry(
22 tex_w: f32,
23 tex_h: f32,
24 crop: [u32; 4],
25 width_cells: usize,
26 height_cells: usize,
27 cell_w: f32,
28 cell_h: f32,
29 clip_px: f32,
30 has_cols: bool,
31 has_rows: bool,
32 preserve_aspect: bool,
33 is_virtual: bool,
34 window_w: f32,
35 window_h: f32,
36) -> ([f32; 4], [f32; 2]) {
37 let has_crop = crop != [0, 0, 0, 0];
38
39 let (sx, sy, sw, sh) = if has_crop && tex_w > 0.0 && tex_h > 0.0 {
41 let x = (crop[0] as f32).min(tex_w);
42 let y = (crop[1] as f32).min(tex_h);
43 let w = if crop[2] > 0 {
44 (crop[2] as f32).min(tex_w - x)
45 } else {
46 tex_w - x
47 };
48 let h = if crop[3] > 0 {
49 (crop[3] as f32).min(tex_h - y)
50 } else {
51 tex_h - y
52 };
53 (x, y, w.max(0.0), h.max(0.0))
54 } else {
55 (0.0, 0.0, tex_w, tex_h)
56 };
57
58 if sw <= 0.0 || sh <= 0.0 {
67 return ([0.0, 0.0, 0.0, 0.0], [0.0, 0.0]);
68 }
69 let aspect = sw / sh;
70 let (dest_w, dest_h) = if is_virtual || (has_cols && has_rows) {
71 (width_cells as f32 * cell_w, height_cells as f32 * cell_h)
72 } else if has_cols && !has_rows {
73 let dw = width_cells as f32 * cell_w;
74 (dw, dw / aspect)
75 } else if has_rows && !has_cols {
76 let dh = height_cells as f32 * cell_h;
77 (dh * aspect, dh)
78 } else if has_crop && sw > 0.0 && sh > 0.0 {
79 (sw, sh)
80 } else if preserve_aspect && tex_w > 0.0 && tex_h > 0.0 {
81 (tex_w, tex_h)
82 } else {
83 (width_cells as f32 * cell_w, height_cells as f32 * cell_h)
84 };
85
86 let visible_frac = if dest_h > 0.0 {
88 ((dest_h - clip_px) / dest_h).clamp(0.0, 1.0)
89 } else {
90 0.0
91 };
92 let scrolled_frac = if dest_h > 0.0 {
93 (clip_px / dest_h).clamp(0.0, 1.0)
94 } else {
95 0.0
96 };
97
98 let uv = if sw > 0.0 && sh > 0.0 && tex_w > 0.0 && tex_h > 0.0 {
100 [
101 sx / tex_w,
102 (sy + sh * scrolled_frac) / tex_h,
103 sw / tex_w,
104 (sh * visible_frac) / tex_h,
105 ]
106 } else {
107 [0.0, 0.0, 1.0, 1.0]
108 };
109
110 let size = (dest_w / window_w, dest_h * visible_frac / window_h);
111
112 (uv, size.into())
113}
114
115#[cfg(test)]
116mod geometry_tests {
117 use super::compute_graphic_geometry;
118
119 const WW: f32 = 800.0;
120 const WH: f32 = 600.0;
121 const CW: f32 = 10.0;
122 const CH: f32 = 20.0;
123
124 #[test]
127 fn no_crop_both_cells_uses_dest_fraction_for_uv() {
128 let (uv, size) = compute_graphic_geometry(
129 100.0,
130 100.0,
131 [0, 0, 0, 0],
132 10,
133 2,
134 CW,
135 CH,
136 20.0, true,
138 true, false,
140 false, WW,
142 WH,
143 );
144 let expected_uv_y = 50.0 / 100.0;
145 let expected_uv_h = 50.0 / 100.0;
146 assert!((uv[1] - expected_uv_y).abs() < 1e-5);
147 assert!((uv[3] - expected_uv_h).abs() < 1e-5);
148 assert!((size[1] - 20.0 / WH).abs() < 1e-5);
149 }
150
151 #[test]
153 fn natural_crop_without_cells_uses_crop_height_for_dest() {
154 let (uv, size) = compute_graphic_geometry(
155 100.0,
156 100.0,
157 [0, 0, 0, 25],
158 1,
159 1,
160 CW,
161 CH,
162 20.0,
163 false,
164 false,
165 false,
166 false,
167 WW,
168 WH,
169 );
170 let expected_uv_y = (0.0 + 25.0 * 0.8) / 100.0;
171 let expected_uv_h = (25.0 * 0.2) / 100.0;
172 assert!((uv[1] - expected_uv_y).abs() < 1e-5);
173 assert!((uv[3] - expected_uv_h).abs() < 1e-5);
174 assert!((size[1] - 5.0 / WH).abs() < 1e-5);
175 }
176
177 #[test]
179 fn y_offset_produces_sub_row_clip() {
180 let top_px = -CH + 5.0;
181 let clip_px = (-top_px).max(0.0);
182 assert_eq!(clip_px, 15.0);
183
184 let (uv, size) = compute_graphic_geometry(
185 100.0,
186 100.0,
187 [0, 0, 0, 0],
188 10,
189 3,
190 CW,
191 CH,
192 clip_px,
193 true,
194 true,
195 false,
196 false,
197 WW,
198 WH,
199 );
200 assert!((uv[1] - 25.0 / 100.0).abs() < 1e-5);
201 assert!((uv[3] - 75.0 / 100.0).abs() < 1e-5);
202 assert!((size[1] - 45.0 / WH).abs() < 1e-5);
203 }
204
205 #[test]
207 fn c_only_computes_exact_height_from_aspect() {
208 let (_uv, size) = compute_graphic_geometry(
209 100.0,
210 100.0,
211 [0, 0, 0, 0],
212 5,
213 3,
214 CW,
215 CH,
216 0.0,
217 true,
218 false, false,
220 false,
221 WW,
222 WH,
223 );
224 assert!((size[0] - 50.0 / WW).abs() < 1e-5);
226 assert!((size[1] - 50.0 / WH).abs() < 1e-5);
227 }
228
229 #[test]
231 fn r_only_computes_exact_width_from_aspect() {
232 let (_uv, size) = compute_graphic_geometry(
233 100.0,
234 100.0,
235 [0, 0, 0, 0],
236 4,
237 2,
238 CW,
239 CH,
240 0.0,
241 false,
242 true, false,
244 false,
245 WW,
246 WH,
247 );
248 assert!((size[0] - 40.0 / WW).abs() < 1e-5);
249 assert!((size[1] - 40.0 / WH).abs() < 1e-5);
250 }
251
252 #[test]
254 fn c_only_wide_source_computes_proportional_height() {
255 let (_uv, size) = compute_graphic_geometry(
256 100.0,
257 50.0,
258 [0, 0, 0, 0],
259 5,
260 1,
261 CW,
262 CH,
263 0.0,
264 true,
265 false,
266 false,
267 false,
268 WW,
269 WH,
270 );
271 assert!((size[0] - 50.0 / WW).abs() < 1e-5);
272 assert!((size[1] - 25.0 / WH).abs() < 1e-5);
273 }
274
275 #[test]
279 fn zero_size_crop_at_edge_returns_zero_output() {
280 let (uv, size) = compute_graphic_geometry(
281 100.0,
282 100.0,
283 [100, 0, 0, 0],
284 5,
285 3,
286 CW,
287 CH,
288 0.0,
289 true,
290 false,
291 false,
292 false,
293 WW,
294 WH,
295 );
296 assert_eq!(uv, [0.0, 0.0, 0.0, 0.0]);
298 assert_eq!(size, [0.0, 0.0]);
299 }
300}