Today, let’s talk about stamps. At least a little bit.
Having a custom stamp is useful if, for instance, you need to stamp stuff. You can get custom stamps off of the internet, but you have to pay money, and often there are various properties of the stamps which are limited (e.g. size, content, etc.).
One can buy little pads of foam in “make-your-own-stamp” kits, which you simply heat up using a hair dryer and then pressing in an impression that you want the foam to hold. This is fine, but making the impression then poses a problem. You can’t simply print it like you would with an inkjet printer, you have to carve it.
Unless you have a 3d printer, which I do. Now you can just print the impression.
That’s all I really have to say about stamps. I could talk on end about how I tried different types of impressions, but I eventually settled on beveling the text. Not many programs handle beveling text well, though. Blender’s builtin bevel function dislikes the many corners that appear in text. OpenSCAD (famously) doesn’t even have a bevel function, and doing a minkowski transform with a sphere serves only (predominantly) to make the text smaller.
So, I wrote a Python script that converts a black-and-white bitmap into a beveled STL file.
It’s not a complicated script, most of the heavy lifting is done using numpy. Essentially, the bitmap is loaded, and then eroded away one step at a time. Then the erosion steps are layered, and the layered volume is converted into a STL file using the marching cubes implementation.
Here’s the code, complete with hardcoded values:
import numpy as np
from scipy import misc, ndimage
from skimage import measure
im = misc.imread("lflstamp.png", flatten=False, mode="L")
im = misc.imresize(im, 20)
print im.shape
volume = []#np.empty((im.shape[0], im.shape[1], 0))
# Have a few all-one layers
volume.append(np.pad(np.zeros(im.shape), 1, "constant", constant_values=0))
for i in xrange(5):
volume.append(np.pad(np.ones(im.shape), 1, "constant", constant_values=0))
# Duplicate the raw image for a few layers
for i in xrange(3):
volume.append(np.pad(im, 1, "constant", constant_values=0))
# For each layer, dilate & add to the volume
for i in xrange(20):
print im.shape, len(volume)
volume.append(np.pad(im, 1, "constant", constant_values=0))
#volume = np.concatenate((volume, im), axis=2)
im = ndimage.binary_erosion(im)
#print im
pass
volume = np.stack(volume)
verts,faces = measure.marching_cubes(volume, 0.5)
print verts
print faces
# Figure out a scale for the thingy
# Also, transpose into the X-Y plane
verts = map(lambda v: (-v[2], v[1], v[0]), verts)
maxs = [0,0,0]
for v in verts:
if v[0] > maxs[0]:
maxs[0] = v[0]
if v[1] > maxs[1]:
maxs[1] = v[1]
if v[2] > maxs[2]:
maxs[2] = v[2]
print maxs
limits = [90,100,50]
scales = [maxs[0]/limits[0], maxs[1]/limits[1],maxs[2]/limits[2]]
print scales
scale = 1.0 / max(scales)
# Write out the STL
f = open("out.stl", "w")
f.write("solid foo\n")
for face in faces:
f.write("facet normal 0 0 1\n outer loop\n")
for vn in face:
f.write("vertex %f %f %f\n"%tuple(map(lambda x: x*scale,verts[vn])))
f.write("endloop\nendfacet\n")
pass
f.close()
