mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +03:00
PolyLine struct
This commit is contained in:
parent
7f09f22bcf
commit
8e1f9b79f0
@ -123,12 +123,11 @@ wait slow down even more -- before any of this change, lanes on adjacent roads s
|
||||
- cant easily serialize ordered float, so move away from it first?
|
||||
|
||||
|
||||
- polyline too; search for Vec<Pt2D>
|
||||
- isolate vec2d
|
||||
- turn's slope() and vecmath::normalized
|
||||
- split out geometry crate again
|
||||
- let ezgui stuff depend on it too
|
||||
- also a polygon struct?
|
||||
- also a polygon struct? for parcels and buildings
|
||||
|
||||
|
||||
|
||||
|
@ -6,7 +6,7 @@ use graphics;
|
||||
use graphics::math::Vec2d;
|
||||
use graphics::types::Color;
|
||||
use gui;
|
||||
use map_model::{geometry, polygons_for_polyline, shift_polyline, Pt2D};
|
||||
use map_model::{geometry, PolyLine, Pt2D};
|
||||
use piston::input::Key;
|
||||
use piston::window::Size;
|
||||
use std::f64;
|
||||
@ -106,40 +106,39 @@ impl UI {
|
||||
let width = 50.0;
|
||||
|
||||
// TODO retain this as a regression test
|
||||
let center_pts: Vec<Pt2D> = vec![
|
||||
//Pt2D::new(2623.117354164207, 1156.9671270455774),
|
||||
//Pt2D::new(2623.0950086610856, 1162.8272397294127),
|
||||
Pt2D::new(2623.0956685132396, 1162.7341864981956),
|
||||
// One problem happens starting here -- some overlap
|
||||
Pt2D::new(2622.8995366939575, 1163.2433695162579),
|
||||
Pt2D::new(2620.4658232463926, 1163.9861244298272),
|
||||
Pt2D::new(2610.979416102837, 1164.2392149291984),
|
||||
//Pt2D::new(2572.5481805300115, 1164.2059309889344),
|
||||
].iter()
|
||||
.map(|pt| Pt2D::new(pt.x() - 2500.0, pt.y() - 1000.0))
|
||||
.collect();
|
||||
let center_pts = PolyLine::new(
|
||||
vec![
|
||||
//Pt2D::new(2623.117354164207, 1156.9671270455774),
|
||||
//Pt2D::new(2623.0950086610856, 1162.8272397294127),
|
||||
Pt2D::new(2623.0956685132396, 1162.7341864981956),
|
||||
// One problem happens starting here -- some overlap
|
||||
Pt2D::new(2622.8995366939575, 1163.2433695162579),
|
||||
Pt2D::new(2620.4658232463926, 1163.9861244298272),
|
||||
Pt2D::new(2610.979416102837, 1164.2392149291984),
|
||||
//Pt2D::new(2572.5481805300115, 1164.2059309889344),
|
||||
].iter()
|
||||
.map(|pt| Pt2D::new(pt.x() - 2500.0, pt.y() - 1000.0))
|
||||
.collect(),
|
||||
);
|
||||
draw_polyline(g, ¢er_pts, thin, RED);
|
||||
for (idx, pt) in center_pts.iter().enumerate() {
|
||||
for (idx, pt) in center_pts.points().iter().enumerate() {
|
||||
labels.push((*pt, format!("p{}", idx + 1)));
|
||||
}
|
||||
|
||||
for p in polygons_for_polyline(¢er_pts, width) {
|
||||
for p in center_pts.make_polygons(width) {
|
||||
draw_polygon(g, p, BLACK);
|
||||
}
|
||||
|
||||
// TODO colored labels!
|
||||
let side1 = shift_polyline(width / 2.0, ¢er_pts);
|
||||
let side1 = center_pts.shift(width / 2.0);
|
||||
//draw_polyline(g, &side1, thin, BLUE);
|
||||
for (idx, pt) in side1.iter().enumerate() {
|
||||
for (idx, pt) in side1.points().iter().enumerate() {
|
||||
labels.push((*pt, format!("L{}", idx + 1)));
|
||||
}
|
||||
|
||||
let mut reversed_center_pts = center_pts.clone();
|
||||
reversed_center_pts.reverse();
|
||||
let mut side2 = shift_polyline(width / 2.0, &reversed_center_pts);
|
||||
side2.reverse();
|
||||
let side2 = center_pts.reversed().shift(width / 2.0).reversed();
|
||||
//draw_polyline(g, &side2, thin, GREEN);
|
||||
for (idx, pt) in side2.iter().enumerate() {
|
||||
for (idx, pt) in side2.points().iter().enumerate() {
|
||||
labels.push((*pt, format!("R{}", idx + 1)));
|
||||
}
|
||||
}
|
||||
@ -175,28 +174,30 @@ impl UI {
|
||||
println!("p1 -> p2 is {}", p1.angle_to(p2));
|
||||
println!("p2 -> p3 is {}", p2.angle_to(p3));
|
||||
|
||||
draw_polyline(g, &vec![p1, p2, p3, p4], thick, RED);
|
||||
let pts = PolyLine::new(vec![p1, p2, p3, p4]);
|
||||
|
||||
for p in polygons_for_polyline(&vec![p1, p2, p3, p4], shift_away) {
|
||||
draw_polyline(g, &pts, thick, RED);
|
||||
|
||||
for p in pts.make_polygons(shift_away) {
|
||||
draw_polygon(g, p, BLACK);
|
||||
}
|
||||
|
||||
// Two lanes on one side of the road
|
||||
let l1_pts = shift_polyline(shift_away, &vec![p1, p2, p3, p4]);
|
||||
for (idx, pt) in l1_pts.iter().enumerate() {
|
||||
let l1_pts = pts.shift(shift_away);
|
||||
for (idx, pt) in l1_pts.points().iter().enumerate() {
|
||||
labels.push((*pt, format!("l1_p{}", idx + 1)));
|
||||
}
|
||||
draw_polyline(g, &l1_pts, thin, GREEN);
|
||||
|
||||
let l2_pts = shift_polyline(shift_away * 2.0, &vec![p1, p2, p3, p4]);
|
||||
for (idx, pt) in l2_pts.iter().enumerate() {
|
||||
let l2_pts = pts.shift(shift_away * 2.0);
|
||||
for (idx, pt) in l2_pts.points().iter().enumerate() {
|
||||
labels.push((*pt, format!("l2_p{}", idx + 1)));
|
||||
}
|
||||
draw_polyline(g, &l2_pts, thin, GREEN);
|
||||
|
||||
// Other side
|
||||
let l3_pts = shift_polyline(shift_away, &vec![p4, p3, p2, p1]);
|
||||
for (idx, pt) in l3_pts.iter().enumerate() {
|
||||
let l3_pts = pts.reversed().shift(shift_away);
|
||||
for (idx, pt) in l3_pts.points().iter().enumerate() {
|
||||
labels.push((*pt, format!("l3_p{}", idx + 1)));
|
||||
}
|
||||
draw_polyline(g, &l3_pts, thin, BLUE);
|
||||
@ -213,7 +214,8 @@ fn draw_line(g: &mut GfxCtx, pt1: Pt2D, pt2: Pt2D, thickness: f64, color: Color)
|
||||
);
|
||||
}
|
||||
|
||||
fn draw_polyline(g: &mut GfxCtx, pts: &Vec<Pt2D>, thickness: f64, color: Color) {
|
||||
fn draw_polyline(g: &mut GfxCtx, pl: &PolyLine, thickness: f64, color: Color) {
|
||||
let pts = pl.points();
|
||||
assert!(pts.len() >= 2);
|
||||
for pair in pts.windows(2) {
|
||||
draw_line(g, pair[0], pair[1], thickness, color);
|
||||
|
@ -21,10 +21,8 @@ impl DrawParcel {
|
||||
pub fn new(p: &map_model::Parcel) -> DrawParcel {
|
||||
DrawParcel {
|
||||
id: p.id,
|
||||
boundary_polygons: map_model::polygons_for_polyline(
|
||||
&p.points,
|
||||
PARCEL_BOUNDARY_THICKNESS,
|
||||
),
|
||||
boundary_polygons: map_model::PolyLine::new(p.points.clone())
|
||||
.make_polygons(PARCEL_BOUNDARY_THICKNESS),
|
||||
fill_polygon: p.points.iter().map(|pt| [pt.x(), pt.y()]).collect(),
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use graphics::math::Vec2d;
|
||||
use graphics::types::Color;
|
||||
use map_model;
|
||||
use map_model::geometry;
|
||||
use map_model::{Line, Pt2D, RoadID};
|
||||
use map_model::{Line, PolyLine, RoadID};
|
||||
use render::PARCEL_BOUNDARY_THICKNESS;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -18,7 +18,7 @@ pub struct DrawRoad {
|
||||
polygons: Vec<Vec<Vec2d>>,
|
||||
// Empty for one-ways and one side of two-ways.
|
||||
// TODO ideally this could be done in the shader or something
|
||||
yellow_center_lines: Vec<Pt2D>,
|
||||
yellow_center_lines: Option<PolyLine>,
|
||||
start_crossing: (Vec2d, Vec2d),
|
||||
end_crossing: (Vec2d, Vec2d),
|
||||
|
||||
@ -33,16 +33,15 @@ impl DrawRoad {
|
||||
|
||||
let (end_1, end_2) = perp_line(road.last_line().reverse(), geometry::LANE_THICKNESS);
|
||||
|
||||
let polygons =
|
||||
map_model::polygons_for_polyline(&road.lane_center_pts, geometry::LANE_THICKNESS);
|
||||
let polygons = road.lane_center_pts.make_polygons(geometry::LANE_THICKNESS);
|
||||
|
||||
DrawRoad {
|
||||
id: road.id,
|
||||
polygons,
|
||||
yellow_center_lines: if road.use_yellow_center_lines {
|
||||
road.unshifted_pts.clone()
|
||||
Some(road.unshifted_pts.clone())
|
||||
} else {
|
||||
Vec::new()
|
||||
None
|
||||
},
|
||||
start_crossing: (start_1, start_2),
|
||||
end_crossing: (end_1, end_2),
|
||||
@ -68,13 +67,15 @@ impl DrawRoad {
|
||||
geometry::BIG_ARROW_THICKNESS,
|
||||
);
|
||||
|
||||
for pair in self.yellow_center_lines.windows(2) {
|
||||
center_marking.draw(
|
||||
[pair[0].x(), pair[0].y(), pair[1].x(), pair[1].y()],
|
||||
&g.ctx.draw_state,
|
||||
g.ctx.transform,
|
||||
g.gfx,
|
||||
);
|
||||
if let Some(ref pl) = self.yellow_center_lines {
|
||||
for pair in pl.points().windows(2) {
|
||||
center_marking.draw(
|
||||
[pair[0].x(), pair[0].y(), pair[1].x(), pair[1].y()],
|
||||
&g.ctx.draw_state,
|
||||
g.ctx.transform,
|
||||
g.gfx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let extra_marking_color = match self.lane_type {
|
||||
@ -102,7 +103,7 @@ impl DrawRoad {
|
||||
graphics::Line::new_round(cs.get(Colors::Debug), PARCEL_BOUNDARY_THICKNESS / 2.0);
|
||||
let circle = graphics::Ellipse::new(cs.get(Colors::BrightDebug));
|
||||
|
||||
for pair in r.lane_center_pts.windows(2) {
|
||||
for pair in r.lane_center_pts.points().windows(2) {
|
||||
let (pt1, pt2) = (pair[0], pair[1]);
|
||||
line.draw(
|
||||
[pt1.x(), pt1.y(), pt2.x(), pt2.y()],
|
||||
@ -246,13 +247,12 @@ fn calculate_driving_lines(road: &map_model::Road) -> Vec<(Vec2d, Vec2d)> {
|
||||
}
|
||||
|
||||
// Project left, so reverse the points.
|
||||
let mut center_pts = road.lane_center_pts.clone();
|
||||
center_pts.reverse();
|
||||
let lane_edge_pts = map_model::shift_polyline(geometry::LANE_THICKNESS / 2.0, ¢er_pts);
|
||||
let center_pts = road.lane_center_pts.reversed();
|
||||
let lane_edge_pts = center_pts.shift(geometry::LANE_THICKNESS / 2.0);
|
||||
|
||||
// This is an incredibly expensive way to compute dashed polyines, and it doesn't follow bends
|
||||
// properly. Just a placeholder.
|
||||
let lane_len = geometry::polyline_len(&lane_edge_pts);
|
||||
let lane_len = lane_edge_pts.length();
|
||||
let dash_separation = 2.0 * si::M;
|
||||
let dash_len = 1.0 * si::M;
|
||||
|
||||
@ -263,8 +263,8 @@ fn calculate_driving_lines(road: &map_model::Road) -> Vec<(Vec2d, Vec2d)> {
|
||||
break;
|
||||
}
|
||||
|
||||
let (pt1, _) = geometry::dist_along(&lane_edge_pts, start);
|
||||
let (pt2, _) = geometry::dist_along(&lane_edge_pts, start + dash_len);
|
||||
let (pt1, _) = lane_edge_pts.dist_along(start);
|
||||
let (pt2, _) = lane_edge_pts.dist_along(start + dash_len);
|
||||
dashes.push((pt1.to_vec(), pt2.to_vec()));
|
||||
start += dash_len + dash_separation;
|
||||
}
|
||||
|
@ -2,12 +2,11 @@
|
||||
|
||||
use Angle;
|
||||
use Bounds;
|
||||
use Line;
|
||||
use PolyLine;
|
||||
use Pt2D;
|
||||
use aabb_quadtree::geom::{Point, Rect};
|
||||
use dimensioned::si;
|
||||
use graphics::math::Vec2d;
|
||||
use polyline;
|
||||
use std::f64;
|
||||
|
||||
pub const LANE_THICKNESS: f64 = 2.5;
|
||||
@ -26,7 +25,7 @@ pub fn thick_line_from_angle(
|
||||
angle: Angle,
|
||||
) -> Vec<Vec<Vec2d>> {
|
||||
let pt2 = pt.project_away(line_length, angle);
|
||||
polyline::polygons_for_polyline(&vec![*pt, pt2], thickness)
|
||||
PolyLine::new(vec![*pt, pt2]).make_polygons(thickness)
|
||||
}
|
||||
|
||||
// Algorithm from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
|
||||
@ -97,27 +96,3 @@ pub fn circle_to_bbox(c: &[f64; 4]) -> Rect {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dist_along(pts: &Vec<Pt2D>, dist_along: si::Meter<f64>) -> (Pt2D, Angle) {
|
||||
let mut dist_left = dist_along;
|
||||
for (idx, pair) in pts.windows(2).enumerate() {
|
||||
let l = Line(pair[0], pair[1]);
|
||||
let length = l.length();
|
||||
let epsilon = if idx == pts.len() - 2 {
|
||||
EPSILON_METERS
|
||||
} else {
|
||||
0.0 * si::M
|
||||
};
|
||||
if dist_left <= length + epsilon {
|
||||
return (l.dist_along(dist_left), l.angle());
|
||||
}
|
||||
dist_left -= length;
|
||||
}
|
||||
panic!("{} is longer than pts by {}", dist_along, dist_left);
|
||||
}
|
||||
|
||||
pub fn polyline_len(pts: &Vec<Pt2D>) -> si::Meter<f64> {
|
||||
pts.windows(2).fold(0.0 * si::M, |so_far, pair| {
|
||||
so_far + Line(pair[0], pair[1]).length()
|
||||
})
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ pub use intersection::{Intersection, IntersectionID};
|
||||
pub use map::Map;
|
||||
use ordered_float::NotNaN;
|
||||
pub use parcel::{Parcel, ParcelID};
|
||||
pub use polyline::{polygons_for_polyline, shift_polyline};
|
||||
pub use polyline::PolyLine;
|
||||
use raw_data::LonLat;
|
||||
pub use road::{LaneType, Road, RoadID};
|
||||
use std::f64;
|
||||
@ -309,5 +309,3 @@ impl 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())
|
||||
}
|
||||
|
||||
pub struct PolyLine(Vec<Pt2D>);
|
||||
|
@ -85,6 +85,7 @@ fn center(pts: &Vec<Pt2D>) -> Pt2D {
|
||||
|
||||
fn road_to_line_string(r: &Road) -> geo::LineString<f64> {
|
||||
let pts: Vec<geo::Point<f64>> = r.lane_center_pts
|
||||
.points()
|
||||
.iter()
|
||||
.map(|pt| geo::Point::new(pt.x(), pt.y()))
|
||||
.collect();
|
||||
|
@ -38,12 +38,13 @@ pub(crate) fn trim_lines(roads: &mut Vec<Road>, i: &Intersection) {
|
||||
|
||||
// Apply the updates
|
||||
for (id, pair) in &shortest_first_line {
|
||||
roads[id.0].lane_center_pts[0] = pair.0.pt1();
|
||||
roads[id.0].lane_center_pts[1] = pair.0.pt2();
|
||||
roads[id.0]
|
||||
.lane_center_pts
|
||||
.replace_first_line(pair.0.pt1(), pair.0.pt2());
|
||||
}
|
||||
for (id, pair) in &shortest_last_line {
|
||||
let len = roads[id.0].lane_center_pts.len();
|
||||
roads[id.0].lane_center_pts[len - 2] = pair.0.pt1();
|
||||
roads[id.0].lane_center_pts[len - 1] = pair.0.pt2();
|
||||
roads[id.0]
|
||||
.lane_center_pts
|
||||
.replace_last_line(pair.0.pt1(), pair.0.pt2());
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use Bounds;
|
||||
use HashablePt2D;
|
||||
use PolyLine;
|
||||
use Pt2D;
|
||||
use abstutil;
|
||||
use building::{Building, BuildingID};
|
||||
@ -12,7 +13,6 @@ use make;
|
||||
use parcel::{Parcel, ParcelID};
|
||||
use raw_data;
|
||||
use road::{Road, RoadID};
|
||||
use shift_polyline;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Error;
|
||||
use turn::{Turn, TurnID};
|
||||
@ -70,17 +70,19 @@ impl Map {
|
||||
let other_side = lane.offset_for_other_id
|
||||
.map(|offset| RoadID(((id.0 as isize) + offset) as usize));
|
||||
|
||||
let mut unshifted_pts: Vec<Pt2D> = r.points
|
||||
.iter()
|
||||
.map(|coord| Pt2D::from_gps(coord, &bounds))
|
||||
.collect();
|
||||
let mut unshifted_pts = PolyLine::new(
|
||||
r.points
|
||||
.iter()
|
||||
.map(|coord| Pt2D::from_gps(coord, &bounds))
|
||||
.collect(),
|
||||
);
|
||||
if lane.reverse_pts {
|
||||
unshifted_pts.reverse();
|
||||
unshifted_pts = unshifted_pts.reversed();
|
||||
}
|
||||
|
||||
// Do this with the original points, before trimming them back
|
||||
let i1 = pt_to_intersection[&HashablePt2D::from(unshifted_pts[0])];
|
||||
let i2 = pt_to_intersection[&HashablePt2D::from(*unshifted_pts.last().unwrap())];
|
||||
let i1 = pt_to_intersection[&HashablePt2D::from(unshifted_pts.first_pt())];
|
||||
let i2 = pt_to_intersection[&HashablePt2D::from(unshifted_pts.last_pt())];
|
||||
m.intersections[i1.0].outgoing_roads.push(id);
|
||||
m.intersections[i2.0].incoming_roads.push(id);
|
||||
|
||||
@ -92,10 +94,8 @@ impl Map {
|
||||
// TODO probably different behavior for oneways
|
||||
// TODO need to factor in yellow center lines (but what's the right thing to even do?
|
||||
// Reverse points for British-style driving on the left
|
||||
let lane_center_pts = shift_polyline(
|
||||
geometry::LANE_THICKNESS * ((lane.offset as f64) + 0.5),
|
||||
&unshifted_pts,
|
||||
);
|
||||
let lane_center_pts =
|
||||
unshifted_pts.shift(geometry::LANE_THICKNESS * ((lane.offset as f64) + 0.5));
|
||||
|
||||
// lane_center_pts will get updated in the next pass
|
||||
m.roads.push(Road {
|
||||
|
@ -1,8 +1,171 @@
|
||||
use Angle;
|
||||
use Line;
|
||||
use Pt2D;
|
||||
use dimensioned::si;
|
||||
use geometry;
|
||||
use graphics::math::Vec2d;
|
||||
use std::f64;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PolyLine {
|
||||
pts: Vec<Pt2D>,
|
||||
}
|
||||
|
||||
impl PolyLine {
|
||||
// TODO helper for lines() would be nice, so we dont have to spam windows(2) and deal with
|
||||
// pairs
|
||||
|
||||
pub fn new(pts: Vec<Pt2D>) -> PolyLine {
|
||||
assert!(pts.len() >= 2);
|
||||
PolyLine { pts }
|
||||
}
|
||||
|
||||
// TODO copy or mut?
|
||||
// TODO this is likely not needed if we just have a way to shift in the other direction
|
||||
pub fn reversed(&self) -> PolyLine {
|
||||
let mut pts = self.pts.clone();
|
||||
pts.reverse();
|
||||
PolyLine::new(pts)
|
||||
}
|
||||
|
||||
// TODO weird to have these specific things?
|
||||
pub(crate) fn replace_first_line(&mut self, pt1: Pt2D, pt2: Pt2D) {
|
||||
self.pts[0] = pt1;
|
||||
self.pts[1] = pt2;
|
||||
}
|
||||
|
||||
pub(crate) fn replace_last_line(&mut self, pt1: Pt2D, pt2: Pt2D) {
|
||||
let len = self.pts.len();
|
||||
self.pts[len - 2] = pt1;
|
||||
self.pts[len - 1] = pt2;
|
||||
}
|
||||
|
||||
pub fn points(&self) -> &Vec<Pt2D> {
|
||||
&self.pts
|
||||
}
|
||||
|
||||
pub fn length(&self) -> si::Meter<f64> {
|
||||
self.pts.windows(2).fold(0.0 * si::M, |so_far, pair| {
|
||||
so_far + Line(pair[0], pair[1]).length()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn dist_along(&self, dist_along: si::Meter<f64>) -> (Pt2D, Angle) {
|
||||
let mut dist_left = dist_along;
|
||||
for (idx, pair) in self.pts.windows(2).enumerate() {
|
||||
let l = Line(pair[0], pair[1]);
|
||||
let length = l.length();
|
||||
let epsilon = if idx == self.pts.len() - 2 {
|
||||
geometry::EPSILON_METERS
|
||||
} else {
|
||||
0.0 * si::M
|
||||
};
|
||||
if dist_left <= length + epsilon {
|
||||
return (l.dist_along(dist_left), l.angle());
|
||||
}
|
||||
dist_left -= length;
|
||||
}
|
||||
panic!("{} is longer than pts by {}", dist_along, dist_left);
|
||||
}
|
||||
|
||||
pub fn first_pt(&self) -> Pt2D {
|
||||
self.pts[0]
|
||||
}
|
||||
pub fn last_pt(&self) -> Pt2D {
|
||||
*self.pts.last().unwrap()
|
||||
}
|
||||
pub fn first_line(&self) -> Line {
|
||||
Line(self.pts[0], self.pts[1])
|
||||
}
|
||||
pub fn last_line(&self) -> Line {
|
||||
Line(self.pts[self.pts.len() - 2], self.pts[self.pts.len() - 1])
|
||||
}
|
||||
|
||||
pub fn shift(&self, width: f64) -> PolyLine {
|
||||
if self.pts.len() == 2 {
|
||||
let l = Line(self.pts[0], self.pts[1]).shift(width);
|
||||
return PolyLine::new(vec![l.pt1(), l.pt2()]);
|
||||
}
|
||||
|
||||
let mut result: Vec<Pt2D> = Vec::new();
|
||||
|
||||
let mut pt3_idx = 2;
|
||||
let mut pt1_raw = self.pts[0];
|
||||
let mut pt2_raw = self.pts[1];
|
||||
|
||||
loop {
|
||||
let pt3_raw = self.pts[pt3_idx];
|
||||
|
||||
let l1 = Line(pt1_raw, pt2_raw).shift(width);
|
||||
let l2 = Line(pt2_raw, pt3_raw).shift(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(l1.pt2());
|
||||
|
||||
if pt3_idx == 2 {
|
||||
result.push(l1.pt1());
|
||||
}
|
||||
result.push(pt2_shift);
|
||||
if pt3_idx == self.pts.len() - 1 {
|
||||
result.push(l2.pt2());
|
||||
break;
|
||||
}
|
||||
|
||||
pt1_raw = pt2_raw;
|
||||
pt2_raw = pt3_raw;
|
||||
pt3_idx += 1;
|
||||
}
|
||||
|
||||
assert!(result.len() == self.pts.len());
|
||||
|
||||
// Check that the angles roughly match up between the original and shifted line
|
||||
for (orig_pair, shifted_pair) in self.pts.windows(2).zip(result.windows(2)) {
|
||||
let orig_angle = orig_pair[0].angle_to(orig_pair[1]).normalized_degrees();
|
||||
let shifted_angle = shifted_pair[0]
|
||||
.angle_to(shifted_pair[1])
|
||||
.normalized_degrees();
|
||||
let delta = (shifted_angle - orig_angle).abs();
|
||||
if delta > 0.00001 {
|
||||
println!(
|
||||
"Points changed angles from {} to {}",
|
||||
orig_angle, shifted_angle
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PolyLine::new(result)
|
||||
}
|
||||
|
||||
// TODO why do we need a bunch of triangles? why doesn't the single polygon triangulate correctly?
|
||||
// TODO ideally, detect when the polygon overlaps itself due to sharp lines and too much width
|
||||
// return Vec2d since this is only used for drawing right now
|
||||
pub fn make_polygons(&self, width: f64) -> Vec<Vec<Vec2d>> {
|
||||
let side1 = self.shift(width / 2.0);
|
||||
let side2 = self.reversed().shift(width / 2.0).reversed();
|
||||
|
||||
let mut result: Vec<Vec<Pt2D>> = Vec::new();
|
||||
for high_idx in 1..self.pts.len() {
|
||||
// Duplicate first point, since that's what graphics layer expects
|
||||
result.push(vec![
|
||||
side1.pts[high_idx],
|
||||
side1.pts[high_idx - 1],
|
||||
side2.pts[high_idx - 1],
|
||||
side1.pts[high_idx],
|
||||
]);
|
||||
result.push(vec![
|
||||
side2.pts[high_idx],
|
||||
side2.pts[high_idx - 1],
|
||||
side1.pts[high_idx],
|
||||
side2.pts[high_idx],
|
||||
]);
|
||||
}
|
||||
result
|
||||
.iter()
|
||||
.map(|pts| pts.iter().map(|pt| pt.to_vec()).collect())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO unsure why this doesn't work. maybe see if mouse is inside polygon to check it out?
|
||||
/*fn polygon_for_polyline(center_pts: &Vec<(f64, f64)>, width: f64) -> Vec<[f64; 2]> {
|
||||
let mut result = shift_polyline(width / 2.0, center_pts);
|
||||
@ -15,94 +178,6 @@ use std::f64;
|
||||
result.iter().map(|pair| [pair.0, pair.1]).collect()
|
||||
}*/
|
||||
|
||||
// TODO why do we need a bunch of triangles? why doesn't the single polygon triangulate correctly?
|
||||
// TODO ideally, detect when the polygon overlaps itself due to sharp lines and too much width
|
||||
// return Vec2d since this is only used for drawing right now
|
||||
pub fn polygons_for_polyline(center_pts: &Vec<Pt2D>, width: f64) -> Vec<Vec<Vec2d>> {
|
||||
let side1 = shift_polyline(width / 2.0, center_pts);
|
||||
let mut reversed_center_pts = center_pts.clone();
|
||||
reversed_center_pts.reverse();
|
||||
let mut side2 = shift_polyline(width / 2.0, &reversed_center_pts);
|
||||
side2.reverse();
|
||||
|
||||
let mut result: Vec<Vec<Pt2D>> = Vec::new();
|
||||
for high_idx in 1..center_pts.len() {
|
||||
// Duplicate first point, since that's what graphics layer expects
|
||||
result.push(vec![
|
||||
side1[high_idx],
|
||||
side1[high_idx - 1],
|
||||
side2[high_idx - 1],
|
||||
side1[high_idx],
|
||||
]);
|
||||
result.push(vec![
|
||||
side2[high_idx],
|
||||
side2[high_idx - 1],
|
||||
side1[high_idx],
|
||||
side2[high_idx],
|
||||
]);
|
||||
}
|
||||
result
|
||||
.iter()
|
||||
.map(|pts| pts.iter().map(|pt| pt.to_vec()).collect())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn shift_polyline(width: f64, pts: &Vec<Pt2D>) -> Vec<Pt2D> {
|
||||
assert!(pts.len() >= 2);
|
||||
if pts.len() == 2 {
|
||||
let l = Line(pts[0], pts[1]).shift(width);
|
||||
return vec![l.pt1(), l.pt2()];
|
||||
}
|
||||
|
||||
let mut result: Vec<Pt2D> = Vec::new();
|
||||
|
||||
let mut pt3_idx = 2;
|
||||
let mut pt1_raw = pts[0];
|
||||
let mut pt2_raw = pts[1];
|
||||
|
||||
loop {
|
||||
let pt3_raw = pts[pt3_idx];
|
||||
|
||||
let l1 = Line(pt1_raw, pt2_raw).shift(width);
|
||||
let l2 = Line(pt2_raw, pt3_raw).shift(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(l1.pt2());
|
||||
|
||||
if pt3_idx == 2 {
|
||||
result.push(l1.pt1());
|
||||
}
|
||||
result.push(pt2_shift);
|
||||
if pt3_idx == pts.len() - 1 {
|
||||
result.push(l2.pt2());
|
||||
break;
|
||||
}
|
||||
|
||||
pt1_raw = pt2_raw;
|
||||
pt2_raw = pt3_raw;
|
||||
pt3_idx += 1;
|
||||
}
|
||||
|
||||
assert!(result.len() == pts.len());
|
||||
|
||||
// Check that the angles roughly match up between the original and shifted line
|
||||
for (orig_pair, shifted_pair) in pts.windows(2).zip(result.windows(2)) {
|
||||
let orig_angle = orig_pair[0].angle_to(orig_pair[1]).normalized_degrees();
|
||||
let shifted_angle = shifted_pair[0]
|
||||
.angle_to(shifted_pair[1])
|
||||
.normalized_degrees();
|
||||
let delta = (shifted_angle - orig_angle).abs();
|
||||
if delta > 0.00001 {
|
||||
println!(
|
||||
"Points changed angles from {} to {}",
|
||||
orig_angle, shifted_angle
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
// NOT segment. Fails for parallel lines.
|
||||
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
|
||||
pub(crate) fn line_intersection(l1: &Line, l2: &Line) -> Option<Pt2D> {
|
||||
@ -148,8 +223,8 @@ fn shift_polyline_equivalence() {
|
||||
let pt5_s = Line(pt4, pt5).shift(width).pt2();
|
||||
|
||||
assert_eq!(
|
||||
shift_polyline(width, &vec![pt1, pt2, pt3, pt4, pt5]),
|
||||
vec![pt1_s, pt2_s, pt3_s, pt4_s, pt5_s]
|
||||
PolyLine::new(vec![pt1, pt2, pt3, pt4, pt5]).shift(width),
|
||||
PolyLine::new(vec![pt1_s, pt2_s, pt3_s, pt4_s, pt5_s])
|
||||
);
|
||||
}
|
||||
|
||||
@ -165,7 +240,7 @@ fn shift_short_polyline_equivalence() {
|
||||
let l = Line(pt1, pt2).shift(width);
|
||||
|
||||
assert_eq!(
|
||||
shift_polyline(width, &vec![pt1, pt2]),
|
||||
vec![l.pt1(), l.pt2()]
|
||||
PolyLine::new(vec![pt1, pt2]).shift(width),
|
||||
PolyLine::new(vec![l.pt1(), l.pt2()])
|
||||
);
|
||||
}
|
||||
|
@ -3,9 +3,9 @@
|
||||
use Angle;
|
||||
use IntersectionID;
|
||||
use Line;
|
||||
use PolyLine;
|
||||
use Pt2D;
|
||||
use dimensioned::si;
|
||||
use geometry;
|
||||
use std::collections::HashMap;
|
||||
use std::f64;
|
||||
use std::fmt;
|
||||
@ -50,11 +50,11 @@ pub struct Road {
|
||||
pub(crate) other_side: Option<RoadID>,
|
||||
|
||||
/// GeomRoad stuff
|
||||
pub lane_center_pts: Vec<Pt2D>,
|
||||
pub lane_center_pts: PolyLine,
|
||||
|
||||
// Unshifted center points. consider computing these twice or otherwise not storing them
|
||||
// These're screen-space. Order implies road orientation.
|
||||
pub unshifted_pts: Vec<Pt2D>,
|
||||
// Order implies road orientation.
|
||||
pub unshifted_pts: PolyLine,
|
||||
}
|
||||
|
||||
impl PartialEq for Road {
|
||||
@ -64,33 +64,32 @@ impl PartialEq for Road {
|
||||
}
|
||||
|
||||
impl Road {
|
||||
// TODO most of these are wrappers; stop doing this?
|
||||
pub fn first_pt(&self) -> Pt2D {
|
||||
self.lane_center_pts[0]
|
||||
self.lane_center_pts.first_pt()
|
||||
}
|
||||
pub fn last_pt(&self) -> Pt2D {
|
||||
*self.lane_center_pts.last().unwrap()
|
||||
self.lane_center_pts.last_pt()
|
||||
}
|
||||
pub fn first_line(&self) -> Line {
|
||||
Line(self.lane_center_pts[0], self.lane_center_pts[1])
|
||||
self.lane_center_pts.first_line()
|
||||
}
|
||||
pub fn last_line(&self) -> Line {
|
||||
Line(
|
||||
self.lane_center_pts[self.lane_center_pts.len() - 2],
|
||||
self.lane_center_pts[self.lane_center_pts.len() - 1],
|
||||
)
|
||||
self.lane_center_pts.last_line()
|
||||
}
|
||||
|
||||
pub fn dist_along(&self, dist_along: si::Meter<f64>) -> (Pt2D, Angle) {
|
||||
geometry::dist_along(&self.lane_center_pts, dist_along)
|
||||
self.lane_center_pts.dist_along(dist_along)
|
||||
}
|
||||
|
||||
pub fn length(&self) -> si::Meter<f64> {
|
||||
geometry::polyline_len(&self.lane_center_pts)
|
||||
self.lane_center_pts.length()
|
||||
}
|
||||
|
||||
pub fn dump_debug(&self) {
|
||||
println!("\nlet debug_r{}_pts = vec![", self.id.0);
|
||||
for pt in &self.lane_center_pts {
|
||||
// TODO nicer display for PolyLine?
|
||||
for pt in self.lane_center_pts.points().iter() {
|
||||
println!(" Pt2D::new({}, {}),", pt.x(), pt.y());
|
||||
}
|
||||
println!("];");
|
||||
|
Loading…
Reference in New Issue
Block a user