diff options
Diffstat (limited to 'src/math')
| -rw-r--r-- | src/math/line_segment.rs | 51 | ||||
| -rw-r--r-- | src/math/mod.rs | 2 | ||||
| -rw-r--r-- | src/math/polygon/mod.rs | 2 | ||||
| -rw-r--r-- | src/math/polygon/polygon_graph.rs | 7 | ||||
| -rw-r--r-- | src/math/rect.rs | 6 | ||||
| -rw-r--r-- | src/math/surface.rs | 2 | ||||
| -rw-r--r-- | src/math/triangle.rs | 2 | ||||
| -rw-r--r-- | src/math/vec2.rs | 25 |
8 files changed, 93 insertions, 4 deletions
diff --git a/src/math/line_segment.rs b/src/math/line_segment.rs index b496787..204cf0c 100644 --- a/src/math/line_segment.rs +++ b/src/math/line_segment.rs @@ -1,3 +1,6 @@ +//! A line segment is like a line, but with a start and an end, with the line only being between +//! those two. + use super::{Rect, Surface, TripletOrientation, Vec2}; use alga::general::{ClosedDiv, ClosedMul, ClosedSub}; use nalgebra::{RealField, Scalar}; @@ -5,6 +8,7 @@ use num_traits::Zero; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; +#[allow(missing_docs)] #[derive(Debug, Clone, Deserialize, Serialize)] pub struct LineSegment<T: Scalar + Copy> { pub start: Vec2<T>, @@ -12,6 +16,7 @@ pub struct LineSegment<T: Scalar + Copy> { } impl<T: Scalar + Copy> LineSegment<T> { + /// Create a new line segment from `start` to `end` pub fn new(start: Vec2<T>, end: Vec2<T>) -> Self { Self { start, end } } @@ -92,6 +97,9 @@ impl<T: Scalar + Copy> LineSegment<T> { false } + /// Try to find the the point where the two line segments intersect. If they do not intersect, + /// `None` is returned. If the lines are parallel and intersect (at least part of a line is on + /// a part of the other line), inside that region is returned. pub fn intersection(line_a: &LineSegment<T>, line_b: &LineSegment<T>) -> Option<Vec2<T>> where T: ClosedSub + ClosedMul + ClosedDiv + Zero + RealField, @@ -104,10 +112,26 @@ impl<T: Scalar + Copy> LineSegment<T> { let dby = line_b.start.y - line_b.end.y; // Calculate determinant to see, if the lines are parallel or not - // TODO: Collinearity check? let d = (dax * dby) - (day * dbx); if d == T::zero() { - None + // The two line segments are parallel, check if one may be on the other. + if super::triplet_orientation(line_a.start, line_a.end, line_b.start) == TripletOrientation::Collinear + && super::triplet_orientation(line_a.start, line_a.end, line_b.end) == TripletOrientation::Collinear + { + if line_a.contains_collinear(line_b.start) { + Some(line_b.start) + } else if line_a.contains_collinear(line_b.end) { + Some(line_b.end) + } else if line_b.contains_collinear(line_a.start) { + Some(line_a.start) + } else if line_b.contains_collinear(line_a.end) { + Some(line_a.end) + } else { + None + } + } else { + None + } } else { let ad = (line_a.start.x * line_a.end.y) - (line_a.start.y * line_a.end.x); let bd = (line_b.start.x * line_b.end.y) - (line_b.start.y * line_b.end.x); @@ -190,4 +214,27 @@ mod test { assert!(!segment.contains_collinear(Vec2::new(3., 3.))); assert!(!segment.contains_collinear(Vec2::new(-1., 0.))); } + + #[test] + fn line_intersection() { + let segment_a = LineSegment::new(Vec2::new(-1., -1.), Vec2::new(1., 1.)); + let segment_b = LineSegment::new(Vec2::new(1., -1.), Vec2::new(-1., 1.)); + let segment_c = LineSegment::new(Vec2::new(1., -1.), Vec2::new(2., 2.)); + + assert!(LineSegment::intersect(&segment_a, &segment_b)); + assert!(LineSegment::intersect(&segment_b, &segment_c)); + assert!(!LineSegment::intersect(&segment_a, &segment_c)); + assert!(LineSegment::intersect(&segment_a, &segment_a)); + + assert_eq!( + LineSegment::intersection(&segment_a, &segment_b), + Some(Vec2::new(0., 0.)) + ); + assert_eq!( + LineSegment::intersection(&segment_b, &segment_c), + Some(Vec2::new(1., -1.)) + ); + assert_eq!(LineSegment::intersection(&segment_a, &segment_c), None); + assert!(LineSegment::intersection(&segment_a, &segment_a).is_some()); + } } diff --git a/src/math/mod.rs b/src/math/mod.rs index 829a3c5..c9c1c6e 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -1,3 +1,5 @@ +//! Useful mathematical operations in graphical contexts. + pub mod line_segment; pub mod polygon; pub mod rect; diff --git a/src/math/polygon/mod.rs b/src/math/polygon/mod.rs index 98b1570..1577f63 100644 --- a/src/math/polygon/mod.rs +++ b/src/math/polygon/mod.rs @@ -15,6 +15,7 @@ use std::ops::Neg; use thiserror::Error; /// Describes errors that can happen when handling polygons, especially on creation. +#[allow(missing_docs)] #[derive(Debug, Error)] pub enum PolygonError<T: Scalar + Copy> { /// Since the polygon is not allowed to be complex, self intersection is an error. @@ -28,6 +29,7 @@ pub enum PolygonError<T: Scalar + Copy> { EdgeNotFound(LineSegment<T>), } +/// Describes a non-complex (non-self-intersecting) polygon, currently without holes. #[derive(Clone, Debug, Deserialize, Serialize)] // TODO: Support polygons with holes pub struct Polygon<T: Scalar + Copy> { diff --git a/src/math/polygon/polygon_graph.rs b/src/math/polygon/polygon_graph.rs index 5a730b0..fd373dd 100644 --- a/src/math/polygon/polygon_graph.rs +++ b/src/math/polygon/polygon_graph.rs @@ -1,3 +1,8 @@ +//! Polygon graphs are used for a more general approach than polygons. +//! +//! They are not polygons, but often describe a polygon and make some algorithms on polygons faster +//! or possible, which may not be practical on the polygon data alone. + use super::Polygon; use crate::math::{self, LineSegment, Vec2}; use nalgebra::{RealField, Scalar}; @@ -16,7 +21,7 @@ struct EdgeIterator<'a, T: Scalar + Copy> { /// An undirected graph, that is optimised for polygon edge operations. Since edges of a polygon /// are an undirected graph, this structure also holds both directions. This makes it rather space -/// inefficient, but makes edge operations rather swift. ß +/// inefficient, but makes edge operations rather swift. #[derive(Debug)] pub struct PolygonGraph<T: Scalar + Copy + PartialOrd> { /// The nodes of the graph, together with their adjacency list. diff --git a/src/math/rect.rs b/src/math/rect.rs index 50c1cb0..6f993d1 100644 --- a/src/math/rect.rs +++ b/src/math/rect.rs @@ -1,3 +1,5 @@ +//! Rectangles where the sides are parallel to the x and y-axes. + use super::{LineSegment, Polygon, Surface, Vec2}; //use alga::general::{Additive, Identity}; use nalgebra::{ClosedAdd, ClosedSub, RealField, Scalar}; @@ -7,7 +9,7 @@ use serde::{Deserialize, Serialize}; use std::ops::{Add, AddAssign}; /// Represents a Rectangle with the value type T. -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct Rect<T: Scalar + Copy> { /// The x coordinate, or leftmost coordinate of the Rect. pub x: T, @@ -20,6 +22,8 @@ pub struct Rect<T: Scalar + Copy> { } impl<T: Scalar + Copy> Rect<T> { + /// Create a new Rectangle from the internal values, where it might be nicer to use a function + /// instead of setting the values directly. pub fn new(x: T, y: T, w: T, h: T) -> Self { Self { x, y, w, h } } diff --git a/src/math/surface.rs b/src/math/surface.rs index da265d8..ab1c703 100644 --- a/src/math/surface.rs +++ b/src/math/surface.rs @@ -1,3 +1,5 @@ +//! Surfaces, which are areas at a certain position in a vector space. + use super::{LineSegment, Polygon, Rect, Vec2}; use nalgebra::Scalar; diff --git a/src/math/triangle.rs b/src/math/triangle.rs index 35bdcec..b5c1bda 100644 --- a/src/math/triangle.rs +++ b/src/math/triangle.rs @@ -1,3 +1,5 @@ +//! Triangles. Nothing more, nothing less. + use super::{LineSegment, Vec2}; use alga::general::{ClosedMul, ClosedSub}; use nalgebra::{RealField, Scalar}; diff --git a/src/math/vec2.rs b/src/math/vec2.rs index d591f1d..b5706a0 100644 --- a/src/math/vec2.rs +++ b/src/math/vec2.rs @@ -1,5 +1,8 @@ +//! Two-dimensional vectors and useful operations on them. + use crate::math::Rect; use alga::general::{ClosedAdd, ClosedSub}; +use nalgebra::Point2; use nalgebra::{RealField, Scalar}; use num_traits::{NumCast, One, ToPrimitive}; use serde::{Deserialize, Serialize}; @@ -8,6 +11,8 @@ use std::convert::{From, Into}; use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign}; use std::{fmt, mem}; +/// Describes a vector, which may be a point or a directed length, depending on the interpretation. +#[allow(missing_docs)] #[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, Eq, Hash)] pub struct Vec2<T: Scalar + Copy> { pub x: T, @@ -15,10 +20,12 @@ pub struct Vec2<T: Scalar + Copy> { } impl<T: Scalar + Copy> Vec2<T> { + /// Create a new vector from its internal values. pub fn new(x: T, y: T) -> Self { Self { x, y } } + /// Finds the euclidian length and returns it. pub fn length(&self) -> T where T: RealField, @@ -26,6 +33,9 @@ impl<T: Scalar + Copy> Vec2<T> { (self.x * self.x + self.y * self.y).sqrt() } + /// Consumes the vector and returns a vector that is rotated by Pi/2 in clockwise direction. + /// This is a special case of rotation and the function is faster than using the nonspecific + /// rotation function. pub fn rotated_90_clockwise(mut self) -> Vec2<T> where T: One + Neg<Output = T> + MulAssign, @@ -35,6 +45,9 @@ impl<T: Scalar + Copy> Vec2<T> { self } + /// Consumes the vector and returns a vector that is rotated by Pi/2 in counterclockwise direction. + /// This is a special case of rotation and the function is faster than using the nonspecific + /// rotation function. pub fn rotated_90_counterclockwise(mut self) -> Vec2<T> where T: One + Neg<Output = T> + MulAssign, @@ -45,6 +58,18 @@ impl<T: Scalar + Copy> Vec2<T> { } } +impl<T: Scalar + Copy> Into<Point2<T>> for Vec2<T> { + fn into(self) -> Point2<T> { + Point2::new(self.x, self.y) + } +} + +impl<T: Scalar + Copy> From<Point2<T>> for Vec2<T> { + fn from(v: Point2<T>) -> Self { + Self::new(v.x, v.y) + } +} + // This is sad, but also sadly necessary :/ impl<T> From<raylib::ffi::Vector2> for Vec2<T> where |
