more synthetic fixes: redo mouseover when model changes, avoid panic

with 0-len new road line, fix reused IDs, fix contains_pt for
some precomputed polygons
This commit is contained in:
Dustin Carlino 2019-09-12 09:54:12 -07:00
parent 92a4f304f7
commit 353d89cd25
5 changed files with 37 additions and 16 deletions

View File

@ -51,9 +51,6 @@ impl<ID: ObjectID> World<ID> {
} }
pub fn handle_mouseover(&mut self, ctx: &EventCtx) { pub fn handle_mouseover(&mut self, ctx: &EventCtx) {
if !ctx.redo_mouseover() {
return;
}
self.current_selection = None; self.current_selection = None;
let cursor = if let Some(pt) = ctx.canvas.get_cursor_in_map_space() { let cursor = if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
@ -111,6 +108,6 @@ impl<ID: ObjectID> World<ID> {
pub fn delete_obj(&mut self, id: ID) { pub fn delete_obj(&mut self, id: ID) {
let obj = self.objects.remove(&id).unwrap(); let obj = self.objects.remove(&id).unwrap();
self.quadtree.remove(obj.quadtree_id); self.quadtree.remove(obj.quadtree_id).unwrap();
} }
} }

View File

@ -76,7 +76,9 @@ impl GUI for UI {
ref mut osd, ref mut osd,
} => { } => {
{ {
self.world.handle_mouseover(ctx); if ctx.redo_mouseover() {
self.world.handle_mouseover(ctx);
}
let len = self.hints.hints.len(); let len = self.hints.hints.len();
let mut txt = Text::prompt("Fix Map Geometry"); let mut txt = Text::prompt("Fix Map Geometry");
@ -284,7 +286,9 @@ impl GUI for UI {
} }
State::BanTurnsBetween { from, ref mut osd } => { State::BanTurnsBetween { from, ref mut osd } => {
ctx.canvas.handle_event(ctx.input); ctx.canvas.handle_event(ctx.input);
self.world.handle_mouseover(ctx); if ctx.redo_mouseover() {
self.world.handle_mouseover(ctx);
}
if ctx.input.key_pressed(Key::Escape, "cancel") { if ctx.input.key_pressed(Key::Escape, "cancel") {
self.state = State::main(ctx); self.state = State::main(ctx);

View File

@ -148,7 +148,7 @@ impl Polygon {
top_left.offset(width, height), top_left.offset(width, height),
top_left.offset(Distance::ZERO, height), top_left.offset(Distance::ZERO, height),
], ],
indices: vec![0, 1, 2, 2, 3, 0], indices: vec![0, 1, 2, 0, 2, 3],
} }
} }

View File

@ -38,6 +38,8 @@ pub struct Model {
intersections: BTreeMap<StableIntersectionID, Intersection>, intersections: BTreeMap<StableIntersectionID, Intersection>,
roads: BTreeMap<StableRoadID, Road>, roads: BTreeMap<StableRoadID, Road>,
buildings: BTreeMap<BuildingID, Building>, buildings: BTreeMap<BuildingID, Building>,
// Never reuse IDs, and don't worry about being sequential
id_counter: usize,
world: World<ID>, world: World<ID>,
} }
@ -137,6 +139,7 @@ impl Model {
intersections: BTreeMap::new(), intersections: BTreeMap::new(),
roads: BTreeMap::new(), roads: BTreeMap::new(),
buildings: BTreeMap::new(), buildings: BTreeMap::new(),
id_counter: 0,
world: World::new(&Bounds::new()), world: World::new(&Bounds::new()),
} }
} }
@ -263,6 +266,7 @@ impl Model {
roads: HashSet::new(), roads: HashSet::new(),
}; };
m.intersections.insert(*id, i); m.intersections.insert(*id, i);
m.id_counter = m.id_counter.max(id.0 + 1);
} }
for (id, r) in &data.roads { for (id, r) in &data.roads {
@ -279,6 +283,7 @@ impl Model {
); );
m.intersections.get_mut(&i1).unwrap().roads.insert(*id); m.intersections.get_mut(&i1).unwrap().roads.insert(*id);
m.intersections.get_mut(&i2).unwrap().roads.insert(*id); m.intersections.get_mut(&i2).unwrap().roads.insert(*id);
m.id_counter = m.id_counter.max(id.0 + 1);
} }
if !exclude_bldgs { if !exclude_bldgs {
@ -288,6 +293,7 @@ impl Model {
center: b.polygon.center(), center: b.polygon.center(),
}; };
m.buildings.insert(idx, b); m.buildings.insert(idx, b);
m.id_counter = m.id_counter.max(idx + 1);
} }
} }
@ -328,7 +334,8 @@ impl Model {
} }
pub fn create_i(&mut self, center: Pt2D, prerender: &Prerender) { pub fn create_i(&mut self, center: Pt2D, prerender: &Prerender) {
let id = StableIntersectionID(self.intersections.len()); let id = StableIntersectionID(self.id_counter);
self.id_counter += 1;
self.intersections.insert( self.intersections.insert(
id, id,
Intersection { Intersection {
@ -431,7 +438,8 @@ impl Model {
println!("Road already exists"); println!("Road already exists");
return; return;
} }
let id = StableRoadID(self.roads.len()); let id = StableRoadID(self.id_counter);
self.id_counter += 1;
self.roads.insert( self.roads.insert(
id, id,
Road { Road {
@ -514,7 +522,8 @@ impl Model {
} }
pub fn create_b(&mut self, center: Pt2D, prerender: &Prerender) { pub fn create_b(&mut self, center: Pt2D, prerender: &Prerender) {
let id = self.buildings.len(); let id = self.id_counter;
self.id_counter += 1;
self.buildings.insert( self.buildings.insert(
id, id,
Building { Building {

View File

@ -40,7 +40,9 @@ impl UI {
impl GUI for UI { impl GUI for UI {
fn event(&mut self, ctx: &mut EventCtx) -> EventLoopMode { fn event(&mut self, ctx: &mut EventCtx) -> EventLoopMode {
ctx.canvas.handle_event(ctx.input); ctx.canvas.handle_event(ctx.input);
self.model.handle_mouseover(ctx); if ctx.redo_mouseover() {
self.model.handle_mouseover(ctx);
}
let cursor = { let cursor = {
if let Some(c) = ctx.canvas.get_cursor_in_map_space() { if let Some(c) = ctx.canvas.get_cursor_in_map_space() {
@ -99,10 +101,12 @@ impl GUI for UI {
State::CreatingRoad(i1) => { State::CreatingRoad(i1) => {
if ctx.input.key_pressed(Key::Escape, "stop defining road") { if ctx.input.key_pressed(Key::Escape, "stop defining road") {
self.state = State::Viewing; self.state = State::Viewing;
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_road(i1, i2, ctx.prerender);
self.state = State::Viewing; self.state = State::Viewing;
self.model.handle_mouseover(ctx);
} }
} }
} }
@ -113,8 +117,10 @@ impl GUI for UI {
{ {
self.model.edit_lanes(id, s, ctx.prerender); self.model.edit_lanes(id, s, ctx.prerender);
self.state = State::Viewing; self.state = State::Viewing;
self.model.handle_mouseover(ctx);
} else if wizard.aborted() { } else if wizard.aborted() {
self.state = State::Viewing; self.state = State::Viewing;
self.model.handle_mouseover(ctx);
} }
} }
State::SavingModel(ref mut wizard) => { State::SavingModel(ref mut wizard) => {
@ -134,6 +140,7 @@ impl GUI for UI {
self.state = State::CreatingRoad(i); self.state = State::CreatingRoad(i);
} else if ctx.input.key_pressed(Key::Backspace, "delete intersection") { } else if ctx.input.key_pressed(Key::Backspace, "delete intersection") {
self.model.remove_i(i); self.model.remove_i(i);
self.model.handle_mouseover(ctx);
} else if ctx.input.key_pressed(Key::T, "toggle intersection type") { } else if ctx.input.key_pressed(Key::T, "toggle intersection type") {
self.model.toggle_i_type(i, ctx.prerender); self.model.toggle_i_type(i, ctx.prerender);
} else if ctx.input.key_pressed(Key::L, "label intersection") { } else if ctx.input.key_pressed(Key::L, "label intersection") {
@ -144,6 +151,7 @@ impl GUI for UI {
self.state = State::MovingBuilding(b); self.state = State::MovingBuilding(b);
} else if ctx.input.key_pressed(Key::Backspace, "delete building") { } else if ctx.input.key_pressed(Key::Backspace, "delete building") {
self.model.remove_b(b); self.model.remove_b(b);
self.model.handle_mouseover(ctx);
} else if ctx.input.key_pressed(Key::L, "label building") { } else if ctx.input.key_pressed(Key::L, "label building") {
self.state = State::LabelingBuilding(b, Wizard::new()); self.state = State::LabelingBuilding(b, Wizard::new());
} }
@ -153,6 +161,7 @@ impl GUI for UI {
.key_pressed(Key::Backspace, &format!("delete road {}", r)) .key_pressed(Key::Backspace, &format!("delete road {}", r))
{ {
self.model.remove_road(r); self.model.remove_road(r);
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());
} else if ctx.input.key_pressed(Key::S, "swap lanes") { } else if ctx.input.key_pressed(Key::S, "swap lanes") {
@ -170,8 +179,12 @@ impl GUI for UI {
} }
} else if ctx.input.key_pressed(Key::I, "create intersection") { } else if ctx.input.key_pressed(Key::I, "create intersection") {
self.model.create_i(cursor, ctx.prerender); self.model.create_i(cursor, ctx.prerender);
self.model.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") { } else if ctx.input.key_pressed(Key::B, "create building") {
self.model.create_b(cursor, ctx.prerender); self.model.create_b(cursor, ctx.prerender);
self.model.handle_mouseover(ctx);
} }
} }
} }
@ -187,11 +200,9 @@ impl GUI for UI {
match self.state { match self.state {
State::CreatingRoad(i1) => { State::CreatingRoad(i1) => {
if let Some(cursor) = g.get_cursor_in_map_space() { if let Some(cursor) = g.get_cursor_in_map_space() {
g.draw_line( if let Some(l) = Line::maybe_new(self.model.get_i_center(i1), cursor) {
Color::GREEN, g.draw_line(Color::GREEN, Distance::meters(5.0), &l);
Distance::meters(5.0), }
&Line::new(self.model.get_i_center(i1), cursor),
);
} }
} }
State::LabelingBuilding(_, ref wizard) State::LabelingBuilding(_, ref wizard)