Programming:
Faces (& Bitmaps) for Eggbot
20th
November 2011
On
the 5th December I shall be taking Eggbot to our local Christmas craft
fair, I've made a few Christmassey designs but I really wanted to be
able to take a picture and draw people's faces on Christmas decorations
'live' on the day.
I've found a way to do it with Processing and a
webcam...
The tool chain is Processing exporting to PDF,
followed by Inkscape.
The code produces the vertical strips, an array of these is needed to
make the finished image.
The baubles above are ~70mm diameter, matt silver and they work really
well (they're £2.99 for 6 in Ikea)
I'm taking the bitmapped input from a webcam in the example but it
would easily be adapted to work from an image input. The
webcam is driven with the excellent GSVideo
library.
The code runs fast enough to be able to see the webcam image on screen
quite clearly on my PC, your mileage may vary, nothing gets written out
until you press 'e' to export to a PDF. I drag the PDF into
Inkscape to import it into the 3200x800 pixel page that Inkscape needs
and re-size as needed. The finished image looks like this:
Brightness
values in the image are translated into widths of strips so that there
is more white where the image is bright (narrow strip) and more fill
where the image is dark.
Each face takes about 4 minutes, so I won't be able to do that many if they're popular...
The code for the Strip class is below the main text and zipped here.
If you want to have a go, please feel free to use the included code and let me know how you get on...
====
Source code is available here
class Strip { float theLength, theWidth, theSeparation; //basic dimensions, length, //width and desired separation between pixels float minWidth, theThick; //minimum width & thickness (mainly used for 3D models), int thePixels; //number of pixels (down normally) that this strip represents float[] theBrightness; //array of brightness values, & yes I know this should be int[] float[] t,l; //storage arrays boolean invert; //invert = true assumes light background, dark strips //switch for 2D/3D writing boolean twoD = false; Strip(float theLength, float theWidth, float theSeparation, float[] theBrightness, float minWidth) { twoD = true; // this is the 2D version of the constructor this.theLength = theLength; this.theWidth = theWidth; this.theSeparation = theSeparation; this.theBrightness = theBrightness; this.minWidth = minWidth; thePixels = theBrightness.length; t = new float[thePixels]; l = new float[2*thePixels]; float maxB = max(theBrightness); float minB = min(theBrightness); //setup the widths of the strip for each pixel for (int i=0;i=thePixels;i++) { t[i] = map(theBrightness[i],0,255,theWidth,minWidth); } float pixLen = theLength/thePixels; float cumlLen = pixLen/2; float landLen = pixLen - 2*theSeparation; //setup the positions along the strip for each pixel for (int i=0;i=thePixels;i++) { l[2*i] = cumlLen - landLen/2; l[2*i+1] = cumlLen + landLen/2; cumlLen += pixLen; } } //this is the 3D version of the constructor Strip(float theLength, float theWidth, float theSeparation, float[] theBrightness, float minWidth, float theThick, boolean invert) { this.theLength = theLength; this.theWidth = theWidth; this.theSeparation = theSeparation; this.theBrightness = theBrightness; this.minWidth = minWidth; this.theThick = theThick; this.invert = invert; thePixels = theBrightness.length; t = new float[thePixels]; l = new float[2*thePixels]; float maxB = max(theBrightness); float minB = min(theBrightness); float[] newBrightness = theBrightness; //println(theBrightness); for (int i=0;i=thePixels;i++) { if (!invert) { newBrightness[i] = map(theBrightness[i],minB,maxB,maxB,minB); } t[i] = map(newBrightness[i],0,255,theWidth,minWidth); } //println(t); float pixLen = theLength/thePixels; float cumlLen = pixLen/2; float landLen = pixLen - 2*theSeparation; for (int i=0;i=thePixels;i++) { l[2*i] = cumlLen - landLen/2; l[2*i+1] = cumlLen + landLen/2; cumlLen += pixLen; } } void drawStrip() { //NB: TWO versions of the draw code for 2D and 3D //draw Strip from 0,0,0 to 0,-theLength,0 /* -> =- min width *-* =- pixel start | \ =- half separation |----* | | | | | | | | | | | | | | |----* | / =- half separation | / =- half separation |-* | | | | | | | | | | | | | | |-* This is the pattern for the strip */ //3D if (twoD = false) { //NB: this code will produce MASSIVE normal direction problems // if you use it for 3D printing, if you want to fix it feel // free, otherwise use Blender or Meshlab to fix them later //top surface (along y direction), zigzag across in x direction beginShape(QUAD_STRIP); vertex(0,0,0); vertex(minWidth,0,0); for (int i=0;i=thePixels;i++) { vertex(0,l[2*i],0); vertex(t[i],l[2*i],0); vertex(0,l[2*i+1],0); vertex(t[i],l[2*i+1],0); } vertex(0,theLength,0); vertex(minWidth,theLength,0); endShape(); //bottom surface beginShape(QUAD_STRIP); vertex(0,0,theThick); vertex(minWidth,0,theThick); for (int i=0;i=thePixels;i++) { vertex(0,l[2*i],theThick); vertex(t[i],l[2*i],theThick); vertex(0,l[2*i+1],theThick); vertex(t[i],l[2*i+1],theThick); } vertex(0,theLength,theThick); vertex(minWidth,theLength,theThick); endShape(); //"crennelated" surface beginShape(QUAD_STRIP); vertex(minWidth,0,0); vertex(minWidth,0,theThick); for (int i=0;i=thePixels;i++) { vertex(t[i],l[2*i],0); vertex(t[i],l[2*i],theThick); vertex(t[i],l[2*i+1],0); vertex(t[i],l[2*i+1],theThick); } vertex(minWidth,theLength,0); vertex(minWidth,theLength,theThick); endShape(); //back surface beginShape(QUAD_STRIP); vertex(0,0,0); vertex(0,0,theThick); for (int i=0;i=thePixels;i++) { vertex(0,l[2*i],0); vertex(0,l[2*i],theThick); vertex(0,l[2*i+1],0); vertex(0,l[2*i+1],theThick); } vertex(0,theLength,0); vertex(0,theLength,theThick); endShape(); //ends beginShape(QUAD_STRIP); vertex(0,0,0); vertex(minWidth,0,0); vertex(0,0,theThick); vertex(minWidth,0,theThick); endShape(); beginShape(QUAD_STRIP); vertex(0,theLength,0); vertex(minWidth,theLength,0); vertex(0,theLength,theThick); vertex(minWidth,theLength,theThick); endShape(); } else { //2D //draw the outline beginShape(); vertex(0,0); vertex(minWidth,0); for (int i=0;i=thePixels;i++) { vertex(t[i],l[2*i]); vertex(t[i],l[2*i+1]); } vertex(0,theLength); vertex(minWidth,theLength); endShape(CLOSE); //draw the fill beginShape(); vertex(0,0); vertex(minWidth,0); for (int i=0;i=thePixels;i++) { vertex(t[i]/4,l[2*i]); vertex(t[i],l[2*i+1]); } vertex(0,theLength); vertex(minWidth,theLength); endShape(CLOSE); } } }
|