Water Biscuits

I couldn't quite get it ready for my EdinburghJS presentation but I now have water being properly handled in Linzer. It's a subtlety, but I found when exploring in my Garibaldi iteration that you got better regions if you treated water as part of the boundary.

Just to recap, the way I previously defined regions ('biscuits') was:

  1. Find city boundary
  2. Choose random start/end points in that boundary
  3. Find routes between those points, either biased to be pedestrian or car (auto) routes
  4. Draw the city boundary as black and the routes as white borders (see below); this effectively unions the routes
  5. Find connected white pixels and turn back into regions

(note: image is reflected in x-axis)

The changes I made were to steps 2 and 4:

  • 2: Mask-out the water areas from the city bounds so that they can't be used as start/end points (sorry swimmers!):
  • 4: Draw the water areas as border pixels:

This is hugely impactful for somewhere like Amsterdam which is 15% water! (maybe more on that later in a related side-project)

One of the funny things about long-term side-projects is that you get the "opportunity" to come back to short-cuts you'd taken earlier. I was wondering why in places like Paris it wasn't finding regions on the islands in the center of the city. Before adding water I didn't really have to deal with drawing any polygons with holes, which these parts of Paris happen to have. So, it wasn't finding them because ... I hadn't bothered implementing rendering of interiors.

Turns out it was actually fairly easy to implement on top of tiny-skia using a Mask:

let mut mask = Mask::new(pixmap.width() as u32, pixmap.height() as u32).unwrap();
...
let path = path_from_line(poly.exterior())?;
mask.clear();
for interior in poly.interiors() {
    let interior_path = path_from_line(interior)?;
    mask.fill_path(&interior_path, FillRule::EvenOdd, true, transform);
}
mask.invert();
pixmap.fill_path(&path, paint, FillRule::EvenOdd, transform, Some(&mask));

Anyways, it's been really nice to draw a line under something I've known I've needed to do for a looooong time.