diff --git a/docs/TODO_ux.md b/docs/TODO_ux.md index a509d29a94..a6fb149997 100644 --- a/docs/TODO_ux.md +++ b/docs/TODO_ux.md @@ -13,7 +13,6 @@ - click cycle diagram to edit duration - lane edit validity -- make it easy to see current lane when changing it - revamp stop sign editor - toggle rewind mode diff --git a/editor/src/render/area.rs b/editor/src/render/area.rs index 03693b3dc0..f5de00d824 100644 --- a/editor/src/render/area.rs +++ b/editor/src/render/area.rs @@ -2,7 +2,7 @@ use crate::colors::ColorScheme; use crate::objects::{DrawCtx, ID}; use crate::render::{RenderOptions, Renderable}; use ezgui::{Color, GfxCtx}; -use geom::{Bounds, Polygon, Pt2D}; +use geom::Polygon; use map_model::{Area, AreaID, AreaType, Map}; pub struct DrawArea { @@ -31,11 +31,7 @@ impl Renderable for DrawArea { } } - fn get_bounds(&self, map: &Map) -> Bounds { - map.get_a(self.id).polygon.get_bounds() - } - - fn contains_pt(&self, pt: Pt2D, map: &Map) -> bool { - map.get_a(self.id).polygon.contains_pt(pt) + fn get_outline(&self, map: &Map) -> Polygon { + map.get_a(self.id).polygon.clone() } } diff --git a/editor/src/render/bike.rs b/editor/src/render/bike.rs index d73c91e304..968bff69ad 100644 --- a/editor/src/render/bike.rs +++ b/editor/src/render/bike.rs @@ -2,7 +2,7 @@ use crate::colors::ColorScheme; use crate::objects::{DrawCtx, ID}; use crate::render::{RenderOptions, Renderable}; use ezgui::{Color, Drawable, GfxCtx, Prerender}; -use geom::{Bounds, Distance, Polygon, Pt2D}; +use geom::{Distance, Polygon}; use map_model::Map; use sim::{CarID, CarStatus, DrawCarInput}; @@ -74,12 +74,8 @@ impl Renderable for DrawBike { } } - fn get_bounds(&self, _: &Map) -> Bounds { - self.polygon.get_bounds() - } - - fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool { - self.polygon.contains_pt(pt) + fn get_outline(&self, _: &Map) -> Polygon { + self.polygon.clone() } fn get_zorder(&self) -> isize { diff --git a/editor/src/render/building.rs b/editor/src/render/building.rs index 3c915977ee..9d856212fb 100644 --- a/editor/src/render/building.rs +++ b/editor/src/render/building.rs @@ -2,7 +2,7 @@ use crate::colors::ColorScheme; use crate::objects::{DrawCtx, ID}; use crate::render::{RenderOptions, Renderable}; use ezgui::{Color, GfxCtx}; -use geom::{Bounds, Distance, Line, Polygon, Pt2D}; +use geom::{Distance, Line, Polygon}; use map_model::{Building, BuildingID, BuildingType, Map, LANE_THICKNESS}; pub struct DrawBuilding { @@ -57,13 +57,7 @@ impl Renderable for DrawBuilding { } } - fn get_bounds(&self, map: &Map) -> Bounds { - // This is only used for mouseover, not rendering, now that buildings are drawn in a single - // batch. So don't include the front path. - map.get_b(self.id).polygon.get_bounds() - } - - fn contains_pt(&self, pt: Pt2D, map: &Map) -> bool { - map.get_b(self.id).polygon.contains_pt(pt) + fn get_outline(&self, map: &Map) -> Polygon { + map.get_b(self.id).polygon.clone() } } diff --git a/editor/src/render/bus_stop.rs b/editor/src/render/bus_stop.rs index 13356bf01c..333e5af5fd 100644 --- a/editor/src/render/bus_stop.rs +++ b/editor/src/render/bus_stop.rs @@ -2,7 +2,7 @@ use crate::colors::ColorScheme; use crate::objects::{DrawCtx, ID}; use crate::render::{RenderOptions, Renderable}; use ezgui::{Color, Drawable, GfxCtx, Prerender}; -use geom::{Bounds, Distance, Polygon, Pt2D}; +use geom::{Distance, Polygon}; use map_model::{BusStop, BusStopID, Map, LANE_THICKNESS}; pub struct DrawBusStop { @@ -53,12 +53,8 @@ impl Renderable for DrawBusStop { } } - fn get_bounds(&self, _: &Map) -> Bounds { - self.polygon.get_bounds() - } - - fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool { - self.polygon.contains_pt(pt) + fn get_outline(&self, _: &Map) -> Polygon { + self.polygon.clone() } fn get_zorder(&self) -> isize { diff --git a/editor/src/render/car.rs b/editor/src/render/car.rs index a37edfbb66..c5b9f06712 100644 --- a/editor/src/render/car.rs +++ b/editor/src/render/car.rs @@ -2,7 +2,7 @@ use crate::colors::ColorScheme; use crate::objects::{DrawCtx, ID}; use crate::render::{RenderOptions, Renderable}; use ezgui::{Color, Drawable, GfxCtx, Prerender}; -use geom::{Angle, Bounds, Circle, Distance, PolyLine, Polygon, Pt2D}; +use geom::{Angle, Circle, Distance, PolyLine, Polygon, Pt2D}; use map_model::{Map, TurnType}; use sim::{CarID, CarStatus, DrawCarInput}; use std; @@ -199,12 +199,8 @@ impl Renderable for DrawCar { } } - fn get_bounds(&self, _: &Map) -> Bounds { - self.body_polygon.get_bounds() - } - - fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool { - self.body_polygon.contains_pt(pt) + fn get_outline(&self, _: &Map) -> Polygon { + self.body_polygon.clone() } fn get_zorder(&self) -> isize { diff --git a/editor/src/render/extra_shape.rs b/editor/src/render/extra_shape.rs index 24778711ed..c27c0a88f9 100644 --- a/editor/src/render/extra_shape.rs +++ b/editor/src/render/extra_shape.rs @@ -1,7 +1,7 @@ use crate::objects::{DrawCtx, ID}; use crate::render::{RenderOptions, Renderable, EXTRA_SHAPE_POINT_RADIUS, EXTRA_SHAPE_THICKNESS}; use ezgui::{Color, GfxCtx}; -use geom::{Bounds, Circle, Distance, FindClosest, GPSBounds, PolyLine, Polygon, Pt2D}; +use geom::{Circle, Distance, FindClosest, GPSBounds, PolyLine, Polygon, Pt2D}; use kml::ExtraShape; use map_model::{DirectedRoadID, Map, LANE_THICKNESS}; use std::collections::BTreeMap; @@ -16,14 +16,9 @@ impl fmt::Display for ExtraShapeID { } } -enum Shape { - Polygon(Polygon), - Circle(Circle), -} - pub struct DrawExtraShape { pub id: ExtraShapeID, - shape: Shape, + polygon: Polygon, pub attributes: BTreeMap, pub road: Option, } @@ -43,7 +38,7 @@ impl DrawExtraShape { if pts.len() == 1 { Some(DrawExtraShape { id, - shape: Shape::Circle(Circle::new(pts[0], EXTRA_SHAPE_POINT_RADIUS)), + polygon: Circle::new(pts[0], EXTRA_SHAPE_POINT_RADIUS).to_polygon(), attributes: s.attributes, road: None, }) @@ -59,7 +54,7 @@ impl DrawExtraShape { .map(|(r, _)| r); Some(DrawExtraShape { id, - shape: Shape::Polygon(pl.make_polygons(width)), + polygon: pl.make_polygons(width), attributes: s.attributes, road, }) @@ -67,10 +62,7 @@ impl DrawExtraShape { } pub fn center(&self) -> Pt2D { - match self.shape { - Shape::Polygon(ref p) => Pt2D::center(&p.points()), - Shape::Circle(ref c) => c.center, - } + self.polygon.center() } } @@ -83,24 +75,11 @@ impl Renderable for DrawExtraShape { let color = opts .color .unwrap_or_else(|| ctx.cs.get_def("extra shape", Color::CYAN)); - match self.shape { - Shape::Polygon(ref p) => g.draw_polygon(color, &p), - Shape::Circle(ref c) => g.draw_circle(color, c), - } + g.draw_polygon(color, &self.polygon); } - fn get_bounds(&self, _: &Map) -> Bounds { - match self.shape { - Shape::Polygon(ref p) => p.get_bounds(), - Shape::Circle(ref c) => c.get_bounds(), - } - } - - fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool { - match self.shape { - Shape::Polygon(ref p) => p.contains_pt(pt), - Shape::Circle(ref c) => c.contains_pt(pt), - } + fn get_outline(&self, _: &Map) -> Polygon { + self.polygon.clone() } } diff --git a/editor/src/render/intersection.rs b/editor/src/render/intersection.rs index 543ab5ee52..17d5450fff 100644 --- a/editor/src/render/intersection.rs +++ b/editor/src/render/intersection.rs @@ -3,7 +3,7 @@ use crate::objects::{DrawCtx, ID}; use crate::render::{DrawCrosswalk, DrawTurn, RenderOptions, Renderable}; use abstutil::Timer; use ezgui::{Color, Drawable, GfxCtx, Prerender, ScreenPt, Text}; -use geom::{Bounds, Circle, Distance, Duration, Line, Polygon, Pt2D}; +use geom::{Circle, Distance, Duration, Line, Polygon, Pt2D}; use map_model::{ Cycle, Intersection, IntersectionID, IntersectionType, Map, Road, TurnPriority, TurnType, LANE_THICKNESS, @@ -103,12 +103,8 @@ impl Renderable for DrawIntersection { } } - fn get_bounds(&self, _: &Map) -> Bounds { - self.polygon.get_bounds() - } - - fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool { - self.polygon.contains_pt(pt) + fn get_outline(&self, _: &Map) -> Polygon { + self.polygon.clone() } fn get_zorder(&self) -> isize { diff --git a/editor/src/render/lane.rs b/editor/src/render/lane.rs index 4ae2e27004..fed74aae58 100644 --- a/editor/src/render/lane.rs +++ b/editor/src/render/lane.rs @@ -3,7 +3,7 @@ use crate::objects::{DrawCtx, ID}; use crate::render::{RenderOptions, Renderable, BIG_ARROW_THICKNESS, PARCEL_BOUNDARY_THICKNESS}; use abstutil::Timer; use ezgui::{Color, Drawable, GfxCtx, Prerender}; -use geom::{Bounds, Circle, Distance, Line, Polygon, Pt2D}; +use geom::{Circle, Distance, Line, Polygon}; use map_model::{ IntersectionType, Lane, LaneID, LaneType, Map, Road, Turn, LANE_THICKNESS, PARKING_SPOT_LENGTH, }; @@ -101,12 +101,8 @@ impl Renderable for DrawLane { } } - fn get_bounds(&self, _: &Map) -> Bounds { - self.polygon.get_bounds() - } - - fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool { - self.polygon.contains_pt(pt) + fn get_outline(&self, _: &Map) -> Polygon { + self.polygon.clone() } fn get_zorder(&self) -> isize { diff --git a/editor/src/render/map.rs b/editor/src/render/map.rs index a19cafe7b7..aea7817f44 100644 --- a/editor/src/render/map.rs +++ b/editor/src/render/map.rs @@ -201,26 +201,26 @@ impl DrawMap { let mut quadtree = QuadTree::default(map.get_bounds().as_bbox()); // TODO use iter chain if everything was boxed as a renderable... for obj in &roads { - quadtree.insert_with_box(obj.get_id(), obj.get_bounds(map).as_bbox()); + quadtree.insert_with_box(obj.get_id(), obj.get_outline(map).get_bounds().as_bbox()); } for obj in &lanes { - quadtree.insert_with_box(obj.get_id(), obj.get_bounds(map).as_bbox()); + quadtree.insert_with_box(obj.get_id(), obj.get_outline(map).get_bounds().as_bbox()); } for obj in &intersections { - quadtree.insert_with_box(obj.get_id(), obj.get_bounds(map).as_bbox()); + quadtree.insert_with_box(obj.get_id(), obj.get_outline(map).get_bounds().as_bbox()); } for obj in &buildings { - quadtree.insert_with_box(obj.get_id(), obj.get_bounds(map).as_bbox()); + quadtree.insert_with_box(obj.get_id(), obj.get_outline(map).get_bounds().as_bbox()); } for obj in &parcels { - quadtree.insert_with_box(obj.get_id(), obj.get_bounds(map).as_bbox()); + quadtree.insert_with_box(obj.get_id(), obj.get_outline(map).get_bounds().as_bbox()); } for obj in &extra_shapes { - quadtree.insert_with_box(obj.get_id(), obj.get_bounds(map).as_bbox()); + quadtree.insert_with_box(obj.get_id(), obj.get_outline(map).get_bounds().as_bbox()); } // Don't put BusStops in the quadtree for obj in &areas { - quadtree.insert_with_box(obj.get_id(), obj.get_bounds(map).as_bbox()); + quadtree.insert_with_box(obj.get_id(), obj.get_outline(map).get_bounds().as_bbox()); } timer.stop("create quadtree"); diff --git a/editor/src/render/mod.rs b/editor/src/render/mod.rs index 6cb9f0c71f..62bc865472 100644 --- a/editor/src/render/mod.rs +++ b/editor/src/render/mod.rs @@ -25,7 +25,7 @@ pub use crate::render::pedestrian::DrawPedestrian; pub use crate::render::road::DrawRoad; pub use crate::render::turn::{DrawCrosswalk, DrawTurn}; use ezgui::{Color, GfxCtx, Prerender}; -use geom::{Bounds, Distance, Pt2D}; +use geom::{Distance, Polygon}; use map_model::Map; use sim::{DrawCarInput, VehicleType}; @@ -46,12 +46,12 @@ pub const CROSSWALK_LINE_THICKNESS: Distance = Distance::const_meters(0.25); pub trait Renderable { fn get_id(&self) -> ID; fn draw(&self, g: &mut GfxCtx, opts: RenderOptions, ctx: &DrawCtx); - fn get_bounds(&self, map: &Map) -> Bounds; - fn contains_pt(&self, pt: Pt2D, map: &Map) -> bool; // Higher z-ordered objects are drawn later. Default to low so roads at -1 don't vanish. fn get_zorder(&self) -> isize { -5 } + // This is called at most once per frame; don't worry about cloning and lack of prerendering. + fn get_outline(&self, map: &Map) -> Polygon; } pub struct RenderOptions { @@ -59,7 +59,6 @@ pub struct RenderOptions { pub color: Option, // TODO This should be accessible through ctx... pub debug_mode: bool, - pub is_selected: bool, } pub fn draw_vehicle( diff --git a/editor/src/render/parcel.rs b/editor/src/render/parcel.rs index c8971d2bd5..4a3800e6b5 100644 --- a/editor/src/render/parcel.rs +++ b/editor/src/render/parcel.rs @@ -2,7 +2,7 @@ use crate::colors::ColorScheme; use crate::objects::{DrawCtx, ID}; use crate::render::{RenderOptions, Renderable, PARCEL_BOUNDARY_THICKNESS}; use ezgui::{Color, GfxCtx}; -use geom::{Bounds, PolyLine, Polygon, Pt2D}; +use geom::{PolyLine, Polygon}; use map_model::{Map, Parcel, ParcelID}; const COLORS: [Color; 14] = [ @@ -69,11 +69,7 @@ impl Renderable for DrawParcel { } } - fn get_bounds(&self, _: &Map) -> Bounds { - self.fill_polygon.get_bounds() - } - - fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool { - self.fill_polygon.contains_pt(pt) + fn get_outline(&self, _: &Map) -> Polygon { + self.fill_polygon.clone() } } diff --git a/editor/src/render/pedestrian.rs b/editor/src/render/pedestrian.rs index 6a503e8b5b..fc5be14766 100644 --- a/editor/src/render/pedestrian.rs +++ b/editor/src/render/pedestrian.rs @@ -2,7 +2,7 @@ use crate::colors::ColorScheme; use crate::objects::{DrawCtx, ID}; use crate::render::{RenderOptions, Renderable}; use ezgui::{Color, Drawable, GfxCtx, Prerender}; -use geom::{Bounds, Circle, Distance, Line, Pt2D}; +use geom::{Circle, Distance, Line, Polygon}; use map_model::Map; use sim::{DrawPedestrianInput, PedestrianID}; @@ -78,12 +78,8 @@ impl Renderable for DrawPedestrian { } } - fn get_bounds(&self, _: &Map) -> Bounds { - self.circle.get_bounds() - } - - fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool { - self.circle.contains_pt(pt) + fn get_outline(&self, _: &Map) -> Polygon { + self.circle.to_polygon() } fn get_zorder(&self) -> isize { diff --git a/editor/src/render/road.rs b/editor/src/render/road.rs index ffdd37985e..bc14f300be 100644 --- a/editor/src/render/road.rs +++ b/editor/src/render/road.rs @@ -2,13 +2,11 @@ use crate::colors::ColorScheme; use crate::objects::{DrawCtx, ID}; use crate::render::{RenderOptions, Renderable, BIG_ARROW_THICKNESS}; use ezgui::{Color, Drawable, GfxCtx, Prerender}; -use geom::{Bounds, Pt2D}; +use geom::Polygon; use map_model::{Map, Road, RoadID}; pub struct DrawRoad { pub id: RoadID, - // TODO don't even store Bounds - bounds: Bounds, zorder: isize, draw_center_line: Drawable, @@ -18,8 +16,6 @@ impl DrawRoad { pub fn new(r: &Road, cs: &ColorScheme, prerender: &Prerender) -> DrawRoad { DrawRoad { id: r.id, - // TODO Urgh, gotta pass timer in - bounds: r.get_thick_polygon().unwrap().get_bounds(), zorder: r.get_zorder(), draw_center_line: prerender.upload(vec![( cs.get_def("road center line", Color::YELLOW), @@ -38,13 +34,8 @@ impl Renderable for DrawRoad { g.redraw(&self.draw_center_line); } - fn get_bounds(&self, _: &Map) -> Bounds { - self.bounds.clone() - } - - // Can't select these - fn contains_pt(&self, _: Pt2D, _: &Map) -> bool { - false + fn get_outline(&self, map: &Map) -> Polygon { + map.get_r(self.id).get_thick_polygon().unwrap() } fn get_zorder(&self) -> isize { diff --git a/editor/src/render/turn.rs b/editor/src/render/turn.rs index bea675d96b..38ef101fb0 100644 --- a/editor/src/render/turn.rs +++ b/editor/src/render/turn.rs @@ -5,7 +5,7 @@ use crate::render::{ TURN_ICON_ARROW_LENGTH, TURN_ICON_ARROW_THICKNESS, }; use ezgui::{Color, Drawable, GfxCtx, Prerender}; -use geom::{Bounds, Circle, Distance, Line, Pt2D}; +use geom::{Circle, Distance, Line, Polygon}; use map_model::{Map, Turn, TurnID, LANE_THICKNESS}; pub struct DrawTurn { @@ -80,11 +80,7 @@ impl Renderable for DrawTurn { } g.draw_circle( - if opts.is_selected { - ctx.cs.get("selected") - } else { - ctx.cs.get_def("turn icon circle", Color::grey(0.3)) - }, + ctx.cs.get_def("turn icon circle", Color::grey(0.3)), &self.icon_circle, ); @@ -96,12 +92,8 @@ impl Renderable for DrawTurn { ); } - fn get_bounds(&self, _: &Map) -> Bounds { - self.icon_circle.get_bounds() - } - - fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool { - self.icon_circle.contains_pt(pt) + fn get_outline(&self, _: &Map) -> Polygon { + self.icon_circle.to_polygon() } } diff --git a/editor/src/state.rs b/editor/src/state.rs index f7d5ea54de..43e0c4051c 100644 --- a/editor/src/state.rs +++ b/editor/src/state.rs @@ -106,15 +106,6 @@ impl DefaultUIState { } pub fn color_obj(&self, id: ID, ctx: &DrawCtx) -> Option { - match id { - ID::Turn(_) => {} - _ => { - if Some(id) == self.primary.current_selection { - return Some(ctx.cs.get_def("selected", Color::BLUE)); - } - } - }; - if let Some(ref plugin) = self.primary_plugins.search { if let Some(c) = plugin.color_for(id, ctx) { return Some(c); diff --git a/editor/src/ui.rs b/editor/src/ui.rs index db614ff328..71478b3937 100644 --- a/editor/src/ui.rs +++ b/editor/src/ui.rs @@ -8,7 +8,7 @@ use ezgui::{ Canvas, Color, EventCtx, EventLoopMode, Folder, GfxCtx, Key, ModalMenu, Prerender, Text, TopMenu, BOTTOM_LEFT, GUI, }; -use geom::{Bounds, Circle, Distance}; +use geom::{Bounds, Circle, Distance, Polygon}; use kml; use map_model::{BuildingID, LaneID, Traversable}; use serde_derive::{Deserialize, Serialize}; @@ -296,14 +296,9 @@ impl GUI for UI { // Still show area selection when zoomed out. if state.primary.current_flags.debug_areas { if let Some(ID::Area(id)) = state.primary.current_selection { - state.primary.draw_map.get_a(id).draw( - g, - RenderOptions { - color: state.color_obj(ID::Area(id), &ctx), - debug_mode: state.layers.debug_mode, - is_selected: true, - }, - &ctx, + g.draw_polygon( + state.cs.get("selected"), + &fill_to_boundary_polygon(ctx.draw_map.get_a(id).get_outline(&ctx.map)), ); } } @@ -343,10 +338,16 @@ impl GUI for UI { let opts = RenderOptions { color: state.color_obj(obj.get_id(), &ctx), debug_mode: state.layers.debug_mode, - is_selected: state.primary.current_selection == Some(obj.get_id()), }; obj.draw(g, opts, &ctx); + if state.primary.current_selection == Some(obj.get_id()) { + g.draw_polygon( + state.cs.get_def("selected", Color::YELLOW.alpha(0.4)), + &fill_to_boundary_polygon(obj.get_outline(&ctx.map)), + ); + } + if screencap && sample_intersection.is_none() { if let ID::Intersection(id) = obj.get_id() { sample_intersection = Some(format!("_i{}", id.0)); @@ -459,7 +460,10 @@ impl UI { // Thick roads are only shown when unzoomed, when we don't mouseover at all. ID::Road(_) => {} _ => { - if obj.contains_pt(pt, &self.state.get_state().primary.map) { + if obj + .get_outline(&self.state.get_state().primary.map) + .contains_pt(pt) + { return Some(obj.get_id()); } } @@ -596,3 +600,9 @@ pub struct EditorState { pub cam_y: f64, pub cam_zoom: f64, } + +fn fill_to_boundary_polygon(poly: Polygon) -> Polygon { + // TODO This looks awful for lanes, oops. + //PolyLine::make_polygons_for_boundary(poly.points().clone(), Distance::meters(0.5)) + poly +}