// slightly cleaned up from harvey moon's drawing machine // http://www.kickstarter.com/projects/notever/the-drawing-machine/posts // http://unanything.com/ // https://files.me.com/sami6877/wg01bu // http://www.creativeapplications.net/maxmsp/drawing-machine-maxmsp-processing/ // // v 0.2 - added servo to left pen from page. controlled with G100 S255 (pen down) and G100 S100 (pen up). // v 0.3 - added xml config file for drawing machine parameters // rtwomey@u.washington.edu // Key 1 = 0,0 // Key 2 = width,0 // Key 3 = width,height // Key 4 = 0,height // Keys 5-8, corners of svg image // Key 0 = Home // Key H = home device // Key h = set origin and init coords. // Key S = stop // Key G = go Bot machine; import processing.serial.*; // serial communication import java.io.*; //Java import geomerative.*; // svg parsing // state machine static int STATE_SQUARE = 0; static int STATE_CIRCLE = 1; static int STATE_DONE = 2; // configuration XML xml; boolean serialOn = false; boolean writeToFile = true; boolean printToConsole = true; boolean doRepeat = true; PrintWriter writer; //--------------------------------------------------- // configuration read from XML configuration file // machine geometry float MOTORSEP = 96.0;//72.0; // how far apart are the two steppers float HOMEA = 86.5;//81.0;//82.0;// 58.5225650924496; // 108 float HOMEB = 86.5;//81.0;//82.0;//56.02580320709378; // 108 // page setup float PAGEW = 13.0;//36.0;//17.0;//22.0;//44.0; // width float PAGEH = 17.0;//24.0;//14.0;//30;//30.0; // height float PAGE0Y = 32.0;//6.0-PAGEH; float PAGE0X = (MOTORSEP-PAGEW)/2.0; // upper left float MARGIN = 0.5; // feed rates and gcode float MAXLENGTH=1.0; float MAXARCLENGTH=0.5; // output settings float PPI = 90.0; // SVG (Pt) to plotter space (inch) coordinate conversion float multiplier = 1.0; // global scale // on-screen scale // scale width to full height int windowH = 900; float onScreenScale=windowH/(PAGEH*PPI); int windowW = int((PAGEW*PPI)*onScreenScale); // scale height to full width //int windowW = 1400; //float onScreenScale=windowW/(PAGEW*PPI); //int windowH = int((PAGEH*PPI)*onScreenScale); //----------------------------------- // state variables boolean On = false;// USED SO THAT THE KEY 'S' AND 'G' START AND STOP THE DRAWING SEQUENCE, starts off to calibrate PVector Current; PVector Last; PVector Endpoint; // end of current line boolean jumping = true; boolean atPoint = true; // if the arduino returns that it is at the point, turn true. boolean atHome=true; boolean done = false; float zoom; int state=0; int count=0; int s=0; int repeats=0; float segs=0.0; // how many segments in current line boolean segDone=false; //----------------------------------- // image file setup and coordinates RShape grp; float grpW, grpH, grpX, grpY; RPoint[][] pointPaths; Path[] paths; // global counters int j = 0; int i = 0; // state of point/path iterators int currpath=0; int currpoint=0; int pointinc=1; int stoppoint; //------------------------------------ // main program void setup() { loadSettings(); // graphics //windowW=900; println(windowW); size(windowW, windowH); // load a font for text display PFont font; font = loadFont("Serif-24.vlw"); textFont(font, 12); // // store progress images // Photo = new OutputImage(); // arduino g-code interface machine = new Bot(this); machine.waitForStart(); String filename="circle"+PAGEW+"_"+PAGEH+".txt"; writer = createWriter(filename); machine.setupWriting(writer); background(255); i=0; if (printToConsole) println("(HOME: "+HOMEA + " " +HOMEB+")"); machine.storeHome(); machine.penUp(); Last = new PVector(MOTORSEP/2, sqrt(81*81-(MOTORSEP/2*MOTORSEP/2)), 0); Endpoint = new PVector(); println("(at "+Last.x+","+Last.y+" relative to PAGE(0,0))"); noCursor(); zoom=1.0; println(onScreenScale); } void loadSettings() { xml = loadXML("settings.xml"); // machine geometry XML geometry = xml.getChild("geometry"); HOMEA = float(geometry.getChild("homea").getContent()); HOMEB = float(geometry.getChild("homeb").getContent()); MOTORSEP = float(geometry.getChild("separation").getContent()); // page setup XML page = xml.getChild("page"); PAGEW = float(page.getChild("width").getContent()); PAGEH = float(page.getChild("height").getContent()); PAGE0Y = float(page.getChild("pagezeroy").getContent()); PAGE0X = (MOTORSEP-PAGEW)/2.0; // upper left // output scaling XML output = xml.getChild("output"); PPI = float(output.getChild("ppi").getContent()); // SVG (Pt) to plotter space (inch) coordinate conversion multiplier = float(output.getChild("scale").getContent()); // global scale MAXARCLENGTH = float(output.getChild("maxarclength").getContent()); // global scale MAXLENGTH = float(output.getChild("maxlength").getContent()); // global scale // on-screen display XML screen = xml.getChild("screen"); if (PAGEH>PAGEW) { // portrait: scale width to full height windowH = int(screen.getChild("screenh").getContent()); float onScreenScale=windowH/(PAGEH*PPI); windowW = int((PAGEW*PPI)*onScreenScale); } else { // landscape: scale height to full width windowW = int(screen.getChild("screenw").getContent()); onScreenScale=windowW/(PAGEW*PPI); windowH = int((PAGEH*PPI)*onScreenScale); } } void draw() { stroke(120); strokeWeight(2); fill(220); if (On && !done) { if (drawPatternAtScale(MARGIN+((PAGEW-PAGEH)/2.0), MARGIN, PAGEW-(MARGIN+((PAGEW-PAGEH)/2.0)), PAGEH-MARGIN, zoom)) { doMessage("(next zoom level "+zoom+")"); //zoom=zoom-0.05; state=0; count=0; s=0; println("(done.)"); if (doRepeat) { println("(starting over)"); //background(255); fill(255,192); noStroke(); rect(0,0,windowW,windowH); repeats++; println("(pattern iteration "+repeats+")"); zoom=1.0; } else { println("(stopping)"); done=true; } } } //fill(0); text(repeats, 10, 20); } // keyboard interface void keyPressed() { if (key == 'g') { // start movement On = true; if (printToConsole)println("(GO!)"); machine.DirectCommand("G100 S100"); //moves servo, push pen off page machine.waitForOk(); jumping=true; } if (key == 's') { // stop movement On = false; if (printToConsole)println("(STOP)"); } if (key=='h') { machine.goHome(); } if (key == ' ') { background(255); } if (key=='p') { drawSortedPointPaths(); } if (!On) { switch(key) { case '1': // top left machine.sendVal(0, 0, 0); break; case '2': // top right machine.sendVal(PAGEW, 0, 0); break; case '3': // lower right machine.sendVal(PAGEW, PAGEH, 0); break; case '4': // lower left machine.sendVal(0, PAGEH, 0); break; case '5': // top left machine.sendVal(grpX/PPI, grpY/PPI, 0); break; case '6': // top right machine.sendVal((grpX+grpW)/PPI, grpY/PPI, 0); break; case '7': // lower right machine.sendVal((grpX+grpW)/PPI, (grpY+grpH)/PPI, 0); break; case '8': // lower left machine.sendVal(grpX/PPI, (grpY+grpH)/PPI, 0); break; case '0': machine.penUp(); if (printToConsole)println("(going to stored home position)"); //machine.DirectCommand("G1 X"+HOMEA+" Y"+HOMEB+" Z0.0 F"+FASTFEED); machine.DirectCommand("G0 X"+HOMEA+" Y"+HOMEB+" Z0"); break; case 'd': machine.penDown(); break; case 'u': machine.penUp(); break; default: break; } } } // -------- terminal output and logging -------- // void doMessage(String message) { if (printToConsole) println(message); if (writeToFile) { writer.write(message+"\n"); writer.flush(); } } // -------- striving for the pope's commission -------- // // -------- drawing routines --------// boolean drawPatternAtScale(float left, float top, float right, float bot, float zoom) { float midx=(left+right)/2.0; float midy=(top+bot)/2.0; float tl, tr, tt, tb; tl=lerp(midx, left, zoom); tr=lerp(midx, right, zoom); tt=lerp(midy, top, zoom); tb=lerp(midy, bot, zoom); if (drawcircle((tl+tr)/2.0, (tt+tb)/2.0, (tr-tl)/2.0)) return true; return false; } boolean drawcircle(float xcenter, float ycenter, float radius) { if (drawScaledArc(xcenter, ycenter, radius)) { count=0; s=0; return true; } return false; } boolean drawScaledArc(float xcenter, float ycenter, float radius) { float circum=TWO_PI*radius; int numsteps = int(circum/MAXARCLENGTH); float ang_inc=TWO_PI/numsteps; float theta=TWO_PI-(ang_inc*count); //float theta2=theta+ang_inc; float theta2=theta-ang_inc; float x1, y1, x2, y2; x1 = xcenter - radius * sin(theta); y1 = ycenter - radius * cos(theta); x2 = xcenter - radius * sin(theta2); y2 = ycenter - radius * cos(theta2); s=0; // signalling something drawScaledSegment(x1*PPI, y1*PPI, x2*PPI, y2*PPI); drawCrossHairs(x2*PPI, y2*PPI); count++; if (count>numsteps) return true; return false; } void stop() { machine.penUp(); //machine.DirectCommand("G1 X"+HOMEA+" Y"+HOMEB+" Z0.0 F"+FASTFEED); machine.DirectCommand("G0 X"+HOMEA+" Y"+HOMEB+" Z0.0"); machine.stop(); };