3d Printing Fractals (1)

This blog post goes alongside the video:
So going back a little bit to the fractal slicing blog here I wanted to move to generating 3d printed fractals. The first step is to 3d print a 2d fractal - so we can use the 'escape' level as the height, and it produces a very good 3 dimensional object, even if that is only an alternative representation of the 'colour' of the fractal.
To do this, I generated a point cloud, and the code to do this is relatively trivial - the code to work out if a point is inside or outside the fractal is as follows (where input is a complex number)
def frac_one(input, c):
return input * input + c
def calc_iterations(location, c, limit=10, circle=2):
if abs(location) > circle:
return 0
for i in range(limit):
location = frac_one(location, c)
if abs(location)> circle:
break
return(i+1)
There are multiple python libraries which will convert a coordinates into an .stl or equivalent and then save this nicely, but you can also just save it as a .xyz file and import it into something like meshlab to then turn into a surface, and an .stl and send it to your printer for slicing. Not perfect because the slicing process does loose resolution - i.e. not as good as generating gcode from the direct points, but still a good progress of 'code generated structures'.
For my first iteration, I used numpy-stl
which was relatively straight forwards, but I had to generate the vertices as well as the points. I did this relatively straight forwards because I knew it was going to be a plane with no overhangs, so by offsetting each line by 1/2 a unit, it ended up with a net set of triangles:
data = []
limit = 15
divisor = 40
offset = (-75,-50)
size = 100
const = complex(0,0)
for i in range(size):
data.append([])
for j in range(size):
point = complex((i+offset[0])/divisor, (j+offset[1])/divisor)
counter = calc_iterations(const, point, limit=limit)
data[i].append(counter)
So this is a big single dimension array of heights, and then the following makes an array of vertices neatly - I could possibly have done this in one loop with the above code, but hindsight is always easy. Note here that I offset every other line slightly. The values are all hard coded as a space of 1, because this is experimental art code, not enterprise stuff.
vertices = []
for j in range(size):
for i in range(size):
if j%2==0
coord = [i,j, data[i][j]]
else:
coord = [i+0.5,j, data[i][j]]
vertices.append(coord)
vertices = np.array(vertices)
Okay, so now we need to make faces:
faces = []
for j in range(size):
for i in range(size-1):
this_point = j*size + i
if j != 0:
if j % 2 == 0:
face = [this_point, this_point+1, this_point-size+1]
else:
face = [this_point, this_point+1, this_point-size]
faces.append(face)
if j != size-1:
if j % 2 == 0:
face = [this_point, this_point+1, this_point+size+1]
else:
face = [this_point, this_point+1, this_point+size]
faces.append(face)
faces=np.array(faces)
The +1's are what make it index the mid point of the next line, and finally we can make the object:
object = mesh.Mesh(np.zeros(faces.shape[0], dtype=mesh.Mesh.dtype))
for i, f in enumerate(faces):
for j in range(3):
cube.vectors[i][j] = vertices[f[j],:]
object.save('object.stl')
The outcome is a combination of a file which could certainly be optimised, but is a flat surface - a little bit of work either to make it a closed mesh either in code, or part of the 3d printing toolchain (e.g. meshlab or fusion 360) is still required to make it a printable object with thickness.



And am I satisfied? No, because the issue here is that this is a 3d representation of a 2d object, and I know I can do better.