// Drawbot portrait of Erik Satie // (cc) 2008 AS220 Labs #include #define motorStepsA 400 #define motorStepsB 400 #define motorPinA1 8 #define motorPinA2 9 #define motorPinB1 10 #define motorPinB2 11 // Approximate number of steps per inch, calculated from radius of spool // and the number of steps per radius int StepUnit = 102; // Approximate dimensions of the total drawing area int w= 40.5*StepUnit; int h= 42.5*StepUnit; // Coordinates of current (starting) point int x1= w/2; int y1= h; // Approximate length of strings from marker to staple int a1= sqrt(pow(x1,2)+pow(y1,2)); int b1= sqrt(pow((w-x1),2)+pow(y1,2)); // Size of image array int rows = 31; int columns = 23 ; // Radius of pixel circles, in StepUnits int radius = 40; // equals about .4 inches // Size of page int pageW = columns*radius*2; int pageH = rows*radius*2; // Size of one "pixel" int cellW =2*radius; int cellH = 2*radius; // Coordinate of upper left corner of page int page0X = x1-(pageW/2)-400; int page0Y = y1-pageH-500; // The image: 0=white, 11=black char image[] = { 1, 1, 2, 2, 3, 3, 5, 9, 10, 10, 11, 11, 11, 11, 10, 8, 7, 8, 9, 9, 9, 9, 9, 1, 1, 2, 3, 3, 7, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 10, 8, 8, 8, 8, 8, 8, 1, 2, 2, 3, 5, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 8, 8, 8, 8, 8, 1, 2, 2, 3, 7, 10, 10, 11, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 8, 8, 8, 8, 9, 1, 1, 2, 2, 8, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 9, 8, 8, 8, 9, 1, 1, 2, 2, 8, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 9, 8, 8, 8, 8, 1, 1, 1, 1, 8, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 9, 7, 7, 7, 8, 0, 1, 1, 1, 8, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 9, 8, 8, 9, 1, 4, 6, 8, 9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 9, 8, 7, 9, 9, 9, 10, 9, 9, 9, 9, 10, 10, 10, 9, 7, 7, 9, 10, 11, 11, 11, 11, 11, 9, 2, 7, 9, 9, 9, 7, 5, 6, 8, 6, 6, 9, 7, 3, 1, 2, 7, 10, 11, 11, 11, 11, 10, 0, 0, 3, 5, 0, 1, 4, 5, 4, 0, 0, 3, 7, 3, 0, 1, 4, 9, 10, 11, 11, 11, 9, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 2, 6, 2, 0, 0, 3, 8, 10, 10, 11, 9, 7, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1, 0, 0, 1, 3, 5, 7, 7, 9, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 7, 8, 7, 8, 8, 0, 0, 0, 0, 0, 0, 3, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 8, 7, 7, 8, 0, 0, 0, 0, 0, 1, 6, 5, 2, 0, 0, 0, 1, 1, 0, 1, 1, 3, 4, 3, 3, 4, 4, 0, 0, 0, 0, 1, 4, 6, 5, 3, 3, 1, 1, 1, 2, 2, 1, 2, 3, 3, 4, 4, 5, 5, 0, 0, 0, 2, 5, 8, 8, 9, 8, 8, 5, 3, 3, 2, 3, 3, 3, 2, 4, 4, 4, 4, 5, 0, 0, 0, 2, 6, 5, 3, 6, 9, 9, 8, 6, 6, 5, 4, 5, 2, 2, 3, 3, 4, 5, 5, 0, 0, 0, 0, 4, 3, 1, 3, 6, 8, 9, 9, 8, 6, 8, 6, 2, 2, 3, 3, 4, 5, 5, 0, 0, 0, 0, 5, 2, 0, 1, 4, 9, 9, 9, 9, 8, 8, 2, 1, 0, 2, 3, 4, 5, 5, 0, 0, 0, 0, 3, 4, 2, 6, 9, 9, 10, 9, 9, 7, 4, 0, 0, 0, 4, 4, 4, 5, 6, 0, 0, 0, 0, 3, 6, 7, 9, 9, 9, 9, 8, 3, 0, 0, 0, 0, 1, 8, 7, 4, 5, 6, 0, 0, 0, 0, 3, 6, 9, 9, 9, 9, 9, 3, 0, 0, 0, 0, 1, 7, 10, 10, 6, 5, 6, 0, 0, 0, 0, 5, 8, 9, 9, 9, 9, 5, 1, 0, 0, 0, 2, 7, 10, 10, 10, 10, 8, 7, 0, 0, 0, 0, 7, 9, 9, 9, 9, 8, 3, 1, 0, 3, 7, 9, 10, 10, 10, 11, 11, 11, 9, 0, 0, 0, 1, 7, 9, 9, 9, 6, 3, 1, 4, 8, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 0, 0, 0, 1, 7, 8, 9, 9, 5, 4, 6, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 0, 0, 0, 0, 3, 6, 9, 8, 7, 9, 9, 7, 8, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 0, 0, 0, 0, 3, 9, 9, 1, 8, 9, 2, 3, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11 }; // Stepper motor objects: A is left, B is right Stepper StepperA(motorStepsA, motorPinA1,motorPinA2); Stepper StepperB(motorStepsB, motorPinB1,motorPinB2); void setup() { // Set up stepper motors and random # seed, if needed delay(2000); StepperA.setSpeed(15); StepperB.setSpeed(15); delay(2000); randomSeed(analogRead(0)); } float rads(int n) { // Return an angle in radians return (n/180.0 * PI); } void moveTo(int x2, int y2) { // Turn the stepper motors to move the marker from the current point (x1, // y1) to (x2, y2) // Note: This only moves in a perfectly straight line if // the distance is the same in both dimensions; this should be fixed, but it // works well // a2 and b2 are the final lengths of the left and right strings int a2 = sqrt(pow(x2,2)+pow(y2,2)); int b2 = sqrt(pow((w-x2),2)+pow(y2,2)); int stepA; int stepB; if (a2>a1) { stepA=1; } if (a1>a2) { stepA=-1; } if (a2==a1) { stepA=0; } if (b2>b1) { stepB=1; } if (b1>b2) { stepB=-1; } if (b2==b1) { stepB=0; } // Change the length of a1 and b1 until they are equal to the desired length while ((a1!=a2) || (b1!=b2)) { if (a1!=a2) { a1 += stepA; StepperA.step(stepA); } if (b1!=b2) { b1 += stepB; StepperB.step(-stepB); } } x1 = x2; y1=y2; } void drawCurve(float x, float y, float fx, float fy, float cx, float cy) { // Draw a Quadratic Bezier curve from (x, y) to (fx, fy) using control pt // (cx, cy) float xt=0; float yt=0; for (float t=0; t<=1; t+=.0025) { xt = pow((1-t),2) *x + 2*t*(1-t)*cx+ pow(t,2)*fx; yt = pow((1-t),2) *y + 2*t*(1-t)*cy+ pow(t,2)*fy; moveTo(xt, yt); } } void drawCircle(int centerx, int centery, int radius) { // Estimate a circle using 20 arc Bezier curve segments int segments =20; int angle1 = 0; int midpoint=0; moveTo(centerx+radius, centery); for (float angle2=360/segments; angle2<=360; angle2+=360/segments) { midpoint = angle1+(angle2-angle1)/2; float startx=centerx+radius*cos(rads(angle1)); float starty=centery+radius*sin(rads(angle1)); float endx=centerx+radius*cos(rads(angle2)); float endy=centery+radius*sin(rads(angle2)); int t1 = rads(angle1)*1000 ; int t2 = rads(angle2)*1000; int t3 = angle1; int t4 = angle2; drawCurve(startx,starty,endx,endy, centerx+2*(radius*cos(rads(midpoint))-.25*(radius*cos(rads(angle1)))-.25*(radius*cos(rads(angle2)))), centery+2*(radius*sin(rads(midpoint))-.25*(radius*sin(rads(angle1)))-.25*(radius*sin(rads(angle2)))) ); angle1=angle2; } } void drawCircles(int number, int centerx, int centery, int r) { // Draw a certain number of concentric circles at the given center with // radius r int dr=0; if (number > 0) { dr = r/number; for (int k=0; k=0; i--) { for (int j=columns-1; j>=0; j--) { char n = image[i*columns + j]; n = constrain(n, 0, 11); // Draw big circles drawCircles(n, page0X+(j*cellW), page0Y+(i*cellH), radius); if ((j>0) && (i>0)) { // Draw little circles in-between big circles // Average the values of the adjoining pixels n=(image[(i-1)*columns + j]+ image[(i-1)*columns+j-1]+ image[i*columns+j-1]+ image[i*columns + j])/8; n = constrain(n, 0, 11); drawCircles(n, page0X+(j*cellW-(cellW/2)), page0Y+(i*cellH-(cellH/2)), radius/2); } } } // Move off page and wait moveTo(page0X-500, y1); while (1) {} }