1use crate::trans_affine::TransAffine;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum AspectRatio {
15 Stretch,
17 Meet,
19 Slice,
21}
22
23pub struct TransViewport {
35 world_x1: f64,
36 world_y1: f64,
37 world_x2: f64,
38 world_y2: f64,
39 device_x1: f64,
40 device_y1: f64,
41 device_x2: f64,
42 device_y2: f64,
43 aspect: AspectRatio,
44 is_valid: bool,
45 align_x: f64,
46 align_y: f64,
47 wx1: f64,
49 wy1: f64,
50 wx2: f64,
51 wy2: f64,
52 dx1: f64,
53 dy1: f64,
54 kx: f64,
55 ky: f64,
56}
57
58impl Default for TransViewport {
59 fn default() -> Self {
60 Self::new()
61 }
62}
63
64impl TransViewport {
65 pub fn new() -> Self {
66 Self {
67 world_x1: 0.0,
68 world_y1: 0.0,
69 world_x2: 1.0,
70 world_y2: 1.0,
71 device_x1: 0.0,
72 device_y1: 0.0,
73 device_x2: 1.0,
74 device_y2: 1.0,
75 aspect: AspectRatio::Stretch,
76 is_valid: true,
77 align_x: 0.5,
78 align_y: 0.5,
79 wx1: 0.0,
80 wy1: 0.0,
81 wx2: 1.0,
82 wy2: 1.0,
83 dx1: 0.0,
84 dy1: 0.0,
85 kx: 1.0,
86 ky: 1.0,
87 }
88 }
89
90 pub fn preserve_aspect_ratio(&mut self, align_x: f64, align_y: f64, aspect: AspectRatio) {
92 self.align_x = align_x;
93 self.align_y = align_y;
94 self.aspect = aspect;
95 self.update();
96 }
97
98 pub fn set_device_viewport(&mut self, x1: f64, y1: f64, x2: f64, y2: f64) {
100 self.device_x1 = x1;
101 self.device_y1 = y1;
102 self.device_x2 = x2;
103 self.device_y2 = y2;
104 self.update();
105 }
106
107 pub fn set_world_viewport(&mut self, x1: f64, y1: f64, x2: f64, y2: f64) {
109 self.world_x1 = x1;
110 self.world_y1 = y1;
111 self.world_x2 = x2;
112 self.world_y2 = y2;
113 self.update();
114 }
115
116 pub fn device_viewport(&self) -> (f64, f64, f64, f64) {
118 (
119 self.device_x1,
120 self.device_y1,
121 self.device_x2,
122 self.device_y2,
123 )
124 }
125
126 pub fn world_viewport(&self) -> (f64, f64, f64, f64) {
128 (self.world_x1, self.world_y1, self.world_x2, self.world_y2)
129 }
130
131 pub fn world_viewport_actual(&self) -> (f64, f64, f64, f64) {
133 (self.wx1, self.wy1, self.wx2, self.wy2)
134 }
135
136 pub fn is_valid(&self) -> bool {
137 self.is_valid
138 }
139
140 pub fn align_x(&self) -> f64 {
141 self.align_x
142 }
143
144 pub fn align_y(&self) -> f64 {
145 self.align_y
146 }
147
148 pub fn aspect_ratio(&self) -> AspectRatio {
149 self.aspect
150 }
151
152 pub fn transform(&self, x: &mut f64, y: &mut f64) {
154 *x = (*x - self.wx1) * self.kx + self.dx1;
155 *y = (*y - self.wy1) * self.ky + self.dy1;
156 }
157
158 pub fn transform_scale_only(&self, x: &mut f64, y: &mut f64) {
160 *x *= self.kx;
161 *y *= self.ky;
162 }
163
164 pub fn inverse_transform(&self, x: &mut f64, y: &mut f64) {
166 *x = (*x - self.dx1) / self.kx + self.wx1;
167 *y = (*y - self.dy1) / self.ky + self.wy1;
168 }
169
170 pub fn inverse_transform_scale_only(&self, x: &mut f64, y: &mut f64) {
172 *x /= self.kx;
173 *y /= self.ky;
174 }
175
176 pub fn device_dx(&self) -> f64 {
177 self.dx1 - self.wx1 * self.kx
178 }
179
180 pub fn device_dy(&self) -> f64 {
181 self.dy1 - self.wy1 * self.ky
182 }
183
184 pub fn scale_x(&self) -> f64 {
185 self.kx
186 }
187
188 pub fn scale_y(&self) -> f64 {
189 self.ky
190 }
191
192 pub fn scale(&self) -> f64 {
193 (self.kx + self.ky) * 0.5
194 }
195
196 pub fn to_affine(&self) -> TransAffine {
198 let mut mtx = TransAffine::new_translation(-self.wx1, -self.wy1);
199 mtx.multiply(&TransAffine::new_scaling(self.kx, self.ky));
200 mtx.multiply(&TransAffine::new_translation(self.dx1, self.dy1));
201 mtx
202 }
203
204 pub fn to_affine_scale_only(&self) -> TransAffine {
206 TransAffine::new_scaling(self.kx, self.ky)
207 }
208
209 fn update(&mut self) {
210 const EPSILON: f64 = 1e-30;
211 if (self.world_x1 - self.world_x2).abs() < EPSILON
212 || (self.world_y1 - self.world_y2).abs() < EPSILON
213 || (self.device_x1 - self.device_x2).abs() < EPSILON
214 || (self.device_y1 - self.device_y2).abs() < EPSILON
215 {
216 self.wx1 = self.world_x1;
217 self.wy1 = self.world_y1;
218 self.wx2 = self.world_x1 + 1.0;
219 self.wy2 = self.world_y2 + 1.0;
220 self.dx1 = self.device_x1;
221 self.dy1 = self.device_y1;
222 self.kx = 1.0;
223 self.ky = 1.0;
224 self.is_valid = false;
225 return;
226 }
227
228 let mut world_x1 = self.world_x1;
229 let mut world_y1 = self.world_y1;
230 let mut world_x2 = self.world_x2;
231 let mut world_y2 = self.world_y2;
232 let device_x1 = self.device_x1;
233 let device_y1 = self.device_y1;
234 let device_x2 = self.device_x2;
235 let device_y2 = self.device_y2;
236
237 if self.aspect != AspectRatio::Stretch {
238 self.kx = (device_x2 - device_x1) / (world_x2 - world_x1);
239 self.ky = (device_y2 - device_y1) / (world_y2 - world_y1);
240
241 if (self.aspect == AspectRatio::Meet) == (self.kx < self.ky) {
242 let d = (world_y2 - world_y1) * self.ky / self.kx;
243 world_y1 += (world_y2 - world_y1 - d) * self.align_y;
244 world_y2 = world_y1 + d;
245 } else {
246 let d = (world_x2 - world_x1) * self.kx / self.ky;
247 world_x1 += (world_x2 - world_x1 - d) * self.align_x;
248 world_x2 = world_x1 + d;
249 }
250 }
251
252 self.wx1 = world_x1;
253 self.wy1 = world_y1;
254 self.wx2 = world_x2;
255 self.wy2 = world_y2;
256 self.dx1 = device_x1;
257 self.dy1 = device_y1;
258 self.kx = (device_x2 - device_x1) / (world_x2 - world_x1);
259 self.ky = (device_y2 - device_y1) / (world_y2 - world_y1);
260 self.is_valid = true;
261 }
262}
263
264#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn test_default_identity() {
274 let vp = TransViewport::new();
275 assert!(vp.is_valid());
276 assert_eq!(vp.scale_x(), 1.0);
277 assert_eq!(vp.scale_y(), 1.0);
278 }
279
280 #[test]
281 fn test_stretch_scaling() {
282 let mut vp = TransViewport::new();
283 vp.set_world_viewport(0.0, 0.0, 100.0, 100.0);
284 vp.set_device_viewport(0.0, 0.0, 200.0, 400.0);
285 assert_eq!(vp.scale_x(), 2.0);
286 assert_eq!(vp.scale_y(), 4.0);
287 }
288
289 #[test]
290 fn test_transform() {
291 let mut vp = TransViewport::new();
292 vp.set_world_viewport(0.0, 0.0, 100.0, 100.0);
293 vp.set_device_viewport(0.0, 0.0, 200.0, 200.0);
294
295 let mut x = 50.0;
296 let mut y = 50.0;
297 vp.transform(&mut x, &mut y);
298 assert_eq!(x, 100.0);
299 assert_eq!(y, 100.0);
300 }
301
302 #[test]
303 fn test_inverse_transform() {
304 let mut vp = TransViewport::new();
305 vp.set_world_viewport(0.0, 0.0, 100.0, 100.0);
306 vp.set_device_viewport(0.0, 0.0, 200.0, 200.0);
307
308 let mut x = 100.0;
309 let mut y = 100.0;
310 vp.inverse_transform(&mut x, &mut y);
311 assert_eq!(x, 50.0);
312 assert_eq!(y, 50.0);
313 }
314
315 #[test]
316 fn test_meet_aspect_ratio() {
317 let mut vp = TransViewport::new();
318 vp.set_world_viewport(0.0, 0.0, 100.0, 100.0);
319 vp.set_device_viewport(0.0, 0.0, 200.0, 400.0);
320 vp.preserve_aspect_ratio(0.5, 0.5, AspectRatio::Meet);
321 assert_eq!(vp.scale_x(), 2.0);
323 assert_eq!(vp.scale_y(), 2.0);
324 }
325
326 #[test]
327 fn test_slice_aspect_ratio() {
328 let mut vp = TransViewport::new();
329 vp.set_world_viewport(0.0, 0.0, 100.0, 100.0);
330 vp.set_device_viewport(0.0, 0.0, 200.0, 400.0);
331 vp.preserve_aspect_ratio(0.5, 0.5, AspectRatio::Slice);
332 assert_eq!(vp.scale_x(), 4.0);
334 assert_eq!(vp.scale_y(), 4.0);
335 }
336
337 #[test]
338 fn test_to_affine() {
339 let mut vp = TransViewport::new();
340 vp.set_world_viewport(0.0, 0.0, 100.0, 100.0);
341 vp.set_device_viewport(0.0, 0.0, 200.0, 200.0);
342
343 let mtx = vp.to_affine();
344 let mut x = 50.0;
345 let mut y = 50.0;
346 mtx.transform(&mut x, &mut y);
347 assert!((x - 100.0).abs() < 1e-10);
348 assert!((y - 100.0).abs() < 1e-10);
349 }
350
351 #[test]
352 fn test_invalid_zero_size() {
353 let mut vp = TransViewport::new();
354 vp.set_world_viewport(50.0, 50.0, 50.0, 50.0); assert!(!vp.is_valid());
356 }
357
358 #[test]
359 fn test_device_dx_dy() {
360 let mut vp = TransViewport::new();
361 vp.set_world_viewport(10.0, 20.0, 110.0, 120.0);
362 vp.set_device_viewport(0.0, 0.0, 200.0, 200.0);
363 assert_eq!(vp.device_dx(), -20.0);
366 }
367
368 #[test]
369 fn test_scale() {
370 let mut vp = TransViewport::new();
371 vp.set_world_viewport(0.0, 0.0, 100.0, 100.0);
372 vp.set_device_viewport(0.0, 0.0, 200.0, 400.0);
373 assert_eq!(vp.scale(), 3.0); }
375}