map_editor tool to quickly absorb name, speed limit, lanes of nearby road for synthetic roads

This commit is contained in:
Dustin Carlino 2019-10-02 12:11:11 -07:00
parent 5f9cf2accd
commit 0d27a11b2b
3 changed files with 197 additions and 136 deletions

View File

@ -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),
);
}
}

View File

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

View File

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