1use std::iter::IntoIterator;
2use std::ops::Add;
3use std::ops::AddAssign;
4use std::ops::Div;
5use std::ops::DivAssign;
6use std::ops::Mul;
7use std::ops::MulAssign;
8use std::ops::Neg;
9use std::ops::Rem;
10use std::ops::Shl;
11use std::ops::ShlAssign;
12use std::ops::Shr;
13use std::ops::ShrAssign;
14use std::ops::Sub;
15use std::ops::SubAssign;
16
17use num_traits::sign::Signed;
18
19use crate::num::FromRounded;
20use crate::num::INum;
21use crate::num::Num;
22use crate::num::NumIdentity;
23use crate::num::ToRounded;
24use crate::num::ToSignedClamped;
25
26use crate::geom::Circle;
27use crate::geom::Point;
28use crate::geom::Rect;
29use crate::geom::Size;
30
31use crate::geom::PointPosition;
32use crate::geom::Transform;
33
34use crate::Random;
35
36mod line_iterator;
37pub use self::line_iterator::LineIterator;
38
39#[derive(Copy, Clone, Debug, PartialEq)]
40pub struct Line<N: Num = f32>(pub Point<N>, pub Point<N>);
41
42impl<N: Num> Line<N> {
43 pub fn new_from_points(points: &[N]) -> Self {
44 if points.len() != 4 {
45 panic!(
46 "Given points slice which isn't exactly 4 elements, length given ... {}",
47 points.len()
48 );
49 }
50
51 Line(Point(points[0], points[1]), Point(points[2], points[3]))
52 }
53
54 pub fn new_zero_value() -> Self {
55 Line(Point::new_zero_value(), Point::new_zero_value())
56 }
57
58 #[inline(always)]
59 pub fn start(self) -> Point<N> {
60 self.0
61 }
62
63 #[inline(always)]
64 pub fn end(self) -> Point<N> {
65 self.1
66 }
67
68 pub fn min_point(self) -> Point<N> {
69 self.start().min(self.end())
70 }
71
72 pub fn max_point(self) -> Point<N> {
73 self.start().max(self.end())
74 }
75
76 pub fn left_x(self) -> N {
77 self.start().x().min(self.end().x())
78 }
79
80 pub fn right_x(self) -> N {
81 self.start().x().max(self.end().x())
82 }
83
84 pub fn bottom_y(self) -> N {
85 self.start().y().min(self.end().y())
86 }
87
88 pub fn top_y(self) -> N {
89 self.start().y().max(self.end().y())
90 }
91
92 pub fn is_empty(self) -> bool {
93 self.start() == self.end()
94 }
95
96 pub fn is_horizontal(self) -> bool {
97 self.start().y() == self.end().y()
98 }
99
100 pub fn is_vertical(self) -> bool {
101 self.start().x() == self.end().x()
102 }
103
104 pub fn is_straight(self) -> bool {
105 self.is_horizontal() || self.is_vertical()
106 }
107
108 pub fn to<T: Num + From<N>>(&self) -> Line<T> {
111 Line(self.start().to::<T>(), self.end().to::<T>())
112 }
113
114 pub fn to_rect(self) -> Rect<N> {
115 self.start().rect_to(self.end())
116 }
117
118 pub fn interpolation_point(self, n: N) -> Point<N> {
121 self.start() + self.diff_as_point() * n
122 }
123
124 fn diff_as_point(self) -> Point<N> {
125 self.end() - self.start()
126 }
127
128 pub fn reverse(self) -> Self {
130 Self(self.end(), self.start())
131 }
132
133 pub fn is_direction_negative_x(self) -> bool {
134 self.end().x() < self.start().x()
135 }
136
137 pub fn is_direction_positive_x(self) -> bool {
138 self.start().x() < self.end().x()
139 }
140
141 pub fn is_direction_negative_y(self) -> bool {
142 self.end().y() < self.start().y()
143 }
144
145 pub fn is_direction_positive_y(self) -> bool {
146 self.start().y() < self.end().y()
147 }
148
149 pub fn abs(&self) -> Self {
150 Self(self.start().abs(), self.end().abs())
151 }
152
153 pub fn min(self, other: Self) -> Self {
154 Self(self.start().min(other.start()), self.end().min(other.end()))
155 }
156
157 pub fn max(self, other: Self) -> Self {
158 Self(self.start().max(other.start()), self.end().max(other.end()))
159 }
160
161 pub fn min_x(self) -> N {
162 self.start().x().min(self.end().x())
163 }
164
165 pub fn min_y(self) -> N {
166 self.start().y().min(self.end().y())
167 }
168
169 pub fn max_x(self) -> N {
170 self.start().x().max(self.end().x())
171 }
172
173 pub fn max_y(self) -> N {
174 self.start().y().max(self.end().y())
175 }
176
177 pub fn overlaps_line(self, other: Line<N>) -> bool {
178 self.intersect_line(other).is_some()
179 }
180
181 pub fn overlaps_circle(self, other: Circle<N>) -> bool {
182 other.overlaps_line(self)
183 }
184
185 pub fn intersect_line(self, other: Line<N>) -> Option<Point<N>> {
186 let self_rounded = self.to_rounded();
187 let other_rounded = other.to_rounded();
188
189 let size_1 = self_rounded.diff();
190 let size_2 = other_rounded.diff();
191 let start_dist = self_rounded.start() - other_rounded.start();
192
193 let s = (-size_1.height() * start_dist.x() + size_1.width() * start_dist.y())
194 / (-size_2.width() * size_1.height() + size_1.width() * size_2.height());
195 let t = (size_2.width() * start_dist.y() - size_2.height() * start_dist.x())
196 / (-size_2.width() * size_1.height() + size_1.width() * size_2.height());
197
198 if s >= 0.0 && s <= 1.0 && t >= 0.0 && t <= 1.0 {
199 let intersection_x = self_rounded.start().x() + (t * size_1.width());
200 let intersection_y = self_rounded.start().y() + (t * size_1.height());
201 let intersection = Point(intersection_x, intersection_y).from_f32();
202 return Some(intersection);
203 }
204
205 None
206 }
207
208 pub fn intersect_rect(self, rect: Rect<N>) -> Option<Line<N>> {
216 let mut position = rect.line_position(self);
217
218 if position.is_entirely_inside() {
220 return Some(self);
221 }
222
223 if position.is_entirely_outside() {
224 return None;
225 }
226
227 let mut line_f32 = self.to_f32();
237 let rect_f32 = rect.to_f32();
238 loop {
239 if position.is_entirely_inside() {
242 return Some(line_f32.from_f32());
243 }
244
245 if position.is_within_same_space() && position.start().is_entirely_outside() {
247 return None;
248 }
249
250 let point_position = if position.start().is_outside() {
255 position.start()
256 } else {
257 position.end()
258 };
259
260 match line_f32.calculate_intersection(rect_f32, point_position) {
262 None => return None,
263 Some(new_point) => {
264 if point_position == position.start() {
266 line_f32 = Line(new_point, line_f32.end());
267 position = rect_f32.line_position(line_f32);
268 } else {
269 line_f32 = Line(line_f32.start(), new_point);
270 position = rect_f32.line_position(line_f32);
271 }
272
273 if rect_f32.intersect_rect(line_f32.to_rect()) == None {
288 return None;
289 }
290 }
291 }
292 }
293 }
294
295 pub fn angle(self) -> N {
299 let self_f32 = self.to_f32();
300 let y_diff = self_f32.y_diff();
301 let x_diff = self_f32.x_diff();
302 let angle = y_diff.atan2(x_diff);
303 FromRounded::from_rounded(angle)
304 }
305
306 pub fn direction(self) -> Size<f32> {
307 let angle = self.to_f32().angle();
308 Size(angle.cos(), angle.sin())
309 }
310
311 pub fn hypot(self) -> N {
312 self.start().hypot_to(self.end())
313 }
314
315 pub fn hypot_sqrd(self) -> N {
316 self.start().distance_to(self.end()).hypot_sqrd()
317 }
318
319 pub fn rotate(self, angle: f32) -> Self {
320 let self_f32 = self.to_f32();
321 let midpoint = self_f32.midpoint();
322 let half_len = self_f32.hypot() / 2.0;
323
324 let angle = self_f32.angle() - angle;
325 let cos = angle.cos();
326 let sin = angle.sin();
327
328 let start = midpoint - Point(half_len * cos, half_len * sin);
329 let end = midpoint + Point(half_len * cos, half_len * sin);
330
331 Line(start, end).from_f32()
332 }
333
334 pub fn rotate_around_zero(self, angle: f32) -> Self {
335 Line(
336 self.start().rotate_around_zero(angle),
337 self.end().rotate_around_zero(angle),
338 )
339 }
340
341 pub fn rotate_around_point(self, angle: f32, target: Point<N>) -> Self {
342 Line(
343 self.start().rotate_around_point(angle, target),
344 self.end().rotate_around_point(angle, target),
345 )
346 }
347
348 pub fn rotate_around_start(self, angle: f32) -> Self {
349 Line(
350 self.start(),
351 self.end().rotate_around_point(angle, self.start()),
352 )
353 }
354
355 pub fn rotate_around_end(self, angle: f32) -> Self {
356 Line(
357 self.start().rotate_around_point(angle, self.end()),
358 self.end(),
359 )
360 }
361
362 pub fn midpoint(self) -> Point<N> {
363 ((self.start() + self.end()).to_rounded() / 2.0).from_f32()
364 }
365
366 pub(crate) fn to_f32(self) -> Line<f32> {
367 self.to_rounded()
368 }
369
370 pub fn flip(self) -> Self {
373 Self(self.end(), self.start())
374 }
375
376 pub fn into_iter_with_step(self, step: N) -> LineIterator<N> {
377 LineIterator::new(self, step, true)
378 }
379
380 pub fn into_iter_inclusive(self) -> LineIterator<N> {
381 LineIterator::new(self, <N as NumIdentity>::one(), false)
382 }
383
384 pub fn into_iter_inclusive_with_step(self, step: N) -> LineIterator<N> {
385 LineIterator::new(self, step, false)
386 }
387
388 pub fn into_slice(self) -> [N; 4] {
389 [
390 self.start().x(),
391 self.start().y(),
392 self.end().x(),
393 self.end().y(),
394 ]
395 }
396}
397
398impl<N: Num + Signed> Line<N> {
399 pub fn direction_sign(&self) -> Point<N> {
400 self.diff_as_point().sign()
401 }
402}
403
404impl<N: Num + ToSignedClamped> Line<N>
405where
406 <N as ToSignedClamped>::Output: Signed,
407{
408 pub fn to_signed_clamped(self) -> Line<<N as ToSignedClamped>::Output> {
409 Line(
410 self.start().to_signed_clamped(),
411 self.end().to_signed_clamped(),
412 )
413 }
414
415 pub fn diff(self) -> Size<<N as ToSignedClamped>::Output> {
416 Size(self.x_diff(), self.y_diff())
417 }
418
419 pub fn x_diff(self) -> <N as ToSignedClamped>::Output {
420 self.end().x().to_signed_clamped() - self.start().x().to_signed_clamped()
421 }
422
423 pub fn y_diff(self) -> <N as ToSignedClamped>::Output {
424 self.end().y().to_signed_clamped() - self.start().y().to_signed_clamped()
425 }
426
427 pub fn slope(self) -> <N as ToSignedClamped>::Output {
428 let diff = self.diff();
429 diff.height() / diff.width()
430 }
431
432 pub fn inverse_slope(self) -> <N as ToSignedClamped>::Output {
433 let diff = self.diff();
434 diff.width() / diff.height()
435 }
436
437 pub fn step_direction(self) -> Point<<N as ToSignedClamped>::Output> {
438 self.diff().to_point().step()
439 }
440}
441
442impl Line<f32> {
443 pub(crate) fn from_f32<N: Num>(self) -> Line<N> {
444 Line(self.start().from_f32(), self.end().from_f32())
445 }
446
447 fn calculate_intersection(self, rect: Rect<f32>, clip_to: PointPosition) -> Option<Point<f32>> {
448 let p1 = self.start();
449 let slope = self.slope();
450 let inverse_slope = self.inverse_slope();
451
452 if clip_to.is_above() {
453 let new_x = p1.x() + inverse_slope * (rect.top_y() - p1.y());
454 let new_y = rect.top_y();
455
456 return Some(Point(new_x, new_y));
457 }
458
459 if clip_to.is_below() {
460 let new_x = p1.x() + inverse_slope * (rect.bottom_y() - p1.y());
461 let new_y = rect.bottom_y();
462
463 return Some(Point(new_x, new_y));
464 }
465
466 if clip_to.is_right() {
467 let new_x = rect.right_x();
468 let new_y = p1.y() + slope * (rect.right_x() - p1.x());
469
470 return Some(Point(new_x, new_y));
471 }
472
473 if clip_to.is_left() {
474 let new_x = rect.left_x();
475 let new_y = p1.y() + slope * (rect.left_x() - p1.x());
476
477 return Some(Point(new_x, new_y));
478 }
479
480 None
481 }
482}
483
484impl<O: Num, N: Num + ToRounded<O>> ToRounded<Line<O>> for Line<N> {
485 fn to_rounded(self) -> Line<O> {
486 Line(self.start().to_rounded(), self.end().to_rounded())
487 }
488}
489
490impl Add<Random> for Line {
491 type Output = Self;
492
493 fn add(self, rnd: Random) -> Self {
494 rnd + self
495 }
496}
497
498impl<N: Num> Add<Point<N>> for Line<N> {
499 type Output = Self;
500
501 fn add(self, other: Point<N>) -> Self {
502 Self(self.0 + other, self.1 + other)
503 }
504}
505
506impl<N: Num> AddAssign<Point<N>> for Line<N> {
507 fn add_assign(&mut self, other: Point<N>) {
508 self.0 += other;
509 self.1 += other;
510 }
511}
512
513impl<N: Num> Sub<Point<N>> for Line<N> {
514 type Output = Self;
515
516 fn sub(self, other: Point<N>) -> Self {
517 Self(self.0 - other, self.1 - other)
518 }
519}
520
521impl<N: Num> SubAssign<Point<N>> for Line<N> {
522 fn sub_assign(&mut self, other: Point<N>) {
523 self.0 -= other;
524 self.1 -= other;
525 }
526}
527
528impl<N: Num> Mul<Point<N>> for Line<N> {
529 type Output = Self;
530
531 fn mul(self, other: Point<N>) -> Self {
532 Self(self.0 * other, self.1 * other)
533 }
534}
535
536impl<N: Num> MulAssign<Point<N>> for Line<N> {
537 fn mul_assign(&mut self, other: Point<N>) {
538 self.0 *= other;
539 self.1 *= other;
540 }
541}
542
543impl<N: Num> Div<Point<N>> for Line<N> {
544 type Output = Self;
545
546 fn div(self, other: Point<N>) -> Self {
547 Self(self.0 / other, self.1 / other)
548 }
549}
550
551impl<N: Num> DivAssign<Point<N>> for Line<N> {
552 fn div_assign(&mut self, other: Point<N>) {
553 self.0 /= other;
554 self.1 /= other;
555 }
556}
557
558impl<N: Num> Rem<Point<N>> for Line<N> {
559 type Output = Self;
560
561 fn rem(self, other: Point<N>) -> Self {
562 Self(self.0 % other, self.1 % other)
563 }
564}
565
566impl<N: Num> Add<Line<N>> for Line<N> {
567 type Output = Self;
568
569 fn add(self, other: Line<N>) -> Self {
570 Self(self.0 + other.0, self.1 + other.1)
571 }
572}
573
574impl<N: Num> AddAssign<Line<N>> for Line<N> {
575 fn add_assign(&mut self, other: Line<N>) {
576 self.0 += other.0;
577 self.1 += other.1;
578 }
579}
580
581impl<N: Num> Sub<Line<N>> for Line<N> {
582 type Output = Self;
583
584 fn sub(self, other: Line<N>) -> Self {
585 Self(self.0 - other.0, self.1 - other.1)
586 }
587}
588
589impl<N: Num> SubAssign<Line<N>> for Line<N> {
590 fn sub_assign(&mut self, other: Line<N>) {
591 self.0 -= other.0;
592 self.1 -= other.1;
593 }
594}
595
596impl<N: Num> Mul<Line<N>> for Line<N> {
597 type Output = Self;
598
599 fn mul(self, other: Line<N>) -> Self {
600 Self(self.0 * other.0, self.1 * other.1)
601 }
602}
603
604impl<N: Num> MulAssign<Line<N>> for Line<N> {
605 fn mul_assign(&mut self, other: Line<N>) {
606 self.0 *= other.0;
607 self.1 *= other.1;
608 }
609}
610
611impl<N: Num> Div<Line<N>> for Line<N> {
612 type Output = Self;
613
614 fn div(self, other: Line<N>) -> Self {
615 Self(self.0 / other.0, self.1 / other.1)
616 }
617}
618
619impl<N: Num> DivAssign<Line<N>> for Line<N> {
620 fn div_assign(&mut self, other: Line<N>) {
621 self.0 /= other.0;
622 self.1 /= other.1;
623 }
624}
625
626impl<N: Num> Rem<Line<N>> for Line<N> {
627 type Output = Self;
628
629 fn rem(self, other: Line<N>) -> Self {
630 Self(self.0 % other.0, self.1 % other.1)
631 }
632}
633
634impl<N: Num> Mul<Size<N>> for Line<N> {
635 type Output = Self;
636
637 fn mul(self, other: Size<N>) -> Self {
638 Self(self.0 * other, self.1 * other)
639 }
640}
641
642impl<N: Num> MulAssign<Size<N>> for Line<N> {
643 fn mul_assign(&mut self, other: Size<N>) {
644 self.0 *= other;
645 self.1 *= other;
646 }
647}
648
649impl<N: Num> Div<Size<N>> for Line<N> {
650 type Output = Self;
651
652 fn div(self, other: Size<N>) -> Self {
653 Self(self.0 / other, self.1 / other)
654 }
655}
656
657impl<N: Num> DivAssign<Size<N>> for Line<N> {
658 fn div_assign(&mut self, other: Size<N>) {
659 self.0 /= other;
660 self.1 /= other;
661 }
662}
663
664impl<N: Num> Mul<N> for Line<N> {
665 type Output = Self;
666
667 fn mul(self, other: N) -> Self {
668 Self(self.0 * other, self.1 * other)
669 }
670}
671
672impl<N: Num> MulAssign<N> for Line<N> {
673 fn mul_assign(&mut self, other: N) {
674 self.0 *= other;
675 self.1 *= other;
676 }
677}
678
679impl<N: Num> Div<N> for Line<N> {
680 type Output = Self;
681
682 fn div(self, other: N) -> Self {
683 Self(self.0 / other, self.1 / other)
684 }
685}
686
687impl<N: Num> DivAssign<N> for Line<N> {
688 fn div_assign(&mut self, other: N) {
689 self.0 /= other;
690 self.1 /= other;
691 }
692}
693
694impl<N: INum> Shl<N> for Line<N> {
695 type Output = Self;
696
697 fn shl(self, other: N) -> Self {
698 Self(self.0 << other, self.1 << other)
699 }
700}
701
702impl<N: INum> ShlAssign<N> for Line<N> {
703 fn shl_assign(&mut self, other: N) {
704 *self = *self << other;
705 }
706}
707
708impl<N: INum> Shr<N> for Line<N> {
709 type Output = Self;
710
711 fn shr(self, other: N) -> Self {
712 Self(self.0 >> other, self.1 >> other)
713 }
714}
715
716impl<N: INum> ShrAssign<N> for Line<N> {
717 fn shr_assign(&mut self, other: N) {
718 *self = *self >> other;
719 }
720}
721
722impl<N: Num + Signed> Neg for Line<N> {
723 type Output = Self;
724
725 fn neg(self) -> Self::Output {
726 Self(-self.0, -self.1)
727 }
728}
729
730impl<N> Add<Transform<N>> for Line<N>
731where
732 N: Num,
733{
734 type Output = Line<N>;
735
736 #[inline(always)]
737 fn add(self, transform: Transform<N>) -> Self::Output {
738 transform + self
739 }
740}
741
742impl<N: Num> From<(Point<N>, Point<N>)> for Line<N> {
743 fn from((start, end): (Point<N>, Point<N>)) -> Self {
744 Self(start, end)
745 }
746}
747
748impl<N: Num> Into<(Point<N>, Point<N>)> for Line<N> {
749 fn into(self) -> (Point<N>, Point<N>) {
750 (self.0, self.1)
751 }
752}
753
754impl<N: Num> IntoIterator for Line<N> {
755 type Item = Point<N>;
756 type IntoIter = LineIterator<N>;
757
758 fn into_iter(self) -> Self::IntoIter {
759 LineIterator::new(self, <N as NumIdentity>::one(), true)
760 }
761}
762
763#[cfg(test)]
764mod test_into_iter {
765 use super::*;
766 use crate::geom::testing_utils::assert_approx_points_vec_eq;
767
768 #[test]
769 fn it_should_iterate_horizontally() {
770 let line = Line::<u32>(Point(0, 0), Point(4, 0));
771 let points = line.into_iter().collect::<Vec<_>>();
772
773 assert_eq!(
774 points,
775 [Point(0, 0), Point(1, 0), Point(2, 0), Point(3, 0),]
776 );
777 }
778
779 #[test]
780 fn it_should_iterate_horizontally_in_reverse() {
781 let line = Line::<u32>(Point(4, 0), Point(0, 0));
782 let points = line.into_iter().collect::<Vec<_>>();
783
784 assert_eq!(
785 points,
786 [Point(4, 0), Point(3, 0), Point(2, 0), Point(1, 0),]
787 );
788 }
789
790 #[test]
791 fn it_should_iterate_vertically() {
792 let line = Line::<u32>(Point(0, 0), Point(0, 4));
793 let points = line.into_iter().collect::<Vec<_>>();
794
795 assert_eq!(
796 points,
797 [Point(0, 0), Point(0, 1), Point(0, 2), Point(0, 3),]
798 );
799 }
800
801 #[test]
802 fn it_should_iterate_vertically_in_reverse() {
803 let line = Line::<u32>(Point(0, 4), Point(0, 0));
804 let points = line.into_iter().collect::<Vec<_>>();
805
806 assert_eq!(
807 points,
808 [Point(0, 4), Point(0, 3), Point(0, 2), Point(0, 1),]
809 );
810 }
811
812 #[test]
813 fn it_should_iterate_diagonally() {
814 let line = Line::<u32>(Point(0, 0), Point(4, 4));
815 let points = line.into_iter().collect::<Vec<_>>();
816
817 assert_eq!(
818 points,
819 [Point(0, 0), Point(1, 1), Point(2, 2), Point(3, 3),]
820 );
821 }
822
823 #[test]
824 fn it_should_return_one_point_when_start_and_finish_are_same() {
825 let line: Line<f32> = Line(Point(10.0, 20.0), Point(10.0, 20.0));
826 let points: Vec<Point<f32>> = line.into_iter().collect();
827
828 assert_eq!(points, vec![]);
829 }
830
831 #[test]
832 fn it_should_iterate_all_points_from_start_to_finish() {
833 let line: Line<f32> = Line(Point(10.0, 20.0), Point(15.0, 24.0));
834 let points: Vec<Point<f32>> = line.into_iter().collect();
835
836 #[rustfmt::skip]
837 assert_approx_points_vec_eq(points, vec![
838 Point(10.0, 20.0),
839 Point(10.780869, 20.624695),
840 Point(11.561737, 21.24939),
841 Point(12.342606, 21.874084),
842 Point(13.123474, 22.49878),
843 Point(13.904343, 23.123474),
844 Point(14.685211, 23.748169),
845 ]);
846 }
847
848 #[test]
849 fn it_should_iterate_all_points_from_start_to_finish_in_reverse() {
850 let line: Line<f32> = Line(Point(10.0, 20.0), Point(15.0, 24.0)).reverse();
851 let points: Vec<Point<f32>> = line.into_iter().collect();
852
853 #[rustfmt::skip]
854 assert_approx_points_vec_eq(points, vec![
855 Point(15.0, 24.0),
856 Point(14.219131, 23.375305),
857 Point(13.438263, 22.75061),
858 Point(12.657394, 22.125916),
859 Point(11.876526, 21.50122),
860 Point(11.095657, 20.876526),
861 Point(10.314789, 20.251831),
862 ]);
863 }
864}
865
866#[cfg(test)]
867mod test_into_iter_inclusive {
868 use super::*;
869 use crate::geom::testing_utils::assert_approx_points_vec_eq;
870
871 #[test]
872 fn it_should_return_one_point_when_start_and_finish_are_same() {
873 let line: Line<f32> = Line(Point(10.0, 20.0), Point(10.0, 20.0));
874 let points: Vec<Point<f32>> = line.into_iter_inclusive().collect();
875
876 #[rustfmt::skip]
877 assert_approx_points_vec_eq(points, vec![
878 Point(10.0, 20.0),
879 ]);
880 }
881
882 #[test]
883 fn it_should_iterate_all_points_from_start_to_finish_inclusive() {
884 let line: Line<f32> = Line(Point(10.0, 20.0), Point(15.0, 24.0));
885 let points: Vec<Point<f32>> = line.into_iter_inclusive().collect();
886
887 #[rustfmt::skip]
888 assert_approx_points_vec_eq(points, vec![
889 Point(10.0, 20.0),
890 Point(10.780869, 20.624695),
891 Point(11.561737, 21.24939),
892 Point(12.342606, 21.874084),
893 Point(13.123474, 22.49878),
894 Point(13.904343, 23.123474),
895 Point(14.685211, 23.748169),
896 Point(15.0, 24.0),
897 ]);
898 }
899
900 #[test]
901 fn it_should_iterate_all_points_from_start_to_finish_in_reverse_inclusive() {
902 let line: Line<f32> = Line(Point(10.0, 20.0), Point(15.0, 24.0)).reverse();
903 let points: Vec<Point<f32>> = line.into_iter_inclusive().collect();
904
905 #[rustfmt::skip]
906 assert_approx_points_vec_eq(points, vec![
907 Point(15.0, 24.0),
908 Point(14.219131, 23.375305),
909 Point(13.438263, 22.75061),
910 Point(12.657394, 22.125916),
911 Point(11.876526, 21.50122),
912 Point(11.095657, 20.876526),
913 Point(10.314789, 20.251831),
914 Point(10.0, 20.0),
915 ]);
916 }
917
918 #[test]
919 fn it_should_iterate_diagonally() {
920 let line = Line::<u32>(Point(0, 0), Point(4, 4));
921 let points = line.into_iter_inclusive().collect::<Vec<_>>();
922
923 assert_eq!(
924 points,
925 [
926 Point(0, 0),
927 Point(1, 1),
928 Point(2, 2),
929 Point(3, 3),
930 Point(4, 4)
931 ]
932 );
933 }
934}
935
936#[cfg(test)]
937mod is_horizontal {
938 use super::*;
939
940 #[test]
941 fn it_should_be_true_when_horizontal() {
942 let line = Line(Point(5, 9), Point(8, 9));
943 assert_eq!(line.is_horizontal(), true);
944 }
945
946 #[test]
947 fn it_should_be_false_when_not_horizontal() {
948 let line = Line(Point(5, 9), Point(8, 10));
949 assert_eq!(line.is_horizontal(), false);
950 }
951}
952
953#[cfg(test)]
954mod is_vertical {
955 use super::*;
956
957 #[test]
958 fn it_should_be_true_when_vertical() {
959 let line = Line(Point(5, 9), Point(5, 19));
960 assert_eq!(line.is_vertical(), true);
961 }
962
963 #[test]
964 fn it_should_be_false_when_not_vertical() {
965 let line = Line(Point(5, 9), Point(8, 10));
966 assert_eq!(line.is_vertical(), false);
967 }
968}
969
970#[cfg(test)]
971mod is_straight {
972 use super::*;
973
974 #[test]
975 fn it_should_be_true_when_vertical() {
976 let line = Line(Point(5, 9), Point(5, 19));
977 assert_eq!(line.is_vertical(), true);
978 }
979
980 #[test]
981 fn it_should_be_true_when_horizontal() {
982 let line = Line(Point(5, 9), Point(8, 9));
983 assert_eq!(line.is_horizontal(), true);
984 }
985
986 #[test]
987 fn it_should_be_false_when_angled() {
988 let line = Line(Point(5, 9), Point(8, 10));
989 assert_eq!(line.is_vertical(), false);
990 }
991}
992
993#[cfg(test)]
994mod diff {
995 use super::*;
996
997 #[test]
998 fn it_should_be_end_minus_start() {
999 let diff: Line<i32> = Line(Point(5, 9), Point(24, -3));
1000 assert_eq!(diff.diff(), Size(19, -12));
1001 }
1002}
1003
1004#[cfg(test)]
1005mod intersect {
1006 use super::*;
1007
1008 #[test]
1009 fn it_should_ignore_lines_fully_above() {
1010 let rect = Rect(Point(10.0, 10.0), Size(10.0, 10.0));
1011 let line = Line(Point(5.0, 5.0), Point(25.0, 5.0));
1012 assert_eq!(line.intersect_rect(rect), None);
1013 }
1014
1015 #[test]
1016 fn it_should_ignore_lines_fully_left() {
1017 let rect = Rect(Point(10.0, 10.0), Size(10.0, 10.0));
1018 let line = Line(Point(5.0, 5.0), Point(5.0, 25.0));
1019 assert_eq!(line.intersect_rect(rect), None);
1020 }
1021
1022 #[test]
1023 fn it_should_ignore_lines_fully_right() {
1024 let rect = Rect(Point(10.0, 10.0), Size(10.0, 10.0));
1025 let line = Line(Point(25.0, 5.0), Point(25.0, 25.0));
1026 assert_eq!(line.intersect_rect(rect), None);
1027 }
1028
1029 #[test]
1030 fn it_should_ignore_lines_fully_below() {
1031 let rect = Rect(Point(10.0, 10.0), Size(10.0, 10.0));
1032 let line = Line(Point(5.0, 25.0), Point(25.0, 25.0));
1033 assert_eq!(line.intersect_rect(rect), None);
1034 }
1035
1036 #[test]
1051 fn it_should_ignore_lines_crossing_a_corner() {
1052 let rect = Rect(Point(10.0, 10.0), Size(10.0, 10.0));
1053 let line = Line(Point(5.0, 12.0), Point(12.0, 5.0));
1054 assert_eq!(line.intersect_rect(rect), None);
1055 }
1056
1057 #[test]
1058 fn it_should_not_clip_lines_fully_inside() {
1059 let rect = Rect(Point(10.0, 10.0), Size(10.0, 10.0));
1060
1061 let l1 = Line(Point(12.0, 12.0), Point(18.0, 18.0));
1062 let l2 = Line(Point(12.0, 18.0), Point(18.0, 12.0));
1063 let l3 = Line(Point(18.0, 18.0), Point(12.0, 12.0));
1064
1065 assert_eq!(l1.intersect_rect(rect), Some(l1));
1066 assert_eq!(l2.intersect_rect(rect), Some(l2));
1067 assert_eq!(l3.intersect_rect(rect), Some(l3));
1068 }
1069
1070 #[test]
1071 fn it_should_clip_lines_from_the_left() {
1072 let rect = Rect(Point(10.0, 10.0), Size(10.0, 10.0));
1073 let line = Line(Point(7.0, 11.0), Point(16.0, 17.0));
1074
1075 assert_eq!(
1076 line.intersect_rect(rect),
1077 Some(Line(Point(10.0, 13.0), Point(16.0, 17.0)))
1078 );
1079 }
1080
1081 #[test]
1082 fn it_should_clip_lines_from_the_bottom_right() {
1083 let rect = Rect(Point(10.0, 10.0), Size(10.0, 10.0));
1084 let line = Line(Point(26.0, 29.0), Point(18.0, 17.0));
1085
1086 assert_eq!(
1087 line.intersect_rect(rect),
1088 Some(Line(Point(20.0, 20.0), Point(18.0, 17.0))),
1089 );
1090 }
1091
1092 #[test]
1093 fn it_should_clip_lines_fully_crossing() {
1094 let rect = Rect(Point(10.0, 10.0), Size(8.0, 9.0));
1095 let line = Line(Point(12.0, 7.0), Point(24.0, 25.0));
1096
1097 assert_eq!(
1098 line.intersect_rect(rect),
1099 Some(Line(Point(14.0, 10.0), Point(18.0, 16.0)))
1100 );
1101 }
1102
1103 #[test]
1104 fn it_should_clip_u32_lines() {
1105 let rect: Rect<u32> = Rect(Point(10, 10), Size(10, 10));
1106 let line: Line<u32> = Line(Point(7, 11), Point(16, 17));
1107
1108 assert_eq!(
1109 line.intersect_rect(rect),
1110 Some(Line(Point(10, 13), Point(16, 17)))
1111 );
1112
1113 let rect: Rect<u32> = Rect(Point(10, 10), Size(8, 9));
1114 let line: Line<u32> = Line(Point(12, 7), Point(24, 25));
1115
1116 assert_eq!(
1117 line.intersect_rect(rect),
1118 Some(Line(Point(14, 10), Point(18, 16)))
1119 );
1120 }
1121}
1122
1123#[cfg(test)]
1124mod interpolation_point {
1125 use super::*;
1126
1127 #[test]
1128 fn it_should_return_start_when_zero() {
1129 let line = Line(Point(3.0, 4.0), Point(14.0, 19.0));
1130 assert_eq!(line.interpolation_point(0.0), Point(3.0, 4.0));
1131 }
1132
1133 #[test]
1134 fn it_should_return_end_when_one() {
1135 let line = Line(Point(3.0, 4.0), Point(14.0, 19.0));
1136 assert_eq!(line.interpolation_point(1.0), Point(14.0, 19.0));
1137 }
1138
1139 #[test]
1140 fn it_should_return_middle_when_half() {
1141 let line = Line(Point(3.0, 4.0), Point(14.0, 19.0));
1142 assert_eq!(line.interpolation_point(0.5), Point(8.5, 11.5));
1143 }
1144}
1145
1146#[cfg(test)]
1147mod rotate {
1148 use super::*;
1149 use std::f32::consts::TAU;
1150
1151 #[test]
1152 fn it_should_be_level_after_45_rotation() {
1153 let line = Line(Point(0.0, 0.0), Point(10.0, 10.0));
1154 let rotation: Line<i32> = line.rotate(TAU / 8.0).to_rounded();
1155 assert_eq!(rotation, Line(Point(-2, 5), Point(12, 5)));
1156 }
1157
1158 #[test]
1159 fn it_should_be_flipped_with_180_rotation() {
1160 let line = Line(Point(1.0, 5.0), Point(15.0, 20.0));
1161 let rotation: Line<i32> = line.rotate(TAU / 2.0).to_rounded();
1162 assert_eq!(rotation, line.reverse().to_rounded());
1163 }
1164
1165 #[test]
1166 fn it_should_be_the_same_with_360_rotation() {
1167 let line = Line(Point(0.0, 5.0), Point(15.0, 20.0));
1168 let rotation: Line<i32> = line.rotate(TAU).to_rounded();
1169 assert_eq!(rotation, line.to_rounded());
1170 }
1171}
1172
1173#[cfg(test)]
1174mod rotate_around_point {
1175 use super::*;
1176 use std::f32::consts::TAU;
1177
1178 #[test]
1179 fn it_should_be_rotated_with_90_rotation() {
1180 let line = Line(Point(0.0, 5.0), Point(5.0, 0.0));
1181 let rotation: Line<i32> = line
1182 .rotate_around_point(TAU * 0.25, Point(0.0, 0.0))
1183 .to_rounded();
1184 assert_eq!(rotation, Line(Point(5, 0), Point(0, -5)));
1185 }
1186
1187 #[test]
1188 fn it_should_be_flipped_with_180_rotation() {
1189 let line = Line(Point(0.0, 5.0), Point(5.0, 0.0));
1190 let rotation: Line<i32> = line
1191 .rotate_around_point(TAU * 0.5, Point(0.0, 0.0))
1192 .to_rounded();
1193 assert_eq!(rotation, Line(Point(0, -5), Point(-5, -0)));
1194 }
1195
1196 #[test]
1197 fn it_should_be_rotated_with_90_rotation_around_point() {
1198 let line = Line(Point(8.0, 15.0), Point(13.0, 10.0));
1199 let rotation: Line<i32> = line
1200 .rotate_around_point(TAU * 0.25, Point(8.0, 10.0))
1201 .to_rounded();
1202 assert_eq!(rotation, Line(Point(13, 10), Point(8, 5)));
1203 }
1204
1205 #[test]
1206 fn it_should_be_flipped_with_180_rotation_around_point() {
1207 let line = Line(Point(8.0, 15.0), Point(13.0, 10.0));
1208 let rotation: Line<i32> = line
1209 .rotate_around_point(TAU * 0.5, Point(8.0, 10.0))
1210 .to_rounded();
1211 assert_eq!(rotation, Line(Point(8, 5), Point(3, 10)));
1212 }
1213}
1214
1215#[cfg(test)]
1216mod direction_sign {
1217 use super::*;
1218
1219 #[test]
1220 fn it_should_be_positive_when_end_is_larger() {
1221 let line = Line(Point(0.0, 0.0), Point(100.0, 100.0));
1222
1223 assert_eq!(line.direction_sign(), Point(1.0, 1.0));
1224 }
1225
1226 #[test]
1227 fn it_should_be_positive_when_end_is_the_same() {
1228 let line = Line(Point(0.0, 0.0), Point(0.0, 0.0));
1229
1230 assert_eq!(line.direction_sign(), Point(1.0, 1.0));
1231 }
1232
1233 #[test]
1234 fn it_should_be_negative_when_end_is_smaller() {
1235 let line = Line(Point(0.0, 0.0), Point(-100.0, -100.0));
1236
1237 assert_eq!(line.direction_sign(), Point(-1.0, -1.0));
1238 }
1239
1240 #[test]
1241 fn it_should_be_mix_when_end_is_smaller_and_larger() {
1242 let line = Line(Point(0.0, 0.0), Point(100.0, -100.0));
1243
1244 assert_eq!(line.direction_sign(), Point(1.0, -1.0));
1245 }
1246}
1247
1248#[cfg(test)]
1249mod hypot {
1250 use super::*;
1251
1252 #[test]
1253 fn it_should_be_correct_for_positive() {
1254 let line = Line(Point(2.0, 2.0), Point(5.0, 6.0));
1255
1256 assert_eq!(5.0, line.hypot());
1257 }
1258}
1259
1260#[cfg(test)]
1261mod hypot_sqrd {
1262 use super::*;
1263
1264 #[test]
1265 fn it_should_be_correct_for_positive() {
1266 let line = Line(Point(2.0, 2.0), Point(5.0, 6.0));
1267
1268 assert_eq!(25.0, line.hypot_sqrd());
1269 }
1270}
1271
1272#[cfg(test)]
1273mod intersect_line {
1274 use super::*;
1275
1276 #[test]
1277 fn it_should_intersect_lines() {
1278 let line_1 = Line(Point(0.0, 0.0), Point(10.0, 10.0));
1279 let line_2 = Line(Point(0.0, 10.0), Point(10.0, 0.0));
1280
1281 let intersection = line_1.intersect_line(line_2);
1282 assert_eq!(intersection, Some(Point(5.0, 5.0)));
1283 }
1284
1285 #[test]
1286 fn it_should_have_intersections_working_in_both_directions() {
1287 let line_1 = Line(Point(0.0, 0.0), Point(10.0, 10.0));
1288 let line_2 = Line(Point(0.0, 10.0), Point(10.0, 0.0));
1289
1290 let intersection_1 = line_1.intersect_line(line_2);
1291 let intersection_2 = line_1.intersect_line(line_2);
1292 assert_eq!(intersection_1, intersection_2);
1293 }
1294
1295 #[test]
1296 fn it_should_not_intersect_parallel_lines() {
1297 let line_1 = Line(Point(0.0, 0.0), Point(10.0, 2.0));
1298 let line_2 = Line(Point(0.0, 2.0), Point(10.0, 4.0));
1299
1300 let intersection = line_1.intersect_line(line_2);
1301 assert_eq!(intersection, None);
1302 }
1303}
1304
1305#[cfg(test)]
1306mod flip {
1307 use super::*;
1308
1309 #[test]
1310 fn it_should_swap_start_and_end() {
1311 let line: Line<i32> = Line(Point(123, 456), Point(987, 654));
1312 assert_eq!(line.flip(), Line(Point(987, 654), Point(123, 456)))
1313 }
1314}
1315
1316#[cfg(test)]
1317mod step_direction {
1318 use super::*;
1319
1320 #[test]
1321 fn it_should_return_zero_for_no_direction() {
1322 let point: Point<f32> = Point(0.0, 0.0);
1323 let line = Line(point, point);
1324
1325 let step_direction = line.step_direction();
1326 assert_eq!(Point(0.0_f32, 0.0_f32), step_direction);
1327 }
1328}
1329
1330#[cfg(test)]
1331mod into_slice {
1332 use super::*;
1333
1334 #[test]
1335 fn it_should_return_start_and_end_in_slice() {
1336 let line: Line<u32> = Line(Point(1, 2), Point(3, 4));
1337 assert_eq!(line.into_slice(), [1, 2, 3, 4]);
1338 }
1339}