1use crate::Length;
4use std::fmt;
5
6#[derive(Copy, Clone, PartialEq, Eq, Hash)]
8pub struct Point {
9 pub x: Length,
10 pub y: Length,
11}
12
13impl Point {
14 pub const ZERO: Self = Self {
16 x: Length::ZERO,
17 y: Length::ZERO,
18 };
19
20 #[inline]
22 #[must_use = "this returns a new value without modifying anything"]
23 pub const fn new(x: Length, y: Length) -> Self {
24 Self { x, y }
25 }
26
27 #[inline]
29 #[must_use = "this returns a new value without modifying the original"]
30 pub fn translate(self, dx: Length, dy: Length) -> Self {
31 Self {
32 x: self.x + dx,
33 y: self.y + dy,
34 }
35 }
36}
37
38impl fmt::Debug for Point {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 write!(f, "Point({}, {})", self.x, self.y)
41 }
42}
43
44impl fmt::Display for Point {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 write!(f, "({}, {})", self.x, self.y)
47 }
48}
49
50#[derive(Copy, Clone, PartialEq, Eq, Hash)]
52pub struct Size {
53 pub width: Length,
54 pub height: Length,
55}
56
57impl Size {
58 pub const ZERO: Self = Self {
60 width: Length::ZERO,
61 height: Length::ZERO,
62 };
63
64 #[inline]
66 #[must_use = "this returns a new value without modifying anything"]
67 pub const fn new(width: Length, height: Length) -> Self {
68 Self { width, height }
69 }
70
71 #[inline]
73 #[must_use = "the result should be used"]
74 pub const fn is_empty(self) -> bool {
75 self.width.millipoints() <= 0 || self.height.millipoints() <= 0
76 }
77
78 #[inline]
80 #[must_use = "the result should be used"]
81 pub fn area(self) -> i64 {
82 self.width.millipoints() as i64 * self.height.millipoints() as i64
83 }
84}
85
86impl fmt::Debug for Size {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 write!(f, "Size({}×{})", self.width, self.height)
89 }
90}
91
92impl fmt::Display for Size {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 write!(f, "{}×{}", self.width, self.height)
95 }
96}
97
98#[derive(Copy, Clone, PartialEq, Eq, Hash)]
100pub struct Rect {
101 pub x: Length,
102 pub y: Length,
103 pub width: Length,
104 pub height: Length,
105}
106
107impl Rect {
108 pub const ZERO: Self = Self {
110 x: Length::ZERO,
111 y: Length::ZERO,
112 width: Length::ZERO,
113 height: Length::ZERO,
114 };
115
116 #[inline]
118 #[must_use = "this returns a new value without modifying anything"]
119 pub const fn new(x: Length, y: Length, width: Length, height: Length) -> Self {
120 Self {
121 x,
122 y,
123 width,
124 height,
125 }
126 }
127
128 #[inline]
130 #[must_use = "this returns a new value without modifying anything"]
131 pub const fn from_point_size(origin: Point, size: Size) -> Self {
132 Self {
133 x: origin.x,
134 y: origin.y,
135 width: size.width,
136 height: size.height,
137 }
138 }
139
140 #[inline]
142 #[must_use = "the result should be used"]
143 pub const fn origin(self) -> Point {
144 Point::new(self.x, self.y)
145 }
146
147 #[inline]
149 #[must_use = "the result should be used"]
150 pub const fn size(self) -> Size {
151 Size::new(self.width, self.height)
152 }
153
154 #[inline]
156 #[must_use = "the result should be used"]
157 pub fn right(self) -> Length {
158 self.x + self.width
159 }
160
161 #[inline]
163 #[must_use = "the result should be used"]
164 pub fn bottom(self) -> Length {
165 self.y + self.height
166 }
167
168 #[inline]
170 #[must_use = "the result should be used"]
171 pub const fn is_empty(self) -> bool {
172 self.width.millipoints() <= 0 || self.height.millipoints() <= 0
173 }
174
175 #[inline]
177 #[must_use = "the result should be used"]
178 pub fn contains(self, point: Point) -> bool {
179 point.x.millipoints() >= self.x.millipoints()
180 && point.x.millipoints() < self.right().millipoints()
181 && point.y.millipoints() >= self.y.millipoints()
182 && point.y.millipoints() < self.bottom().millipoints()
183 }
184
185 #[must_use = "the result should be used"]
187 pub fn intersects(self, other: Rect) -> bool {
188 self.x.millipoints() < other.right().millipoints()
189 && self.right().millipoints() > other.x.millipoints()
190 && self.y.millipoints() < other.bottom().millipoints()
191 && self.bottom().millipoints() > other.y.millipoints()
192 }
193
194 #[inline]
196 #[must_use = "this returns a new value without modifying the original"]
197 pub fn translate(self, dx: Length, dy: Length) -> Self {
198 Self {
199 x: self.x + dx,
200 y: self.y + dy,
201 width: self.width,
202 height: self.height,
203 }
204 }
205}
206
207impl fmt::Debug for Rect {
208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209 write!(
210 f,
211 "Rect(x={}, y={}, w={}, h={})",
212 self.x, self.y, self.width, self.height
213 )
214 }
215}
216
217impl fmt::Display for Rect {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 write!(f, "({},{},{}×{})", self.x, self.y, self.width, self.height)
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn test_point() {
229 let p = Point::new(Length::from_pt(10.0), Length::from_pt(20.0));
230 assert_eq!(p.x, Length::from_pt(10.0));
231 assert_eq!(p.y, Length::from_pt(20.0));
232
233 let translated = p.translate(Length::from_pt(5.0), Length::from_pt(3.0));
234 assert_eq!(translated.x, Length::from_pt(15.0));
235 assert_eq!(translated.y, Length::from_pt(23.0));
236 }
237
238 #[test]
239 fn test_size() {
240 let s = Size::new(Length::from_pt(100.0), Length::from_pt(200.0));
241 assert_eq!(s.width, Length::from_pt(100.0));
242 assert_eq!(s.height, Length::from_pt(200.0));
243 assert!(!s.is_empty());
244
245 let empty = Size::new(Length::ZERO, Length::from_pt(100.0));
246 assert!(empty.is_empty());
247 }
248
249 #[test]
250 fn test_rect() {
251 let r = Rect::new(
252 Length::from_pt(10.0),
253 Length::from_pt(20.0),
254 Length::from_pt(100.0),
255 Length::from_pt(50.0),
256 );
257
258 assert_eq!(r.right(), Length::from_pt(110.0));
259 assert_eq!(r.bottom(), Length::from_pt(70.0));
260
261 let p_inside = Point::new(Length::from_pt(50.0), Length::from_pt(40.0));
262 assert!(r.contains(p_inside));
263
264 let p_outside = Point::new(Length::from_pt(5.0), Length::from_pt(40.0));
265 assert!(!r.contains(p_outside));
266 }
267
268 #[test]
269 fn test_rect_intersection() {
270 let r1 = Rect::new(
271 Length::from_pt(0.0),
272 Length::from_pt(0.0),
273 Length::from_pt(100.0),
274 Length::from_pt(100.0),
275 );
276
277 let r2 = Rect::new(
278 Length::from_pt(50.0),
279 Length::from_pt(50.0),
280 Length::from_pt(100.0),
281 Length::from_pt(100.0),
282 );
283
284 assert!(r1.intersects(r2));
285 assert!(r2.intersects(r1));
286
287 let r3 = Rect::new(
288 Length::from_pt(200.0),
289 Length::from_pt(200.0),
290 Length::from_pt(100.0),
291 Length::from_pt(100.0),
292 );
293
294 assert!(!r1.intersects(r3));
295 }
296
297 #[test]
298 fn test_point_display() {
299 let p = Point::new(Length::from_pt(10.0), Length::from_pt(20.0));
300 assert_eq!(format!("{}", p), "(10pt, 20pt)");
301
302 let p_zero = Point::ZERO;
303 assert_eq!(format!("{}", p_zero), "(0pt, 0pt)");
304
305 let p_neg = Point::new(Length::from_pt(-5.5), Length::from_pt(12.25));
306 assert_eq!(format!("{}", p_neg), "(-5.5pt, 12.25pt)");
307 }
308
309 #[test]
310 fn test_size_display() {
311 let s = Size::new(Length::from_pt(100.0), Length::from_pt(200.0));
312 assert_eq!(format!("{}", s), "100pt×200pt");
313
314 let s_zero = Size::ZERO;
315 assert_eq!(format!("{}", s_zero), "0pt×0pt");
316
317 let s_fractional = Size::new(Length::from_pt(12.5), Length::from_pt(7.75));
318 assert_eq!(format!("{}", s_fractional), "12.5pt×7.75pt");
319 }
320
321 #[test]
322 fn test_rect_display() {
323 let r = Rect::new(
324 Length::from_pt(10.0),
325 Length::from_pt(20.0),
326 Length::from_pt(100.0),
327 Length::from_pt(50.0),
328 );
329 assert_eq!(format!("{}", r), "(10pt,20pt,100pt×50pt)");
330
331 let r_zero = Rect::ZERO;
332 assert_eq!(format!("{}", r_zero), "(0pt,0pt,0pt×0pt)");
333
334 let r_neg = Rect::new(
335 Length::from_pt(-10.0),
336 Length::from_pt(-20.0),
337 Length::from_pt(30.0),
338 Length::from_pt(40.0),
339 );
340 assert_eq!(format!("{}", r_neg), "(-10pt,-20pt,30pt×40pt)");
341 }
342}
343
344#[cfg(test)]
345mod geometry_extra_tests {
346 use super::*;
347
348 #[test]
351 fn test_point_zero() {
352 assert_eq!(Point::ZERO.x, Length::ZERO);
353 assert_eq!(Point::ZERO.y, Length::ZERO);
354 }
355
356 #[test]
357 fn test_point_translate_positive() {
358 let p = Point::new(Length::from_pt(5.0), Length::from_pt(10.0));
359 let t = p.translate(Length::from_pt(3.0), Length::from_pt(7.0));
360 assert_eq!(t.x, Length::from_pt(8.0));
361 assert_eq!(t.y, Length::from_pt(17.0));
362 }
363
364 #[test]
365 fn test_point_translate_negative() {
366 let p = Point::new(Length::from_pt(20.0), Length::from_pt(30.0));
367 let t = p.translate(Length::from_pt(-5.0), Length::from_pt(-10.0));
368 assert_eq!(t.x, Length::from_pt(15.0));
369 assert_eq!(t.y, Length::from_pt(20.0));
370 }
371
372 #[test]
373 fn test_point_translate_zero() {
374 let p = Point::new(Length::from_pt(5.0), Length::from_pt(5.0));
375 let t = p.translate(Length::ZERO, Length::ZERO);
376 assert_eq!(t, p);
377 }
378
379 #[test]
380 fn test_point_equality() {
381 let a = Point::new(Length::from_pt(3.0), Length::from_pt(4.0));
382 let b = Point::new(Length::from_pt(3.0), Length::from_pt(4.0));
383 assert_eq!(a, b);
384 }
385
386 #[test]
387 fn test_point_inequality() {
388 let a = Point::new(Length::from_pt(3.0), Length::from_pt(4.0));
389 let b = Point::new(Length::from_pt(3.0), Length::from_pt(5.0));
390 assert_ne!(a, b);
391 }
392
393 #[test]
394 fn test_point_debug_format() {
395 let p = Point::new(Length::from_pt(10.0), Length::from_pt(20.0));
396 let s = format!("{:?}", p);
397 assert!(s.contains("10pt"));
398 assert!(s.contains("20pt"));
399 }
400
401 #[test]
404 fn test_size_zero() {
405 assert!(Size::ZERO.is_empty());
406 }
407
408 #[test]
409 fn test_size_non_empty() {
410 let s = Size::new(Length::from_pt(10.0), Length::from_pt(20.0));
411 assert!(!s.is_empty());
412 }
413
414 #[test]
415 fn test_size_zero_width_is_empty() {
416 let s = Size::new(Length::ZERO, Length::from_pt(100.0));
417 assert!(s.is_empty());
418 }
419
420 #[test]
421 fn test_size_zero_height_is_empty() {
422 let s = Size::new(Length::from_pt(100.0), Length::ZERO);
423 assert!(s.is_empty());
424 }
425
426 #[test]
427 fn test_size_area() {
428 let s = Size::new(Length::from_pt(100.0), Length::from_pt(200.0));
430 let expected: i64 = 100_000_i64 * 200_000_i64;
431 assert_eq!(s.area(), expected);
432 }
433
434 #[test]
435 fn test_size_area_zero() {
436 assert_eq!(Size::ZERO.area(), 0);
437 }
438
439 #[test]
440 fn test_size_equality() {
441 let a = Size::new(Length::from_pt(50.0), Length::from_pt(80.0));
442 let b = Size::new(Length::from_pt(50.0), Length::from_pt(80.0));
443 assert_eq!(a, b);
444 }
445
446 #[test]
447 fn test_size_debug_format() {
448 let s = Size::new(Length::from_pt(100.0), Length::from_pt(200.0));
449 let dbg = format!("{:?}", s);
450 assert!(dbg.contains("100pt"));
451 assert!(dbg.contains("200pt"));
452 }
453
454 #[test]
457 fn test_rect_zero() {
458 let r = Rect::ZERO;
459 assert_eq!(r.x, Length::ZERO);
460 assert_eq!(r.width, Length::ZERO);
461 assert!(r.is_empty());
462 }
463
464 #[test]
465 fn test_rect_from_point_size() {
466 let origin = Point::new(Length::from_pt(5.0), Length::from_pt(10.0));
467 let size = Size::new(Length::from_pt(100.0), Length::from_pt(50.0));
468 let r = Rect::from_point_size(origin, size);
469 assert_eq!(r.x, Length::from_pt(5.0));
470 assert_eq!(r.y, Length::from_pt(10.0));
471 assert_eq!(r.width, Length::from_pt(100.0));
472 assert_eq!(r.height, Length::from_pt(50.0));
473 }
474
475 #[test]
476 fn test_rect_origin() {
477 let r = Rect::new(
478 Length::from_pt(3.0),
479 Length::from_pt(7.0),
480 Length::from_pt(20.0),
481 Length::from_pt(15.0),
482 );
483 let origin = r.origin();
484 assert_eq!(
485 origin,
486 Point::new(Length::from_pt(3.0), Length::from_pt(7.0))
487 );
488 }
489
490 #[test]
491 fn test_rect_size() {
492 let r = Rect::new(
493 Length::from_pt(0.0),
494 Length::from_pt(0.0),
495 Length::from_pt(100.0),
496 Length::from_pt(200.0),
497 );
498 let size = r.size();
499 assert_eq!(
500 size,
501 Size::new(Length::from_pt(100.0), Length::from_pt(200.0))
502 );
503 }
504
505 #[test]
506 fn test_rect_right() {
507 let r = Rect::new(
508 Length::from_pt(10.0),
509 Length::from_pt(0.0),
510 Length::from_pt(50.0),
511 Length::from_pt(0.0),
512 );
513 assert_eq!(r.right(), Length::from_pt(60.0));
514 }
515
516 #[test]
517 fn test_rect_bottom() {
518 let r = Rect::new(
519 Length::from_pt(0.0),
520 Length::from_pt(20.0),
521 Length::from_pt(0.0),
522 Length::from_pt(30.0),
523 );
524 assert_eq!(r.bottom(), Length::from_pt(50.0));
525 }
526
527 #[test]
528 fn test_rect_contains_boundary_left() {
529 let r = Rect::new(
531 Length::from_pt(10.0),
532 Length::from_pt(10.0),
533 Length::from_pt(100.0),
534 Length::from_pt(100.0),
535 );
536 let p_left_edge = Point::new(Length::from_pt(10.0), Length::from_pt(50.0));
537 assert!(r.contains(p_left_edge));
538 }
539
540 #[test]
541 fn test_rect_does_not_contain_right_edge() {
542 let r = Rect::new(
543 Length::from_pt(10.0),
544 Length::from_pt(10.0),
545 Length::from_pt(100.0),
546 Length::from_pt(100.0),
547 );
548 let p_right_edge = Point::new(Length::from_pt(110.0), Length::from_pt(50.0));
549 assert!(!r.contains(p_right_edge));
550 }
551
552 #[test]
553 fn test_rect_contains_top_edge() {
554 let r = Rect::new(
555 Length::from_pt(0.0),
556 Length::from_pt(0.0),
557 Length::from_pt(100.0),
558 Length::from_pt(100.0),
559 );
560 let p = Point::new(Length::from_pt(50.0), Length::from_pt(0.0));
561 assert!(r.contains(p));
562 }
563
564 #[test]
565 fn test_rect_does_not_contain_bottom_edge() {
566 let r = Rect::new(
567 Length::from_pt(0.0),
568 Length::from_pt(0.0),
569 Length::from_pt(100.0),
570 Length::from_pt(100.0),
571 );
572 let p = Point::new(Length::from_pt(50.0), Length::from_pt(100.0));
573 assert!(!r.contains(p));
574 }
575
576 #[test]
577 fn test_rect_not_contains_outside() {
578 let r = Rect::new(
579 Length::from_pt(0.0),
580 Length::from_pt(0.0),
581 Length::from_pt(50.0),
582 Length::from_pt(50.0),
583 );
584 let p = Point::new(Length::from_pt(100.0), Length::from_pt(100.0));
585 assert!(!r.contains(p));
586 }
587
588 #[test]
589 fn test_rect_intersects_self() {
590 let r = Rect::new(
591 Length::from_pt(10.0),
592 Length::from_pt(10.0),
593 Length::from_pt(100.0),
594 Length::from_pt(100.0),
595 );
596 assert!(r.intersects(r));
597 }
598
599 #[test]
600 fn test_rect_intersects_adjacent_no_overlap() {
601 let r1 = Rect::new(
602 Length::from_pt(0.0),
603 Length::from_pt(0.0),
604 Length::from_pt(100.0),
605 Length::from_pt(100.0),
606 );
607 let r2 = Rect::new(
609 Length::from_pt(100.0),
610 Length::from_pt(0.0),
611 Length::from_pt(100.0),
612 Length::from_pt(100.0),
613 );
614 assert!(!r1.intersects(r2));
615 }
616
617 #[test]
618 fn test_rect_translate() {
619 let r = Rect::new(
620 Length::from_pt(10.0),
621 Length::from_pt(20.0),
622 Length::from_pt(50.0),
623 Length::from_pt(30.0),
624 );
625 let t = r.translate(Length::from_pt(5.0), Length::from_pt(-5.0));
626 assert_eq!(t.x, Length::from_pt(15.0));
627 assert_eq!(t.y, Length::from_pt(15.0));
628 assert_eq!(t.width, Length::from_pt(50.0));
630 assert_eq!(t.height, Length::from_pt(30.0));
631 }
632
633 #[test]
634 fn test_rect_is_empty_zero_width() {
635 let r = Rect::new(
636 Length::from_pt(0.0),
637 Length::from_pt(0.0),
638 Length::ZERO,
639 Length::from_pt(100.0),
640 );
641 assert!(r.is_empty());
642 }
643
644 #[test]
645 fn test_rect_is_empty_zero_height() {
646 let r = Rect::new(
647 Length::from_pt(0.0),
648 Length::from_pt(0.0),
649 Length::from_pt(100.0),
650 Length::ZERO,
651 );
652 assert!(r.is_empty());
653 }
654
655 #[test]
656 fn test_rect_debug_format() {
657 let r = Rect::ZERO;
658 let s = format!("{:?}", r);
659 assert!(s.contains("Rect"));
660 }
661}