aboutsummaryrefslogtreecommitdiff
path: root/src/math
diff options
context:
space:
mode:
Diffstat (limited to 'src/math')
-rw-r--r--src/math/line_segment.rs51
-rw-r--r--src/math/mod.rs2
-rw-r--r--src/math/polygon/mod.rs2
-rw-r--r--src/math/polygon/polygon_graph.rs7
-rw-r--r--src/math/rect.rs4
-rw-r--r--src/math/surface.rs2
-rw-r--r--src/math/triangle.rs2
-rw-r--r--src/math/vec2.rs14
8 files changed, 80 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 b571644..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};
@@ -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 b9e3215..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};
@@ -7,8 +10,9 @@ use std::cmp::Ordering;
use std::convert::{From, Into};
use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};
use std::{fmt, mem};
-use nalgebra::Point2;
+/// 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,
@@ -16,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,
@@ -27,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,
@@ -36,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,