mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 15:33:44 +03:00
refactoring map_editor, by letting main reach into RawMap and World directly for simple stuff
This commit is contained in:
parent
3fd73af32b
commit
71096cf122
@ -7,7 +7,7 @@ use ezgui::{
|
||||
};
|
||||
use geom::{Distance, Line, Polygon, Pt2D};
|
||||
use map_model::raw::{RestrictionType, StableBuildingID, StableIntersectionID, StableRoadID};
|
||||
use map_model::LANE_THICKNESS;
|
||||
use map_model::{osm, LANE_THICKNESS};
|
||||
use model::{Direction, Model, ID};
|
||||
use std::process;
|
||||
|
||||
@ -62,7 +62,7 @@ impl GUI for UI {
|
||||
fn event(&mut self, ctx: &mut EventCtx) -> EventLoopMode {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
if ctx.redo_mouseover() {
|
||||
self.model.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
|
||||
match self.state {
|
||||
@ -93,7 +93,11 @@ impl GUI for UI {
|
||||
State::LabelingBuilding(id, ref mut wizard) => {
|
||||
if let Some(label) = wizard.wrap(ctx).input_string_prefilled(
|
||||
"Label the building",
|
||||
self.model.get_b_label(id).unwrap_or_else(String::new),
|
||||
self.model.map.buildings[&id]
|
||||
.osm_tags
|
||||
.get(osm::LABEL)
|
||||
.cloned()
|
||||
.unwrap_or_else(String::new),
|
||||
) {
|
||||
self.model.set_b_label(id, label, ctx.prerender);
|
||||
self.state = State::Viewing;
|
||||
@ -115,7 +119,10 @@ impl GUI for UI {
|
||||
State::LabelingIntersection(id, ref mut wizard) => {
|
||||
if let Some(label) = wizard.wrap(ctx).input_string_prefilled(
|
||||
"Label the intersection",
|
||||
self.model.get_i_label(id).unwrap_or_else(String::new),
|
||||
self.model.map.intersections[&id]
|
||||
.label
|
||||
.clone()
|
||||
.unwrap_or_else(String::new),
|
||||
) {
|
||||
self.model.set_i_label(id, label, ctx.prerender);
|
||||
self.state = State::Viewing;
|
||||
@ -126,30 +133,42 @@ impl GUI for UI {
|
||||
State::CreatingRoad(i1) => {
|
||||
if ctx.input.key_pressed(Key::Escape, "stop defining road") {
|
||||
self.state = State::Viewing;
|
||||
self.model.handle_mouseover(ctx);
|
||||
} else if let Some(ID::Intersection(i2)) = self.model.get_selection() {
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
} else if let Some(ID::Intersection(i2)) = self.model.world.get_selection() {
|
||||
if i1 != i2 && ctx.input.key_pressed(Key::R, "finalize road") {
|
||||
self.model.create_r(i1, i2, ctx.prerender);
|
||||
self.state = State::Viewing;
|
||||
self.model.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
State::EditingLanes(id, ref mut wizard) => {
|
||||
if let Some(s) = wizard
|
||||
.wrap(ctx)
|
||||
.input_string_prefilled("Specify the lanes", self.model.get_road_spec(id))
|
||||
{
|
||||
if let Some(s) = wizard.wrap(ctx).input_string_prefilled(
|
||||
"Specify the lanes",
|
||||
self.model.map.roads[&id].get_spec().to_string(),
|
||||
) {
|
||||
self.model.edit_lanes(id, s, ctx.prerender);
|
||||
self.state = State::Viewing;
|
||||
self.model.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
} else if wizard.aborted() {
|
||||
self.state = State::Viewing;
|
||||
self.model.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
}
|
||||
State::EditingRoadAttribs(id, ref mut wizard) => {
|
||||
let (orig_name, orig_speed) = self.model.get_r_name_and_speed(id);
|
||||
let (orig_name, orig_speed) = {
|
||||
let r = &self.model.map.roads[&id];
|
||||
(
|
||||
r.osm_tags
|
||||
.get(osm::NAME)
|
||||
.cloned()
|
||||
.unwrap_or_else(String::new),
|
||||
r.osm_tags
|
||||
.get(osm::MAXSPEED)
|
||||
.cloned()
|
||||
.unwrap_or_else(String::new),
|
||||
)
|
||||
};
|
||||
|
||||
let mut wiz = wizard.wrap(ctx);
|
||||
let mut done = false;
|
||||
@ -161,7 +180,7 @@ impl GUI for UI {
|
||||
}
|
||||
if done || wizard.aborted() {
|
||||
self.state = State::Viewing;
|
||||
self.model.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
}
|
||||
State::SavingModel(ref mut wizard) => {
|
||||
@ -175,7 +194,7 @@ impl GUI for UI {
|
||||
}
|
||||
State::Viewing => {
|
||||
let cursor = ctx.canvas.get_cursor_in_map_space();
|
||||
match self.model.get_selection() {
|
||||
match self.model.world.get_selection() {
|
||||
Some(ID::Intersection(i)) => {
|
||||
if ctx.input.key_pressed(Key::LeftControl, "move intersection") {
|
||||
self.state = State::MovingIntersection(i);
|
||||
@ -186,7 +205,7 @@ impl GUI for UI {
|
||||
.key_pressed(Key::Backspace, &format!("delete {}", i))
|
||||
{
|
||||
self.model.delete_i(i);
|
||||
self.model.handle_mouseover(ctx);
|
||||
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") {
|
||||
@ -207,7 +226,7 @@ impl GUI for UI {
|
||||
.key_pressed(Key::Backspace, &format!("delete {}", b))
|
||||
{
|
||||
self.model.delete_b(b);
|
||||
self.model.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
} else if ctx.input.key_pressed(Key::L, "label building") {
|
||||
self.state = State::LabelingBuilding(b, Wizard::new());
|
||||
}
|
||||
@ -218,24 +237,24 @@ impl GUI for UI {
|
||||
.key_pressed(Key::Backspace, &format!("delete {}", r))
|
||||
{
|
||||
self.model.delete_r(r);
|
||||
self.model.handle_mouseover(ctx);
|
||||
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.handle_mouseover(ctx);
|
||||
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.handle_mouseover(ctx);
|
||||
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.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
} else if ctx
|
||||
.input
|
||||
.key_pressed(Key::R, "create turn restriction from here")
|
||||
@ -248,7 +267,7 @@ impl GUI for UI {
|
||||
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.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
}
|
||||
Some(ID::TurnRestriction(from, to, idx)) => {
|
||||
@ -257,7 +276,7 @@ impl GUI for UI {
|
||||
.key_pressed(Key::Backspace, "delete turn restriction")
|
||||
{
|
||||
self.model.delete_tr(from, to, idx, ctx.prerender);
|
||||
self.model.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
@ -274,14 +293,14 @@ impl GUI for UI {
|
||||
} else if ctx.input.key_pressed(Key::I, "create intersection") {
|
||||
if let Some(pt) = cursor {
|
||||
self.model.create_i(pt, ctx.prerender);
|
||||
self.model.handle_mouseover(ctx);
|
||||
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.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
} else if ctx.input.key_pressed(Key::LeftShift, "select area") {
|
||||
if let Some(pt) = cursor {
|
||||
@ -317,7 +336,7 @@ impl GUI for UI {
|
||||
{
|
||||
if let Some(rect) = Polygon::rectangle_two_corners(pt1, *pt2) {
|
||||
self.model.delete_everything_inside(rect, ctx.prerender);
|
||||
self.model.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
self.state = State::Viewing;
|
||||
}
|
||||
@ -328,13 +347,13 @@ impl GUI for UI {
|
||||
.key_pressed(Key::Escape, "stop defining turn restriction")
|
||||
{
|
||||
self.state = State::Viewing;
|
||||
self.model.handle_mouseover(ctx);
|
||||
} else if let Some(ID::Lane(to, _, _)) = self.model.get_selection() {
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
} else if let Some(ID::Lane(to, _, _)) = self.model.world.get_selection() {
|
||||
if ctx
|
||||
.input
|
||||
.key_pressed(Key::R, "create turn restriction to here")
|
||||
{
|
||||
if self.model.can_add_tr(from, to) {
|
||||
if self.model.map.can_add_turn_restriction(from, to) {
|
||||
self.state = State::CreatingTurnRestrictionPt2(from, to, Wizard::new());
|
||||
} else {
|
||||
println!("These roads aren't connected");
|
||||
@ -356,10 +375,10 @@ impl GUI for UI {
|
||||
{
|
||||
self.model.add_tr(from, restriction, to, ctx.prerender);
|
||||
self.state = State::Viewing;
|
||||
self.model.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
} else if wizard.aborted() {
|
||||
self.state = State::Viewing;
|
||||
self.model.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
}
|
||||
State::PreviewIntersection(_, _, ref mut show_tooltip) => {
|
||||
@ -374,7 +393,7 @@ impl GUI for UI {
|
||||
.key_pressed(Key::P, "stop previewing intersection")
|
||||
{
|
||||
self.state = State::Viewing;
|
||||
self.model.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
}
|
||||
State::EnteringWarp(ref mut wizard) => {
|
||||
@ -383,7 +402,8 @@ impl GUI for UI {
|
||||
if let Ok(num) = usize::from_str_radix(&line[1..line.len()], 10) {
|
||||
if &line[0..=0] == "i" {
|
||||
let id = StableIntersectionID(num);
|
||||
ctx.canvas.center_on_map_pt(self.model.get_i_center(id));
|
||||
ctx.canvas
|
||||
.center_on_map_pt(self.model.map.intersections[&id].point);
|
||||
ok = true;
|
||||
} else if &line[0..=0] == "r" {
|
||||
let id = StableRoadID(num);
|
||||
@ -395,10 +415,10 @@ impl GUI for UI {
|
||||
println!("Sorry, don't understand {}", line);
|
||||
}
|
||||
self.state = State::Viewing;
|
||||
self.model.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
} else if wizard.aborted() {
|
||||
self.state = State::Viewing;
|
||||
self.model.handle_mouseover(ctx);
|
||||
self.model.world.handle_mouseover(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -409,12 +429,16 @@ impl GUI for UI {
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
self.model.draw(g);
|
||||
g.clear(Color::BLACK);
|
||||
g.draw_polygon(Color::rgb(242, 239, 233), &self.model.map.boundary_polygon);
|
||||
self.model.world.draw(g);
|
||||
|
||||
match self.state {
|
||||
State::CreatingRoad(i1) => {
|
||||
if let Some(cursor) = g.get_cursor_in_map_space() {
|
||||
if let Some(l) = Line::maybe_new(self.model.get_i_center(i1), cursor) {
|
||||
if let Some(l) =
|
||||
Line::maybe_new(self.model.map.intersections[&i1].point, cursor)
|
||||
{
|
||||
g.draw_line(Color::GREEN, Distance::meters(5.0), &l);
|
||||
}
|
||||
}
|
||||
@ -429,16 +453,16 @@ impl GUI for UI {
|
||||
wizard.draw(g);
|
||||
}
|
||||
State::Viewing => {
|
||||
if let Some(ID::Lane(id, _, _)) = self.model.get_selection() {
|
||||
if let Some(ID::Lane(id, _, _)) = self.model.world.get_selection() {
|
||||
let mut txt = Text::new();
|
||||
for (k, v) in self.model.get_tags(id) {
|
||||
for (k, v) in &self.model.map.roads[&id].osm_tags {
|
||||
txt.add_appended(vec![
|
||||
Line(k).fg(Color::RED),
|
||||
Line(" = "),
|
||||
Line(v).fg(Color::CYAN),
|
||||
]);
|
||||
}
|
||||
for (restriction, dst) in self.model.get_turn_restrictions(id) {
|
||||
for (restriction, dst) in &self.model.map.roads[&id].turn_restrictions {
|
||||
txt.add_appended(vec![
|
||||
Line("Restriction: "),
|
||||
Line(format!("{:?}", restriction)).fg(Color::RED),
|
||||
@ -453,10 +477,13 @@ impl GUI for UI {
|
||||
ezgui::VerticalAlignment::Top,
|
||||
),
|
||||
);
|
||||
} else if let Some(ID::Intersection(i)) = self.model.get_selection() {
|
||||
} else if let Some(ID::Intersection(i)) = self.model.world.get_selection() {
|
||||
let mut txt = Text::new();
|
||||
txt.add(Line(format!("{} is {:?}", i, self.model.get_i_orig_id(i))));
|
||||
for r in self.model.get_roads_per_i(i) {
|
||||
txt.add(Line(format!(
|
||||
"{} is {:?}",
|
||||
i, self.model.map.intersections[&i].orig_id
|
||||
)));
|
||||
for r in self.model.map.roads_per_intersection(i) {
|
||||
txt.add(Line(format!("- {}", r)));
|
||||
}
|
||||
g.draw_blocking_text(
|
||||
@ -515,7 +542,7 @@ fn preview_intersection(
|
||||
model: &Model,
|
||||
ctx: &EventCtx,
|
||||
) -> (Drawable, Vec<(Text, Pt2D)>) {
|
||||
let (intersection, roads, debug) = model.preview_i(i);
|
||||
let (intersection, roads, debug) = model.map.preview_intersection(i);
|
||||
let mut batch = GeomBatch::new();
|
||||
let mut labels = Vec::new();
|
||||
batch.push(Color::ORANGE.alpha(0.5), intersection);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use abstutil::{read_binary, Timer};
|
||||
use ezgui::world::{Object, ObjectID, World};
|
||||
use ezgui::{Color, EventCtx, GfxCtx, Line, Prerender, Text};
|
||||
use ezgui::{Color, Line, Prerender, Text};
|
||||
use geom::{Bounds, Circle, Distance, PolyLine, Polygon, Pt2D};
|
||||
use map_model::raw::{
|
||||
MapFixes, OriginalIntersection, OriginalRoad, RawBuilding, RawIntersection, RawMap, RawRoad,
|
||||
@ -19,14 +19,16 @@ const FORWARDS: Direction = true;
|
||||
const BACKWARDS: Direction = false;
|
||||
|
||||
pub struct Model {
|
||||
// map and world are pub. The main crate should use them directly for simple stuff, to avoid
|
||||
// boilerplate delegation methods. Complex changes should be proper methods on the model.
|
||||
pub map: RawMap,
|
||||
// TODO Not sure this should be pub...
|
||||
pub showing_pts: Option<StableRoadID>,
|
||||
pub world: World<ID>,
|
||||
|
||||
include_bldgs: bool,
|
||||
fixes: MapFixes,
|
||||
edit_fixes: Option<String>,
|
||||
world: World<ID>,
|
||||
}
|
||||
|
||||
// Construction
|
||||
@ -86,20 +88,6 @@ impl Model {
|
||||
|
||||
// General
|
||||
impl Model {
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
g.clear(Color::BLACK);
|
||||
g.draw_polygon(Color::rgb(242, 239, 233), &self.map.boundary_polygon);
|
||||
self.world.draw(g);
|
||||
}
|
||||
|
||||
pub fn handle_mouseover(&mut self, ctx: &EventCtx) {
|
||||
self.world.handle_mouseover(ctx);
|
||||
}
|
||||
|
||||
pub fn get_selection(&self) -> Option<ID> {
|
||||
self.world.get_selection()
|
||||
}
|
||||
|
||||
// TODO Only for truly synthetic maps...
|
||||
pub fn export(&mut self) {
|
||||
assert!(self.map.name != "");
|
||||
@ -188,7 +176,7 @@ impl Model {
|
||||
.iter()
|
||||
.any(|pt| area.contains_pt(*pt))
|
||||
{
|
||||
for (rt, to) in self.get_turn_restrictions(id).clone() {
|
||||
for (rt, to) in self.map.roads[&id].turn_restrictions.clone() {
|
||||
self.delete_tr(id, rt, to, prerender);
|
||||
}
|
||||
|
||||
@ -262,10 +250,6 @@ impl Model {
|
||||
self.intersection_added(id, prerender);
|
||||
}
|
||||
|
||||
pub fn get_i_label(&self, id: StableIntersectionID) -> Option<String> {
|
||||
self.map.intersections[&id].label.clone()
|
||||
}
|
||||
|
||||
pub fn toggle_i_type(&mut self, id: StableIntersectionID, prerender: &Prerender) {
|
||||
self.world.delete(ID::Intersection(id));
|
||||
let (it, label) = {
|
||||
@ -295,28 +279,6 @@ impl Model {
|
||||
self.map.delete_intersection(id, &mut self.fixes);
|
||||
self.world.delete(ID::Intersection(id));
|
||||
}
|
||||
|
||||
// TODO Reconsider this spammy amount of stuff, just expose RawMap to main in some readonly
|
||||
// way.
|
||||
pub fn get_i_center(&self, id: StableIntersectionID) -> Pt2D {
|
||||
self.map.intersections[&id].point
|
||||
}
|
||||
|
||||
pub fn get_i_orig_id(&self, id: StableIntersectionID) -> OriginalIntersection {
|
||||
self.map.intersections[&id].orig_id
|
||||
}
|
||||
|
||||
pub fn get_roads_per_i(&self, id: StableIntersectionID) -> Vec<StableRoadID> {
|
||||
self.map.roads_per_intersection(id)
|
||||
}
|
||||
|
||||
// (Intersection polygon, polygons for roads, labeled polygons to debug)
|
||||
pub fn preview_i(
|
||||
&self,
|
||||
id: StableIntersectionID,
|
||||
) -> (Polygon, Vec<Polygon>, Vec<(String, Polygon)>) {
|
||||
self.map.preview_intersection(id)
|
||||
}
|
||||
}
|
||||
|
||||
// Roads
|
||||
@ -491,20 +453,6 @@ impl Model {
|
||||
self.road_added(id, prerender);
|
||||
}
|
||||
|
||||
pub fn get_r_name_and_speed(&self, id: StableRoadID) -> (String, String) {
|
||||
let r = &self.map.roads[&id];
|
||||
(
|
||||
r.osm_tags
|
||||
.get(osm::NAME)
|
||||
.cloned()
|
||||
.unwrap_or_else(String::new),
|
||||
r.osm_tags
|
||||
.get(osm::MAXSPEED)
|
||||
.cloned()
|
||||
.unwrap_or_else(String::new),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn delete_r(&mut self, id: StableRoadID) {
|
||||
assert!(self.showing_pts != Some(id));
|
||||
match self.map.can_delete_road(id) {
|
||||
@ -516,14 +464,6 @@ impl Model {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_road_spec(&self, id: StableRoadID) -> String {
|
||||
self.map.roads[&id].get_spec().to_string()
|
||||
}
|
||||
|
||||
pub fn get_tags(&self, id: StableRoadID) -> &BTreeMap<String, String> {
|
||||
&self.map.roads[&id].osm_tags
|
||||
}
|
||||
|
||||
fn lanes(&self, id: StableRoadID) -> Vec<Object<ID>> {
|
||||
let r = &self.map.roads[&id];
|
||||
|
||||
@ -707,14 +647,6 @@ impl Model {
|
||||
|
||||
// Turn restrictions
|
||||
impl Model {
|
||||
pub fn get_turn_restrictions(&self, id: StableRoadID) -> &Vec<(RestrictionType, StableRoadID)> {
|
||||
&self.map.roads[&id].turn_restrictions
|
||||
}
|
||||
|
||||
pub fn can_add_tr(&self, from: StableRoadID, to: StableRoadID) -> bool {
|
||||
self.map.can_add_turn_restriction(from, to)
|
||||
}
|
||||
|
||||
pub fn add_tr(
|
||||
&mut self,
|
||||
from: StableRoadID,
|
||||
@ -801,10 +733,6 @@ impl Model {
|
||||
self.bldg_added(id, prerender);
|
||||
}
|
||||
|
||||
pub fn get_b_label(&self, id: StableBuildingID) -> Option<String> {
|
||||
self.map.buildings[&id].osm_tags.get(osm::LABEL).cloned()
|
||||
}
|
||||
|
||||
pub fn delete_b(&mut self, id: StableBuildingID) {
|
||||
self.world.delete(ID::Building(id));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user