diff --git a/editor/src/render/bus_stop.rs b/editor/src/render/bus_stop.rs index bb627296a5..66a3fbd2b1 100644 --- a/editor/src/render/bus_stop.rs +++ b/editor/src/render/bus_stop.rs @@ -1,11 +1,12 @@ use crate::helpers::{ColorScheme, ID}; -use crate::render::{DrawCtx, DrawOptions, Renderable}; +use crate::render::{DrawCtx, DrawOptions, Renderable, OUTLINE_THICKNESS}; use ezgui::{Color, Drawable, GfxCtx, Prerender}; -use geom::{Distance, Polygon}; +use geom::{Distance, PolyLine, Polygon, Pt2D}; use map_model::{BusStop, BusStopID, Map, LANE_THICKNESS}; pub struct DrawBusStop { pub id: BusStopID, + polyline: PolyLine, polygon: Polygon, zorder: isize, @@ -18,13 +19,11 @@ impl DrawBusStop { // Kinda sad that bus stops might be very close to the start of the lane, but it's // happening. let lane = map.get_l(stop.id.sidewalk); - let polygon = lane - .lane_center_pts - .exact_slice( - Distance::ZERO.max(stop.sidewalk_pos.dist_along() - radius), - lane.length().min(stop.sidewalk_pos.dist_along() + radius), - ) - .make_polygons(LANE_THICKNESS * 0.8); + let polyline = lane.lane_center_pts.exact_slice( + Distance::ZERO.max(stop.sidewalk_pos.dist_along() - radius), + lane.length().min(stop.sidewalk_pos.dist_along() + radius), + ); + let polygon = polyline.make_polygons(LANE_THICKNESS * 0.8); let draw_default = prerender.upload_borrowed(vec![( cs.get_def("bus stop marking", Color::rgba(220, 160, 220, 0.8)), &polygon, @@ -32,6 +31,7 @@ impl DrawBusStop { DrawBusStop { id: stop.id, + polyline, polygon, zorder: map.get_parent(lane.id).get_zorder(), draw_default, @@ -53,8 +53,13 @@ impl Renderable for DrawBusStop { } fn get_outline(&self, _: &Map) -> Polygon { - // TODO need PolyLine->boundary - self.polygon.clone() + self.polyline + .to_thick_boundary(LANE_THICKNESS * 0.8, OUTLINE_THICKNESS) + .unwrap_or_else(|| self.polygon.clone()) + } + + fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool { + self.polygon.contains_pt(pt) } fn get_zorder(&self) -> isize { diff --git a/editor/src/render/car.rs b/editor/src/render/car.rs index d14f2bf170..eb537b19ce 100644 --- a/editor/src/render/car.rs +++ b/editor/src/render/car.rs @@ -1,5 +1,5 @@ use crate::helpers::{ColorScheme, ID}; -use crate::render::{should_draw_blinkers, DrawCtx, DrawOptions, Renderable}; +use crate::render::{should_draw_blinkers, DrawCtx, DrawOptions, Renderable, OUTLINE_THICKNESS}; use ezgui::{Color, Drawable, GfxCtx, Prerender}; use geom::{Angle, Circle, Distance, PolyLine, Polygon, Pt2D}; use map_model::{Map, TurnType}; @@ -9,6 +9,7 @@ const CAR_WIDTH: Distance = Distance::const_meters(2.0); pub struct DrawCar { pub id: CarID, + body: PolyLine, body_polygon: Polygon, // Optional and could be empty for super short cars near borders. window_polygons: Vec, @@ -90,6 +91,7 @@ impl DrawCar { DrawCar { id: input.id, + body: input.body, body_polygon, window_polygons: vec![front_window, back_window], left_blinkers: Some(( @@ -177,8 +179,13 @@ impl Renderable for DrawCar { } fn get_outline(&self, _: &Map) -> Polygon { - // TODO need PolyLine->boundary - self.body_polygon.clone() + self.body + .to_thick_boundary(CAR_WIDTH, OUTLINE_THICKNESS) + .unwrap_or_else(|| self.body_polygon.clone()) + } + + fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool { + self.body_polygon.contains_pt(pt) } fn get_zorder(&self) -> isize { diff --git a/editor/src/render/lane.rs b/editor/src/render/lane.rs index 134fe67bea..262080912b 100644 --- a/editor/src/render/lane.rs +++ b/editor/src/render/lane.rs @@ -1,8 +1,8 @@ use crate::helpers::{ColorScheme, ID}; -use crate::render::{DrawCtx, DrawOptions, Renderable, BIG_ARROW_THICKNESS}; +use crate::render::{DrawCtx, DrawOptions, Renderable, BIG_ARROW_THICKNESS, OUTLINE_THICKNESS}; use abstutil::Timer; use ezgui::{Color, Drawable, GfxCtx, Prerender}; -use geom::{Circle, Distance, Line, PolyLine, Polygon}; +use geom::{Circle, Distance, Line, PolyLine, Polygon, Pt2D}; use map_model::{ IntersectionType, Lane, LaneID, LaneType, Map, Road, LANE_THICKNESS, PARKING_SPOT_LENGTH, }; @@ -100,9 +100,15 @@ impl Renderable for DrawLane { } } - fn get_outline(&self, _: &Map) -> Polygon { - // TODO need PolyLine->boundary - self.polygon.clone() + fn get_outline(&self, map: &Map) -> Polygon { + map.get_l(self.id) + .lane_center_pts + .to_thick_boundary(LANE_THICKNESS, OUTLINE_THICKNESS) + .unwrap_or_else(|| self.polygon.clone()) + } + + fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool { + self.polygon.contains_pt(pt) } fn get_zorder(&self) -> isize { diff --git a/editor/src/render/mod.rs b/editor/src/render/mod.rs index 7f4ac704b5..74b0a5f552 100644 --- a/editor/src/render/mod.rs +++ b/editor/src/render/mod.rs @@ -39,7 +39,7 @@ const TURN_ICON_ARROW_THICKNESS: Distance = Distance::const_meters(0.15); const TURN_ICON_ARROW_LENGTH: Distance = Distance::const_meters(2.0); pub const CROSSWALK_LINE_THICKNESS: Distance = Distance::const_meters(0.25); -pub const OUTLINE_THICKNESS: Distance = Distance::const_meters(1.5); +pub const OUTLINE_THICKNESS: Distance = Distance::const_meters(0.5); // Does something belong here or as a method on ID? If it ONLY applies to renderable things, then // here. For example, trips aren't drawn, so it's meaningless to ask what their bounding box is. diff --git a/editor/src/render/road.rs b/editor/src/render/road.rs index 467522ef3b..343bc34a72 100644 --- a/editor/src/render/road.rs +++ b/editor/src/render/road.rs @@ -1,7 +1,7 @@ use crate::helpers::{ColorScheme, ID}; -use crate::render::{DrawCtx, DrawOptions, Renderable, BIG_ARROW_THICKNESS}; +use crate::render::{DrawCtx, DrawOptions, Renderable, BIG_ARROW_THICKNESS, OUTLINE_THICKNESS}; use ezgui::{Color, Drawable, GfxCtx, Prerender}; -use geom::Polygon; +use geom::{Polygon, Pt2D}; use map_model::{Map, Road, RoadID}; pub struct DrawRoad { @@ -34,8 +34,16 @@ impl Renderable for DrawRoad { } fn get_outline(&self, map: &Map) -> Polygon { - // TODO need PolyLine->boundary - map.get_r(self.id).get_thick_polygon().unwrap() + let (pl, width) = map.get_r(self.id).get_thick_polyline().unwrap(); + pl.to_thick_boundary(width, OUTLINE_THICKNESS) + .unwrap_or_else(|| map.get_r(self.id).get_thick_polygon().unwrap()) + } + + fn contains_pt(&self, pt: Pt2D, map: &Map) -> bool { + map.get_r(self.id) + .get_thick_polygon() + .unwrap() + .contains_pt(pt) } fn get_zorder(&self) -> isize { diff --git a/editor/src/ui.rs b/editor/src/ui.rs index 309f9c7cf9..54ba58519d 100644 --- a/editor/src/ui.rs +++ b/editor/src/ui.rs @@ -121,7 +121,7 @@ impl UI { if self.primary.current_selection == Some(obj.get_id()) { g.draw_polygon( - self.cs.get_def("selected", Color::YELLOW.alpha(0.4)), + self.cs.get_def("selected", Color::RED.alpha(0.7)), &obj.get_outline(&ctx.map), ); } diff --git a/geom/src/polyline.rs b/geom/src/polyline.rs index aa7dafd9bb..e4bc2b3a4a 100644 --- a/geom/src/polyline.rs +++ b/geom/src/polyline.rs @@ -59,6 +59,27 @@ impl PolyLine { pl.make_polygons(thickness) } + pub fn to_thick_boundary( + &self, + self_width: Distance, + boundary_width: Distance, + ) -> Option { + assert!(self_width > boundary_width); + if self.length() <= boundary_width { + return None; + } + let slice = self.exact_slice(boundary_width / 2.0, self.length() - boundary_width / 2.0); + let mut side1 = slice.shift_with_sharp_angles((self_width - boundary_width) / 2.0); + let mut side2 = slice.shift_with_sharp_angles(-(self_width - boundary_width) / 2.0); + side2.reverse(); + side1.extend(side2); + side1.push(side1[0]); + Some(PolyLine::make_polygons_for_boundary( + Pt2D::approx_dedupe(side1, EPSILON_DIST), + boundary_width, + )) + } + pub fn reversed(&self) -> PolyLine { let mut pts = self.pts.clone(); pts.reverse(); diff --git a/map_model/src/road.rs b/map_model/src/road.rs index 0ce74ec541..18e00a75a7 100644 --- a/map_model/src/road.rs +++ b/map_model/src/road.rs @@ -291,21 +291,26 @@ impl Road { search.iter().find(|(_, t)| lt == *t).map(|(id, _)| *id) } - pub fn get_thick_polygon(&self) -> Warn { + pub fn get_thick_polyline(&self) -> Warn<(PolyLine, Distance)> { let width_right = (self.children_forwards.len() as f64) * LANE_THICKNESS; let width_left = (self.children_backwards.len() as f64) * LANE_THICKNESS; let total_width = width_right + width_left; if width_right >= width_left { self.center_pts .shift_right((width_right - width_left) / 2.0) - .map(|pl| pl.make_polygons(total_width)) + .map(|pl| (pl, total_width)) } else { self.center_pts .shift_left((width_left - width_right) / 2.0) - .map(|pl| pl.make_polygons(total_width)) + .map(|pl| (pl, total_width)) } } + pub fn get_thick_polygon(&self) -> Warn { + self.get_thick_polyline() + .map(|(pl, width)| pl.make_polygons(width)) + } + // Also returns width. The polyline points the correct direction. None if no lanes that // direction. pub fn get_center_for_side(&self, fwds: bool) -> Option> {