How to Add Your Own Style to the Makelangelo
This post is about how to modify the open source code for the Makelangelo and bend it to your will. I hope to teach you how to create new styles of drawing for the Makelangelo so that you can make it more artsy or more funny or … well, whatever you want to do with it, right? Right.
Warning! This post… this post is for the ambitious among you. It’s not easy, but like they say: nothing worth doing is easy.
Process
Download and install Eclipse.
Use Git to clone a copy of the dev branch of http://github.com/MarginallyClever/Makelangelo. Make sure it is the dev branch, please.
Import the Makelangelo project into Eclipse.
Run the project and test that you can connect to your robot. If you can, you’re ready to proceed.
Clone a Filter or Filter_Generator class.
Change the name of the class, and add your custom styling.
Edit Makelangelo::LoadImageConverters() to include your new filter.
Save & Test your new class.
Make a Git commit and syncronize the commit to Github. The world can now see your code.
Make a pull request. I’ll get an email telling me you’re ready and I can add it to the code.
On the next release of an official version, your code will be avialable to the hundreds of users around the globe.
Image conversion
I’ve created a base Filter class as the source of coding power for all specialized filters. Some filters convert color to black and white. Some resize a picture. Most are Filter_Generators, which use a combination of Filters and some custom logic to create new styles of art.
To make writing Generators easier, I hid all mention of gcode. You should never have to see any of it! Your job is to tell the pen where to go, when to start drawing (be down) or stop (be up). I’ve also added sample1x1 and sample3x3 so you can say “tell me what the greyscale value at this spot in the user’s chosen picture looks like.”
Here is the Scanline style filter. It moves left to right, top to bottom in an “S” pattern. When the picture is dark, then pen is down. When the picture is light, the pen is up. Once it scans the whole image, it signs its name and goes back to the starting position.
[code]public class Filter_GeneratorScanline extends Filter {
public String GetName() { return "Scanline"; }
/**
* create horizontal lines across the image. Raise and lower the pen to darken the appropriate areas
* @param img the image to convert.
*/
public void Convert(BufferedImage img) throws IOException {
// COPY+PASTE THIS NEXT PART UNTIL YOU UNDERSTAND HOW IT WORKS.
// The picture might be in color. Smash it to 255 shades of grey.
Filter_BlackAndWhite bw = new Filter_BlackAndWhite(255);
img = bw.Process(img);
// Open the destination file
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(dest),"UTF-8");
// Set up the conversion from image space to paper space, select the current tool, etc.
ImageStart(img,out);
// "please change to tool X and press any key to continue"
tool.WriteChangeTo(out);
// Make sure the pen is up for the first move
liftPen(out);
// COPY+PASTE ENDS. CUSTOM STUFF STARTS HERE
// figure out how many lines we’re going to have on this image.
int steps = (int)Math.ceil(tool.GetDiameter()/(1.75*scale));
if(steps<1) steps=1;
// Color values are from 0…255 inclusive. 255 is white, 0 is black.
// Lift the pen any time the color value is > level (128 or more).
double level=255.0/2.0;
// from top to bottom of the image…
int x,y,z,i=0;
for(y=0;y<image_height;y+=steps) {
++i;
if((i%2)==0) {
// every even line move left to right
//MoveTo(file,x,y,pen up?)
MoveTo(out,(float)0,(float)y,true);
for(x=0;x<image_width;++x) {
// read the image at x,y
z=sample3x3(img,x,y);
MoveTo(out,(float)x,(float)y,( z > level ));
}
// Make sure the pen is up at the end of a line.
MoveTo(out,(float)image_width,(float)y,true);
} else {
// every odd line move right to left
MoveTo(out,(float)image_width,(float)y,true);
for(x=image_width-1;x>=0;–x) {
z=sample3x3(img,x,y);
MoveTo(out,(float)x,(float)y,( z > level ));
}
// Make sure the pen is up at the end of a line.
MoveTo(out,(float)0,(float)y,true);
}
}
// We know pen is up here, it has to be after a line.
// In other programs we would check first.
// COPY+PASTE THE REST
SignName(out);
// go home
MoveTo(out, 0, 0, true);
// close the file
out.close();
}
}[/code]
Similarities
It might help to know the order in which I created the filters, because reading them in the same order will show you how my thinking evolved.
After I wrote Filter_GeneratorScanline I wrote Filter_GeneratorPulse, that makes each line further apart but also adds heart monitor style zigzags that get bigger when that part of the picture is darker. Then I tried making little boxes instead of zigzags and got Filter_GeneratorBoxes. Then I tried one pass over the image in red boxes, one in blue, one in green, and one in black, all wrapped up in Filter_GeneratorRGB.
The very first Filter_Generator I wrote was Filter_GeneratorTSP. Filter_GeneratorTSP never raises the pen because when I wrote it the Makelangelo 1 couldn’t lift the pen! This class probably makes a lot less sense than the others because it was the first. Filter_GeneratorTSP uses Filter_DitherFloydSteinberg to turn a picture into dots, where the dots get closer together in the dark part of each picture. Then makes a rough attempt at solving the Travelling Salesman Problem (TSP) to play connect the dots.
Now you try
Go for it! I’d love to see what you make. If you have any project setup errors, any questions about filters, or you just want to jazz about style *ideas*, please join me in the R&D forum we host.
Final Boss
The process for converting pictures isn’t as nice as Processing. It would be much more intuitive if the image on the screen updated as the filters did their job. That’s a major rewrite of the graphics system.