Procedural Maze with Revit API
Revit is a great BIM software. Even though it's not exactly good for simulation or animation, it has such a strong API, which opens the ways to use Revit in various ways.
In this short article, I would like to explore how to generate a procedural maze by utilizing the Revit API.
Recently I am interested in simulations such as analysing water sheds, procedural terrain/cities, reshaping the geometry in cubes similar to Minecraft etc.
So I wondered if I could make some mazes/labyrinths using Revit. I googled and found this excellent sample. Archicad Maze Generator!
I became even more interested when I saw that the idea is already done with Archicad. I'm terrible at understanding other people's codes. So I decided to code myself and chose another algorithm.
For this kind of coding, I strongly recommend looking for articles, papers and algorithms. No need to reinvent the wheel for every small step!
There are already simple and great algorithms in the following wiki page.
Among those, I chose Aldous-Broder algorithm. Pikachu I choose you! Sorry..
1. Pick a random neighbour.
2. If the chosen neighbour has not been visited:
1. Remove the wall between the current cell and the chosen neighbour.
领英推荐
2. Mark the chosen neighbour as visited.
3. Make the chosen neighbour the current cell.
Basically, I implemented a 2d array and iterate until each cell is checked and marked with bool.
void AldousBroder(int x, int y)
{
_cells[x, y] = true;
List<int[]> cellsNeighbours = new List<int[]>();
if (x > 0 && !_cells[x - 1, y])
cellsNeighbours.Add(new int[] { x - 1, y });
if (x + 1 < _size && !_cells[x + 1, y])
cellsNeighbours.Add(new int[] { x + 1, y });
if (y > 0 && !_cells[x, y - 1])
cellsNeighbours.Add(new int[] { x, y - 1 });
if (y + 1 < _size && !_cells[x, y + 1])
cellsNeighbours.Add(new int[] { x, y + 1 });
if (!cellsNeighbours.Any())
return;
int randNeighbour = _random.Next(0, cellsNeighbours.Count);
int[] neighbour = cellsNeighbours[randNeighbour];
if (!_cells[neighbour[0], neighbour[1]])
{
if (x == neighbour[0])
_wallsH[x, Math.Max(y, neighbour[1])] = true;
else
_wallsV[Math.Max(x, neighbour[0]), y] = true;
AldousBroder(neighbour[0], neighbour[1]);
}
}
Next is to generate curves then walls.
Therefore using a single transaction is a better performance-wise choice, not only in this situation but arguably in every situation. However, performance is not the only criterion to consider, sometimes processing under separate transactions is a better choice. There are even times it is necessary to implement separate transactions. But that's a whole other discussion maybe for another article someday.
Returning to our topic I went with separate transactions so I can show how each wall is generated. And I wanted to override the current wall colour since it gives a nice animation feeling.
I used a simple method to override walls in order, to get the red animation highlighting the most recent several walls.
This article is somehow just a showcase of how to use Revit API for interesting usages. Maybe I can make a simple tutorial which explains in more detail though I am not sure if anyone is interested.
Finally, it would be shame to leave this only as a fun simulation. I like to improvise and use in real practice as well. Instead of generating walls, these curves can be used to make patterns in Revit. So when you want some unique patterns in Revit, this small feature can give you procedurally generated maze patterns. I think it would be interesting to have maze patterns in walls, ceilings or or even tiles.