#![allow(clippy::needless_bool)]
use super::graph_path::*;
use super::super::curve::*;
use super::super::normal::*;
use super::super::intersection::*;
use super::super::super::geo::*;
use super::super::super::line::*;
use super::super::super::consts::*;
use smallvec::*;
use std::cmp::Ordering;
pub (crate) trait RayPath {
type Point: Coordinate+Coordinate2D;
type Curve: BezierCurve<Point=Self::Point>;
fn num_points(&self) -> usize;
fn num_edges(&self, point_idx: usize) -> usize;
fn reverse_edges_for_point(&self, point_idx: usize) -> SmallVec<[GraphEdgeRef; 8]>;
fn edges_for_point(&self, point_idx: usize) -> SmallVec<[GraphEdgeRef; 8]>;
fn get_edge(&self, edge: GraphEdgeRef) -> Self::Curve;
fn get_next_edge(&self, edge: GraphEdgeRef) -> (GraphEdgeRef, Self::Curve);
fn point_position(&self, point: usize) -> Self::Point;
fn edge_start_point_idx(&self, edge: GraphEdgeRef) -> usize;
fn edge_end_point_idx(&self, edge: GraphEdgeRef) -> usize;
fn edge_following_edge_idx(&self, edge: GraphEdgeRef) -> usize;
}
fn control_points_overlap<Curve: BezierCurve>(curve1: &Curve, curve2: &Curve) -> bool
where Curve::Point: Coordinate2D {
const SMALL_DISTANCE_SQ: f64 = SMALL_DISTANCE * SMALL_DISTANCE;
let (cp1_a, cp2_a) = curve1.control_points();
let (cp1_b, cp2_b) = curve2.control_points();
let distance_1 = cp1_a - cp1_b;
let distance_2 = cp2_a - cp2_b;
let distance_1 = distance_1.dot(&distance_1);
let distance_2 = distance_2.dot(&distance_2);
if distance_1 <= SMALL_DISTANCE_SQ && distance_2 <= SMALL_DISTANCE_SQ {
true
} else {
let start = curve1.start_point();
let end = curve2.end_point();
let (a, b, c) = (start, end).coefficients();
let dist_cp1_a = a*cp1_a.x() + b*cp1_a.y() + c;
let dist_cp2_a = a*cp2_a.x() + b*cp2_a.y() + c;
let dist_cp1_b = a*cp2_b.x() + b*cp2_b.y() + c;
let dist_cp2_b = a*cp2_b.x() + b*cp2_b.y() + c;
if dist_cp1_a <= SMALL_DISTANCE && dist_cp1_b <= SMALL_DISTANCE && dist_cp2_a <= SMALL_DISTANCE && dist_cp2_b < SMALL_DISTANCE {
true
} else {
false
}
}
}
pub (crate) fn group_overlapped_collisions<Path: RayPath>(path: Path, collisions: Vec<(GraphRayCollision, f64, f64, Path::Point)>) -> Vec<SmallVec<[(GraphRayCollision, f64, f64, Path::Point); 1]>> {
let mut grouped_collisions = Vec::with_capacity(collisions.len());
let mut collisions = collisions;
let mut collision_iter = collisions.drain(..);
let next_collision = collision_iter.next();
let next_collision = if let Some(next_collision) = next_collision { next_collision } else { return grouped_collisions };
grouped_collisions.push(smallvec![next_collision]);
loop {
let next_collision = collision_iter.next();
let next_collision = if let Some(next_collision) = next_collision { next_collision } else { break; };
let last_group = grouped_collisions.last_mut().unwrap();
let (ref last_edge, _, _, last_pos) = last_group.last().unwrap();
let (ref next_edge, _, _, next_pos) = next_collision;
let dx = last_pos.x() - next_pos.x();
let dy = last_pos.y() - next_pos.y();
if dx >= SMALL_DISTANCE || dy >= SMALL_DISTANCE {
grouped_collisions.push(smallvec![next_collision]);
} else if !edges_overlap(&path, last_edge.edge(), next_edge.edge()) {
grouped_collisions.push(smallvec![next_collision]);
} else {
last_group.push(next_collision);
}
}
grouped_collisions
}
pub (crate) fn edges_overlap<Path: RayPath>(path: &Path, edge_a: GraphEdgeRef, edge_b: GraphEdgeRef) -> bool {
let start_idx_a = edge_a.start_idx;
let end_idx_a = path.edge_end_point_idx(edge_a);
let start_idx_b = edge_b.start_idx;
let end_idx_b = path.edge_end_point_idx(edge_b);
if start_idx_a == start_idx_b && end_idx_a == end_idx_b {
control_points_overlap(&path.get_edge(edge_a), &path.get_edge(edge_b))
} else if start_idx_a == end_idx_b && end_idx_a == start_idx_b {
control_points_overlap(&path.get_edge(edge_a), &path.get_edge(edge_b.reversed()))
} else {
false
}
}
#[inline]
fn curve_is_collinear<Edge: BezierCurve>(edge: &Edge, (a, b, c): (f64, f64, f64)) -> bool
where Edge::Point: Coordinate+Coordinate2D {
let start_point = edge.start_point();
let end_point = edge.end_point();
let (cp1, cp2) = edge.control_points();
if (start_point.x()*a + start_point.y()*b + c).abs() < SMALL_DISTANCE
&& (end_point.x()*a + end_point.y()*b + c).abs() < SMALL_DISTANCE
&& (cp1.x()*a + cp1.y()*b + c).abs() < SMALL_DISTANCE
&& (cp2.x()*a + cp2.y()*b + c).abs() < SMALL_DISTANCE {
true
} else {
false
}
}
#[derive(PartialEq)]
enum RayCanIntersect {
WrongSide,
Collinear,
CrossesRay
}
fn ray_can_intersect<Edge: BezierCurve>(edge: &Edge, (a, b, c): (f64, f64, f64)) -> RayCanIntersect
where Edge::Point: Coordinate+Coordinate2D {
let start_point = edge.start_point();
let end_point = edge.end_point();
let (cp1, cp2) = edge.control_points();
let start_distance = a*start_point.x() + b*start_point.y() + c;
let cp1_distance = a*cp1.x() + b*cp1.y() + c;
let cp2_distance = a*cp2.x() + b*cp2.y() + c;
let end_distance = a*end_point.x()+ b*end_point.y() + c;
let side = start_distance.signum() + end_distance.signum() + cp1_distance.signum() + cp2_distance.signum();
if start_distance.abs() < SMALL_DISTANCE && end_distance.abs() < SMALL_DISTANCE && cp1_distance.abs() < SMALL_DISTANCE && cp2_distance.abs() < SMALL_DISTANCE {
RayCanIntersect::Collinear
} else if !(-3.99..=3.99).contains(&side) {
RayCanIntersect::WrongSide
} else {
RayCanIntersect::CrossesRay
}
}
fn crossing_edges<Path: RayPath>(path: &Path, (a, b, c): (f64, f64, f64), points: Vec<usize>) -> Vec<GraphEdgeRef> {
let mut crossing_edges = vec![];
for point_idx in points.into_iter() {
for incoming_ref in path.reverse_edges_for_point(point_idx) {
let incoming_ref = incoming_ref.reversed();
let incoming = path.get_edge(incoming_ref);
if curve_is_collinear(&incoming, (a, b, c)) {
continue;
}
let following_ref = path.edge_following_edge_idx(incoming_ref);
let mut leaving_ref = GraphEdgeRef { start_idx: point_idx, edge_idx: following_ref, reverse: false };
let mut leaving = path.get_edge(leaving_ref);
while curve_is_collinear(&leaving, (a, b, c)) {
let (next_ref, next_edge) = path.get_next_edge(leaving_ref);
leaving_ref = next_ref;
leaving = next_edge;
if path.edge_start_point_idx(leaving_ref) == point_idx {
break;
}
}
if !curve_is_collinear(&leaving, (a, b, c)) {
let incoming_cp2 = incoming.control_points().1;
let leaving_cp1 = leaving.control_points().0;
let incoming_side = a*incoming_cp2.x() + b*incoming_cp2.y() + c;
let leaving_side = a*leaving_cp1.x() + b*leaving_cp1.y() + c;
if incoming_side.signum() != leaving_side.signum() {
crossing_edges.push(leaving_ref);
}
}
}
}
crossing_edges
}
#[inline(never)]
fn crossing_and_collinear_collisions<Path: RayPath, L: Line>(path: &Path, ray: &L) -> (SmallVec<[(GraphEdgeRef, f64, f64, Path::Point); 32]>, SmallVec<[(GraphEdgeRef, f64, f64, Path::Point); 8]>)
where Path::Point: Coordinate+Coordinate2D,
L: Line<Point=Path::Point> {
let mut raw_collisions = smallvec![];
let mut section_with_point: Option<Vec<Option<usize>>> = None;
let mut collinear_sections: SmallVec<[Vec<_>; 8]> = smallvec![];
let ray_coeffs = ray.coefficients();
for point_idx in 0..(path.num_points()) {
for edge_idx in 0..(path.num_edges(point_idx)) {
let edge_ref = GraphEdgeRef { start_idx: point_idx, edge_idx: edge_idx, reverse: false };
let edge = path.get_edge(edge_ref);
let intersection_type = ray_can_intersect(&edge, ray_coeffs);
match intersection_type {
RayCanIntersect::CrossesRay => {
for (curve_t, line_t, collide_pos) in curve_intersects_ray(&edge, ray) {
raw_collisions.push((edge_ref, curve_t, line_t, collide_pos));
}
}
RayCanIntersect::Collinear => {
let section_with_point = section_with_point.get_or_insert_with(|| vec![None; path.num_points()]);
let start_idx = path.edge_start_point_idx(edge_ref);
let end_idx = path.edge_end_point_idx(edge_ref);
if let Some(start_section) = section_with_point[start_idx] {
if let Some(_end_section) = section_with_point[end_idx] {
} else {
collinear_sections[start_section].push(end_idx);
}
} else if let Some(end_section) = section_with_point[end_idx] {
collinear_sections[end_section].push(start_idx);
} else {
let new_section = collinear_sections.len();
collinear_sections.push(vec![start_idx, end_idx]);
section_with_point[start_idx] = Some(new_section);
section_with_point[end_idx] = Some(new_section);
}
}
RayCanIntersect::WrongSide => {
}
}
}
}
let collinear_collisions = collinear_sections
.into_iter()
.flat_map(move |colinear_edge_points| crossing_edges(path, ray_coeffs, colinear_edge_points)
.into_iter()
.map(move |crossing_edge| {
let point = path.edge_start_point_idx(crossing_edge);
let point = path.point_position(point);
let line_t = ray.pos_for_point(&point);
(crossing_edge, 0.0, line_t, point)
}))
.collect();
(raw_collisions, collinear_collisions)
}
#[inline]
fn remove_collisions_before_or_after_collinear_section<'a, Path: RayPath, L: Line, Collisions: 'a+IntoIterator<Item=(GraphEdgeRef, f64, f64, Path::Point)>>(path: &'a Path, ray: &L, collisions: Collisions) -> impl 'a+Iterator<Item=(GraphEdgeRef, f64, f64, Path::Point)>
where Path::Point: Coordinate+Coordinate2D,
L: Line<Point=Path::Point> {
let ray_coeffs = ray.coefficients();
collisions.into_iter()
.filter(move |(collision, curve_t, _line_t, position)| {
if *curve_t > 0.9 {
let end_point_idx = path.edge_end_point_idx(*collision);
let end_point = path.point_position(end_point_idx);
if position.is_near_to(&end_point, CLOSE_DISTANCE) && path.edges_for_point(end_point_idx).into_iter().map(|edge| path.get_edge(edge)).any(|next| curve_is_collinear(&next, ray_coeffs)) {
false
} else {
true
}
} else if *curve_t < 0.1 {
let start_point_idx = path.edge_start_point_idx(*collision);
let start_point = path.point_position(start_point_idx);
if position.is_near_to(&start_point, CLOSE_DISTANCE) && path.reverse_edges_for_point(start_point_idx).into_iter().map(|edge| path.get_edge(edge)).any(|previous| curve_is_collinear(&previous, ray_coeffs)) {
false
} else {
true
}
} else {
true
}
})
}
#[inline]
fn move_collinear_collisions_to_end<'a, Path: RayPath, L: Line, Collisions: 'a+IntoIterator<Item=(GraphEdgeRef, f64, f64, Path::Point)>>(path: &'a Path, ray: &L, collisions: Collisions) -> impl 'a+Iterator<Item=(GraphEdgeRef, f64, f64, Path::Point)>
where Path::Point: Coordinate+Coordinate2D,
L: Line<Point=Path::Point> {
let ray_coeffs = ray.coefficients();
collisions.into_iter()
.map(move |(collision, curve_t, line_t, position)| {
let edge = path.get_edge(collision);
if curve_is_collinear(&edge, ray_coeffs) {
let mut edge_ref = collision;
let mut edge;
loop {
let (next_edge_ref, next_edge) = path.get_next_edge(edge_ref);
edge_ref = next_edge_ref;
edge = next_edge;
if !curve_is_collinear(&edge, ray_coeffs) {
break;
}
}
let position = edge.start_point();
(edge_ref, 0.0, line_t, position)
} else {
(collision, curve_t, line_t, position)
}
})
}
#[inline]
fn collision_is_at_start<Path: RayPath>(path: &Path, edge: &GraphEdgeRef, curve_t: f64, position: &Path::Point) -> bool {
if curve_t > 0.1 {
false
} else {
let start_point = path.point_position(edge.start_idx);
start_point.is_near_to(position, SMALL_DISTANCE)
}
}
#[inline]
fn collision_is_at_end<Path: RayPath>(path: &Path, edge: &GraphEdgeRef, curve_t: f64, position: &Path::Point) -> bool {
if curve_t < 0.9 {
false
} else {
let next_point_idx = path.edge_end_point_idx(*edge);
let end_point = path.point_position(next_point_idx);
end_point.is_near_to(position, SMALL_DISTANCE)
}
}
#[inline]
fn edges_are_glancing<Path: RayPath>(path: &Path, ray: (f64, f64, f64), previous_edge: &GraphEdgeRef, following_edge: &GraphEdgeRef) -> bool {
let following_edge = path.get_edge(*following_edge);
let previous_edge = path.get_edge(*previous_edge);
let (a, b, c) = ray;
let cp_in = previous_edge.control_points().1;
let cp_out = following_edge.control_points().0;
let side_in = cp_in.x()*a + cp_in.y()*b + c;
let side_out = cp_out.x()*a + cp_out.y()*b + c;
let side_in = if side_in.abs() < 0.001 { 0.0 } else { side_in.signum() };
let side_out = if side_out.abs() < 0.001 { 0.0 } else { side_out.signum() };
side_in == side_out
}
fn filter_collisions_near_vertices<'a, Path: RayPath, L: Line, Collisions: 'a+IntoIterator<Item=(GraphEdgeRef, f64, f64, Path::Point)>>(path: &'a Path, ray: &'a L, collisions: Collisions) -> impl 'a+Iterator<Item=(GraphEdgeRef, f64, f64, Path::Point)>
where L: Line<Point=Path::Point> {
let (a, b, c) = ray.coefficients();
let mut visited_start = None;
collisions.into_iter()
.filter_map(move |(edge, curve_t, line_t, position)| {
let is_at_start = collision_is_at_start(path, &edge, curve_t, &position);
let is_at_end = !is_at_start && collision_is_at_end(path, &edge, curve_t, &position);
if is_at_start || is_at_end {
let (preceding_edge, following_edge) = if is_at_start {
let previous_edge = path.reverse_edges_for_point(edge.start_idx)
.into_iter()
.map(|previous_edge| previous_edge.reversed())
.find(|previous_edge| path.edge_following_edge_idx(*previous_edge) == edge.edge_idx)
.expect("Previous edge for a collision at start");
(previous_edge, edge)
} else {
let (next_edge, _) = path.get_next_edge(edge);
(edge, next_edge)
};
if edges_are_glancing(path, (a, b, c), &preceding_edge, &following_edge) {
let both_glancing = if is_at_start {
let edge = path.get_edge(preceding_edge);
let collisions = curve_intersects_ray(&edge, ray);
collisions.into_iter().any(|(curve_t, _line_t, position)| collision_is_at_end(path, &preceding_edge, curve_t, &position))
} else {
let edge = path.get_edge(following_edge);
let collisions = curve_intersects_ray(&edge, ray);
collisions.into_iter().any(|(curve_t, _line_t, position)| collision_is_at_start(path, &following_edge, curve_t, &position))
};
if both_glancing {
None
} else {
Some((edge, curve_t, line_t, position))
}
} else {
let visited_start = visited_start.get_or_insert_with(|| vec![None; path.num_points()]);
let was_visited = visited_start[following_edge.start_idx]
.as_ref()
.map(|collisions: &SmallVec<[_; 2]>| collisions.contains(&following_edge.edge_idx))
.unwrap_or(false);
if !was_visited {
visited_start[following_edge.start_idx].get_or_insert_with(|| smallvec![]).push(following_edge.edge_idx);
}
if !was_visited {
Some((following_edge, 0.0, line_t, position))
} else {
None
}
}
} else {
Some((edge, curve_t, line_t, position))
}
})
}
#[inline]
fn remove_tangent_collisions<'a, Path: RayPath, L: Line, Collisions: 'a+IntoIterator<Item=(GraphEdgeRef, f64, f64, Path::Point)>>(path: &'a Path, ray: &'a L, collisions: Collisions) -> impl 'a+Iterator<Item=(GraphEdgeRef, f64, f64, Path::Point)>
where L: Line<Point=Path::Point> {
let ray_vector = (ray.point_at_pos(1.0) - ray.point_at_pos(0.0)).to_unit_vector();
collisions.into_iter()
.filter(move |(edge, curve_t, _line_t, _position)| {
let edge = path.get_edge(*edge);
let tangent = edge.tangent_at_pos(*curve_t).to_unit_vector();
let dot_product = ray_vector.dot(&tangent);
let dot_product_mag = dot_product.abs() - 1.0;
if dot_product_mag > -0.00000001 && dot_product_mag < 0.00000001 {
false
} else {
true
}
})
}
#[inline]
fn flag_collisions_at_intersections<'a, Path: RayPath, Collisions: 'a+IntoIterator<Item=(GraphEdgeRef, f64, f64, Path::Point)>>(path: &'a Path, collisions: Collisions) -> impl 'a+Iterator<Item=(GraphRayCollision, f64, f64, Path::Point)>
where Path::Point: Coordinate+Coordinate2D {
collisions
.into_iter()
.map(move |(collision, curve_t, line_t, position)| {
if curve_t <= 0.000 {
if path.num_edges(collision.start_idx) > 1 {
(GraphRayCollision::Intersection(collision), curve_t, line_t, position)
} else {
(GraphRayCollision::SingleEdge(collision), curve_t, line_t, position)
}
} else {
(GraphRayCollision::SingleEdge(collision), curve_t, line_t, position)
}
})
}
pub (crate) fn ray_collisions<Path: RayPath, L: Line>(path: &Path, ray: &L) -> Vec<(GraphRayCollision, f64, f64, Path::Point)>
where Path::Point: Coordinate+Coordinate2D,
L: Line<Point=Path::Point> {
let (p1, p2) = ray.points();
let ray_direction = p2 - p1;
let (crossing_collisions, collinear_collisions) = crossing_and_collinear_collisions(path, ray);
let collinear_collisions = collinear_collisions.into_iter();
let crossing_collisions = crossing_collisions.into_iter();
let crossing_collisions = remove_collisions_before_or_after_collinear_section(path, ray, crossing_collisions);
let collisions = collinear_collisions.chain(crossing_collisions);
let collisions = move_collinear_collisions_to_end(path, ray, collisions);
let collisions = filter_collisions_near_vertices(path, ray, collisions);
let collisions = remove_tangent_collisions(path, ray, collisions);
let collisions = flag_collisions_at_intersections(path, collisions);
let mut collisions = collisions.collect::<Vec<_>>();
collisions.sort_by(|(edge_a, curve_t_a, line_t_a, pos_a), (edge_b, curve_t_b, line_t_b, pos_b)| {
let dx = pos_a.x() - pos_b.x();
let dy = pos_a.y() - pos_b.y();
if dx.abs() > SMALL_DISTANCE || dy.abs() > SMALL_DISTANCE {
line_t_a.partial_cmp(line_t_b).unwrap_or(Ordering::Equal)
} else if !edges_overlap(path, edge_a.edge(), edge_b.edge()) {
line_t_a.partial_cmp(line_t_b).unwrap_or(Ordering::Equal)
} else {
let edge_a = edge_a.edge();
let edge_b = edge_b.edge();
let result = edge_a.start_idx.cmp(&edge_b.start_idx);
if result != Ordering::Equal {
result
} else {
let edge_order = edge_a.edge_idx.cmp(&edge_b.edge_idx);
let (earlier_edge, edge_t) = match edge_order {
Ordering::Greater => (edge_b, *curve_t_b),
Ordering::Less => (edge_a, *curve_t_a),
Ordering::Equal => { return Ordering::Equal; }
};
let earlier_edge = path.get_edge(earlier_edge);
let earlier_normal = earlier_edge.normal_at_pos(edge_t);
let earlier_direction = ray_direction.dot(&earlier_normal);
if earlier_direction < 0.0 {
edge_order.reverse()
} else {
edge_order
}
}
}
});
collisions
}
#[cfg(test)]
mod test {
use super::*;
use super::super::path::*;
use super::super::path_builder::*;
use super::super::graph_path::test::*;
#[test]
fn raw_donut_collisions() {
let donut = donut();
let donut = &donut;
let raw_collisions = crossing_and_collinear_collisions(&donut, &(Coord2(7.000584357101389, 8.342524209216537), Coord2(6.941479643691172, 8.441210096108172))).0.into_iter();
println!("{:?}", raw_collisions.collect::<Vec<_>>());
}
#[test]
fn collinear_collision_along_convex_edge_produces_no_collisions() {
let rectangle1 = BezierPathBuilder::<SimpleBezierPath>::start(Coord2(1.0, 1.0))
.line_to(Coord2(5.0, 1.0))
.line_to(Coord2(5.0, 5.0))
.line_to(Coord2(1.0, 5.0))
.line_to(Coord2(1.0, 1.0))
.build();
let gp = GraphPath::from_path(&rectangle1, ());
let gp = &gp;
let collisions = crossing_and_collinear_collisions(&gp, &(Coord2(5.0, 0.0), Coord2(5.0, 5.0))).1;
assert!(collisions.is_empty());
}
#[test]
fn raw_collision_along_convex_edge_produces_no_collisions() {
let rectangle1 = BezierPathBuilder::<SimpleBezierPath>::start(Coord2(1.0, 1.0))
.line_to(Coord2(5.0, 1.0))
.line_to(Coord2(5.0, 5.0))
.line_to(Coord2(1.0, 5.0))
.line_to(Coord2(1.0, 1.0))
.build();
let gp = GraphPath::from_path(&rectangle1, ());
let gp = &gp;
let collisions = crossing_and_collinear_collisions(&gp, &(Coord2(5.0, 0.0), Coord2(5.0, 5.0))).0.into_iter();
let collisions = remove_collisions_before_or_after_collinear_section(&gp, &(Coord2(5.0, 0.0), Coord2(5.0, 5.0)), collisions);
let collisions = collisions.collect::<Vec<_>>();
assert!(collisions.is_empty());
}
#[test]
fn collinear_collision_along_concave_edge_produces_single_collision() {
let concave_shape = BezierPathBuilder::<SimpleBezierPath>::start(Coord2(1.0, 1.0))
.line_to(Coord2(5.0, 1.0))
.line_to(Coord2(5.0, 5.0))
.line_to(Coord2(6.0, 7.0))
.line_to(Coord2(3.0, 7.0))
.line_to(Coord2(1.0, 5.0))
.line_to(Coord2(1.0, 1.0))
.build();
let gp = GraphPath::from_path(&concave_shape, ());
let gp = &gp;
let ray = (Coord2(5.0, 0.0), Coord2(5.0, 5.0));
let collisions = crossing_and_collinear_collisions(&gp, &ray).1;
assert!(collisions.len() == 1);
}
#[test]
fn raw_collision_along_concave_edge_produces_single_collision() {
let concave_shape = BezierPathBuilder::<SimpleBezierPath>::start(Coord2(1.0, 1.0))
.line_to(Coord2(5.0, 1.0))
.line_to(Coord2(5.0, 5.0))
.line_to(Coord2(6.0, 7.0))
.line_to(Coord2(3.0, 7.0))
.line_to(Coord2(1.0, 5.0))
.line_to(Coord2(1.0, 1.0))
.build();
let gp = GraphPath::from_path(&concave_shape, ());
let gp = &gp;
let ray = (Coord2(5.0, 0.0), Coord2(5.0, 5.0));
let collisions = crossing_and_collinear_collisions(&gp, &ray).0.into_iter();
let collisions = remove_collisions_before_or_after_collinear_section(&gp, &(Coord2(5.0, 0.0), Coord2(5.0, 5.0)), collisions);
let collisions = collisions.collect::<Vec<_>>();
assert!(collisions.len() == 1);
}
#[test]
fn concave_collision_breakdown() {
let concave_shape = BezierPathBuilder::<SimpleBezierPath>::start(Coord2(1.0, 1.0))
.line_to(Coord2(5.0, 1.0))
.line_to(Coord2(5.0, 5.0))
.line_to(Coord2(6.0, 7.0))
.line_to(Coord2(3.0, 7.0))
.line_to(Coord2(1.0, 5.0))
.line_to(Coord2(1.0, 1.0))
.build();
let gp = GraphPath::from_path(&concave_shape, ());
let gp = &gp;
let ray = (Coord2(5.0, 0.0), Coord2(5.0, 5.0));
let (normal_collisions, collinear_collisions) = crossing_and_collinear_collisions(&gp, &ray);
let normal_collisions = remove_collisions_before_or_after_collinear_section(&gp, &ray, normal_collisions).collect::<Vec<_>>();
assert!(collinear_collisions.len() == 1);
assert!(normal_collisions.len() == 1);
let collisions = collinear_collisions.into_iter().chain(normal_collisions.into_iter()).collect::<Vec<_>>();
assert!(collisions.len() == 2);
let collisions = move_collinear_collisions_to_end(&gp, &ray, collisions).collect::<Vec<_>>();
assert!(collisions.len() == 2);
let collisions = filter_collisions_near_vertices(&gp, &ray, collisions).collect::<Vec<_>>();
assert!(collisions.len() == 2);
let collisions = flag_collisions_at_intersections(&gp, collisions).collect::<Vec<_>>();
assert!(collisions.len() == 2);
}
#[test]
fn interior_point_produces_four_collisions() {
let with_interior_point = BezierPathBuilder::<SimpleBezierPath>::start(Coord2(1.0, 1.0))
.line_to(Coord2(5.0, 1.0))
.line_to(Coord2(5.0, 5.0))
.line_to(Coord2(2.0, 2.0))
.line_to(Coord2(4.0, 2.0))
.line_to(Coord2(1.0, 5.0))
.line_to(Coord2(1.0, 1.0))
.build();
let mut with_interior_point = GraphPath::from_path(&with_interior_point, ());
with_interior_point.self_collide(0.01);
let with_interior_point = &with_interior_point;
let ray = (Coord2(0.0, 3.0), Coord2(1.0, 3.0));
let collisions = crossing_and_collinear_collisions(&with_interior_point, &ray).0;
println!("{:?}", with_interior_point);
println!("{:?}", collisions);
assert!(collisions.len() == 4);
let collisions = move_collinear_collisions_to_end(&with_interior_point, &ray, collisions).collect::<Vec<_>>();
assert!(collisions.len() == 4);
let collisions = filter_collisions_near_vertices(&with_interior_point, &ray, collisions).collect::<Vec<_>>();
println!("{:?}", collisions);
assert!(collisions.len() == 4);
let collisions = flag_collisions_at_intersections(&with_interior_point, collisions).collect::<Vec<_>>();
assert!(collisions.len() == 4);
}
}