diff --git a/geo-types/src/line.rs b/geo-types/src/line.rs index f82ce430c..ed138dfcd 100644 --- a/geo-types/src/line.rs +++ b/geo-types/src/line.rs @@ -36,6 +36,53 @@ where end } } + + /// Calculate the difference in ‘x’ components (Δx). + /// + /// Equivalent to: + /// + /// ```txt + /// line.end.x() - line.start.x() + /// ``` + pub fn dx(&self) -> T { + self.end.x() - self.start.x() + } + + /// Calculate the difference in ‘y’ components (Δy). + /// + /// Equivalent to: + /// + /// ```txt + /// line.end.y() - line.start.y() + /// ``` + pub fn dy(&self) -> T { + self.end.y() - self.start.y() + } + + /// Calculate the slope (Δy/Δx). + /// + /// Equivalent to: + /// + /// ```txt + /// line.dy() / line.dx() + /// ``` + pub fn slope(&self) -> T { + self.dy() / self.dx() + } + + /// Calculate the [determinant] of the line. + /// + /// Equivalent to: + /// + /// ```txt + /// line.start.x() * line.end.y() - + /// line.start.y() * line.end.x() + /// ``` + /// + /// [determinant]: https://en.wikipedia.org/wiki/Determinant + pub fn determinant(&self) -> T { + self.start.x() * self.end.y() - self.start.y() * self.end.x() + } } #[cfg(feature = "spade")] diff --git a/geo/src/algorithm/centroid.rs b/geo/src/algorithm/centroid.rs index f0e611d8c..1c93abc9b 100644 --- a/geo/src/algorithm/centroid.rs +++ b/geo/src/algorithm/centroid.rs @@ -37,7 +37,7 @@ where } let mut tmp = T::zero(); for line in linestring.lines() { - tmp = tmp + (line.start.x() * line.end.y() - line.end.x() * line.start.y()); + tmp = tmp + line.determinant(); } tmp / (T::one() + T::one()) } @@ -55,7 +55,7 @@ where let mut sum_x = T::zero(); let mut sum_y = T::zero(); for line in poly_ext.lines() { - let tmp = line.start.x() * line.end.y() - line.end.x() * line.start.y(); + let tmp = line.determinant(); sum_x = sum_x + ((line.end.x() + line.start.x()) * tmp); sum_y = sum_y + ((line.end.y() + line.start.y()) * tmp); } @@ -71,8 +71,8 @@ where fn centroid(&self) -> Self::Output { let two = T::one() + T::one(); - let x = (self.start.x() + self.end.x()) / two; - let y = (self.start.y() + self.end.y()) / two; + let x = self.start.x() + self.dx() / two; + let y = self.start.y() + self.dy() / two; Point::new(x, y) } } diff --git a/geo/src/algorithm/euclidean_distance.rs b/geo/src/algorithm/euclidean_distance.rs index 309b025c6..c11aaa43c 100644 --- a/geo/src/algorithm/euclidean_distance.rs +++ b/geo/src/algorithm/euclidean_distance.rs @@ -4,6 +4,7 @@ use algorithm::polygon_distance_fast_path::*; use num_traits::float::FloatConst; use num_traits::{Float, Signed, ToPrimitive}; use {Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon}; +use algorithm::euclidean_length::EuclideanLength; use spade::rtree::RTree; use spade::SpadeFloat; @@ -101,8 +102,7 @@ where { /// Minimum distance between two Points fn euclidean_distance(&self, p: &Point) -> T { - let (dx, dy) = (self.x() - p.x(), self.y() - p.y()); - dx.hypot(dy) + Line::new(*self, *p).euclidean_length() } } diff --git a/geo/src/algorithm/euclidean_length.rs b/geo/src/algorithm/euclidean_length.rs index 1c07b7547..16483282e 100644 --- a/geo/src/algorithm/euclidean_length.rs +++ b/geo/src/algorithm/euclidean_length.rs @@ -1,7 +1,6 @@ use num_traits::Float; use ::{Line, LineString, MultiLineString}; -use algorithm::euclidean_distance::EuclideanDistance; /// Calculation of the length @@ -30,7 +29,7 @@ where T: Float, { fn euclidean_length(&self) -> T { - self.start.euclidean_distance(&self.end) + self.dx().hypot(self.dy()) } } diff --git a/geo/src/algorithm/intersects.rs b/geo/src/algorithm/intersects.rs index 56bd46bcc..42438e52b 100644 --- a/geo/src/algorithm/intersects.rs +++ b/geo/src/algorithm/intersects.rs @@ -29,17 +29,15 @@ where T: Float, { fn intersects(&self, p: &Point) -> bool { - let dx = self.end.x() - self.start.x(); - let dy = self.end.y() - self.start.y(); - let tx = if dx == T::zero() { + let tx = if self.dx() == T::zero() { None } else { - Some((p.x() - self.start.x()) / dx) + Some((p.x() - self.start.x()) / self.dx()) }; - let ty = if dy == T::zero() { + let ty = if self.dy() == T::zero() { None } else { - Some((p.y() - self.start.y()) / dy) + Some((p.y() - self.start.y()) / self.dy()) }; match (tx, ty) { (None, None) => { @@ -78,14 +76,12 @@ where fn intersects(&self, line: &Line) -> bool { // Using Cramer's Rule: // https://en.wikipedia.org/wiki/Intersection_%28Euclidean_geometry%29#Two_line_segments - let (x1, y1, x2, y2) = (self.start.x(), self.start.y(), self.end.x(), self.end.y()); - let (x3, y3, x4, y4) = (line.start.x(), line.start.y(), line.end.x(), line.end.y()); - let a1 = x2 - x1; - let a2 = y2 - y1; - let b1 = x3 - x4; // == -(x4 - x3) - let b2 = y3 - y4; // == -(y4 - y3) - let c1 = x3 - x1; - let c2 = y3 - y1; + let a1 = self.dx(); + let a2 = self.dy(); + let b1 = -line.dx(); + let b2 = -line.dy(); + let c1 = line.start.x() - self.start.x(); + let c2 = line.start.y() - self.start.y(); let d = a1 * b2 - a2 * b1; if d == T::zero() { @@ -149,15 +145,14 @@ where } for a in self.lines() { for b in linestring.lines() { - let u_b = (b.end.y() - b.start.y()) * (a.end.x() - a.start.x()) - - (b.end.x() - b.start.x()) * (a.end.y() - a.start.y()); + let u_b = b.dy() * a.dx() - b.dx() * a.dy(); if u_b == T::zero() { continue; } - let ua_t = (b.end.x() - b.start.x()) * (a.start.y() - b.start.y()) - - (b.end.y() - b.start.y()) * (a.start.x() - b.start.x()); - let ub_t = (a.end.x() - a.start.x()) * (a.start.y() - b.start.y()) - - (a.end.y() - a.start.y()) * (a.start.x() - b.start.x()); + let ua_t = b.dx() * (a.start.y() - b.start.y()) + - b.dy() * (a.start.x() - b.start.x()); + let ub_t = a.dx() * (a.start.y() - b.start.y()) + - a.dy() * (a.start.x() - b.start.x()); let u_a = ua_t / u_b; let u_b = ub_t / u_b; if (T::zero() <= u_a) && (u_a <= T::one()) && (T::zero() <= u_b) diff --git a/geo/src/algorithm/polygon_distance_fast_path.rs b/geo/src/algorithm/polygon_distance_fast_path.rs index 04dfbbb5f..7ea1c22f6 100644 --- a/geo/src/algorithm/polygon_distance_fast_path.rs +++ b/geo/src/algorithm/polygon_distance_fast_path.rs @@ -139,7 +139,7 @@ where if pprev.x() > p.x() { if pprev.y() >= p.y() && pnext.y() >= p.y() { if *slope > T::zero() { - slope_prev = (pprev.y() - p.y()) / (pprev.x() - p.x()); + slope_prev = Line::new(*p, pprev).slope(); if clockwise && *slope <= slope_prev || !clockwise && *slope >= slope_prev { cos = -cos; sin = -sin; @@ -156,8 +156,8 @@ where sin = -sin; } } else { - slope_prev = (pprev.y() - p.y()) / (pprev.x() - p.x()); - slope_next = (pnext.y() - p.y()) / (pnext.x() - p.x()); + slope_prev = Line::new(*p, pprev).slope(); + slope_next = Line::new(*p, pnext).slope(); if clockwise { if *slope <= slope_prev { cos = -cos; @@ -192,8 +192,8 @@ where sin = -sin; } } else { - slope_prev = (p.y() - pprev.y()) / (p.x() - pprev.x()); - slope_next = (p.y() - pnext.y()) / (p.x() - pnext.x()); + slope_prev = Line::new(*p, pprev).slope(); + slope_next = Line::new(*p, pnext).slope(); if clockwise { if *slope <= slope_prev { sin = -sin; @@ -208,7 +208,7 @@ where } } else if pprev.y() <= p.y() && pnext.y() <= p.y() { if *slope > T::zero() { - slope_next = (p.y() - pnext.y()) / (p.x() - pnext.x()); + slope_next = Line::new(*p, pnext).slope(); if *slope >= slope_next { cos = -cos; sin = -sin; @@ -274,9 +274,9 @@ where let slope = if vertical || p.y() == u.y() { T::zero() } else if u.x() > p.x() { - (u.y() - p.y()) / (u.x() - p.x()) + Line::new(*p, *u).slope() } else { - (p.y() - u.y()) / (p.x() - u.x()) + Line::new(*u, *p).slope() }; let upx; let upy; @@ -402,9 +402,9 @@ fn triangle_area(a: &Point, b: &Point, c: &Point) -> T where T: Float, { - (T::from(0.5).unwrap() - * (a.x() * b.y() - a.y() * b.x() + a.y() * c.x() - a.x() * c.y() + b.x() * c.y() - - c.x() * b.y())) + (Line::new(*a, *b).determinant() + + Line::new(*b, *c).determinant() + + Line::new(*c, *a).determinant()) / (T::one() + T::one()) } /// Does abc turn left? @@ -491,13 +491,11 @@ where } false => { state.vertical = false; - if state.p1.x() > state.p1next.x() { - state.slope = - (state.p1.y() - state.p1next.y()) / (state.p1.x() - state.p1next.x()); + state.slope = if state.p1.x() > state.p1next.x() { + Line::new(state.p1next, state.p1).slope() } else { - state.slope = - (state.p1next.y() - state.p1.y()) / (state.p1next.x() - state.p1.x()); - } + Line::new(state.p1, state.p1next).slope() + }; } } } @@ -510,13 +508,11 @@ where } false => { state.vertical = false; - if state.q2.x() > state.q2next.x() { - state.slope = - (state.q2.y() - state.q2next.y()) / (state.q2.x() - state.q2next.x()); + state.slope = if state.q2.x() > state.q2next.x() { + Line::new(state.q2next, state.q2).slope() } else { - state.slope = - (state.q2next.y() - state.q2.y()) / (state.q2next.x() - state.q2.x()); - } + Line::new(state.q2, state.q2next).slope() + }; } } } diff --git a/geo/src/algorithm/simplify.rs b/geo/src/algorithm/simplify.rs index 5f86502ef..bee42924e 100644 --- a/geo/src/algorithm/simplify.rs +++ b/geo/src/algorithm/simplify.rs @@ -1,5 +1,5 @@ use num_traits::Float; -use ::{LineString, MultiLineString, MultiPolygon, Point, Polygon}; +use ::{LineString, MultiLineString, MultiPolygon, Point, Polygon, Line}; use algorithm::euclidean_distance::EuclideanDistance; // perpendicular distance from a point to a line diff --git a/geo/src/algorithm/winding_order.rs b/geo/src/algorithm/winding_order.rs index dff0affcd..a86936935 100644 --- a/geo/src/algorithm/winding_order.rs +++ b/geo/src/algorithm/winding_order.rs @@ -8,7 +8,7 @@ pub(crate) fn twice_signed_ring_area(linestring: &LineString) -> T where T } let mut tmp = T::zero(); for line in linestring.lines() { - tmp = tmp + (line.start.x() * line.end.y() - line.end.x() * line.start.y()); + tmp = tmp + line.determinant(); } tmp