From 0d27a11b2be627c0ed9a8b7d736a06d52e2128b6 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Wed, 2 Oct 2019 12:11:11 -0700 Subject: [PATCH] map_editor tool to quickly absorb name, speed limit, lanes of nearby road for synthetic roads --- game/src/sandbox/stats.rs | 25 +++- map_editor/src/main.rs | 291 ++++++++++++++++++++++---------------- map_editor/src/model.rs | 17 ++- 3 files changed, 197 insertions(+), 136 deletions(-) diff --git a/game/src/sandbox/stats.rs b/game/src/sandbox/stats.rs index ac167d55cc..7103edf0c4 100644 --- a/game/src/sandbox/stats.rs +++ b/game/src/sandbox/stats.rs @@ -2,7 +2,8 @@ use crate::common::ColorLegend; use crate::game::{State, Transition}; use crate::ui::UI; use ezgui::{ - ScreenPt, hotkey, Color, Drawable, EventCtx, GeomBatch, GfxCtx, Key, Line, ModalMenu, MultiText, Text, + hotkey, Color, Drawable, EventCtx, GeomBatch, GfxCtx, Key, Line, ModalMenu, MultiText, + ScreenPt, Text, }; use geom::{Distance, Duration, PolyLine, Polygon, Pt2D}; use sim::TripMode; @@ -154,21 +155,31 @@ impl ShowStats { .map(|(name, color, _)| (*name, *color)) .collect(), ); - let max_y = stats.samples.iter().map(|s| - lines.iter().map(|(_, _, getter)| getter(s)).max().unwrap() - ).max().unwrap(); + let max_y = stats + .samples + .iter() + .map(|s| lines.iter().map(|(_, _, getter)| getter(s)).max().unwrap()) + .max() + .unwrap(); // Y-axis labels for i in 0..=5 { let percent = (i as f64) / 5.0; - labels.add(Text::from(Line(((percent * (max_y as f64)) as usize).to_string())), ScreenPt::new(x1, y2 - percent * (y2 - y1))); + labels.add( + Text::from(Line(((percent * (max_y as f64)) as usize).to_string())), + ScreenPt::new(x1, y2 - percent * (y2 - y1)), + ); } // X-axis labels (currently nonlinear!) { let num_pts = stats.samples.len().min(5); for i in 0..num_pts { let percent_x = (i as f64) / ((num_pts - 1) as f64); - let t = stats.samples[(percent_x * ((stats.samples.len() - 1) as f64)) as usize].time; - labels.add(Text::from(Line(t.to_string())), ScreenPt::new(x1 + percent_x * (x2 - x1), y2)); + let t = + stats.samples[(percent_x * ((stats.samples.len() - 1) as f64)) as usize].time; + labels.add( + Text::from(Line(t.to_string())), + ScreenPt::new(x1 + percent_x * (x2 - x1), y2), + ); } } diff --git a/map_editor/src/main.rs b/map_editor/src/main.rs index 79ec52f9b8..3d22e8b8f0 100644 --- a/map_editor/src/main.rs +++ b/map_editor/src/main.rs @@ -35,6 +35,7 @@ enum State { CreatingTurnRestrictionPt2(StableRoadID, StableRoadID, Wizard), PreviewIntersection(Drawable, Vec<(Text, Pt2D)>, bool), EnteringWarp(Wizard), + StampingRoads(String, String, String), } impl UI { @@ -66,6 +67,146 @@ impl GUI for UI { } match self.state { + State::Viewing => { + let cursor = ctx.canvas.get_cursor_in_map_space(); + match self.model.world.get_selection() { + Some(ID::Intersection(i)) => { + if ctx.input.key_pressed(Key::LeftControl, "move intersection") { + self.state = State::MovingIntersection(i); + } else if ctx.input.key_pressed(Key::R, "create road") { + self.state = State::CreatingRoad(i); + } else if ctx + .input + .key_pressed(Key::Backspace, &format!("delete {}", i)) + { + self.model.delete_i(i); + self.model.world.handle_mouseover(ctx); + } else if ctx.input.key_pressed(Key::T, "toggle intersection type") { + self.model.toggle_i_type(i, ctx.prerender); + } else if ctx.input.key_pressed(Key::L, "label intersection") { + self.state = State::LabelingIntersection(i, Wizard::new()); + } else if ctx + .input + .key_pressed(Key::P, "preview intersection geometry") + { + let (draw, labels) = preview_intersection(i, &self.model, ctx); + self.state = State::PreviewIntersection(draw, labels, false); + } + } + Some(ID::Building(b)) => { + if ctx.input.key_pressed(Key::LeftControl, "move building") { + self.state = State::MovingBuilding(b); + } else if ctx + .input + .key_pressed(Key::Backspace, &format!("delete {}", b)) + { + self.model.delete_b(b); + self.model.world.handle_mouseover(ctx); + } else if ctx.input.key_pressed(Key::L, "label building") { + self.state = State::LabelingBuilding(b, Wizard::new()); + } + } + Some(ID::Lane(r, dir, _)) => { + if ctx + .input + .key_pressed(Key::Backspace, &format!("delete {}", r)) + { + self.model.delete_r(r); + self.model.world.handle_mouseover(ctx); + } else if ctx.input.key_pressed(Key::E, "edit lanes") { + self.state = State::EditingLanes(r, Wizard::new()); + } else if ctx.input.key_pressed(Key::N, "edit name/speed") { + self.state = State::EditingRoadAttribs(r, Wizard::new()); + } else if ctx.input.key_pressed(Key::S, "swap lanes") { + self.model.swap_lanes(r, ctx.prerender); + self.model.world.handle_mouseover(ctx); + } else if ctx.input.key_pressed(Key::L, "label side of the road") { + self.state = State::LabelingRoad((r, dir), Wizard::new()); + } else if self.model.showing_pts.is_none() + && ctx.input.key_pressed(Key::P, "move road points") + { + self.model.show_r_points(r, ctx.prerender); + self.model.world.handle_mouseover(ctx); + } else if ctx.input.key_pressed(Key::M, "merge road") { + self.model.merge_r(r, ctx.prerender); + self.model.world.handle_mouseover(ctx); + } else if ctx + .input + .key_pressed(Key::R, "create turn restriction from here") + { + self.state = State::CreatingTurnRestrictionPt1(r); + } else if ctx + .input + .key_pressed(Key::C, "copy road name and speed to other roads") + { + let road = &self.model.map.roads[&r]; + self.state = State::StampingRoads( + road.get_spec().to_string(), + road.osm_tags + .get(osm::NAME) + .cloned() + .unwrap_or_else(String::new), + road.osm_tags + .get(osm::MAXSPEED) + .cloned() + .unwrap_or_else(String::new), + ); + } + } + Some(ID::RoadPoint(r, idx)) => { + if ctx.input.key_pressed(Key::LeftControl, "move point") { + self.state = State::MovingRoadPoint(r, idx); + } else if ctx.input.key_pressed(Key::Backspace, "delete point") { + self.model.delete_r_pt(r, idx, ctx.prerender); + self.model.world.handle_mouseover(ctx); + } + } + Some(ID::TurnRestriction(from, to, idx)) => { + if ctx + .input + .key_pressed(Key::Backspace, "delete turn restriction") + { + self.model.delete_tr(from, to, idx, ctx.prerender); + self.model.world.handle_mouseover(ctx); + } + } + None => { + if ctx.input.unimportant_key_pressed(Key::Escape, "quit") { + process::exit(0); + } else if ctx.input.key_pressed(Key::S, "save") { + if self.model.map.name != "" { + self.model.export(); + } else { + self.state = State::SavingModel(Wizard::new()); + } + } else if ctx.input.key_pressed(Key::F, "save map fixes") { + self.model.save_fixes(); + } else if ctx.input.key_pressed(Key::I, "create intersection") { + if let Some(pt) = cursor { + self.model.create_i(pt, ctx.prerender); + self.model.world.handle_mouseover(ctx); + } + // TODO Silly bug: Mouseover doesn't actually work! I think the cursor being + // dead-center messes up the precomputed triangles. + } else if ctx.input.key_pressed(Key::B, "create building") { + if let Some(pt) = cursor { + self.model.create_b(pt, ctx.prerender); + self.model.world.handle_mouseover(ctx); + } + } else if ctx.input.key_pressed(Key::LeftShift, "select area") { + if let Some(pt) = cursor { + self.state = State::SelectingRectangle(pt, pt, true); + } + } else if self.model.showing_pts.is_some() + && ctx.input.key_pressed(Key::P, "stop moving road points") + { + self.model.stop_showing_pts(); + } else if ctx.input.key_pressed(Key::J, "warp to something") { + self.state = State::EnteringWarp(Wizard::new()); + } + } + } + } State::MovingIntersection(id) => { if let Some(cursor) = ctx.canvas.get_cursor_in_map_space() { self.model.move_i(id, cursor, ctx.prerender); @@ -192,130 +333,6 @@ impl GUI for UI { self.state = State::Viewing; } } - State::Viewing => { - let cursor = ctx.canvas.get_cursor_in_map_space(); - match self.model.world.get_selection() { - Some(ID::Intersection(i)) => { - if ctx.input.key_pressed(Key::LeftControl, "move intersection") { - self.state = State::MovingIntersection(i); - } else if ctx.input.key_pressed(Key::R, "create road") { - self.state = State::CreatingRoad(i); - } else if ctx - .input - .key_pressed(Key::Backspace, &format!("delete {}", i)) - { - self.model.delete_i(i); - self.model.world.handle_mouseover(ctx); - } else if ctx.input.key_pressed(Key::T, "toggle intersection type") { - self.model.toggle_i_type(i, ctx.prerender); - } else if ctx.input.key_pressed(Key::L, "label intersection") { - self.state = State::LabelingIntersection(i, Wizard::new()); - } else if ctx - .input - .key_pressed(Key::P, "preview intersection geometry") - { - let (draw, labels) = preview_intersection(i, &self.model, ctx); - self.state = State::PreviewIntersection(draw, labels, false); - } - } - Some(ID::Building(b)) => { - if ctx.input.key_pressed(Key::LeftControl, "move building") { - self.state = State::MovingBuilding(b); - } else if ctx - .input - .key_pressed(Key::Backspace, &format!("delete {}", b)) - { - self.model.delete_b(b); - self.model.world.handle_mouseover(ctx); - } else if ctx.input.key_pressed(Key::L, "label building") { - self.state = State::LabelingBuilding(b, Wizard::new()); - } - } - Some(ID::Lane(r, dir, _)) => { - if ctx - .input - .key_pressed(Key::Backspace, &format!("delete {}", r)) - { - self.model.delete_r(r); - self.model.world.handle_mouseover(ctx); - } else if ctx.input.key_pressed(Key::E, "edit lanes") { - self.state = State::EditingLanes(r, Wizard::new()); - } else if ctx.input.key_pressed(Key::N, "edit name/speed") { - self.state = State::EditingRoadAttribs(r, Wizard::new()); - } else if ctx.input.key_pressed(Key::S, "swap lanes") { - self.model.swap_lanes(r, ctx.prerender); - self.model.world.handle_mouseover(ctx); - } else if ctx.input.key_pressed(Key::L, "label side of the road") { - self.state = State::LabelingRoad((r, dir), Wizard::new()); - } else if self.model.showing_pts.is_none() - && ctx.input.key_pressed(Key::P, "move road points") - { - self.model.show_r_points(r, ctx.prerender); - self.model.world.handle_mouseover(ctx); - } else if ctx.input.key_pressed(Key::M, "merge road") { - self.model.merge_r(r, ctx.prerender); - self.model.world.handle_mouseover(ctx); - } else if ctx - .input - .key_pressed(Key::R, "create turn restriction from here") - { - self.state = State::CreatingTurnRestrictionPt1(r); - } - } - Some(ID::RoadPoint(r, idx)) => { - if ctx.input.key_pressed(Key::LeftControl, "move point") { - self.state = State::MovingRoadPoint(r, idx); - } else if ctx.input.key_pressed(Key::Backspace, "delete point") { - self.model.delete_r_pt(r, idx, ctx.prerender); - self.model.world.handle_mouseover(ctx); - } - } - Some(ID::TurnRestriction(from, to, idx)) => { - if ctx - .input - .key_pressed(Key::Backspace, "delete turn restriction") - { - self.model.delete_tr(from, to, idx, ctx.prerender); - self.model.world.handle_mouseover(ctx); - } - } - None => { - if ctx.input.unimportant_key_pressed(Key::Escape, "quit") { - process::exit(0); - } else if ctx.input.key_pressed(Key::S, "save") { - if self.model.map.name != "" { - self.model.export(); - } else { - self.state = State::SavingModel(Wizard::new()); - } - } else if ctx.input.key_pressed(Key::F, "save map fixes") { - self.model.save_fixes(); - } else if ctx.input.key_pressed(Key::I, "create intersection") { - if let Some(pt) = cursor { - self.model.create_i(pt, ctx.prerender); - self.model.world.handle_mouseover(ctx); - } - // TODO Silly bug: Mouseover doesn't actually work! I think the cursor being - // dead-center messes up the precomputed triangles. - } else if ctx.input.key_pressed(Key::B, "create building") { - if let Some(pt) = cursor { - self.model.create_b(pt, ctx.prerender); - self.model.world.handle_mouseover(ctx); - } - } else if ctx.input.key_pressed(Key::LeftShift, "select area") { - if let Some(pt) = cursor { - self.state = State::SelectingRectangle(pt, pt, true); - } - } else if self.model.showing_pts.is_some() - && ctx.input.key_pressed(Key::P, "stop moving road points") - { - self.model.stop_showing_pts(); - } else if ctx.input.key_pressed(Key::J, "warp to something") { - self.state = State::EnteringWarp(Wizard::new()); - } - } - } - } State::SelectingRectangle(pt1, ref mut pt2, ref mut keydown) => { if ctx.input.key_pressed(Key::LeftShift, "select area") { *keydown = true; @@ -421,6 +438,29 @@ impl GUI for UI { self.model.world.handle_mouseover(ctx); } } + State::StampingRoads(ref lanespec, ref name, ref speed) => { + if ctx + .input + .key_pressed(Key::Escape, "stop copying road metadata") + { + self.state = State::Viewing; + self.model.world.handle_mouseover(ctx); + } else if let Some(ID::Lane(id, _, _)) = self.model.world.get_selection() { + if ctx.input.key_pressed( + Key::C, + &format!("set name={}, speed={}, lanes={}", name, speed, lanespec), + ) { + self.model.set_r_name_and_speed( + id, + name.to_string(), + speed.to_string(), + ctx.prerender, + ); + self.model + .edit_lanes(id, lanespec.to_string(), ctx.prerender); + } + } + } } self.osd = Text::new(); @@ -497,7 +537,8 @@ impl GUI for UI { } State::MovingIntersection(_) | State::MovingBuilding(_) - | State::MovingRoadPoint(_, _) => {} + | State::MovingRoadPoint(_, _) + | State::StampingRoads(_, _, _) => {} State::SelectingRectangle(pt1, pt2, _) => { if let Some(rect) = Polygon::rectangle_two_corners(pt1, pt2) { g.draw_polygon(Color::BLUE.alpha(0.5), &rect); diff --git a/map_editor/src/model.rs b/map_editor/src/model.rs index 02b61434d2..3ff0ddb36b 100644 --- a/map_editor/src/model.rs +++ b/map_editor/src/model.rs @@ -478,12 +478,14 @@ impl Model { let mut result = Vec::new(); let synthetic = r.synthetic(); + let unset = + synthetic && r.osm_tags.get(osm::NAME) == Some(&"Streety McStreetFace".to_string()); let spec = r.get_spec(); let center_pts = PolyLine::new(r.center_points.clone()); for (idx, lt) in spec.fwd.iter().enumerate() { let mut obj = Object::new( ID::Lane(id, FORWARDS, idx), - Model::lt_to_color(*lt, synthetic), + Model::lt_to_color(*lt, synthetic, unset), center_pts .shift_right(LANE_THICKNESS * (0.5 + (idx as f64))) .unwrap() @@ -503,7 +505,7 @@ impl Model { for (idx, lt) in spec.back.iter().enumerate() { let mut obj = Object::new( ID::Lane(id, BACKWARDS, idx), - Model::lt_to_color(*lt, synthetic), + Model::lt_to_color(*lt, synthetic, unset), center_pts .reversed() .shift_right(LANE_THICKNESS * (0.5 + (idx as f64))) @@ -544,7 +546,7 @@ impl Model { } // Copied from render/lane.rs. :( - fn lt_to_color(lt: LaneType, synthetic: bool) -> Color { + fn lt_to_color(lt: LaneType, synthetic: bool, unset: bool) -> Color { let color = match lt { LaneType::Driving => Color::BLACK, LaneType::Bus => Color::rgb(190, 74, 76), @@ -553,7 +555,14 @@ impl Model { LaneType::Biking => Color::rgb(15, 125, 75), }; if synthetic { - color.alpha(0.5) + if unset { + match color { + Color::RGBA(_, g, b, _) => Color::rgba_f(0.9, g, b, 0.5), + _ => unreachable!(), + } + } else { + color.alpha(0.5) + } } else { color }