easy way to select and delete big chunk of stuff in synthetic

This commit is contained in:
Dustin Carlino 2019-09-16 10:16:56 -07:00
parent 0543cb015e
commit 4a12cdd36a
5 changed files with 137 additions and 7 deletions

View File

@ -77,5 +77,6 @@ Street strives to set the accessibility bar high, by being a fun, engaging game.
I'm a one-person team. If you want to bring this to your city or if you're I'm a one-person team. If you want to bring this to your city or if you're
skilled in user experience design, traffic simulation, data visualization, or skilled in user experience design, traffic simulation, data visualization, or
civic/government outreach, please contact Dustin Carlino at civic/government outreach, please contact Dustin Carlino at
<dabreegster@gmail.com>. I also welcome any contributions on <dabreegster@gmail.com> or post at
[Patreon](https://www.patreon.com/abstreet). [r/abstreet](https://www.reddit.com/r/abstreet/). I also welcome any
contributions on [Patreon](https://www.patreon.com/abstreet).

View File

@ -0,0 +1,56 @@
// TODO None of this works yet.
use crate::{Color, MultiKey, ScreenDims, ScreenPt, Text};
// TODO This is the concrete state at some time. When something changes, how do we recalculate it?
struct Panel {
rows: Vec<Row>,
top_left: ScreenPt,
bg: Option<Color>,
}
struct Row {
total_width: f64,
total_height: f64,
bg: Option<Color>,
items: Vec<Item>,
}
enum Item {
Text(Text),
Button(Option<MultiKey>, String),
HideIcon,
UnhideIcon,
Spacer(ScreenDims),
}
fn modal_menu_ex() {
let mut panel = Panel {
rows: vec![
Row {
bg: Some(Color::BLUE),
items: vec![Item::Text(Text::from(Line("title").fg(Color::WHITE))), Item::HideIcon],
},
Row {
bg: None,
items: vec![Item::Button(hotkey(Key::L), "load thingy")],
}
Row {
bg: None,
items: vec![Item::Button(hotkey(Key::M), "manage thingy")],
}
Row {
bg: Some(Color::BLACK),
// TODO really the spacer should expand to full width...
items: vec![Item::Spacer(ScreenDims::new(1.0, 30.0))],
},
Row {
bg: None,
items: vec![Item::Button(None, "complex action")],
}
],
bg: Some(Color::grey(0.5)),
};
}
// TODO how could scrolling be built on top of this?

View File

@ -152,6 +152,26 @@ impl Polygon {
} }
} }
pub fn rectangle_two_corners(pt1: Pt2D, pt2: Pt2D) -> Option<Polygon> {
if Pt2D::new(pt1.x(), 0.0).epsilon_eq(Pt2D::new(pt2.x(), 0.0))
|| Pt2D::new(0.0, pt1.y()).epsilon_eq(Pt2D::new(0.0, pt2.y()))
{
return None;
}
let (x1, width) = if pt1.x() < pt2.x() {
(pt1.x(), Distance::meters(pt2.x() - pt1.x()))
} else {
(pt2.x(), Distance::meters(pt1.x() - pt2.x()))
};
let (y1, height) = if pt1.y() < pt2.y() {
(pt1.y(), Distance::meters(pt2.y() - pt1.y()))
} else {
(pt2.y(), Distance::meters(pt1.y() - pt2.y()))
};
Some(Polygon::rectangle_topleft(Pt2D::new(x1, y1), width, height))
}
pub fn union(self, other: Polygon) -> Polygon { pub fn union(self, other: Polygon) -> Polygon {
let mut points = self.points; let mut points = self.points;
let mut indices = self.indices; let mut indices = self.indices;

View File

@ -1,7 +1,7 @@
mod model; mod model;
use ezgui::{Color, EventCtx, EventLoopMode, GfxCtx, Key, Line, Text, Wizard, GUI}; use ezgui::{Color, EventCtx, EventLoopMode, GfxCtx, Key, Line, Text, Wizard, GUI};
use geom::{Distance, Line}; use geom::{Distance, Line, Polygon, Pt2D};
use map_model::raw_data::{StableIntersectionID, StableRoadID}; use map_model::raw_data::{StableIntersectionID, StableRoadID};
use model::{BuildingID, Direction, Model, ID}; use model::{BuildingID, Direction, Model, ID};
use std::{env, process}; use std::{env, process};
@ -22,6 +22,8 @@ enum State {
CreatingRoad(StableIntersectionID), CreatingRoad(StableIntersectionID),
EditingRoad(StableRoadID, Wizard), EditingRoad(StableRoadID, Wizard),
SavingModel(Wizard), SavingModel(Wizard),
// bool is if key is down
SelectingRectangle(Pt2D, Pt2D, bool),
} }
impl UI { impl UI {
@ -102,7 +104,7 @@ impl GUI for UI {
self.model.handle_mouseover(ctx); self.model.handle_mouseover(ctx);
} else if let Some(ID::Intersection(i2)) = self.model.get_selection() { } else if let Some(ID::Intersection(i2)) = self.model.get_selection() {
if i1 != i2 && ctx.input.key_pressed(Key::R, "finalize road") { if i1 != i2 && ctx.input.key_pressed(Key::R, "finalize road") {
self.model.create_road(i1, i2, ctx.prerender); self.model.create_r(i1, i2, ctx.prerender);
self.state = State::Viewing; self.state = State::Viewing;
self.model.handle_mouseover(ctx); self.model.handle_mouseover(ctx);
} }
@ -159,7 +161,7 @@ impl GUI for UI {
.input .input
.key_pressed(Key::Backspace, &format!("delete road {}", r)) .key_pressed(Key::Backspace, &format!("delete road {}", r))
{ {
self.model.remove_road(r); self.model.remove_r(r);
self.model.handle_mouseover(ctx); self.model.handle_mouseover(ctx);
} else if ctx.input.key_pressed(Key::E, "edit lanes") { } else if ctx.input.key_pressed(Key::E, "edit lanes") {
self.state = State::EditingRoad(r, Wizard::new()); self.state = State::EditingRoad(r, Wizard::new());
@ -185,6 +187,33 @@ impl GUI for UI {
} else if cursor.is_some() && ctx.input.key_pressed(Key::B, "create building") { } else if cursor.is_some() && ctx.input.key_pressed(Key::B, "create building") {
self.model.create_b(cursor.unwrap(), ctx.prerender); self.model.create_b(cursor.unwrap(), ctx.prerender);
self.model.handle_mouseover(ctx); self.model.handle_mouseover(ctx);
} else if cursor.is_some() && ctx.input.key_pressed(Key::LeftShift, "select area") {
self.state = State::SelectingRectangle(cursor.unwrap(), cursor.unwrap(), true);
}
}
State::SelectingRectangle(pt1, ref mut pt2, ref mut keydown) => {
if ctx.input.key_pressed(Key::LeftShift, "select area") {
*keydown = true;
} else if ctx.input.key_released(Key::LeftShift) {
*keydown = false;
}
if *keydown {
if let Some(cursor) = ctx.canvas.get_cursor_in_map_space() {
*pt2 = cursor;
}
}
if ctx.input.key_pressed(Key::Escape, "stop selecting area") {
self.state = State::Viewing;
} else if ctx
.input
.key_pressed(Key::Backspace, "delete everything area")
{
if let Some(rect) = Polygon::rectangle_two_corners(pt1, *pt2) {
self.model.delete_everything_inside(rect);
self.model.handle_mouseover(ctx);
}
self.state = State::Viewing;
} }
} }
} }
@ -232,6 +261,11 @@ impl GUI for UI {
} }
} }
State::MovingIntersection(_) | State::MovingBuilding(_) => {} State::MovingIntersection(_) | State::MovingBuilding(_) => {}
State::SelectingRectangle(pt1, pt2, _) => {
if let Some(rect) = Polygon::rectangle_two_corners(pt1, pt2) {
g.draw_polygon(Color::BLUE.alpha(0.5), &rect);
}
}
}; };
g.draw_blocking_text(&self.osd, ezgui::BOTTOM_LEFT); g.draw_blocking_text(&self.osd, ezgui::BOTTOM_LEFT);

View File

@ -361,6 +361,25 @@ impl Model {
self.road_added(id, prerender); self.road_added(id, prerender);
} }
} }
pub fn delete_everything_inside(&mut self, area: Polygon) {
for id in self.buildings.keys().cloned().collect::<Vec<_>>() {
if area.contains_pt(self.buildings[&id].center) {
self.remove_b(id);
}
}
for id in self.roads.keys().cloned().collect::<Vec<_>>() {
if area.contains_pt(self.intersections[&self.roads[&id].i1].center) {
self.remove_r(id);
}
}
for id in self.intersections.keys().cloned().collect::<Vec<_>>() {
if area.contains_pt(self.intersections[&id].center) {
self.remove_i(id);
}
}
}
} }
impl Model { impl Model {
@ -475,7 +494,7 @@ impl Model {
} }
} }
pub fn create_road( pub fn create_r(
&mut self, &mut self,
i1: StableIntersectionID, i1: StableIntersectionID,
i2: StableIntersectionID, i2: StableIntersectionID,
@ -561,7 +580,7 @@ impl Model {
} }
} }
pub fn remove_road(&mut self, id: StableRoadID) { pub fn remove_r(&mut self, id: StableRoadID) {
self.road_deleted(id); self.road_deleted(id);
let r = self.roads.remove(&id).unwrap(); let r = self.roads.remove(&id).unwrap();