← Blog index

Generative Art - Project 2 - Populous

Published: 2022.02.22

02/22/2022

Over the last few days, I've been reading a paper that uses L-systems to procedurally generate a city. The city creator consists of two components: a road generator, and a building generator. I found the generated roadmaps in the paper to be quite appealing and wanted to generate more with my input parameters. Here's an example from the paper, with the basic style I'm looking to imitate:

L-system example from paper

Another possibility was to render the resulting roadmap with the visual style of games like Mini Metro:

Mini Metro screenshot

The Harry Beck tube maps that Mini Metro's style is based on are famous for their minimalism. However, I do want to preserve the geographic locations of streets so I will be largely following the example from the paper.

The authors created an L-system template generator to make the city creator extensible without having to constantly update many production rules every time they wanted to add something. They would then supplement the template with parameters derived from input data such as population density image maps. Finally, they combined these with one or more rules that dictate the general "layout" of the roads that emphasized the following: density-dependent, rectangular raster, radial, elevation-dependent, and potentially many more patterns. Throughout the process, they would use functions to fill in the rest of the template and modify the generated road parameters based on their immediate surroundings.

I'm excited to implement this paper because getting each of the components to work together will be quite the challenge. I will need:

Local constraints

As for beyond this project (project 3), here are some other things I could add:

Reading this paper has also left me with many questions that I will need to answer:

And on top of that, I'm working to understand the following production rules:

Production rules 1 Production rules 2

Those are just for the template generation; the parameter setting functions haven't been covered yet! As was mentioned in the critique, it would already be a lot to get just the L-system generator working.

I did find a blog post by Sean Barrett that decomposes this very L-system into an algorithm using a priority queue. The page where I found the link to that explanation will also be a good one to read.

02/23/2022

After reading the two links I posted above, I know how to simplify some parts of the project.

I also learned that Chris Delay of Introversion Software used this algorithm (or a variant) in his project Subversion - cool! I remember him from the monthly update videos he made for Prison Architect, another game of his.

The paper has a somewhat hand-wavy approach towards the actual generation and modification of road parameters, and there are many of them. For example, the stochastic processes used to determine many of the attributes in their example, such as the starts and ends of highways, are completely glossed over. Of course, only so much can be explained in a couple of pages.

The second link contains a repository containing an implementation of the paper in CoffeeScript. I don't want to copy it entirely, but skimming the code helped me to come to some conclusions that hadn't been clear in the paper. It also helped me with my implementation, by giving me the idea to use a quadtree to store segments instead of a k-d tree (though both would require rebalancing at points).

The upside of using an algorithm instead of generating the entire L-system is that I no longer need to devote time to the token parser and can focus more on the actual road generation procedure and its aesthetics.

02/24/2022

Here is a non-exhaustive list of what the program needs, from research.

There is much, much more in addition to this.

02/26/2022

I'm slowly getting a better handle on de-formalizing that L-system. It was complicated, but I now have a good picture of what each of my road-building functions is doing. My intended program design does not rewrite strings using a list of production rules - it no longer employs an L-system under the hood.

Following this, I plugged away at my renderer for a while. Over the last month, I've been working on a geographic information system visualization tool with Dr. Ergun Akleman and several others. Some of the code that I wrote for it is reused here. Specifically, I was already able to load multiple image maps into pixel arrays, render a 3D mesh using one of those image maps (that I had designated as a height field) as vertex data, and project a satellite image onto it. The applet also allowed users to look around using the mouse.

However, I also needed to superimpose roads onto the mesh's surface. This is done by using Xiaolin Wu's line plotting algorithm (albeit with the antialiasing turned off for clarity for the time being) onto another texture, which is then blended with the projected satellite image. Simple lines will be all that's needed to demonstrate the capabilities of the road generator for project 2. To test out my rasterizer, I generated 200 random line segments. Below is an image of these random lines colored red and strewn about San Francisco:

Random lines across San Francisco

The generated roads follow the slope of the mountains, but it will be unlikely for the "L-system" generated roads to be placed there as those areas will tend to have low population density. According to the basic pattern of road generation, most roads will steer clear of areas with lower population density in general.

Next, the satellite photo of San Francisco already includes the city's actual road system. The generated road system will understandably not match. Moreover, my estimation of population density will be discerned from the satellite map and will be a rough approximation at best.

I also learned about using modules to better manage my multiple-file JavaScript project.

02/28/2022

I focused on dropping my roads into the render system. Since JavaScript doesn't include a seedable random function, I found another one on the internet to use. Also, I needed to find a way to draw thicker lines, since Wu's algorithm only creates antialiased lines that are 1 pixel wide.

Additions:

Here are two images of my progress with the above functionality. The first one shows generated highways and streets of any length, while the second one shows generated highways and streets fixed to a length of 100 pixels.

Random roads and highways across San Francisco, any length

Random roads and highways across San Francisco, fixed-length

03/02/2022

I finally generated some road networks with a basic version of the paper's global goals system.

Satellite image with roads

Population density map with roads

One of the methods I used to avoid having too dense of a road network was to cull roads with a 10% base chance, and then check to see if there were too many other connections in the immediate vicinity.

Because the local constraint algorithm isn't finished, and because some roads are pretty long, this doesn't look quite like a logical road system just yet. There are some neat parts though - for example, the generated network follows the actual network along the coast.

When I run local constraints, I will be scanning the nearby area for any intersections that new roads can connect to. I do not expect there to be more than 5000 or so road connections at any one time. In this case, I could sort my connection list by x coordinate and then do linear searches within bounding boxes of a certain size for my local constraints, although it would be much more inefficient than using an organized structure like a quadtree.

This method wouldn't cover any roads that might be intersected with or are close to being intersected with; that is yet another corner case. For example, if a road begins at the top left corner of the map and ends at the bottom right corner of the map, I'd still need to check for intersections across its entire length. Using a bounding box to find nearby endpoints would not always yield a result.

Perhaps, I could condition the bounding box size to be at least twice the longest possible length of a road.

Several next steps:

Additional corner cases for constraints to think about:

03/03/2022

Global goal system with roads based on population density, animated

previous post next post