mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 07:25:47 +03:00
making an infinite line type, to be less confusing
This commit is contained in:
parent
191b87f148
commit
2e982ec827
@ -18,6 +18,7 @@
|
||||
## Stability
|
||||
|
||||
- test results per git commit
|
||||
- https://github.com/spotify/git-test
|
||||
- way to view later
|
||||
- also could be benchmarks; just arbitrary data over time
|
||||
- also screenshots
|
||||
@ -38,6 +39,8 @@
|
||||
|
||||
### Current major geometry problems
|
||||
|
||||
- return more info with intersection stuff.
|
||||
|
||||
- line intersection code is giving completely silly results for 23rd
|
||||
- Line::new(
|
||||
Pt2D::new(2220.510790392476, 17.372151672558502),
|
||||
@ -46,5 +49,7 @@
|
||||
Pt2D::new(2220.5127307848657, 16.63215421656464),
|
||||
Pt2D::new(2220.514671177255, 15.892156760571009),
|
||||
) intersect, but first line doesn't contain_pt(Pt2D(2220.495346552271, 22.98459165963943))
|
||||
- use https://crates.io/crates/line_intersection or ncollide for some of this math
|
||||
- actually, copy in code from line_intersection and use new geo... or maybe dont use geo at all
|
||||
|
||||
- bad shifted polylines on 45th st in 23rd map
|
||||
|
@ -134,7 +134,8 @@ fn calculate_corners(i: &Intersection, map: &Map) -> Vec<Polygon> {
|
||||
let dst_line = l2.first_line().shift_left(LANE_THICKNESS / 2.0);
|
||||
|
||||
let pt_maybe_in_intersection = src_line
|
||||
.intersection_two_infinite_lines(&dst_line)
|
||||
.infinite()
|
||||
.intersection(&dst_line.infinite())
|
||||
.expect("SharedSidewalkCorner between parallel sidewalks");
|
||||
|
||||
// Now find all of the points on the intersection polygon between the two sidewalks.
|
||||
|
@ -9,7 +9,7 @@ mod pt;
|
||||
pub use crate::angle::Angle;
|
||||
pub use crate::circle::Circle;
|
||||
pub use crate::gps::{GPSBounds, LonLat};
|
||||
pub use crate::line::Line;
|
||||
pub use crate::line::{InfiniteLine, Line};
|
||||
pub use crate::polygon::{Polygon, Triangle};
|
||||
pub use crate::polyline::PolyLine;
|
||||
pub use crate::pt::{Bounds, HashablePt2D, Pt2D};
|
||||
@ -21,26 +21,3 @@ pub const EPSILON_DIST: si::Meter<f64> = si::Meter {
|
||||
value_unsafe: 0.01,
|
||||
_marker: marker::PhantomData,
|
||||
};
|
||||
|
||||
// NOT segment. Fails for parallel lines.
|
||||
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
|
||||
pub fn line_intersection(l1: &Line, l2: &Line) -> Option<Pt2D> {
|
||||
let x1 = l1.pt1().x();
|
||||
let y1 = l1.pt1().y();
|
||||
let x2 = l1.pt2().x();
|
||||
let y2 = l1.pt2().y();
|
||||
|
||||
let x3 = l2.pt1().x();
|
||||
let y3 = l2.pt1().y();
|
||||
let x4 = l2.pt2().x();
|
||||
let y4 = l2.pt2().y();
|
||||
|
||||
let numer_x = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4);
|
||||
let numer_y = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4);
|
||||
let denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
||||
if denom == 0.0 {
|
||||
None
|
||||
} else {
|
||||
Some(Pt2D::new(numer_x / denom, numer_y / denom))
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::{line_intersection, Angle, PolyLine, Polygon, Pt2D, EPSILON_DIST};
|
||||
use crate::{Angle, PolyLine, Polygon, Pt2D, EPSILON_DIST};
|
||||
use dimensioned::si;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
// Segment, technically
|
||||
// Segment, technically. Should rename.
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct Line(Pt2D, Pt2D);
|
||||
|
||||
@ -13,6 +13,10 @@ impl Line {
|
||||
Line(pt1, pt2)
|
||||
}
|
||||
|
||||
pub fn infinite(&self) -> InfiniteLine {
|
||||
InfiniteLine(self.0, self.1)
|
||||
}
|
||||
|
||||
// TODO we call these frequently here; unnecessary copies?
|
||||
pub fn pt1(&self) -> Pt2D {
|
||||
self.0
|
||||
@ -72,32 +76,38 @@ impl Line {
|
||||
* si::M
|
||||
}
|
||||
|
||||
// TODO Also return the distance along self
|
||||
pub fn intersection(&self, other: &Line) -> Option<Pt2D> {
|
||||
// TODO shoddy way of implementing this
|
||||
// TODO doesn't handle nearly parallel lines
|
||||
if !self.intersects(other) {
|
||||
None
|
||||
// From http://bryceboe.com/2006/10/23/line-segment-intersection-algorithm/
|
||||
if is_counter_clockwise(self.pt1(), other.pt1(), other.pt2())
|
||||
== is_counter_clockwise(self.pt2(), other.pt1(), other.pt2())
|
||||
|| is_counter_clockwise(self.pt1(), self.pt2(), other.pt1())
|
||||
== is_counter_clockwise(self.pt1(), self.pt2(), other.pt2())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let hit = self.infinite().intersection(&other.infinite())?;
|
||||
if self.contains_pt(hit) {
|
||||
Some(hit)
|
||||
} else {
|
||||
if let Some(hit) = line_intersection(self, other) {
|
||||
if self.contains_pt(hit) {
|
||||
Some(hit)
|
||||
} else {
|
||||
// TODO This shouldn't be possible! :D
|
||||
println!(
|
||||
"{} and {} intersect, but first line doesn't contain_pt({})",
|
||||
self, other, hit
|
||||
);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
// TODO This shouldn't be possible! :D
|
||||
println!(
|
||||
"{} and {} intersect, but first line doesn't contain_pt({})",
|
||||
self, other, hit
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// TODO separate LineSegment and InfiniteLine types
|
||||
pub fn intersection_two_infinite_lines(&self, other: &Line) -> Option<Pt2D> {
|
||||
line_intersection(self, other)
|
||||
// TODO Also return the distance along self
|
||||
pub fn intersection_infinite(&self, other: &InfiniteLine) -> Option<Pt2D> {
|
||||
let hit = self.infinite().intersection(other)?;
|
||||
if self.contains_pt(hit) {
|
||||
Some(hit)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shift_right(&self, width: f64) -> Line {
|
||||
@ -130,14 +140,6 @@ impl Line {
|
||||
Line(self.pt2(), self.pt1())
|
||||
}
|
||||
|
||||
pub fn intersects(&self, other: &Line) -> bool {
|
||||
// From http://bryceboe.com/2006/10/23/line-segment-intersection-algorithm/
|
||||
is_counter_clockwise(self.pt1(), other.pt1(), other.pt2())
|
||||
!= is_counter_clockwise(self.pt2(), other.pt1(), other.pt2())
|
||||
&& is_counter_clockwise(self.pt1(), self.pt2(), other.pt1())
|
||||
!= is_counter_clockwise(self.pt1(), self.pt2(), other.pt2())
|
||||
}
|
||||
|
||||
pub fn angle(&self) -> Angle {
|
||||
self.pt1().angle_to(self.pt2())
|
||||
}
|
||||
@ -238,3 +240,31 @@ impl fmt::Display for Line {
|
||||
fn is_counter_clockwise(pt1: Pt2D, pt2: Pt2D, pt3: Pt2D) -> bool {
|
||||
(pt3.y() - pt1.y()) * (pt2.x() - pt1.x()) > (pt2.y() - pt1.y()) * (pt3.x() - pt1.x())
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct InfiniteLine(Pt2D, Pt2D);
|
||||
|
||||
impl InfiniteLine {
|
||||
// Fails for parallel lines.
|
||||
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
|
||||
pub fn intersection(&self, other: &InfiniteLine) -> Option<Pt2D> {
|
||||
let x1 = self.0.x();
|
||||
let y1 = self.0.y();
|
||||
let x2 = self.1.x();
|
||||
let y2 = self.1.y();
|
||||
|
||||
let x3 = other.0.x();
|
||||
let y3 = other.0.y();
|
||||
let x4 = other.1.x();
|
||||
let y4 = other.1.y();
|
||||
|
||||
let numer_x = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4);
|
||||
let numer_y = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4);
|
||||
let denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
||||
if denom == 0.0 {
|
||||
None
|
||||
} else {
|
||||
Some(Pt2D::new(numer_x / denom, numer_y / denom))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{line_intersection, Angle, Bounds, HashablePt2D, Line, Polygon, Pt2D, EPSILON_DIST};
|
||||
use crate::{Angle, Bounds, HashablePt2D, InfiniteLine, Line, Polygon, Pt2D, EPSILON_DIST};
|
||||
use dimensioned::si;
|
||||
use ordered_float::NotNan;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@ -223,7 +223,10 @@ impl PolyLine {
|
||||
let l2 = Line::new(pt2_raw, pt3_raw).shift_either_direction(width);
|
||||
// When the lines are perfectly parallel, it means pt2_shift_1st == pt2_shift_2nd and the
|
||||
// original geometry is redundant.
|
||||
let pt2_shift = line_intersection(&l1, &l2).unwrap_or_else(|| l1.pt2());
|
||||
let pt2_shift = l1
|
||||
.infinite()
|
||||
.intersection(&l2.infinite())
|
||||
.unwrap_or_else(|| l1.pt2());
|
||||
|
||||
if pt3_idx == 2 {
|
||||
result.push(l1.pt1());
|
||||
@ -318,13 +321,11 @@ impl PolyLine {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn intersection_infinite_line(&self, other: Line) -> Option<Pt2D> {
|
||||
// TODO There must be better ways to do this. :)
|
||||
// TODO Also distance along
|
||||
pub fn intersection_infinite(&self, other: &InfiniteLine) -> Option<Pt2D> {
|
||||
for l in self.lines() {
|
||||
if let Some(hit) = line_intersection(&l, &other) {
|
||||
if l.contains_pt(hit) {
|
||||
return Some(hit);
|
||||
}
|
||||
if let Some(hit) = l.intersection_infinite(other) {
|
||||
return Some(hit);
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -101,10 +101,11 @@ fn generalized_trim_back(
|
||||
|
||||
if let Some((hit, angle)) = pl1.intersection(pl2) {
|
||||
// Find where the perpendicular hits the original road line
|
||||
let perp = Line::new(hit, hit.project_away(1.0, angle.rotate_degs(90.0)));
|
||||
let perp =
|
||||
Line::new(hit, hit.project_away(1.0, angle.rotate_degs(90.0))).infinite();
|
||||
// How could something perpendicular to a shifted polyline never hit the original
|
||||
// polyline?
|
||||
let trim_to = road_center.intersection_infinite_line(perp).unwrap();
|
||||
let trim_to = road_center.intersection_infinite(&perp).unwrap();
|
||||
let trimmed = road_center.get_slice_ending_at(trim_to).unwrap();
|
||||
if trimmed.length() < shortest_center.length() {
|
||||
shortest_center = trimmed;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::runner::TestRunner;
|
||||
use geom::{line_intersection, Line, PolyLine, Pt2D};
|
||||
use geom::{Line, PolyLine, Pt2D};
|
||||
use rand;
|
||||
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
@ -25,21 +25,21 @@ pub fn run(t: &mut TestRunner) {
|
||||
|
||||
let width = 50.0;
|
||||
let pt1_s = Line::new(pt1, pt2).shift_right(width).pt1();
|
||||
let pt2_s = line_intersection(
|
||||
&Line::new(pt1, pt2).shift_right(width),
|
||||
&Line::new(pt2, pt3).shift_right(width),
|
||||
)
|
||||
.unwrap();
|
||||
let pt3_s = line_intersection(
|
||||
&Line::new(pt2, pt3).shift_right(width),
|
||||
&Line::new(pt3, pt4).shift_right(width),
|
||||
)
|
||||
.unwrap();
|
||||
let pt4_s = line_intersection(
|
||||
&Line::new(pt3, pt4).shift_right(width),
|
||||
&Line::new(pt4, pt5).shift_right(width),
|
||||
)
|
||||
.unwrap();
|
||||
let pt2_s = Line::new(pt1, pt2)
|
||||
.shift_right(width)
|
||||
.infinite()
|
||||
.intersection(&Line::new(pt2, pt3).shift_right(width).infinite())
|
||||
.unwrap();
|
||||
let pt3_s = Line::new(pt2, pt3)
|
||||
.shift_right(width)
|
||||
.infinite()
|
||||
.intersection(&Line::new(pt3, pt4).shift_right(width).infinite())
|
||||
.unwrap();
|
||||
let pt4_s = Line::new(pt3, pt4)
|
||||
.shift_right(width)
|
||||
.infinite()
|
||||
.intersection(&Line::new(pt4, pt5).shift_right(width).infinite())
|
||||
.unwrap();
|
||||
let pt5_s = Line::new(pt4, pt5).shift_right(width).pt2();
|
||||
|
||||
assert_eq!(
|
||||
|
Loading…
Reference in New Issue
Block a user