1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
use super::coefficients::*;
use super::super::geo::*;
///
/// Represents a straight line
///
pub trait Line : Geo {
///
/// Creates a new line from points
///
fn from_points(p1: Self::Point, p2: Self::Point) -> Self;
///
/// Returns the two points that mark the start and end of this line
///
fn points(&self) -> (Self::Point, Self::Point);
///
/// Given a value 't' from 0 to 1, returns the point at that position along the line
///
fn point_at_pos(&self, t: f64) -> Self::Point {
let (p1, p2) = self.points();
let delta = p2-p1;
p1 + delta*t
}
///
/// Given a point (assumed to be on the line), returns the 't' value on the line
///
/// If the point is not on the line, this will return a t value where at least one of the components of the point matches with
/// the point on the line.
///
fn pos_for_point(&self, point: &Self::Point) -> f64 {
let (p1, p2) = self.points();
let delta_line = p2-p1;
let delta_point = *point-p1;
for component_idx in 0..Self::Point::len() {
let line_component = delta_line.get(component_idx);
let point_component = delta_point.get(component_idx);
if line_component.abs() > 0.000001 && point_component.abs() > 0.000001 {
return point_component/line_component;
}
}
// Result is 0 if there are no components that map to the line somehow
0.0
}
}
///
/// Trait implemented by a 2D line
///
pub trait Line2D {
type Point: Coordinate+Coordinate2D;
///
/// Returns the coefficients (a, b, c) for this line, such that ax+by+c = 0 for
/// any point on the line and also such that a^2 + b^2 = 1
///
fn coefficients(&self) -> (f64, f64, f64);
///
/// Returns the distance from a point to the closest point along this line
///
/// Note that this will project the line to infinity so this can return a distance to a point outside of the start or end point
/// of the line. To determine if this has occurred, the `pos_for_point()` call can be used to determine the `t` value for the
/// closest point: it will return a value in the range `0.0..1.0` if the closest point is within the line.
///
fn distance_to(&self, p: &Self::Point) -> f64;
///
/// Returns a value indicating which side of the line the specified point is on (+1, 0 or -1)
///
fn which_side(&self, p: &Self::Point) -> i8;
}
impl<Point: Coordinate+Clone> Geo for (Point, Point) {
type Point = Point;
}
///
/// Simplest line is just a tuple of two points
///
impl<Point: Coordinate+Clone> Line for (Point, Point) {
///
/// Creates a new line from points
///
#[inline]
fn from_points(p1: Self::Point, p2: Self::Point) -> Self {
(p1, p2)
}
///
/// Returns the two points that mark the start and end of this line
///
#[inline]
fn points(&self) -> (Self::Point, Self::Point) {
self.clone()
}
}
impl<Point: Coordinate2D+Coordinate+Clone, L: Line<Point=Point>> Line2D for L {
type Point=Point;
///
/// Returns the coefficients (a, b, c) for this line, such that ax+by+c = 0 for
/// any point on the line and also such that a^2 + b^2 = 1
///
#[inline]
fn coefficients(&self) -> (f64, f64, f64) {
line_coefficients_2d(self)
}
///
/// Returns the distance from a point to this line
///
#[inline]
fn distance_to(&self, p: &Self::Point) -> f64 {
let (a, b, c) = self.coefficients();
a*p.x() + b*p.y() + c
}
///
/// Returns a value indicating which side of the line the specified point is on (+1, 0 or -1)
///
#[inline]
fn which_side(&self, p: &Self::Point) -> i8 {
let (start, end) = self.points();
let side = ((p.x()-start.x())*(end.y()-start.y()) - (p.y()-start.y())*(end.x()-start.x())).signum();
if side < 0.0 {
-1
} else if side > 0.0 {
1
} else {
0
}
}
}