draw the selected thing as an additional transparent polygon. makes changing lane types reasonable.

This commit is contained in:
Dustin Carlino 2019-03-28 12:54:04 +09:00
parent b0035cde08
commit 9744c22955
17 changed files with 73 additions and 150 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<String, String>,
pub road: Option<DirectedRoadID>,
}
@ -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()
}
}

View File

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

View File

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

View File

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

View File

@ -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<Color>,
// TODO This should be accessible through ctx...
pub debug_mode: bool,
pub is_selected: bool,
}
pub fn draw_vehicle(

View File

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

View File

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

View File

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

View File

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

View File

@ -106,15 +106,6 @@ impl DefaultUIState {
}
pub fn color_obj(&self, id: ID, ctx: &DrawCtx) -> Option<Color> {
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);

View File

@ -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<S: UIState> GUI<RenderingHints> for UI<S> {
// 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<S: UIState> GUI<RenderingHints> for UI<S> {
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<S: UIState> UI<S> {
// 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
}