introduce a proper closed polyline abstraction, sub it in

This commit is contained in:
Dustin Carlino 2019-10-25 13:14:05 -07:00
parent fe14e1709b
commit 3dfeae8b2c
6 changed files with 76 additions and 37 deletions

View File

@ -1,7 +1,7 @@
use crate::helpers::{ColorScheme, ID}; use crate::helpers::{ColorScheme, ID};
use crate::render::{DrawCtx, DrawOptions, Renderable, OUTLINE_THICKNESS}; use crate::render::{DrawCtx, DrawOptions, Renderable, OUTLINE_THICKNESS};
use ezgui::{Color, GeomBatch, GfxCtx, Line, Text}; use ezgui::{Color, GeomBatch, GfxCtx, Line, Text};
use geom::{Circle, Distance, Line, PolyLine, Polygon, Pt2D}; use geom::{Circle, Distance, Line, Polygon, Pt2D, Ring};
use map_model::{Building, BuildingID, Map, LANE_THICKNESS}; use map_model::{Building, BuildingID, Map, LANE_THICKNESS};
pub struct DrawBuilding { pub struct DrawBuilding {
@ -111,10 +111,7 @@ impl Renderable for DrawBuilding {
} }
fn get_outline(&self, map: &Map) -> Polygon { fn get_outline(&self, map: &Map) -> Polygon {
PolyLine::make_polygons_for_boundary( Ring::new(map.get_b(self.id).polygon.points().clone()).make_polygons(OUTLINE_THICKNESS)
map.get_b(self.id).polygon.points().clone(),
OUTLINE_THICKNESS,
)
} }
fn contains_pt(&self, pt: Pt2D, map: &Map) -> bool { fn contains_pt(&self, pt: Pt2D, map: &Map) -> bool {

View File

@ -3,7 +3,7 @@ use crate::render::{
DrawCtx, DrawOptions, Renderable, EXTRA_SHAPE_POINT_RADIUS, EXTRA_SHAPE_THICKNESS, DrawCtx, DrawOptions, Renderable, EXTRA_SHAPE_POINT_RADIUS, EXTRA_SHAPE_THICKNESS,
}; };
use ezgui::{Color, Drawable, GeomBatch, GfxCtx, Prerender}; use ezgui::{Color, Drawable, GeomBatch, GfxCtx, Prerender};
use geom::{Circle, FindClosest, GPSBounds, PolyLine, Polygon, Pt2D}; use geom::{Circle, FindClosest, GPSBounds, PolyLine, Polygon, Pt2D, Ring};
use kml::ExtraShape; use kml::ExtraShape;
use map_model::{DirectedRoadID, Map, LANE_THICKNESS}; use map_model::{DirectedRoadID, Map, LANE_THICKNESS};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -54,9 +54,9 @@ impl DrawExtraShape {
} else if pts[0] == *pts.last().unwrap() { } else if pts[0] == *pts.last().unwrap() {
// TODO Toggle between these better // TODO Toggle between these better
//Polygon::new(&pts) //Polygon::new(&pts)
PolyLine::make_polygons_for_boundary(pts, EXTRA_SHAPE_THICKNESS) Ring::new(pts).make_polygons(EXTRA_SHAPE_THICKNESS)
} else { } else {
PolyLine::make_polygons_for_boundary(pts, EXTRA_SHAPE_THICKNESS) PolyLine::new(pts).make_polygons(EXTRA_SHAPE_THICKNESS)
}; };
let mut batch = GeomBatch::new(); let mut batch = GeomBatch::new();
batch.push( batch.push(

View File

@ -5,7 +5,7 @@ use crate::render::{
}; };
use abstutil::Timer; use abstutil::Timer;
use ezgui::{Color, Drawable, GeomBatch, GfxCtx, Prerender}; use ezgui::{Color, Drawable, GeomBatch, GfxCtx, Prerender};
use geom::{Angle, Distance, Duration, Line, PolyLine, Polygon, Pt2D, EPSILON_DIST}; use geom::{Angle, Distance, Duration, Line, PolyLine, Polygon, Pt2D, Ring, EPSILON_DIST};
use map_model::{ use map_model::{
Intersection, IntersectionID, IntersectionType, Map, Road, RoadWithStopSign, Turn, TurnID, Intersection, IntersectionID, IntersectionType, Map, Road, RoadWithStopSign, Turn, TurnID,
TurnType, LANE_THICKNESS, TurnType, LANE_THICKNESS,
@ -170,10 +170,7 @@ impl Renderable for DrawIntersection {
} }
fn get_outline(&self, map: &Map) -> Polygon { fn get_outline(&self, map: &Map) -> Polygon {
PolyLine::make_polygons_for_boundary( Ring::new(map.get_i(self.id).polygon.points().clone()).make_polygons(OUTLINE_THICKNESS)
map.get_i(self.id).polygon.points().clone(),
OUTLINE_THICKNESS,
)
} }
fn contains_pt(&self, pt: Pt2D, map: &Map) -> bool { fn contains_pt(&self, pt: Pt2D, map: &Map) -> bool {

View File

@ -9,6 +9,7 @@ mod line;
mod polygon; mod polygon;
mod polyline; mod polyline;
mod pt; mod pt;
mod ring;
mod speed; mod speed;
pub use crate::angle::Angle; pub use crate::angle::Angle;
@ -22,6 +23,7 @@ pub use crate::line::{InfiniteLine, Line};
pub use crate::polygon::{Polygon, Triangle}; pub use crate::polygon::{Polygon, Triangle};
pub use crate::polyline::PolyLine; pub use crate::polyline::PolyLine;
pub use crate::pt::{HashablePt2D, Pt2D}; pub use crate::pt::{HashablePt2D, Pt2D};
pub use crate::ring::Ring;
pub use crate::speed::Speed; pub use crate::speed::Speed;
// About 0.4 inches... which is quite tiny on the scale of things. :) // About 0.4 inches... which is quite tiny on the scale of things. :)

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
Angle, Bounds, Distance, HashablePt2D, InfiniteLine, Line, Polygon, Pt2D, EPSILON_DIST, Angle, Bounds, Distance, HashablePt2D, InfiniteLine, Line, Polygon, Pt2D, Ring, EPSILON_DIST,
}; };
use abstutil::Warn; use abstutil::Warn;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
@ -27,7 +27,7 @@ impl PolyLine {
// needs to be squished down, why, and how. // needs to be squished down, why, and how.
if pts.windows(2).any(|pair| pair[0] == pair[1]) { if pts.windows(2).any(|pair| pair[0] == pair[1]) {
panic!( panic!(
"PL with total length {} and {} pts has ~dupe pts: {:?}", "PL with total length {} and {} pts has ~dupe adjacent pts: {:?}",
length, length,
pts.len(), pts.len(),
pts pts
@ -68,7 +68,7 @@ impl PolyLine {
Some(result) Some(result)
} }
pub fn make_polygons_for_boundary(pts: Vec<Pt2D>, thickness: Distance) -> Polygon { pub(crate) fn make_polygons_for_boundary(pts: Vec<Pt2D>, thickness: Distance) -> Polygon {
// Points WILL repeat -- fast-path some stuff. // Points WILL repeat -- fast-path some stuff.
let pl = PolyLine { let pl = PolyLine {
pts, pts,
@ -77,15 +77,6 @@ impl PolyLine {
pl.make_polygons(thickness) pl.make_polygons(thickness)
} }
pub fn to_thick_boundary_pts(&self, width: Distance) -> Vec<Pt2D> {
let mut side1 = self.shift_with_sharp_angles(width / 2.0);
let mut side2 = self.shift_with_sharp_angles(-width / 2.0);
side2.reverse();
side1.extend(side2);
side1.push(side1[0]);
side1
}
pub fn to_thick_boundary( pub fn to_thick_boundary(
&self, &self,
self_width: Distance, self_width: Distance,
@ -102,7 +93,7 @@ impl PolyLine {
side1.extend(side2); side1.extend(side2);
side1.push(side1[0]); side1.push(side1[0]);
side1.dedup(); side1.dedup();
Some(PolyLine::make_polygons_for_boundary(side1, boundary_width)) Some(Ring::new(side1).make_polygons(boundary_width))
} }
pub fn reversed(&self) -> PolyLine { pub fn reversed(&self) -> PolyLine {
@ -534,17 +525,15 @@ impl PolyLine {
let angle = slice.last_pt().angle_to(self.last_pt()); let angle = slice.last_pt().angle_to(self.last_pt());
Warn::ok(vec![ Warn::ok(vec![
p, p,
PolyLine::make_polygons_for_boundary( Ring::new(vec![
vec![ self.last_pt(),
self.last_pt(), self.last_pt()
self.last_pt() .project_away(head_size, angle.rotate_degs(-135.0)),
.project_away(head_size, angle.rotate_degs(-135.0)), self.last_pt()
self.last_pt() .project_away(head_size, angle.rotate_degs(135.0)),
.project_away(head_size, angle.rotate_degs(135.0)), self.last_pt(),
self.last_pt(), ])
], .make_polygons(outline_thickness),
outline_thickness,
),
]) ])
} else { } else {
Warn::warn( Warn::warn(

54
geom/src/ring.rs Normal file
View File

@ -0,0 +1,54 @@
use crate::{Distance, PolyLine, Polygon, Pt2D};
use serde_derive::{Deserialize, Serialize};
use std::collections::HashSet;
use std::fmt;
// Maybe a misnomer, but like a PolyLine, but closed.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Ring {
// first equals last
pts: Vec<Pt2D>,
}
impl Ring {
pub fn new(pts: Vec<Pt2D>) -> Ring {
assert!(pts.len() >= 3);
assert_eq!(pts[0], *pts.last().unwrap());
// This checks no lines are too small. Could take the other approach and automatically
// squish down points here and make sure the final result is at least EPSILON_DIST.
// But probably better for the callers to do this -- they have better understanding of what
// needs to be squished down, why, and how.
if pts.windows(2).any(|pair| pair[0] == pair[1]) {
panic!("Ring has ~dupe adjacent pts: {:?}", pts);
}
let result = Ring { pts };
let mut seen_pts = HashSet::new();
for pt in result.pts.iter().skip(1) {
seen_pts.insert(pt.to_hashable());
}
if seen_pts.len() != result.pts.len() - 1 {
panic!("Ring has repeat points: {}", result);
}
result
}
pub fn make_polygons(&self, thickness: Distance) -> Polygon {
// TODO Has a weird corner. Use the polygon offset thing instead? And move the
// implementation here, ideally.
PolyLine::make_polygons_for_boundary(self.pts.clone(), thickness)
}
}
impl fmt::Display for Ring {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Ring::new(vec![")?;
for pt in &self.pts {
writeln!(f, " Pt2D::new({}, {}),", pt.x(), pt.y())?;
}
write!(f, "])")
}
}