Add line segment shape and functions
Create a new line segment struct that contains two Points in either 2D or 3D space. Also create an implementation of the Intersect trait for testing whether two line segments intersect, and where.
This commit is contained in:
parent
10e911d180
commit
8ff2598dd9
4 changed files with 202 additions and 0 deletions
|
@ -30,6 +30,7 @@ pub mod vector;
|
|||
pub mod angle;
|
||||
pub mod plane;
|
||||
pub mod point;
|
||||
pub mod line;
|
||||
pub mod ray;
|
||||
pub mod rotation;
|
||||
pub mod transform;
|
||||
|
|
121
src/cgmath/line.rs
Normal file
121
src/cgmath/line.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
// Copyright 2013 The CGMath Developers. For a full listing of the authors,
|
||||
// refer to the AUTHORS file at the top-level directionectory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Line segments
|
||||
|
||||
use std::num::{Zero, zero, One, one};
|
||||
|
||||
use point::{Point, Point2, Point3};
|
||||
use vector::{Vector, Vector2};
|
||||
use partial_ord::PartOrdFloat;
|
||||
use intersect::Intersect;
|
||||
|
||||
/// A generic directed line segment
|
||||
#[deriving(Clone, Eq)]
|
||||
pub struct Line<P>
|
||||
{
|
||||
pub origin: P,
|
||||
pub dest: P,
|
||||
}
|
||||
|
||||
impl
|
||||
<
|
||||
S: Primitive,
|
||||
Slice,
|
||||
V: Vector<S,Slice>,
|
||||
P: Point<S,V,Slice>
|
||||
> Line<P>
|
||||
{
|
||||
pub fn new(origin: P, dest: P) -> Line<P> {
|
||||
Line { origin:origin, dest:dest }
|
||||
}
|
||||
}
|
||||
|
||||
pub type Line2<S> = Line<Point2<S>>;
|
||||
pub type Line3<S> = Line<Point3<S>>;
|
||||
|
||||
/// Determines if an intersection between two line segments is found. If the segments are
|
||||
/// collinear and overlapping, the intersection point that will be returned will be the first
|
||||
/// intersection point found by traversing the first line segment, starting at its origin.
|
||||
impl<S: PartOrdFloat<S>> Intersect<Option<Point2<S>>> for (Line2<S>, Line2<S>) {
|
||||
fn intersection(&self) -> Option<Point2<S>> {
|
||||
match *self {
|
||||
(ref l1, ref l2) => {
|
||||
let p = l1.origin;
|
||||
let mut q = l2.origin;
|
||||
let r = Vector2::new(l1.dest.x - l1.origin.x, l1.dest.y - l1.origin.y);
|
||||
let mut s = Vector2::new(l2.dest.x - l2.origin.x, l2.dest.y - l2.origin.y);
|
||||
let zero: S = Zero::zero();
|
||||
|
||||
let cross = r.perp_dot(&s);
|
||||
let mut q_minus_p = Vector2::new(q.x - p.x, q.y - p.y);
|
||||
let mut p_minus_q = Vector2::new(p.x - q.x, p.y - q.y);
|
||||
let cross_r = q_minus_p.perp_dot(&r);
|
||||
let cross_s = q_minus_p.perp_dot(&s);
|
||||
|
||||
if cross.is_zero() {
|
||||
if cross_r.is_zero() {
|
||||
// line segments are collinear
|
||||
|
||||
// special case of both lines being the same single point
|
||||
if r.x == zero && r.y == zero && s.x == zero && s.y == zero && p == q {
|
||||
return Some(p);
|
||||
}
|
||||
|
||||
// ensure l2 (q,q+s) is pointing the same direction as l1 (p,p+r)
|
||||
// if it is not, then swap the two endpoints of the second segment.
|
||||
// If this is not done, the algorithm below will not find an
|
||||
// intersection if the two directed line segments point towards the
|
||||
// opposite segment's origin.
|
||||
if (r.x != zero && s.x != zero && r.x.signum() != s.x.signum()) ||
|
||||
(r.y != zero && s.y != zero && r.y.signum() != s.y.signum()) {
|
||||
q = Point2::new(q.x + s.x, q.y + s.y);
|
||||
s = Vector2::new(-s.x, -s.y);
|
||||
q_minus_p = Vector2::new(q.x - p.x, q.y - p.y);
|
||||
p_minus_q = Vector2::new(p.x - q.x, p.y - q.y);
|
||||
}
|
||||
let d1 = q_minus_p.dot(&r);
|
||||
let d2 = p_minus_q.dot(&s);
|
||||
let rdotr = r.dot(&r);
|
||||
let sdots = s.dot(&s);
|
||||
|
||||
// make sure to take into account that one or both of the segments could
|
||||
// be a single point (r.r or s.s = 0) and ignore that case
|
||||
if (rdotr > zero && zero <= d1 && d1 <= rdotr) ||
|
||||
(sdots > zero && zero <= d2 && d2 <= sdots) {
|
||||
// overlapping
|
||||
if (q_minus_p.x != zero && q_minus_p.x.signum() == r.x.signum()) ||
|
||||
(q_minus_p.y != zero && q_minus_p.y.signum() == r.y.signum()) {
|
||||
return Some(q);
|
||||
}
|
||||
return Some(p);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
let t = cross_s / cross;
|
||||
let u = cross_r / cross;
|
||||
|
||||
if zero <= t && t <= One::one() &&
|
||||
zero <= One::one() && u <= One::one() {
|
||||
return Some(Point2::new(p.x + t*r.x, p.y + t*r.y));
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
79
src/test/line.rs
Normal file
79
src/test/line.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2013 The CGMath Developers. For a full listing of the authors,
|
||||
// refer to the AUTHORS file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use cgmath::line::*;
|
||||
use cgmath::point::*;
|
||||
use cgmath::intersect::Intersect;
|
||||
|
||||
#[test]
|
||||
fn test_line_intersection() {
|
||||
// collinear, origins pointing towards each other, first intersection
|
||||
// from l1.origin is in an endpoint in l2
|
||||
let l1 = Line::new(Point2::new(0.0, 0.0), Point2::new(10.0, 0.0));
|
||||
let l2 = Line::new(Point2::new(1.5, 0.0), Point2::new(0.5, 0.0));
|
||||
assert_eq!((l1, l2).intersection(), Some(Point2::new(0.5, 0.0)));
|
||||
|
||||
// collinear, first intersection from p1.origin is at p1.origin itself
|
||||
let l3 = Line::new(Point2::new(0.0, 0.0), Point2::new(10.0, 0.0));
|
||||
let l4 = Line::new(Point2::new(-11.0, 0.0), Point2::new(1.0, 0.0));
|
||||
assert_eq!((l3, l4).intersection(), Some(Point2::new(0.0, 0.0)));
|
||||
|
||||
// no intersection
|
||||
let l5 = Line::new(Point2::new(5.0, 5.0), Point2::new(10.0, 6.0));
|
||||
let l6 = Line::new(Point2::new(5.0, 4.8), Point2::new(10.0, 4.1));
|
||||
assert_eq!((l5, l6).intersection(), None); // no intersection
|
||||
|
||||
// collinear, origins pointing same direction
|
||||
let l7 = Line::new(Point2::new(0.0, 1.0), Point2::new(0.0, 0.0));
|
||||
let l8 = Line::new(Point2::new(0.0, 0.5), Point2::new(0.0, -0.5));
|
||||
assert_eq!((l7, l8).intersection(), Some(Point2::new(0.0, 0.5)));
|
||||
|
||||
// collinear, no overlap
|
||||
let l9 = Line::new(Point2::new(0.0, 0.0), Point2::new(3.0, 0.0));
|
||||
let l10 = Line::new(Point2::new(10.0, 0.0), Point2::new(5.0, 0.0));
|
||||
assert_eq!((l9, l10).intersection(), None);
|
||||
|
||||
// intersection found
|
||||
let l11 = Line::new(Point2::new(0.0, 0.0), Point2::new(10.0, 10.0));
|
||||
let l12 = Line::new(Point2::new(0.0, 10.0), Point2::new(10.0, 0.0));
|
||||
assert_eq!((l11, l12).intersection(), Some(Point2::new(5.0, 5.0)));
|
||||
|
||||
// special case of both lines being the same point
|
||||
let l13 = Line::new(Point2::new(0.0, 0.0), Point2::new(0.0, 0.0));
|
||||
let l14 = Line::new(Point2::new(0.0, 0.0), Point2::new(0.0, 0.0));
|
||||
assert_eq!((l13, l14).intersection(), Some(Point2::new(0.0, 0.0)));
|
||||
|
||||
// both lines are points that are distinct
|
||||
let l15 = Line::new(Point2::new(0.0, 0.0), Point2::new(0.0, 0.0));
|
||||
let l16 = Line::new(Point2::new(1.0, 0.0), Point2::new(1.0, 0.0));
|
||||
assert_eq!((l15, l16).intersection(), None);
|
||||
|
||||
// one line is a point that intersects the other segment
|
||||
let l15 = Line::new(Point2::new(0.0, 0.0), Point2::new(10.0, 0.0));
|
||||
let l16 = Line::new(Point2::new(3.0, 0.0), Point2::new(3.0, 0.0));
|
||||
assert_eq!((l15, l16).intersection(), Some(Point2::new(3.0, 0.0)));
|
||||
|
||||
// one line is a point that is collinear but does not intersect with
|
||||
// the other line
|
||||
let l17 = Line::new(Point2::new(0.0, 0.0), Point2::new(0.0, 0.0));
|
||||
let l18 = Line::new(Point2::new(1.0, 0.0), Point2::new(3.0, 0.0));
|
||||
assert_eq!((l17, l18).intersection(), None);
|
||||
|
||||
// one line is a point that is not collinear but does not intersect
|
||||
// with the other line
|
||||
let l19 = Line::new(Point2::new(0.0, 0.0), Point2::new(0.0, 0.0));
|
||||
let l20 = Line::new(Point2::new(1.0, 0.0), Point2::new(2.0, 10.0));
|
||||
assert_eq!((l19, l20).intersection(), None);
|
||||
}
|
|
@ -26,6 +26,7 @@ pub mod vector;
|
|||
pub mod angle;
|
||||
pub mod plane;
|
||||
pub mod point;
|
||||
pub mod line;
|
||||
// pub mod ray;
|
||||
// pub mod rotation;
|
||||
pub mod transform;
|
||||
|
|
Loading…
Reference in a new issue