use crate::cmp;
use crate::point::Point;
use crate::rect::Rect;
use crate::vector::Vector;
use crate::CoordinateType;
use num_traits::cast::NumCast;
pub use crate::traits::{BoundingBox, RotateOrtho, TryBoundingBox, TryCastCoord};
pub use crate::types::Angle;
use super::prelude::{Edge, EdgeIntersection};
pub use super::types::{ContainsResult, Side};
use crate::prelude::{EdgeEndpoints, EdgeIntersect};
use std::convert::TryFrom;
use std::ops::Sub;
pub type REdgeIntersection<T> = EdgeIntersection<T, T, REdge<T>>;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum RLineIntersection<T: CoordinateType> {
None,
Point(Point<T>),
Collinear,
}
impl<T: Copy> From<REdge<T>> for (Point<T>, Point<T>) {
fn from(e: REdge<T>) -> Self {
(e.start(), e.end())
}
}
impl<T: Copy> From<&REdge<T>> for (Point<T>, Point<T>) {
fn from(e: &REdge<T>) -> Self {
(e.start(), e.end())
}
}
impl<T: Copy> From<REdge<T>> for Edge<T> {
fn from(e: REdge<T>) -> Self {
Edge::new(e.start(), e.end())
}
}
impl<T: Copy> From<&REdge<T>> for Edge<T> {
fn from(e: &REdge<T>) -> Self {
Edge::new(e.start(), e.end())
}
}
impl<T: CoordinateType> TryFrom<&Edge<T>> for REdge<T> {
type Error = ();
fn try_from(value: &Edge<T>) -> Result<Self, Self::Error> {
match REdge::try_from_points(value.start, value.end) {
None => Err(()),
Some(e) => Ok(e),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum REdgeOrientation {
Horizontal,
Vertical,
}
impl REdgeOrientation {
pub fn other(&self) -> Self {
match self {
Self::Horizontal => Self::Vertical,
Self::Vertical => Self::Horizontal,
}
}
pub fn is_horizontal(self) -> bool {
self == Self::Horizontal
}
pub fn is_vertical(self) -> bool {
self == Self::Vertical
}
}
#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct REdge<T> {
pub start: T,
pub end: T,
pub offset: T,
pub orientation: REdgeOrientation,
}
impl<T: Copy> EdgeEndpoints<T> for REdge<T> {
fn start(&self) -> Point<T> {
self.start()
}
fn end(&self) -> Point<T> {
self.end()
}
}
impl<T: CoordinateType> EdgeIntersect for REdge<T> {
type Coord = T;
type IntersectionCoord = T;
fn edge_intersection(
&self,
other: &Self,
) -> EdgeIntersection<Self::Coord, Self::IntersectionCoord, REdge<T>> {
REdge::edge_intersection(self, other)
}
}
impl<T> REdge<T> {
pub fn new_raw(start: T, end: T, offset: T, orientation: REdgeOrientation) -> Self {
Self {
start,
end,
offset,
orientation,
}
}
}
impl<T: Copy> REdge<T> {
pub fn start(&self) -> Point<T> {
match self.orientation {
REdgeOrientation::Horizontal => Point::new(self.start, self.offset),
REdgeOrientation::Vertical => Point::new(self.offset, self.start),
}
}
pub fn end(&self) -> Point<T> {
match self.orientation {
REdgeOrientation::Horizontal => Point::new(self.end, self.offset),
REdgeOrientation::Vertical => Point::new(self.offset, self.end),
}
}
pub fn reversed(&self) -> Self {
Self {
start: self.end,
end: self.start,
offset: self.offset,
orientation: self.orientation,
}
}
}
impl<T: PartialEq> REdge<T> {
#[inline]
pub fn is_degenerate(&self) -> bool {
self.start == self.end
}
#[inline]
pub fn is_ortho(&self) -> bool {
!self.is_degenerate()
}
#[inline]
pub fn is_horizontal(&self) -> bool {
!self.is_degenerate() && self.orientation == REdgeOrientation::Horizontal
}
#[inline]
pub fn is_vertical(&self) -> bool {
!self.is_degenerate() && self.orientation == REdgeOrientation::Vertical
}
}
impl<T: PartialOrd + Sub<Output = T> + Copy> REdge<T> {
pub fn length(&self) -> T {
if self.start < self.end {
self.end - self.start
} else {
self.start - self.end
}
}
}
impl<T: CoordinateType> REdge<T> {
pub fn new<C>(start: C, end: C) -> Self
where
C: Into<Point<T>>,
{
Self::try_from_points(start, end).expect("Points must be on a vertical or horizontal line.")
}
pub fn try_from_points<C>(start: C, end: C) -> Option<Self>
where
C: Into<Point<T>>,
{
let s = start.into();
let e = end.into();
if s.x == e.x {
Some(REdge {
start: s.y,
end: e.y,
offset: s.x,
orientation: REdgeOrientation::Vertical,
})
} else if s.y == e.y {
Some(REdge {
start: s.x,
end: e.x,
offset: s.y,
orientation: REdgeOrientation::Horizontal,
})
} else {
None
}
}
pub fn vector(&self) -> Vector<T> {
match self.orientation {
REdgeOrientation::Horizontal => Vector::new(self.end - self.start, T::zero()),
REdgeOrientation::Vertical => Vector::new(T::zero(), self.end - self.start),
}
}
pub fn direction(&self) -> Option<Vector<T>> {
#![allow(clippy::just_underscores_and_digits)]
let _0 = T::zero();
let _1 = T::one();
let _1_minus = _0 - _1;
let d = if self.start < self.end {
Some(Vector::new(_1, _0))
} else if self.start > self.end {
Some(Vector::new(_1_minus, _0))
} else {
None
};
if self.orientation.is_vertical() {
d.map(|d| d.rotate_ortho(Angle::R90))
} else {
d
}
}
pub fn side_of(&self, point: Point<T>) -> Side {
assert!(!self.is_degenerate(), "Edge is degenerate.");
let p_offset = match self.orientation {
REdgeOrientation::Horizontal => T::zero() - point.y,
REdgeOrientation::Vertical => point.x,
};
if p_offset < self.offset {
if self.start < self.end {
Side::Left
} else {
Side::Right
}
} else if p_offset > self.offset {
if self.start < self.end {
Side::Right
} else {
Side::Left
}
} else {
debug_assert!(p_offset == self.offset);
Side::Center
}
}
pub fn manhattan_distance_to_point(self, p: Point<T>) -> T {
let projected = self.projection(p);
if self.contains_point(projected).inclusive_bounds() {
self.distance_to_line(p)
} else {
let dist_to_point1 = (self.start() - p).norm1();
let dist_to_point2 = (self.end() - p).norm1();
if dist_to_point1 < dist_to_point2 {
dist_to_point1
} else {
dist_to_point2
}
}
}
pub fn contains_point(&self, point: Point<T>) -> ContainsResult {
let (p_offset, p_projected) = match self.orientation {
REdgeOrientation::Horizontal => (point.y, point.x),
REdgeOrientation::Vertical => (point.x, point.y),
};
if p_offset != self.offset || self.is_degenerate() {
ContainsResult::No
} else if p_projected == self.start || p_projected == self.end {
ContainsResult::OnBounds
} else if (p_projected >= self.start && p_projected <= self.end)
|| (p_projected >= self.end && p_projected <= self.start)
{
ContainsResult::WithinBounds
} else {
ContainsResult::No
}
}
pub fn line_contains_point(&self, point: Point<T>) -> bool {
let p_offset = match self.orientation {
REdgeOrientation::Horizontal => point.y,
REdgeOrientation::Vertical => point.x,
};
p_offset == self.offset
}
pub fn is_parallel(&self, other: &REdge<T>) -> bool {
self.orientation == other.orientation
}
pub fn is_collinear(&self, other: &REdge<T>) -> bool
where
T: CoordinateType,
{
self.is_parallel(other) && self.offset == other.offset
}
pub fn is_coincident(&self, other: &REdge<T>) -> bool {
self.is_collinear(other)
&& (self.start <= other.start && other.start <= self.end
|| self.start <= other.end && other.end <= self.end
|| self.end <= other.start && other.start <= self.start
|| self.end <= other.end && other.end <= self.start
|| other.start <= self.start && self.start <= other.end
|| other.start <= self.end && self.end <= other.end
|| other.end <= self.start && self.start <= other.start
|| other.end <= self.end && self.end <= other.start)
}
pub fn crossed_by_line(&self, line: &REdge<T>) -> ContainsResult {
let side1 = line.side_of(self.start());
if side1 == Side::Center {
ContainsResult::OnBounds
} else {
let side2 = line.side_of(self.end());
if side2 == Side::Center {
ContainsResult::OnBounds
} else if side1 == side2 {
ContainsResult::No
} else {
ContainsResult::WithinBounds
}
}
}
pub fn lines_intersect(&self, other: &REdge<T>) -> bool {
!self.is_parallel(other) || self.is_collinear(other)
}
pub fn edges_intersect(&self, other: &REdge<T>) -> ContainsResult {
match self.edge_intersection(other) {
REdgeIntersection::None => ContainsResult::No,
REdgeIntersection::Overlap(_) => ContainsResult::WithinBounds,
REdgeIntersection::Point(_) => ContainsResult::WithinBounds,
REdgeIntersection::EndPoint(_) => ContainsResult::OnBounds,
}
}
pub fn oriented_distance_to_line(&self, point: Point<T>) -> T {
assert!(!self.is_degenerate());
let diff = match self.orientation {
REdgeOrientation::Horizontal => self.offset - point.y,
REdgeOrientation::Vertical => point.x - self.offset,
};
if self.start > self.end {
T::zero() - diff
} else {
diff
}
}
pub fn distance_to_line(&self, point: Point<T>) -> T {
let diff = self.oriented_distance_to_line(point);
if diff < T::zero() {
T::zero() - diff
} else {
diff
}
}
pub fn projection(&self, point: Point<T>) -> Point<T> {
assert!(!self.is_degenerate());
match self.orientation {
REdgeOrientation::Horizontal => (point.x, self.offset),
REdgeOrientation::Vertical => (self.offset, point.y),
}
.into()
}
pub fn line_intersection(&self, other: &REdge<T>) -> RLineIntersection<T> {
match (self.orientation, other.orientation) {
(REdgeOrientation::Horizontal, REdgeOrientation::Horizontal)
| (REdgeOrientation::Vertical, REdgeOrientation::Vertical) => {
if self.offset == other.offset {
RLineIntersection::Collinear
} else {
RLineIntersection::None
}
}
(REdgeOrientation::Horizontal, REdgeOrientation::Vertical) => {
RLineIntersection::Point(Point::new(other.offset, self.offset))
}
(REdgeOrientation::Vertical, REdgeOrientation::Horizontal) => {
RLineIntersection::Point(Point::new(self.offset, other.offset))
}
}
}
pub fn edge_intersection(&self, other: &REdge<T>) -> REdgeIntersection<T> {
match (self.orientation, other.orientation) {
(REdgeOrientation::Horizontal, REdgeOrientation::Horizontal)
| (REdgeOrientation::Vertical, REdgeOrientation::Vertical) => {
if self.offset == other.offset {
let (s1, e1) = if self.start < self.end {
(self.start, self.end)
} else {
(self.end, self.start)
};
let (s2, e2) = if other.start < other.end {
(other.start, other.end)
} else {
(other.end, other.start)
};
debug_assert!(s1 <= e1);
debug_assert!(s2 <= e2);
let s = cmp::max(s1, s2);
let e = cmp::min(e1, e2);
if s > e {
REdgeIntersection::None
} else if s < e {
let (s, e) = if self.start < self.end {
(s, e)
} else {
(e, s)
};
REdgeIntersection::Overlap(REdge {
start: s,
end: e,
offset: self.offset,
orientation: self.orientation,
})
} else {
debug_assert!(s == e);
let p = match self.orientation {
REdgeOrientation::Vertical => Point::new(self.offset, s),
REdgeOrientation::Horizontal => Point::new(s, self.offset),
};
debug_assert!(
p == self.start()
|| p == self.end()
|| p == other.start()
|| p == other.end(),
"`p` must be an end point."
);
REdgeIntersection::EndPoint(p)
}
} else {
REdgeIntersection::None
}
}
(o1, o2) => {
let (horizontal, vertical) = match (o1, o2) {
(REdgeOrientation::Horizontal, REdgeOrientation::Vertical) => (self, other),
(REdgeOrientation::Vertical, REdgeOrientation::Horizontal) => (other, self),
_ => panic!(),
};
let p = Point::new(vertical.offset, horizontal.offset);
let is_on_horizontal = (horizontal.start <= p.x && p.x <= horizontal.end)
|| (horizontal.end <= p.x && p.x <= horizontal.start);
let is_on_vertical = (vertical.start <= p.y && p.y <= vertical.end)
|| (vertical.end <= p.y && p.y <= vertical.start);
if is_on_horizontal && is_on_vertical {
let is_endpoint_horizontal = p.x == horizontal.start || p.x == horizontal.end;
let is_endpoint_vertical = p.y == vertical.start || p.y == vertical.end;
if is_endpoint_horizontal || is_endpoint_vertical {
debug_assert!(
p == self.start()
|| p == self.end()
|| p == other.start()
|| p == other.end(),
"`p` must be an end point."
);
REdgeIntersection::EndPoint(p)
} else {
REdgeIntersection::Point(p)
}
} else {
REdgeIntersection::None
}
}
}
}
pub fn rotate_ortho(&self, a: Angle) -> Self {
let (p1, p2) = (self.start(), self.end());
let new_p1 = p1.rotate_ortho(a);
let new_p2 = p2.rotate_ortho(a);
Self::new(new_p1, new_p2)
}
}
#[test]
fn test_rotate_ortho() {
let e = REdge::new((1, 0), (1, 2));
assert_eq!(e.rotate_ortho(Angle::R0), e);
assert_eq!(e.rotate_ortho(Angle::R90), REdge::new((0, 1), (-2, 1)));
assert_eq!(e.rotate_ortho(Angle::R180), REdge::new((-1, 0), (-1, -2)));
assert_eq!(e.rotate_ortho(Angle::R270), REdge::new((0, -1), (2, -1)));
}
impl<T: CoordinateType> BoundingBox<T> for REdge<T> {
fn bounding_box(&self) -> Rect<T> {
Rect::new(self.start(), self.end())
}
}
impl<T: CoordinateType> TryBoundingBox<T> for REdge<T> {
fn try_bounding_box(&self) -> Option<Rect<T>> {
Some(self.bounding_box())
}
}
impl<T: CoordinateType + NumCast, Dst: CoordinateType + NumCast> TryCastCoord<T, Dst> for REdge<T> {
type Output = REdge<Dst>;
fn try_cast(&self) -> Option<Self::Output> {
match (
Dst::from(self.start),
Dst::from(self.end),
Dst::from(self.offset),
) {
(Some(s), Some(e), Some(o)) => Some(REdge {
start: s,
end: e,
offset: o,
orientation: self.orientation,
}),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use crate::point::Point;
use crate::redge::{REdge, REdgeIntersection, RLineIntersection};
use crate::types::*;
use crate::vector::Vector;
#[test]
fn test_is_parallel() {
let e1 = REdge::new((0, 0), (1, 0));
let e2 = REdge::new((100, 200), (101, 200));
let e3 = REdge::new((1000, 2000), (1000, 2001));
assert!(e1.is_parallel(&e2));
assert!(!e1.is_parallel(&e3));
}
#[test]
fn test_is_collinear() {
let e1 = REdge::new((0, 0), (1, 0));
let e2 = REdge::new((3, 0), (4, 0));
assert!(e1.is_collinear(&e2));
assert!(e2.is_collinear(&e1));
let e1 = REdge::new((0, 0), (1, 0));
let e2 = REdge::new((3, 1), (4, 1));
assert!(!e1.is_collinear(&e2));
assert!(!e2.is_collinear(&e1));
let e1 = REdge::new((0, 0), (1, 0));
let e2 = REdge::new((0, 0), (0, 1));
assert!(!e1.is_collinear(&e2));
assert!(!e2.is_collinear(&e1));
}
#[test]
fn test_oriented_distance_to_line() {
let xaxis = REdge::new((0, 0), (1, 0));
let xaxis_neg = REdge::new((0, 0), (-1, 0));
let yaxis = REdge::new((0, 0), (0, 1));
let yaxis_neg = REdge::new((0, 0), (0, -1));
let p = Point::new(2, 3);
assert_eq!(xaxis.oriented_distance_to_line(p), -p.y);
assert_eq!(xaxis_neg.oriented_distance_to_line(p), p.y);
assert_eq!(yaxis.oriented_distance_to_line(p), p.x);
assert_eq!(yaxis_neg.oriented_distance_to_line(p), -p.x);
}
#[test]
fn test_distance_to_line() {
let xaxis = REdge::new((0, 0), (1, 0));
let yaxis = REdge::new((0, 0), (0, 1));
let p = Point::new(2, 3);
assert_eq!(xaxis.distance_to_line(p), p.y);
assert_eq!(yaxis.distance_to_line(p), p.x);
}
#[test]
fn test_manhattan_distance_to_edge() {
let e = REdge::new((0, 0), (10, 0));
assert_eq!(e.manhattan_distance_to_point(Point::new(5, 0)), 0);
assert_eq!(e.manhattan_distance_to_point(Point::new(5, 1)), 1);
assert_eq!(e.manhattan_distance_to_point(Point::new(5, -1)), 1);
assert_eq!(e.manhattan_distance_to_point(Point::new(-1, 0)), 1);
assert_eq!(e.manhattan_distance_to_point(Point::new(-1, -1)), 2);
assert_eq!(e.manhattan_distance_to_point(Point::new(11, 1)), 2);
assert_eq!(e.manhattan_distance_to_point(Point::new(11, -1)), 2);
}
#[test]
fn test_line_contains_point() {
let xaxis = REdge::new((0, 0), (1, 0));
let yaxis = REdge::new((0, 0), (0, 1));
let p0 = Point::new(2, 0);
let p1 = Point::new(2, 3);
let p2 = Point::new(0, 3);
assert!(xaxis.line_contains_point(p0));
assert!(!yaxis.line_contains_point(p0));
assert!(!xaxis.line_contains_point(p1));
assert!(yaxis.line_contains_point(p2));
assert!(!xaxis.line_contains_point(p2));
}
#[test]
fn test_contains() {
let e1 = REdge::new((0, 0), (10, 0));
let p0 = Point::new(0, 0);
let p1 = Point::new(1, 0);
let p2 = Point::new(0, 1);
let p3 = Point::new(10, 0);
let p4 = Point::new(11, 0);
assert!(e1.contains_point(p0).inclusive_bounds());
assert!(!e1.contains_point(p0).is_within_bounds());
assert!(e1.contains_point(p1).inclusive_bounds());
assert!(e1.contains_point(p2).is_no());
assert!(e1.contains_point(p3).inclusive_bounds());
assert!(e1.contains_point(p4).is_no());
}
#[test]
fn test_projection() {
let horizontal = REdge::new((0, 0), (1, 0));
let vertical = REdge::new((0, 0), (0, 1));
let p = Point::new(1, 2);
assert_eq!(horizontal.projection(p), Point::new(p.x, 0));
assert_eq!(vertical.projection(p), Point::new(0, p.y));
}
#[test]
fn test_side_of() {
let xaxis = REdge::new((0, 0), (1, 0));
let yaxis = REdge::new((0, 0), (0, 1));
let p1 = Point::new(-10, 0);
let p2 = Point::new(10, -10);
let p3 = Point::new(0, 10);
assert_eq!(yaxis.side_of(p1), Side::Left);
assert_eq!(yaxis.side_of(p2), Side::Right);
assert_eq!(yaxis.side_of(p3), Side::Center);
assert_eq!(xaxis.side_of(p1), Side::Center);
assert_eq!(xaxis.side_of(p2), Side::Right);
assert_eq!(xaxis.side_of(p3), Side::Left);
}
#[test]
fn test_crossed_by() {
let e1 = REdge::new((1, 0), (3, 0));
let e2 = REdge::new((1, 1), (3, 1));
let e3 = REdge::new((2, -1), (2, 1));
let e4 = REdge::new((2, 100), (2, 101));
assert!(e1.crossed_by_line(&e1).inclusive_bounds());
assert!(e1.is_parallel(&e2));
assert!(!e1.crossed_by_line(&e2).inclusive_bounds());
assert!(e1.crossed_by_line(&e3).inclusive_bounds());
assert!(e3.crossed_by_line(&e1).inclusive_bounds());
assert!(e1.crossed_by_line(&e4).inclusive_bounds());
assert!(!e4.crossed_by_line(&e1).inclusive_bounds());
}
#[test]
fn test_intersect() {
let e1 = REdge::new((0, 0), (2, 0));
let e2 = REdge::new((1, -1), (1, 1));
let e3 = REdge::new((1, 100), (1, 101));
assert!(e1.edges_intersect(&e1).inclusive_bounds());
assert!(e1.edges_intersect(&e2).inclusive_bounds());
assert!(e2.edges_intersect(&e1).inclusive_bounds());
assert_eq!(e3.side_of(e1.start()), Side::Left);
assert_eq!(e3.side_of(e1.end()), Side::Right);
assert!(e1.crossed_by_line(&e3).inclusive_bounds());
assert!(!e1.edges_intersect(&e3).inclusive_bounds());
let e1 = REdge::new((0, 0), (0, 1));
let e2 = REdge::new((0, 1), (2, 1));
assert_eq!(e1.edges_intersect(&e2), ContainsResult::OnBounds);
assert_eq!(e2.edges_intersect(&e1), ContainsResult::OnBounds);
}
#[test]
fn test_line_intersection() {
let v = REdge::new((7, 1), (7, 2));
let h = REdge::new((100, 77), (101, 77));
assert_eq!(
v.line_intersection(&h),
RLineIntersection::Point(Point::new(7, 77))
);
assert_eq!(v.line_intersection(&v), RLineIntersection::Collinear);
assert_eq!(h.line_intersection(&h), RLineIntersection::Collinear);
let v1 = REdge::new((0, 1), (0, 2));
let v2 = REdge::new((1, 1), (1, 2));
assert_eq!(v1.line_intersection(&v2), RLineIntersection::None);
}
#[test]
fn test_edge_intersection() {
let e1 = REdge::new((0, 0), (0, 2));
let e2 = REdge::new((-1, 1), (1, 1));
assert_eq!(
e1.edge_intersection(&e2),
REdgeIntersection::Point(Point::new(0, 1))
);
assert_eq!(
e2.edge_intersection(&e1),
REdgeIntersection::Point(Point::new(0, 1))
);
let e1 = REdge::new((0, 0), (0, 1));
let e2 = REdge::new((-1, 0), (1, 0));
assert_eq!(
e1.edge_intersection(&e2),
REdgeIntersection::EndPoint(Point::new(0, 0))
);
assert_eq!(
e2.edge_intersection(&e1),
REdgeIntersection::EndPoint(Point::new(0, 0))
);
let e1 = REdge::new((0, 0), (0, 1));
let e2 = REdge::new((0, 2), (0, 3));
assert_eq!(e1.edge_intersection(&e2), REdgeIntersection::None);
assert_eq!(e2.edge_intersection(&e1), REdgeIntersection::None);
let e1 = REdge::new((0, 0), (0, 1));
let e2 = REdge::new((1, 0), (2, 0));
assert_eq!(e1.edge_intersection(&e2), REdgeIntersection::None);
assert_eq!(e2.edge_intersection(&e1), REdgeIntersection::None);
}
#[test]
fn test_edge_intersection_overlap() {
let e1 = REdge::new((1, 1), (1, 3));
let e2 = REdge::new((1, 1), (1, 2));
assert!(e1.edges_intersect(&e2).inclusive_bounds());
assert!(e1.is_collinear(&e2));
assert_eq!(e1.edge_intersection(&e2), REdgeIntersection::Overlap(e2));
assert_eq!(e2.edge_intersection(&e1), REdgeIntersection::Overlap(e2));
let e1 = REdge::new((1, 1), (1, 3));
let e2 = REdge::new((1, 2), (1, 1));
assert!(e1.edges_intersect(&e2).inclusive_bounds());
assert!(e1.is_collinear(&e2));
assert_eq!(
e1.edge_intersection(&e2),
REdgeIntersection::Overlap(e2.reversed())
);
assert_eq!(e2.edge_intersection(&e1), REdgeIntersection::Overlap(e2));
let e1 = REdge::new((1, 1), (1, 100));
let e2 = REdge::new((1, 2), (1, 3));
assert!(e1.edges_intersect(&e2).inclusive_bounds());
assert!(e1.is_collinear(&e2));
assert_eq!(e1.edge_intersection(&e2), REdgeIntersection::Overlap(e2));
assert_eq!(e2.edge_intersection(&e1), REdgeIntersection::Overlap(e2));
let e1 = REdge::new((1, 1), (1, 100));
let e2 = REdge::new((1, 3), (1, 2));
assert!(e1.edges_intersect(&e2).inclusive_bounds());
assert!(e1.is_collinear(&e2));
assert_eq!(
e1.edge_intersection(&e2),
REdgeIntersection::Overlap(e2.reversed())
);
assert_eq!(e2.edge_intersection(&e1), REdgeIntersection::Overlap(e2));
let e1 = REdge::new((0, 0), (0, 1));
let e2 = REdge::new((0, 1), (0, 2));
assert!(e1.edges_intersect(&e2).inclusive_bounds());
assert!(e1.is_collinear(&e2));
assert_eq!(
e1.edge_intersection(&e2),
REdgeIntersection::EndPoint(Point::new(0, 1))
);
assert_eq!(
e2.edge_intersection(&e1),
REdgeIntersection::EndPoint(Point::new(0, 1))
);
}
#[test]
fn test_coincident() {
let e1 = REdge::new((0, 0), (0, 2));
let e2 = REdge::new((0, 1), (0, 3));
assert!(e1.is_coincident(&e2));
assert!(e2.is_coincident(&e1));
let e1 = REdge::new((0, 0), (0, 1));
let e2 = REdge::new((0, 1), (0, 3));
assert!(e1.is_coincident(&e2));
assert!(e2.is_coincident(&e1));
let e1 = REdge::new((0, 0), (2, 0));
let e2 = REdge::new((1, 0), (3, 0));
assert!(e1.is_coincident(&e2));
assert!(e2.is_coincident(&e1));
let e1 = REdge::new((0, 0), (0, 1));
let e2 = REdge::new((0, 0), (1, 0));
assert!(!e1.is_coincident(&e2));
assert!(!e2.is_coincident(&e1));
}
#[test]
fn test_direction() {
let e_up = REdge::new((0, 0), (0, 2));
assert_eq!(e_up.direction(), Some(Vector::new(0, 1)));
let e_down = REdge::new((0, 0), (0, -2));
assert_eq!(e_down.direction(), Some(Vector::new(0, -1)));
let e_right = REdge::new((0, 0), (2, 0));
assert_eq!(e_right.direction(), Some(Vector::new(1, 0)));
let e_left = REdge::new((0, 0), (-2, 0));
assert_eq!(e_left.direction(), Some(Vector::new(-1, 0)));
}
}