This is a randomizable and customizable cellular automaton snowflake generator in BlocksCAD. It comes in two versions, a 20-level version (board radius up to 20) and a 40-level version. The 40-level version is just barely within the limits of BlocksCAD abilities: BlocksCAD becomes sluggish but it still works (I recommend using Firefox instead of Chrome for much better responsiveness).
The automaton puts down a live (colored) hex at the middle of the board, and then follows evolution rules (like in Conway's Game of Life). The default rule set (darker blue screenshots) is Stephen Wolfram's: a live cell survives forever, and a new cell generates when it has exactly one live neighbor.
You can vary the rule set by varying what effect different numbers of live neighbors (0-6) have on survival or generation. You can also set indeterministic rules, where there are probabilities between 0 and 1 of survival or generation in the different neighbor count cases. In indeterministic cases, snowflake symmetry (six fold rotational, plus reflection) is still enforced by the code.
The light blue images were made with an indeterministic rule set where the only change made was to set the probability of generation when there is one neighbor to 0.5, so the generation parameters were 0,0.5,0,0,0,0,0. These images look more snowflake-like to me. Another interesting set of generation parameters is 0,0.5,0,0.1,0,0,1 (0.5 chance of generation with one neighbor; 0.1 chance with 3 neighbors, certainty with 6 neighbors).
You can post in comments what are some interesting rule sets. The default rule and its above probabilistic variant are quite printable. Rules where all the survival probabilities are 1 will result in completely connected snowflakes. If some survival probabilities are less than 1, there may be outliers.
The OpenSCAD-based Customizer was generated by exporting OpenSCAD code from BlocksCAD, and moving the variable definitions to the top of the file for Customizer compatibility.
If you turn on color shading (by having different color1 and color2 settings), the color shading goes by the step during which a cell was generated. You can also turn on periodic banding for a special color effect.
It was difficult to implement a cellular automaton in BlocksCAD since BlocksCAD does not support arrays and, for further complication, is a functional language.
To speed up the code, the cellular automaton is only implemented on a 1/12 segment of the board, with the rest reconstructed by symmetry.
The heart of the code is a giant recursive
evolve module that (in the radius 40 version) takes 421 arguments. The first argument counts iterations. The remaining 420 arguments each store one cell of data. If the the iteration count reaches 0, then
draw(i,j,v) for each cell. It has to do this by a union of 420 invocation, since there are no arrays and hence no way of iterating over the arguments. The
draw(i,j,v) function draws the hex if
v is non-zero, as well as drawing the up to 11 other hexes that are related to it by the snowflake symmetries.
evolve module recursively calls itself with the iteration count decremented, and each cell argument evolved. The evolution of the cell arguments is done by adding up the neighbor counts for each argument checking to see if the cell should be alive. There is a hard-coded call to a
cell evolution function for every single one of the 420 arguments.
There is also a
go module that calls
evolve with its 421 (radius 40 version) initial arguments: an iterations count, follows by a central 1, followed by zeroes. Finally, the code linearly extrudes the output of the
go module to make it 3D.
At this point you might think I was crazy to drag-and-drop evolution code for every one of 420 cells in
evolve. Well, I didn't. I wrote a quick-and-dirty python script (included as
cell6.py: the latter is the main script) which can generate a subset of BlocksCAD xml code, and used it to automatically generates the giant
evolve module that calls
cell for each cell, and the big
The rest of the code was manually entered in BlocksCAD: the
go module, some top-level code, the
cell evolution function, and the
Assembling all the code in BlocksCAD was a bit of work. Most of the manually entered code was saved to xml as a backup (included as the "hand code"). Then I imported the xml file generated by python into BlocksCAD. Then I added the extrusion wrapper around the
go module. (Or at least that's how I did it when I finally figured out how to make it work.) I then made some optimizations.