In this post I plan to set the scene for a nice visualization of cellular automata and pretty much anything else.

The idea is basically a mix of Github ability to preview .stl 3d files and the illustration in Wolfram’s “A new kind of science”.

Bulding on what I did in the previos post, once the run for a CA rule has been generate, it’s easy to manipulare the bits and generate vertexes and faces of the 3d figure (in this case the format is .obj)

import numpy as np
import math
import matplotlib.pyplot as plt

import import_ipynb
from automata_generation import generate, plot_run, make_columns

# manually specify the logic to map a cube face to 4 of its 8 vertexes
def face(face, count):
    if face == 1:
        return f'f {count+1} {count+2} {count+4} {count+3}'
    elif face == 2:
        return f'f {count+1} {count+2} {count+6} {count+5}'
    elif face == 3:
        return f'f {count+2} {count+6} {count+8} {count+4}'
    elif face == 4:
        return f'f {count+3} {count+4} {count+8} {count+7}'
    elif face == 5:
        return f'f {count+1} {count+5} {count+7} {count+3}'
    elif face == 6:
        return f'f {count+5} {count+6} {count+8} {count+7}'

def generate_cell(file, count, i, j):
    start = count * 8

    lines = [f'g cell{i}-{j}\n', 'usemtl Red\n']
    vertex = []
    z = 0
    for di in [0, 1]:
        for dj in [0, 1]:
            for dz in [0, 1]:
                vertex.append(f'v {j+dj} {i+di} {z+dz}\n')

    for v in vertex:
        lines.append(v)

    for i in range(6):
        lines.append(face(i+1, start) + '\n')

    lines.append("\n")
    file.writelines(lines)


def create_3d_obj(rule, data):
    count = 0
    filename = f'{rule}.obj'
    file = open(filename, "w")
    file.writelines(["mtllib automata.mtl\n"])

    for i, row in enumerate(data):
        for j, col in enumerate(row):
            if data[i][j] == 1: # 0 or 1 depending on the CA
                generate_cell(file, count, len(data)-i, j)
                count += 1

    file.close()
    return filename
rule = 30
size = 200
steps = 200
start = np.zeros(size)
start[45:55] = 1
data, _ = generate(rule, size, steps, with_init=start)

create_3d_obj(rule, data)
'30.obj'

And here’s the result

rule 30
rule 110
rule 169

This gets even more cool with 2D CA

code 422
code 460

Coming soon:

I’m no expert on 3d visualization but I think the reason the figures are a bit laggy, especially if they are large enough, is that each CA cell is its own figure. One optimization that I’ll try to implement in the future is to merge all adjacent cells into one figure (this mean that all non-edge vertex can be removed and several faces merged together).

I’ll probably need to use use SCC to undersand which cells should be merged together. The tricky thing will probably be specifying the vertexes for such non regular faces (since the list of vertex needs to be ordered - in general counter clock wise). I will probably need to used some idea from the convex hull to specify such ordering.

It should be fun