geometry_model/infinite_line.rs
1//! Infinite 2D line in general form, `a*x + b*y + c = 0`.
2//!
3//! Mirrors `boost::geometry::model::infinite_line` from
4//! `geometries/infinite_line.hpp:29-52` and the operations in
5//! `arithmetic/infinite_line_functions.hpp:24-107`.
6
7use geometry_coords::CoordinateScalar;
8use geometry_cs::Cartesian;
9use geometry_trait::Point;
10
11use crate::Point2D;
12
13/// An infinite line in the general form `a*x + b*y + c = 0`.
14///
15/// Mirrors `model::infinite_line<Type>` from
16/// `geometries/infinite_line.hpp:35-52`. It is intentionally not a
17/// [`geometry_trait::Geometry`] because the C++ source likewise notes that the
18/// type is not conceptized.
19#[derive(Debug, Clone, Copy, PartialEq)]
20pub struct InfiniteLine<T: CoordinateScalar = f64> {
21 /// Coefficient of x. A horizontal line has `a == 0`.
22 pub a: T,
23 /// Coefficient of y. A vertical line has `b == 0`.
24 pub b: T,
25 /// Constant term. A line through the origin has `c == 0`.
26 pub c: T,
27 /// Whether the coefficients have been normalized by a caller.
28 pub normalized: bool,
29}
30
31impl<T: CoordinateScalar> InfiniteLine<T> {
32 /// Construct a line from its general-form coefficients.
33 #[inline]
34 #[must_use]
35 pub const fn new(a: T, b: T, c: T) -> Self {
36 Self {
37 a,
38 b,
39 c,
40 normalized: false,
41 }
42 }
43
44 /// Construct the infinite line through `(x1, y1)` and `(x2, y2)`.
45 ///
46 /// Mirrors `detail::make::make_infinite_line` from
47 /// `algorithms/detail/make/make.hpp:21-32`:
48 /// `a = y1-y2`, `b = x2-x1`, `c = -a*x1-b*y1`.
49 #[inline]
50 #[must_use]
51 pub fn from_coordinates(x1: T, y1: T, x2: T, y2: T) -> Self {
52 let a = y1 - y2;
53 let b = x2 - x1;
54 let c = -(a * x1) - b * y1;
55 Self::new(a, b, c)
56 }
57
58 /// Construct the infinite line through two points.
59 ///
60 /// Mirrors `make_infinite_line(PointA, PointB)` from
61 /// `algorithms/detail/make/make.hpp:34-40`.
62 #[inline]
63 #[must_use]
64 pub fn from_points<P>(start: &P, end: &P) -> Self
65 where
66 P: Point<Scalar = T>,
67 {
68 Self::from_coordinates(
69 start.get::<0>(),
70 start.get::<1>(),
71 end.get::<0>(),
72 end.get::<1>(),
73 )
74 }
75
76 /// Return the unnormalized signed side measure at `(x, y)`.
77 ///
78 /// Positive is left, negative is right, and zero is on the line.
79 /// Mirrors `arithmetic::side_value` from
80 /// `arithmetic/infinite_line_functions.hpp:70-93`.
81 #[inline]
82 #[must_use]
83 pub fn side_value(&self, x: T, y: T) -> T {
84 self.a * x + self.b * y + self.c
85 }
86
87 /// Return whether both directional coefficients are zero.
88 ///
89 /// Mirrors `arithmetic::is_degenerate` from
90 /// `arithmetic/infinite_line_functions.hpp:99-104`.
91 #[inline]
92 #[must_use]
93 pub fn is_degenerate(&self) -> bool {
94 self.a == T::ZERO && self.b == T::ZERO
95 }
96
97 /// Calculate the intersection of two non-parallel infinite lines.
98 ///
99 /// Mirrors `arithmetic::intersection_point` and
100 /// `assign_intersection_point` from
101 /// `arithmetic/infinite_line_functions.hpp:32-67`. `None` represents
102 /// parallel or collinear lines.
103 #[inline]
104 #[must_use]
105 pub fn intersection(&self, other: &Self) -> Option<Point2D<T, Cartesian>> {
106 let denominator = self.a * other.b - self.b * other.a;
107 if denominator == T::ZERO {
108 return None;
109 }
110 let x = (self.b * other.c - self.c * other.b) / denominator;
111 let y = (self.c * other.a - self.a * other.c) / denominator;
112 Some(Point2D::new(x, y))
113 }
114}
115
116impl<T: CoordinateScalar> Default for InfiniteLine<T> {
117 #[inline]
118 fn default() -> Self {
119 Self::new(T::ZERO, T::ZERO, T::ZERO)
120 }
121}