1use std::convert::TryFrom;
34use std::num::TryFromIntError;
35
36#[macro_use]
37mod def_macro;
38
39define_two_property_arithmetic_struct!(Position, UPosition, FPosition, x, y, ORIGIN, "({}, {})");
40define_two_property_arithmetic_struct!(Size, USize, FSize, width, height, ZERO, "{}x{}");
41
42impl Size {
43 pub fn area(self) -> i32 {
45 self.width * self.height
46 }
47}
48
49impl USize {
50 pub fn area(self) -> u32 {
52 self.width * self.height
53 }
54}
55
56impl FSize {
57 pub fn area(self) -> f32 {
59 self.width * self.height
60 }
61}
62
63#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
65#[cfg_attr(
66 feature = "serialization",
67 derive(serde_derive::Serialize, serde_derive::Deserialize)
68)]
69pub struct Rectangle {
70 pub position: Position,
72 pub size: USize,
74}
75
76impl Rectangle {
77 pub fn new(position: Position, size: USize) -> Self {
79 Self { position, size }
80 }
81
82 pub fn new_from_raw(x: i32, y: i32, width: u32, height: u32) -> Self {
84 Self {
85 position: Position::new(x, y),
86 size: USize::new(width, height),
87 }
88 }
89
90 pub fn contains_position(&self, position: Position) -> bool {
92 position.x >= self.position.x
93 && position.x <= self.position.x + self.size.width as i32
94 && position.y >= self.position.y
95 && position.y <= self.position.y + self.size.height as i32
96 }
97
98 pub fn contains_fposition(&self, position: FPosition) -> bool {
100 position.x >= self.position.x as f32
101 && position.x <= self.position.x as f32 + self.size.width as f32
102 && position.y >= self.position.y as f32
103 && position.y <= self.position.y as f32 + self.size.height as f32
104 }
105}
106
107#[derive(Copy, Clone, Default, PartialEq, Debug)]
109#[cfg_attr(
110 feature = "serialization",
111 derive(serde_derive::Serialize, serde_derive::Deserialize)
112)]
113pub struct FRectangle {
114 pub position: FPosition,
116 pub size: FSize,
118}
119
120impl FRectangle {
121 pub fn new(position: FPosition, size: FSize) -> Self {
126 assert!(size.width >= 0.0);
127 assert!(size.height >= 0.0);
128
129 Self { position, size }
130 }
131
132 pub fn new_from_raw(x: f32, y: f32, width: f32, height: f32) -> Self {
137 assert!(width >= 0.0);
138 assert!(height >= 0.0);
139
140 Self {
141 position: FPosition::new(x, y),
142 size: FSize::new(width, height),
143 }
144 }
145
146 pub fn contains_position(&self, position: FPosition) -> bool {
148 position.x >= self.position.x
149 && position.x <= self.position.x + self.size.width
150 && position.y >= self.position.y
151 && position.y <= self.position.y + self.size.height
152 }
153}
154
155impl std::ops::Add<USize> for Position {
156 type Output = Rectangle;
157
158 fn add(self, rhs: USize) -> Self::Output {
159 Rectangle::new(self, rhs)
160 }
161}
162
163impl std::ops::Add<FSize> for FPosition {
164 type Output = FRectangle;
165
166 fn add(self, rhs: FSize) -> Self::Output {
167 FRectangle::new(self, rhs)
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
179 #[allow(clippy::float_cmp)]
180 fn new_sets_values() {
181 let p = Position::new(-1, -2);
182 assert_eq!(p.x, -1);
183 assert_eq!(p.y, -2);
184
185 let up = UPosition::new(1, 2);
186 assert_eq!(up.x, 1);
187 assert_eq!(up.y, 2);
188
189 let fp = FPosition::new(1., -2.);
190 assert_eq!(fp.x, 1.);
191 assert_eq!(fp.y, -2.);
192
193 let us = USize::new(1, 2);
194 let r = Rectangle::new(p, us);
195 assert_eq!(r.position, p);
196 assert_eq!(r.size, us);
197
198 let r2 = Rectangle::new_from_raw(1, 2, 3, 4);
199 assert_eq!(r2.position, Position::new(1, 2));
200 assert_eq!(r2.size, USize::new(3, 4));
201 }
202
203 #[test]
204 #[allow(clippy::float_cmp)]
205 fn from_sets_values() {
206 let p: Position = From::from((-1, -2));
207 assert_eq!(p.x, -1);
208 assert_eq!(p.y, -2);
209
210 let up: UPosition = From::from((1, 2));
211 assert_eq!(up.x, 1);
212 assert_eq!(up.y, 2);
213
214 let fp: FPosition = From::from((1., -2.));
215 assert_eq!(fp.x, 1.);
216 assert_eq!(fp.y, -2.);
217 }
218
219 #[test]
220 #[allow(clippy::float_cmp)]
221 fn from_gets_values() {
222 let p = Position::new(-1, -2);
223 let (px, py): (i32, i32) = From::from(p);
224 assert_eq!(px, -1);
225 assert_eq!(py, -2);
226
227 let up = UPosition::new(1, 2);
228 let (upx, upy): (u32, u32) = From::from(up);
229 assert_eq!(upx, 1);
230 assert_eq!(upy, 2);
231
232 let fp = FPosition::new(1., -2.);
233 let (fpx, fpy): (f32, f32) = From::from(fp);
234 assert_eq!(fpx, 1.);
235 assert_eq!(fpy, -2.);
236 }
237
238 #[test]
239 fn display_is_correct() {
240 let p = Position::new(-1, -2);
241 assert_eq!(p.to_string(), "(-1, -2)");
242
243 let up = UPosition::new(1, 2);
244 assert_eq!(up.to_string(), "(1, 2)");
245
246 let fp = FPosition::new(1.5, -2.7);
247 assert_eq!(fp.to_string(), "(1.5, -2.7)");
248
249 let s = Size::new(-1, -2);
250 assert_eq!(s.to_string(), "-1x-2");
251
252 let us = USize::new(1, 2);
253 assert_eq!(us.to_string(), "1x2");
254
255 let fs = FSize::new(1.5, -2.7);
256 assert_eq!(fs.to_string(), "1.5x-2.7");
257 }
258
259 #[test]
260 fn addition() {
261 let p = Position::new(-1, -2);
262 let p2 = Position::new(3, 4);
263 assert_eq!(p + p2, Position::new(2, 2));
264
265 let up = UPosition::new(1, 2);
266 let up2 = UPosition::new(3, 4);
267 assert_eq!(up + up2, UPosition::new(4, 6));
268
269 let fp = FPosition::new(-1.5, -3.0);
270 let fp2 = FPosition::new(4.5, 6.0);
271 assert_eq!(fp + fp2, FPosition::new(3., 3.));
272
273 let us = USize::new(1, 2);
274 assert_eq!(p + us, Rectangle::new(p, us));
275 let fs = FSize::new(4.5, 6.0);
276 assert_eq!(fp + fs, FRectangle::new(fp, fs));
277 }
278
279 #[test]
280 fn addition_scalar() {
281 let p = Position::new(-1, -2);
282 let p2 = 2;
283 assert_eq!(p + p2, Position::new(1, 0));
284
285 let up = UPosition::new(1, 2);
286 let up2 = 3;
287 assert_eq!(up + up2, UPosition::new(4, 5));
288
289 let fp = FPosition::new(-1.5, -3.0);
290 let fp2 = 1.5;
291 assert_eq!(fp + fp2, FPosition::new(0., -1.5));
292 }
293
294 #[test]
295 fn addition_tuple() {
296 let p = Position::new(-1, -2);
297 let p2 = (3, 4);
298 assert_eq!(p + p2, Position::new(2, 2));
299
300 let up = UPosition::new(1, 2);
301 let up2 = (3, 4);
302 assert_eq!(up + up2, UPosition::new(4, 6));
303
304 let fp = FPosition::new(-1.5, -3.0);
305 let fp2 = (4.5, 6.0);
306 assert_eq!(fp + fp2, FPosition::new(3., 3.));
307 }
308
309 #[test]
310 fn add_assign_scalar() {
311 let mut p = Position::new(-1, -2);
312 p += 2;
313 assert_eq!(p, Position::new(1, 0));
314
315 let mut up = UPosition::new(1, 2);
316 up += 3;
317 assert_eq!(up, UPosition::new(4, 5));
318
319 let mut fp = FPosition::new(-1.5, -3.0);
320 fp += 1.5;
321 assert_eq!(fp, FPosition::new(0., -1.5));
322 }
323
324 #[test]
325 fn add_assign_tuple() {
326 let mut p = Position::new(-1, -2);
327 p += (3, 4);
328 assert_eq!(p, Position::new(2, 2));
329
330 let mut up = UPosition::new(1, 2);
331 up += (3, 4);
332 assert_eq!(up, UPosition::new(4, 6));
333
334 let mut fp = FPosition::new(-1.5, -3.0);
335 fp += (4.5, 6.0);
336 assert_eq!(fp, FPosition::new(3., 3.));
337 }
338
339 #[test]
340 fn subtraction() {
341 let p = Position::new(-1, -2);
342 let p2 = Position::new(3, 4);
343 assert_eq!(p - p2, Position::new(-4, -6));
344
345 let up = UPosition::new(3, 4);
346 let up2 = UPosition::new(1, 2);
347 assert_eq!(up - up2, UPosition::new(2, 2));
348
349 let fp = FPosition::new(-1.5, -3.0);
350 let fp2 = FPosition::new(4.5, 6.0);
351 assert_eq!(fp - fp2, FPosition::new(-6., -9.));
352 }
353
354 #[test]
355 fn subtraction_scalar() {
356 let p = Position::new(-1, -2);
357 let p2 = 2;
358 assert_eq!(p - p2, Position::new(-3, -4));
359
360 let up = UPosition::new(1, 2);
361 let up2 = 1;
362 assert_eq!(up - up2, UPosition::new(0, 1));
363
364 let fp = FPosition::new(-1.5, -3.0);
365 let fp2 = 1.5;
366 assert_eq!(fp - fp2, FPosition::new(-3., -4.5));
367 }
368
369 #[test]
370 fn subtraction_tuple() {
371 let p = Position::new(-1, -2);
372 let p2 = (3, 4);
373 assert_eq!(p - p2, Position::new(-4, -6));
374
375 let up = UPosition::new(3, 4);
376 let up2 = (1, 2);
377 assert_eq!(up - up2, UPosition::new(2, 2));
378
379 let fp = FPosition::new(-1.5, -3.0);
380 let fp2 = (4.5, 6.0);
381 assert_eq!(fp - fp2, FPosition::new(-6., -9.));
382 }
383
384 #[test]
385 fn sub_assign_scalar() {
386 let mut p = Position::new(-1, -2);
387 p -= 2;
388 assert_eq!(p, Position::new(-3, -4));
389
390 let mut up = UPosition::new(6, 3);
391 up -= 3;
392 assert_eq!(up, UPosition::new(3, 0));
393
394 let mut fp = FPosition::new(-1.5, -3.0);
395 fp -= 1.5;
396 assert_eq!(fp, FPosition::new(-3.0, -4.5));
397 }
398
399 #[test]
400 fn sub_assign_tuple() {
401 let mut p = Position::new(-1, -2);
402 p -= (3, 4);
403 assert_eq!(p, Position::new(-4, -6));
404
405 let mut up = UPosition::new(3, 4);
406 up -= (1, 2);
407 assert_eq!(up, UPosition::new(2, 2));
408
409 let mut fp = FPosition::new(-1.5, -3.0);
410 fp -= (4.5, 6.0);
411 assert_eq!(fp, FPosition::new(-6., -9.));
412 }
413
414 #[test]
415 fn multiplication_scalar() {
416 let p = Position::new(-1, -2);
417 let p2 = 2;
418 assert_eq!(p * p2, Position::new(-2, -4));
419
420 let up = UPosition::new(1, 2);
421 let up2 = 3;
422 assert_eq!(up * up2, UPosition::new(3, 6));
423
424 let fp = FPosition::new(-1.5, -3.0);
425 let fp2 = 1.5;
426 assert_eq!(fp * fp2, FPosition::new(-2.25, -4.5));
427 }
428
429 #[test]
430 fn mul_assign_scalar() {
431 let mut p = Position::new(-1, -2);
432 p *= 2;
433 assert_eq!(p, Position::new(-2, -4));
434
435 let mut up = UPosition::new(6, 3);
436 up *= 3;
437 assert_eq!(up, UPosition::new(18, 9));
438
439 let mut fp = FPosition::new(-1.5, -3.0);
440 fp *= 1.5;
441 assert_eq!(fp, FPosition::new(-2.25, -4.5));
442 }
443
444 #[test]
445 fn division_scalar() {
446 let p = Position::new(-2, -4);
447 let p2 = 2;
448 assert_eq!(p / p2, Position::new(-1, -2));
449
450 let up = UPosition::new(18, 9);
451 let up2 = 3;
452 assert_eq!(up / up2, UPosition::new(6, 3));
453
454 let fp = FPosition::new(-1.5, -3.0);
455 let fp2 = 1.5;
456 assert_eq!(fp / fp2, FPosition::new(-1., -2.));
457 }
458
459 #[test]
460 fn div_assign_scalar() {
461 let mut p = Position::new(-2, -4);
462 p /= 2;
463 assert_eq!(p, Position::new(-1, -2));
464
465 let mut up = UPosition::new(6, 3);
466 up /= 3;
467 assert_eq!(up, UPosition::new(2, 1));
468
469 let mut fp = FPosition::new(-1.5, -3.0);
470 fp /= 1.5;
471 assert_eq!(fp, FPosition::new(-1., -2.));
472 }
473
474 #[test]
475 fn rem_scalar() {
476 let p = Position::new(-5, -6);
477 let p2 = 4;
478 assert_eq!(p % p2, Position::new(-1, -2));
479
480 let up = UPosition::new(18, 9);
481 let up2 = 4_u32;
482 assert_eq!(up % up2, UPosition::new(2, 1));
483
484 let fp = FPosition::new(-2., -4.);
485 let fp2 = 1.5;
486 assert_eq!(fp % fp2, FPosition::new(-0.5, -1.));
487 }
488
489 #[test]
490 fn rem_assign_scalar() {
491 let mut p = Position::new(-3, -5);
492 p %= 2;
493 assert_eq!(p, Position::new(-1, -1));
494
495 let mut up = UPosition::new(6, 3);
496 up %= 4_u32;
497 assert_eq!(up, UPosition::new(2, 3));
498
499 let mut fp = FPosition::new(-5.5, -7.0);
500 fp %= 1.5;
501 assert_eq!(fp, FPosition::new(-1., -1.));
502 }
503
504 #[test]
505 fn negate() {
506 let p = Position::new(-5, -6);
507 assert_eq!(-p, Position::new(5, 6));
508
509 let fp = FPosition::new(-2., -4.);
510 assert_eq!(-fp, FPosition::new(2., 4.));
511 }
512
513 #[test]
514 fn round() {
515 let fp = FPosition::new(-2.5, 2.5);
516 assert_eq!(fp.round(), Position::new(-3, 3));
517
518 let ufp = FPosition::new(2.5, 2.5);
519 assert_eq!(ufp.round_u(), UPosition::new(3, 3));
520 }
521
522 #[test]
523 #[should_panic]
524 fn round_u_less_than_zero_panics() {
525 let fp = FPosition::new(-3.5, 2.5);
526 fp.round_u();
527 }
528
529 #[test]
530 fn trunc() {
531 let fp = FPosition::new(-2.5, 2.5);
532 assert_eq!(fp.trunc(), Position::new(-2, 2));
533
534 let ufp = FPosition::new(3.5, 2.5);
535 assert_eq!(ufp.trunc_u(), UPosition::new(3, 2));
536 }
537
538 #[test]
539 #[should_panic]
540 fn trunc_u_less_than_zero_panics() {
541 let fp = FPosition::new(-3.5, 2.5);
542 fp.trunc_u();
543 }
544
545 #[test]
546 #[allow(clippy::float_cmp)]
547 fn area() {
548 let s = Size::new(3, 2);
549 assert_eq!(s.area(), 6);
550
551 let us = USize::new(3, 2);
552 assert_eq!(us.area(), 6);
553
554 let fs = FSize::new(3.5, 2.5);
555 assert_eq!(fs.area(), 8.75);
556 }
557
558 #[test]
559 fn contains_position() {
560 let r = Rectangle::new_from_raw(-5, -10, 10, 20);
561 let fr = FRectangle::new_from_raw(-5., -10., 10., 20.);
562
563 for x in -5..=5 {
565 for y in -10..=10 {
566 assert!(r.contains_position(Position::new(x, y)));
567 assert!(r.contains_fposition(FPosition::new(x as f32, y as f32)));
568 assert!(fr.contains_position(FPosition::new(x as f32, y as f32)));
569 }
570 }
571
572 for &x in &[-6, 6] {
574 for y in -11..=11 {
575 assert!(!r.contains_position(Position::new(x, y)));
576 assert!(!r.contains_fposition(FPosition::new(x as f32, y as f32)));
577 assert!(!fr.contains_position(FPosition::new(x as f32, y as f32)));
578 }
579 }
580 for x in -6..=6 {
581 for &y in &[-11, 11] {
582 assert!(!r.contains_position(Position::new(x, y)));
583 assert!(!r.contains_fposition(FPosition::new(x as f32, y as f32)));
584 assert!(!fr.contains_position(FPosition::new(x as f32, y as f32)));
585 }
586 }
587 }
588
589 #[test]
590 fn from_position_conversions() {
591 use std::convert::TryFrom;
592
593 let p = Position::new(1, 2);
594 let p_up = UPosition::try_from(p);
595 assert!(p_up.is_ok());
596 assert_eq!(p_up.unwrap(), UPosition::new(1, 2));
597
598 let np = Position::new(-1, -2);
599 let np_up = UPosition::try_from(np);
600 assert!(np_up.is_err());
601
602 let p_fp = FPosition::from(p);
603 assert_eq!(p_fp, FPosition::new(1.0, 2.0));
604 let np_fp = FPosition::from(np);
605 assert_eq!(np_fp, FPosition::new(-1.0, -2.0));
606 }
607
608 #[test]
609 fn from_uposition_conversions() {
610 use std::convert::TryFrom;
611
612 let up = UPosition::new(1, 2);
613 let up_p = Position::try_from(up);
614 assert!(up_p.is_ok());
615 assert_eq!(up_p.unwrap(), Position::new(1, 2));
616
617 let bp = UPosition::new(u32::MAX / 2 + 1, u32::MAX);
618 let bp_p = Position::try_from(bp);
619 assert!(bp_p.is_err());
620
621 let up_fp = FPosition::from(up);
622 assert_eq!(up_fp, FPosition::new(1.0, 2.0));
623 let bp_fp = FPosition::from(bp);
624 assert_eq!(
625 bp_fp,
626 FPosition::new((u32::MAX / 2 + 1) as f32, u32::MAX as f32)
627 );
628 }
629
630 #[test]
631 fn from_fposition_conversions() {
632 use std::convert::TryFrom;
633
634 let fp = FPosition::new(1., 2.);
635 let fp_p = Position::try_from(fp);
636 assert!(fp_p.is_ok());
637 assert_eq!(fp_p.unwrap(), Position::new(1, 2));
638
639 let bfp = FPosition::new((u32::MAX / 2 + 1) as f32, u32::MAX as f32);
640 let bfp_p = Position::try_from(bfp);
641 assert!(bfp_p.is_err());
642 assert_eq!(bfp_p.unwrap_err(), TryFromPositionError::FloatToInt);
643
644 let fp_up = UPosition::try_from(fp);
645 assert!(fp_up.is_ok());
646 assert_eq!(fp_up.unwrap(), UPosition::new(1, 2));
647
648 let ebfp = FPosition::new((u64::MAX / 2 + 1) as f32, u64::MAX as f32);
649 let ebfp_up = UPosition::try_from(ebfp);
650 assert!(ebfp_up.is_err());
651 assert_eq!(ebfp_up.unwrap_err(), TryFromPositionError::FloatToInt);
652 }
653}