Skip to content

Commit b36e507

Browse files
committed
Try to add missing intermediate blocks in the LTN tool automatically. [rebuild] [release] #857
The result is still quite buggy, but feels like a step forwards.
1 parent d3694af commit b36e507

3 files changed

Lines changed: 85 additions & 52 deletions

File tree

apps/ltn/src/app.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ pub struct Session {
124124
// Plan a route:
125125
pub main_road_penalty: f64,
126126
pub show_walking_cycling_routes: bool,
127+
// Select boundary:
128+
pub add_intermediate_blocks: bool,
127129

128130
// Shared in all modes
129131
pub layers: crate::components::Layers,
@@ -229,6 +231,7 @@ impl App {
229231
draw_neighbourhood_style: pages::PickAreaStyle::Simple,
230232
main_road_penalty: 1.0,
231233
show_walking_cycling_routes: false,
234+
add_intermediate_blocks: true,
232235

233236
layers: crate::components::Layers::new(ctx),
234237
manage_proposals: false,

apps/ltn/src/logic/partition.rs

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -180,19 +180,19 @@ impl Partitioning {
180180
p
181181
}
182182

183-
// TODO Explain return value
184-
pub fn transfer_block(
183+
/// Add all specified blocks to new_owner. `Ok(None)` is success. `Ok(Some(x))` is also
184+
/// success, but means the old neighbourhood of SOME block in `add_all` is now gone and
185+
/// replaced with something new. (This call shouldn't be used to remove multiple blocks at
186+
/// once, since interpreting the result is confusing!)
187+
pub fn transfer_blocks(
185188
&mut self,
186189
map: &Map,
187-
add_block: BlockID,
190+
add_all: Vec<BlockID>,
188191
new_owner: NeighbourhoodID,
189192
) -> Result<Option<NeighbourhoodID>> {
190-
let old_owner = self.block_to_neighbourhood(add_block);
191-
assert_ne!(old_owner, new_owner);
192-
193193
// Is the newly expanded neighbourhood a valid perimeter?
194194
let mut new_owner_blocks = self.neighbourhood_to_blocks(new_owner);
195-
new_owner_blocks.insert(add_block);
195+
new_owner_blocks.extend(add_all.clone());
196196
let mut new_neighbourhood_blocks = self.make_merged_blocks(map, new_owner_blocks)?;
197197
if new_neighbourhood_blocks.len() != 1 {
198198
// This happens when a hole would be created by adding this block. There are probably
@@ -201,46 +201,53 @@ impl Partitioning {
201201
}
202202
let new_neighbourhood_block = new_neighbourhood_blocks.pop().unwrap();
203203

204-
// Is the old neighbourhood, minus this block, still valid?
205-
// TODO refactor Neighbourhood to BlockIDs?
206-
let mut old_owner_blocks = self.neighbourhood_to_blocks(old_owner);
207-
old_owner_blocks.remove(&add_block);
208-
if old_owner_blocks.is_empty() {
209-
// We're deleting the old neighbourhood!
210-
self.neighbourhoods.get_mut(&new_owner).unwrap().block = new_neighbourhood_block;
211-
self.neighbourhoods.remove(&old_owner).unwrap();
212-
self.block_to_neighbourhood.insert(add_block, new_owner);
213-
// Tell the caller to recreate this SelectBoundary state, switching to the neighbourhood
214-
// we just donated to, since the old is now gone
215-
return Ok(Some(new_owner));
216-
}
204+
let old_owners: BTreeSet<NeighbourhoodID> = add_all
205+
.iter()
206+
.map(|block| self.block_to_neighbourhood[block])
207+
.collect();
208+
// Are each of the old neighbourhoods, minus any new blocks, still valid?
209+
let mut return_value = None;
210+
for old_owner in old_owners {
211+
let mut old_owner_blocks = self.neighbourhood_to_blocks(old_owner);
212+
for x in &add_all {
213+
old_owner_blocks.remove(x);
214+
}
215+
if old_owner_blocks.is_empty() {
216+
self.neighbourhoods.remove(&old_owner).unwrap();
217+
return_value = Some(new_owner);
218+
continue;
219+
}
217220

218-
let mut old_neighbourhood_blocks =
219-
self.make_merged_blocks(map, old_owner_blocks.clone())?;
220-
// We might be splitting the old neighbourhood into multiple pieces! Pick the largest piece
221-
// as the old_owner (so the UI for trimming a neighbourhood is less jarring), and create new
222-
// neighbourhoods for the others.
223-
old_neighbourhood_blocks.sort_by_key(|block| block.perimeter.interior.len());
224-
self.neighbourhoods.get_mut(&old_owner).unwrap().block =
225-
old_neighbourhood_blocks.pop().unwrap();
226-
let new_splits = !old_neighbourhood_blocks.is_empty();
227-
for split_piece in old_neighbourhood_blocks {
228-
let new_neighbourhood = NeighbourhoodID(self.neighbourhood_id_counter);
229-
self.neighbourhood_id_counter += 1;
230-
self.neighbourhoods
231-
.insert(new_neighbourhood, NeighbourhoodInfo::new(split_piece));
232-
}
233-
if new_splits {
234-
// We need to update the owner of all single blocks in these new pieces
235-
for id in old_owner_blocks {
236-
self.block_to_neighbourhood
237-
.insert(id, self.neighbourhood_containing(id).unwrap());
221+
let mut old_neighbourhood_blocks =
222+
self.make_merged_blocks(map, old_owner_blocks.clone())?;
223+
// We might be splitting the old neighbourhood into multiple pieces! Pick the largest piece
224+
// as the old_owner (so the UI for trimming a neighbourhood is less jarring), and create new
225+
// neighbourhoods for the others.
226+
old_neighbourhood_blocks.sort_by_key(|block| block.perimeter.interior.len());
227+
self.neighbourhoods.get_mut(&old_owner).unwrap().block =
228+
old_neighbourhood_blocks.pop().unwrap();
229+
let new_splits = !old_neighbourhood_blocks.is_empty();
230+
for split_piece in old_neighbourhood_blocks {
231+
let new_neighbourhood = NeighbourhoodID(self.neighbourhood_id_counter);
232+
self.neighbourhood_id_counter += 1;
233+
self.neighbourhoods
234+
.insert(new_neighbourhood, NeighbourhoodInfo::new(split_piece));
235+
}
236+
if new_splits {
237+
// We need to update the owner of all single blocks in these new pieces
238+
for id in old_owner_blocks {
239+
self.block_to_neighbourhood
240+
.insert(id, self.neighbourhood_containing(id).unwrap());
241+
}
238242
}
239243
}
240244

245+
// Set up the newly expanded neighbourhood
241246
self.neighbourhoods.get_mut(&new_owner).unwrap().block = new_neighbourhood_block;
242-
self.block_to_neighbourhood.insert(add_block, new_owner);
243-
Ok(None)
247+
for id in add_all {
248+
self.block_to_neighbourhood.insert(id, new_owner);
249+
}
250+
Ok(return_value)
244251
}
245252

246253
/// Needs to find an existing neighbourhood to take the block, or make a new one
@@ -273,7 +280,7 @@ impl Partitioning {
273280
.iter()
274281
.find(|(_, info)| info.block.perimeter.roads.contains(&other_side))
275282
{
276-
return self.transfer_block(map, id, *new_owner);
283+
return self.transfer_blocks(map, vec![id], *new_owner);
277284
}
278285
}
279286

@@ -285,7 +292,7 @@ impl Partitioning {
285292
new_owner,
286293
NeighbourhoodInfo::new(self.get_block(id).clone()),
287294
);
288-
let result = self.transfer_block(map, id, new_owner);
295+
let result = self.transfer_blocks(map, vec![id], new_owner);
289296
if result.is_err() {
290297
// Revert the change above!
291298
self.neighbourhoods.remove(&new_owner).unwrap();

apps/ltn/src/pages/select_boundary.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use widgetry::mapspace::{World, WorldOutcome};
77
use widgetry::tools::{Lasso, PopupMsg};
88
use widgetry::{
99
Color, Drawable, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome, Panel, State, Text, TextExt,
10-
Widget,
10+
Toggle, Widget,
1111
};
1212

1313
use crate::components::{legend_entry, AppwidePanel, Mode};
@@ -188,9 +188,19 @@ impl SelectBoundary {
188188
if self.currently_have_block(app, id) {
189189
mut_partitioning!(app).remove_block_from_neighbourhood(&app.per_map.map, id)
190190
} else {
191-
// Ignore the return value if the old neighbourhood is deleted
192-
mut_partitioning!(app).transfer_block(&app.per_map.map, id, self.id)?;
193-
Ok(None)
191+
match mut_partitioning!(app).transfer_blocks(&app.per_map.map, vec![id], self.id) {
192+
// Ignore the return value if the old neighbourhood is deleted
193+
Ok(_) => Ok(None),
194+
Err(err) => {
195+
if app.session.add_intermediate_blocks {
196+
let mut add_all = app.partitioning().find_intermediate_blocks(self.id, id);
197+
add_all.push(id);
198+
mut_partitioning!(app).transfer_blocks(&app.per_map.map, add_all, self.id)
199+
} else {
200+
Err(err)
201+
}
202+
}
203+
}
194204
}
195205
}
196206

@@ -224,12 +234,13 @@ impl SelectBoundary {
224234
let mut changed = false;
225235
let mut still_todo = Vec::new();
226236
timer.start_iter("try to add blocks", add_blocks.len());
237+
// TODO Sometimes it'd help to add all at once!
227238
for block_id in add_blocks.drain(..) {
228239
timer.next();
229240
if self.frontier.contains(&block_id) {
230-
if let Ok(_) = mut_partitioning!(app).transfer_block(
241+
if let Ok(_) = mut_partitioning!(app).transfer_blocks(
231242
&app.per_map.map,
232-
block_id,
243+
vec![block_id],
233244
self.id,
234245
) {
235246
changed = true;
@@ -285,8 +296,8 @@ impl State<App> for SelectBoundary {
285296
{
286297
return t;
287298
}
288-
if let Outcome::Clicked(x) = self.left_panel.event(ctx) {
289-
match x.as_ref() {
299+
match self.left_panel.event(ctx) {
300+
Outcome::Clicked(x) => match x.as_ref() {
290301
"Cancel" => {
291302
// TODO If we destroyed the current neighbourhood, then we cancel, we'll pop
292303
// back to a different neighbourhood than we started with. And also the original
@@ -302,7 +313,13 @@ impl State<App> for SelectBoundary {
302313
self.left_panel = make_panel_for_lasso(ctx, &self.appwide_panel.top_panel);
303314
}
304315
_ => unreachable!(),
316+
},
317+
Outcome::Changed(_) => {
318+
app.session.add_intermediate_blocks = self
319+
.left_panel
320+
.is_checked("add intermediate blocks automatically");
305321
}
322+
_ => {}
306323
}
307324

308325
match self.world.event(ctx) {
@@ -371,6 +388,12 @@ fn make_panel(ctx: &mut EventCtx, app: &App, id: NeighbourhoodID, top_panel: &Pa
371388
Line(" and paint over blocks to remove"),
372389
])
373390
.into_widget(ctx),
391+
Toggle::checkbox(
392+
ctx,
393+
"add intermediate blocks automatically",
394+
None,
395+
app.session.add_intermediate_blocks,
396+
),
374397
format!(
375398
"Neighbourhood area: {}",
376399
app.partitioning().neighbourhood_area_km2(id)

0 commit comments

Comments
 (0)