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()