Draw building driveways per zoomed-in road, rather than as one map-wide batch. Otherwise, every single edit to road width needs to redraw all driveways. This doesn't scale with larger maps!

When zoomed in on just a building but not its road, this means the driveway won't show up until the road is in view. Acceptable trade-off.
This commit is contained in:
Dustin Carlino 2021-07-15 18:05:04 -07:00
parent 628f75f9fb
commit b3ce53aa5f
11 changed files with 73 additions and 97 deletions

View File

@ -141,7 +141,6 @@ impl App {
if layers.show_buildings {
g.redraw(&draw_map.draw_all_buildings);
g.redraw(&draw_map.draw_all_building_outlines);
// Not the building driveways
}
// Still show some shape selection when zoomed out.
@ -193,9 +192,6 @@ impl App {
match obj.get_id() {
ID::Building(_) => {
if !drawn_all_buildings {
if opts.show_building_driveways {
g.redraw(&draw_map.draw_all_building_driveways);
}
g.redraw(&draw_map.draw_all_buildings);
g.redraw(&draw_map.draw_all_building_outlines);
drawn_all_buildings = true;

View File

@ -720,13 +720,6 @@ pub fn apply_map_edits(ctx: &mut EventCtx, app: &mut App, edits: MapEdits) {
.recreate_intersection(i, &app.primary.map);
}
timer.start("resnap buildings");
if effects.resnapped_buildings {
app.primary
.draw_map
.recreate_building_driveways(ctx, &app.primary.map, &app.cs, &app.opts);
}
timer.stop("resnap buildings");
for pl in effects.changed_parking_lots {
app.primary.draw_map.get_pl(pl).clear_rendering();
}

View File

@ -35,6 +35,8 @@ pub struct Options {
pub min_zoom_for_detail: f64,
/// Draw buildings in different perspectives
pub camera_angle: CameraAngle,
/// Draw building driveways.
pub show_building_driveways: bool,
/// When making a screen recording, enable this option to hide some UI elements
pub minimal_controls: bool,
@ -80,6 +82,7 @@ impl Options {
toggle_day_night_colors: false,
min_zoom_for_detail: 4.0,
camera_angle: CameraAngle::TopDown,
show_building_driveways: true,
time_increment: Duration::minutes(10),
dont_draw_time_warp: false,
@ -361,7 +364,6 @@ impl<A: AppLike> State<A> for OptionsPanel {
ctx.loading_screen("rerendering buildings", |ctx, timer| {
let mut all_buildings = GeomBatch::new();
let mut all_building_outlines = GeomBatch::new();
let mut all_building_driveways = GeomBatch::new();
timer
.start_iter("rendering buildings", app.map().all_buildings().len());
for b in app.map().all_buildings() {
@ -375,18 +377,13 @@ impl<A: AppLike> State<A> for OptionsPanel {
&mut all_buildings,
&mut all_building_outlines,
);
DrawBuilding::draw_driveway(
b,
app.map(),
app.cs(),
app.opts(),
&mut all_building_driveways,
);
}
for r in &mut app.mut_draw_map().roads {
r.clear_rendering();
}
timer.start("upload geometry");
app.mut_draw_map().draw_all_buildings = all_buildings.upload(ctx);
app.mut_draw_map().draw_all_building_driveways =
all_building_driveways.upload(ctx);
app.mut_draw_map().draw_all_building_outlines =
all_building_outlines.upload(ctx);
timer.stop("upload geometry");
@ -411,7 +408,11 @@ impl<A: AppLike> State<A> for OptionsPanel {
}
}
// Be careful -- there are some options not exposed by this panel, but per app.
let show_building_driveways = opts.show_building_driveways;
opts.show_building_driveways = true;
abstio::write_json(abstio::path_player("settings.json"), &opts);
opts.show_building_driveways = show_building_driveways;
*app.mut_opts() = opts;
return widgetry::Transition::Pop;

View File

@ -1,10 +1,10 @@
use std::cell::RefCell;
use geom::{Angle, Distance, Line, Polygon, Pt2D, Ring};
use map_model::{Building, BuildingID, LaneType, Map, OffstreetParking, NORMAL_LANE_THICKNESS};
use map_model::{Building, BuildingID, Map, OffstreetParking};
use widgetry::{Color, Drawable, EventCtx, GeomBatch, GfxCtx, Line, Text};
use crate::colors::{ColorScheme, ColorSchemeChoice};
use crate::colors::ColorScheme;
use crate::options::{CameraAngle, Options};
use crate::render::{DrawOptions, Renderable, OUTLINE_THICKNESS};
use crate::{AppLike, ID};
@ -195,41 +195,6 @@ impl DrawBuilding {
}
}
pub fn draw_driveway(
bldg: &Building,
map: &Map,
cs: &ColorScheme,
opts: &Options,
batch: &mut GeomBatch,
) {
if opts.camera_angle != CameraAngle::Abstract {
// Trim the driveway away from the sidewalk's center line, so that it doesn't overlap.
// For now, this cleanup is visual; it doesn't belong in the map_model
// layer.
let orig_pl = &bldg.driveway_geom;
let driveway = orig_pl
.slice(
Distance::ZERO,
orig_pl.length() - map.get_l(bldg.sidewalk()).width / 2.0,
)
.map(|(pl, _)| pl)
.unwrap_or_else(|_| orig_pl.clone());
if driveway.length() > Distance::meters(0.1) {
batch.push(
if opts.color_scheme == ColorSchemeChoice::NightMode {
Color::hex("#4B4B4B")
} else {
cs.zoomed_road_surface(
LaneType::Sidewalk,
map.get_parent(bldg.sidewalk()).get_rank(),
)
},
driveway.make_polygons(NORMAL_LANE_THICKNESS),
);
}
}
}
pub fn clear_rendering(&mut self) {
*self.label.borrow_mut() = None;
}

View File

@ -32,7 +32,6 @@ pub struct DrawMap {
pub boundary_polygon: Drawable,
pub draw_all_unzoomed_roads_and_intersections: Drawable,
pub draw_all_buildings: Drawable,
pub draw_all_building_driveways: Drawable,
pub draw_all_building_outlines: Drawable,
pub draw_all_unzoomed_parking_lots: Drawable,
pub draw_all_areas: Drawable,
@ -84,7 +83,6 @@ impl DrawMap {
let mut buildings: Vec<DrawBuilding> = Vec::new();
let mut all_buildings = GeomBatch::new();
let mut all_building_outlines = GeomBatch::new();
let mut all_building_driveways = GeomBatch::new();
timer.start_iter("make DrawBuildings", map.all_buildings().len());
for b in map.all_buildings() {
timer.next();
@ -97,11 +95,9 @@ impl DrawMap {
&mut all_buildings,
&mut all_building_outlines,
));
DrawBuilding::draw_driveway(b, map, cs, opts, &mut all_building_driveways);
}
timer.start("upload all buildings");
let draw_all_buildings = all_buildings.upload(ctx);
let draw_all_building_driveways = all_building_driveways.upload(ctx);
let draw_all_building_outlines = all_building_outlines.upload(ctx);
timer.stop("upload all buildings");
@ -196,7 +192,6 @@ impl DrawMap {
boundary_polygon,
draw_all_unzoomed_roads_and_intersections,
draw_all_buildings,
draw_all_building_driveways,
draw_all_building_outlines,
draw_all_unzoomed_parking_lots,
draw_all_areas,
@ -436,7 +431,6 @@ impl DrawMap {
let mut bldgs_batch = GeomBatch::new();
let mut outlines_batch = GeomBatch::new();
let mut driveways_batch = GeomBatch::new();
for b in map.all_buildings() {
DrawBuilding::new(
ctx,
@ -447,9 +441,7 @@ impl DrawMap {
&mut bldgs_batch,
&mut outlines_batch,
);
DrawBuilding::draw_driveway(b, map, cs, app.opts(), &mut driveways_batch);
}
batch.append(driveways_batch);
batch.append(bldgs_batch);
batch.append(outlines_batch);
@ -505,20 +497,6 @@ impl DrawMap {
self.roads[road.id.0] = draw;
}
pub fn recreate_building_driveways(
&mut self,
ctx: &mut EventCtx,
map: &Map,
cs: &ColorScheme,
opts: &Options,
) {
let mut batch = GeomBatch::new();
for b in map.all_buildings() {
DrawBuilding::draw_driveway(b, map, cs, opts, &mut batch);
}
self.draw_all_building_driveways = ctx.upload(batch);
}
pub fn free_memory(&mut self) {
// Clear the lazily evaluated zoomed-in details
for r in &mut self.roads {

View File

@ -78,8 +78,6 @@ pub struct DrawOptions {
pub suppress_traffic_signal_details: Vec<IntersectionID>,
/// Label every building.
pub label_buildings: bool,
/// Draw building driveways.
pub show_building_driveways: bool,
}
impl DrawOptions {
@ -88,7 +86,6 @@ impl DrawOptions {
DrawOptions {
suppress_traffic_signal_details: Vec::new(),
label_buildings: false,
show_building_driveways: true,
}
}
}

View File

@ -1,9 +1,12 @@
use std::cell::RefCell;
use abstutil::MultiMap;
use geom::{Distance, Polygon, Pt2D};
use map_model::{LaneType, Map, Road, RoadID};
use widgetry::{Drawable, GeomBatch, GfxCtx, Line, Prerender, Text};
use map_model::{Building, BuildingID, LaneType, Map, Road, RoadID, NORMAL_LANE_THICKNESS};
use widgetry::{Color, Drawable, GeomBatch, GfxCtx, Line, Prerender, Text};
use crate::colors::ColorSchemeChoice;
use crate::options::CameraAngle;
use crate::render::{DrawOptions, Renderable};
use crate::{AppLike, ID};
@ -104,6 +107,13 @@ impl DrawRoad {
}
}
// Driveways of connected buildings. These are grouped by road to limit what has to be
// recalculated when road edits cause buildings to re-snap.
// TODO Cache road_to_buildings in app... or Map?
for b in road_to_buildings(app).get(self.id) {
draw_building_driveway(app, app.map().get_b(*b), &mut batch);
}
batch
}
@ -138,3 +148,42 @@ impl Renderable for DrawRoad {
self.zorder
}
}
fn draw_building_driveway(app: &dyn AppLike, bldg: &Building, batch: &mut GeomBatch) {
if app.opts().camera_angle == CameraAngle::Abstract || !app.opts().show_building_driveways {
return;
}
// Trim the driveway away from the sidewalk's center line, so that it doesn't overlap. For
// now, this cleanup is visual; it doesn't belong in the map_model layer.
let orig_pl = &bldg.driveway_geom;
let driveway = orig_pl
.slice(
Distance::ZERO,
orig_pl.length() - app.map().get_l(bldg.sidewalk()).width / 2.0,
)
.map(|(pl, _)| pl)
.unwrap_or_else(|_| orig_pl.clone());
if driveway.length() > Distance::meters(0.1) {
batch.push(
if app.opts().color_scheme == ColorSchemeChoice::NightMode {
Color::hex("#4B4B4B")
} else {
app.cs().zoomed_road_surface(
LaneType::Sidewalk,
app.map().get_parent(bldg.sidewalk()).get_rank(),
)
},
driveway.make_polygons(NORMAL_LANE_THICKNESS),
);
}
}
fn road_to_buildings(app: &dyn AppLike) -> MultiMap<RoadID, BuildingID> {
let map = app.map();
let mut mapping = MultiMap::new();
for b in map.all_buildings() {
mapping.insert(map.get_l(b.sidewalk_pos.lane()).parent, b.id);
}
mapping
}

View File

@ -132,9 +132,6 @@ impl<T: 'static> SimpleApp<T> {
match obj.get_id() {
ID::Building(_) => {
if !drawn_all_buildings {
if opts.show_building_driveways {
g.redraw(&self.draw_map.draw_all_building_driveways);
}
g.redraw(&self.draw_map.draw_all_buildings);
g.redraw(&self.draw_map.draw_all_building_outlines);
drawn_all_buildings = true;

View File

@ -139,7 +139,6 @@ pub struct EditEffects {
pub changed_intersections: BTreeSet<IntersectionID>,
pub added_turns: BTreeSet<TurnID>,
pub deleted_turns: BTreeSet<TurnID>,
pub resnapped_buildings: bool,
pub changed_parking_lots: BTreeSet<ParkingLotID>,
}
@ -516,10 +515,9 @@ fn modify_lanes(map: &mut Map, r: RoadID, lanes_ltr: Vec<LaneSpec>, effects: &mu
for b in map.all_buildings() {
if modified_lanes.contains(&b.sidewalk()) {
recalc_buildings.push(b.id);
effects.resnapped_buildings = true;
}
}
fix_building_driveways(map, recalc_buildings);
fix_building_driveways(map, recalc_buildings, effects);
// Same for parking lots
let mut recalc_parking_lots = Vec::new();
@ -617,7 +615,7 @@ fn recalculate_intersection_polygon(
}
/// Recalculate the driveways of some buildings after map edits.
fn fix_building_driveways(map: &mut Map, input: Vec<BuildingID>) {
fn fix_building_driveways(map: &mut Map, input: Vec<BuildingID>, effects: &mut EditEffects) {
// TODO Copying from make/buildings.rs
let mut center_per_bldg: BTreeMap<BuildingID, HashablePt2D> = BTreeMap::new();
let mut query: HashSet<HashablePt2D> = HashSet::new();
@ -649,6 +647,10 @@ fn fix_building_driveways(map: &mut Map, input: Vec<BuildingID>) {
let b = &mut map.buildings[id.0];
b.sidewalk_pos = sidewalk_pos;
b.driveway_geom = driveway_geom.to_polyline();
// We may need to redraw the road that now has this building snapped to it
effects
.changed_roads
.insert(map.get_l(sidewalk_pos.lane()).parent);
}
None => {
// TODO Not sure what to do here yet.
@ -776,7 +778,6 @@ impl Map {
changed_intersections: BTreeSet::new(),
added_turns: BTreeSet::new(),
deleted_turns: BTreeSet::new(),
resnapped_buildings: false,
changed_parking_lots: BTreeSet::new(),
};

View File

@ -11,7 +11,8 @@ pub fn main() {
}
pub fn run(mut settings: Settings) {
let options = map_gui::options::Options::load_or_default();
let mut options = map_gui::options::Options::load_or_default();
options.show_building_driveways = false;
settings = settings
.read_svg(Box::new(abstio::slurp_bytes))
.canvas_settings(options.canvas_settings.clone());

View File

@ -366,9 +366,7 @@ impl State<App> for Viewer {
if g.canvas.cam_zoom < app.opts.min_zoom_for_detail {
app.draw_unzoomed(g);
} else {
let mut opts = DrawOptions::new();
opts.show_building_driveways = false;
app.draw_zoomed(g, opts);
app.draw_zoomed(g, DrawOptions::new());
}
self.top_panel.draw(g);