PolyLine struct

This commit is contained in:
Dustin Carlino 2018-06-28 17:16:09 -07:00
parent 7f09f22bcf
commit 8e1f9b79f0
11 changed files with 258 additions and 210 deletions

View File

@ -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

View File

@ -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, &center_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(&center_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, &center_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);

View File

@ -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(),
}
}

View File

@ -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, &center_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;
}

View File

@ -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()
})
}

View File

@ -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>);

View File

@ -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();

View File

@ -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());
}
}

View File

@ -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 {

View File

@ -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()])
);
}

View File

@ -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!("];");