mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-27 00:12:55 +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
|
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).
|
||||||
|
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 {
|
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;
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user