mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 23:43:25 +03:00
easy way to select and delete big chunk of stuff in synthetic
This commit is contained in:
parent
0543cb015e
commit
4a12cdd36a
@ -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
|
||||
skilled in user experience design, traffic simulation, data visualization, or
|
||||
civic/government outreach, please contact Dustin Carlino at
|
||||
<dabreegster@gmail.com>. I also welcome any contributions on
|
||||
[Patreon](https://www.patreon.com/abstreet).
|
||||
<dabreegster@gmail.com> or post at
|
||||
[r/abstreet](https://www.reddit.com/r/abstreet/). I also welcome any
|
||||
contributions on [Patreon](https://www.patreon.com/abstreet).
|
||||
|
56
ezgui/src/widgets/panel.rs
Normal file
56
ezgui/src/widgets/panel.rs
Normal 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?
|
@ -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 {
|
||||
let mut points = self.points;
|
||||
let mut indices = self.indices;
|
||||
|
@ -1,7 +1,7 @@
|
||||
mod model;
|
||||
|
||||
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 model::{BuildingID, Direction, Model, ID};
|
||||
use std::{env, process};
|
||||
@ -22,6 +22,8 @@ enum State {
|
||||
CreatingRoad(StableIntersectionID),
|
||||
EditingRoad(StableRoadID, Wizard),
|
||||
SavingModel(Wizard),
|
||||
// bool is if key is down
|
||||
SelectingRectangle(Pt2D, Pt2D, bool),
|
||||
}
|
||||
|
||||
impl UI {
|
||||
@ -102,7 +104,7 @@ impl GUI for UI {
|
||||
self.model.handle_mouseover(ctx);
|
||||
} else if let Some(ID::Intersection(i2)) = self.model.get_selection() {
|
||||
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.model.handle_mouseover(ctx);
|
||||
}
|
||||
@ -159,7 +161,7 @@ impl GUI for UI {
|
||||
.input
|
||||
.key_pressed(Key::Backspace, &format!("delete road {}", r))
|
||||
{
|
||||
self.model.remove_road(r);
|
||||
self.model.remove_r(r);
|
||||
self.model.handle_mouseover(ctx);
|
||||
} else if ctx.input.key_pressed(Key::E, "edit lanes") {
|
||||
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") {
|
||||
self.model.create_b(cursor.unwrap(), ctx.prerender);
|
||||
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::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);
|
||||
|
@ -361,6 +361,25 @@ impl Model {
|
||||
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 {
|
||||
@ -475,7 +494,7 @@ impl Model {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_road(
|
||||
pub fn create_r(
|
||||
&mut self,
|
||||
i1: 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);
|
||||
|
||||
let r = self.roads.remove(&id).unwrap();
|
||||
|
Loading…
Reference in New Issue
Block a user