/********************************************************************** DRUM PLOTTER Version 2 Code by lingib Last update 15 November 2016 Biarc curves added 15 November 2016. COPYRIGHT My draw_line() function uses an optimised version of Bresenham's algorithm from a paper "Bresenham Line and Circle Drawing", copyright © 1994-2006, W Randolph Franklin (WRF). His material may be used for non-profit research and education, provided that you credit him, and link back to his home page, https://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/bresenham.html The code for my gcode interpreter is a variation of that found at https://github.com/MarginallyClever/gcodecncdemo. The remaining code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License. If not, see . ***************************************************************************/ // ------------------------------- // GLOBALS // ------------------------------- // ----- constants #define PI 3.1415926535897932384626433832795 #define HALF_PI 1.5707963267948966192313216916398 #define TWO_PI 6.283185307179586476925286766559 #define DEG_TO_RAD 0.017453292519943295769236907684886 #define RAD_TO_DEG 57.295779513082320876798154814105 // ----- motor definitions #define MOTOR1 PORTB //pins 8,9,10,11 #define MOTOR2 PORTC //pins A0,A1,A2,A3 #define X_STEPS_PER_MM 51.2 //for GT-2 pulley #define Y_STEPS_PER_MM 7.6 //for 90mm diameter drum #define UP false //clockwise rotation #define DOWN true //counter-clockwise rotation #define LEFT true //counter-clockwise rotation #define RIGHT false //clockwise rotation // ----- plotter definitions #define PEN 3 #define BAUD 9600 #define XON 0x11 //resume transmission (17 decimal) #define XOFF 0x13 //pause transmission (19 decimal) float SCALE_FACTOR = 1; //drawing scale (1 = 100%) float LAST_X = 0; //current X co-ordinate float LAST_Y = 0; //current Y co-ordinate bool DEBUG_ON = false; //true=on; false=off // ----- gcode buffer definitions #define MAX_LENGTH 256 //maximum Inkscape message length char MESSAGE[MAX_LENGTH]; //INKSCAPE string stored here int INDEX=0; //character position in MESSAGE[] char CHARACTER; //an actual character // ----- gcode parameters float X = -9999; //X co-ordinate: -9999 indicates not found float Y = -9999; //X co-ordinate: -9999 indicates not found float I = -9999; //biarc: -9999 indicates not found float J = -9999; //biarc: -9999 indicates not found // ----- gcode circle definitions float LINE_MAX = 2; //maximum line length (mm) when plotting arc // ----------------------- // SETUP // ----------------------- void setup() { // ----- motor1 (X-axis) int pattern = DDRB; //get PORTB data directions pattern = pattern | B00001111; //preserve MSN data direction & DDRB = pattern; //make pins 8,9,10,11 outputs // ----- motor2 (Y-axis) pattern = DDRC; //get PORTC data directions pattern = pattern | B00001111; //preserve MSN data direction & DDRC = pattern; //make pins A0,A1,A2,A3 outputs // ----- pen-lift pinMode(PEN, OUTPUT); //D3 TCCR2A = _BV(COM2B1) | _BV(COM2B0) | _BV(WGM20); //PWM TCCR2B = _BV(WGM22) | _BV(CS22) | _BV(CS21) | _BV(CS20); //div 1024 OCR2A = 156; //20mS period OCR2B = 148; //2mS (pen-up) /* The above pen-lift comprises a standard servo which requires 1mS or 2mS pulses with a fixed period of 20mS for pen-down or pen-up. The Arduino "bit value" macro, #define _BV(x) (1 << x), is used to set the Timer2 mode to "phase-correct PWM" with a variable "top-limit". In this mode the timer counts up to the value entered into register OCR2A then back down to zero. The following values were used to obtain a 20mS period at pin D3: clock: 16 MHz prescaler: 1024 top-limit (OCR2A): 156 period: 16MHz/1024/(156*2) = 50Hz (20mS) If you enter a value into register OCR2B that is LESS than the value in register OCR2A then timer2 will will pass through the value in OCR2B twice ... once on the way up ... and once on the way down. The duration of the output pulse on pin D3 is the time that the count in OCR2A is greater than the value in OCR2B. A value of 148 entered into OCR2B creates a 1mS pulse: period: 156-148)*20mS/156 = 1mS (pen-up) A value of 140 entered into OCR2B creates a 2mS pulse): period: 156-140)*20mS/156 = 2mS (pen-down) */ // ----- plotter setup Serial.begin(BAUD); //open serial link menu(); //display commands } //-------------------------------------------------------------------------- // MAIN LOOP //-------------------------------------------------------------------------- void loop() { if (Serial.available() > 0){ //check serial input for data CHARACTER = Serial.read(); //get character if (INDEX < (MAX_LENGTH-1)){ MESSAGE[INDEX++] = CHARACTER; //store it Serial.print(CHARACTER); //display it }else{ Serial.println(""); Serial.print(F("Error: buffer overflow")); } if (CHARACTER == '\n'){ //Inkscape lines end with \r\n MESSAGE[INDEX]=0; //insert end-of-string char Serial.print(XOFF); //tell terminal to stop sending Serial.print(": "); //screen formatting process(); //perform the task INDEX=0; //prepare for next message Serial.print(XON); //tell terminal to resume sending Serial.print(": "); //screen formatting } } } //--------------------------------------------------------------------------- // MENU // The Arduino F() macro is used to conserve RAM. //--------------------------------------------------------------------------- void menu() { Serial.println(F("")); Serial.println(F(" ------------------------------------------------------")); Serial.println(F(" MENU")); Serial.println(F(" (all keys case-sensitive)")); Serial.println(F(" ------------------------------------------------------")); Serial.println(F(" M0...................menu")); Serial.println(F(" G0 X## Y##...........goto XY (pen-up)")); Serial.println(F(" G1 X## Y##...........goto XY (pen-down)")); Serial.println(F(" T1...................move pen to 0,0")); Serial.println(F(" T2 S##.##............set drawing Scale (1=100%)")); Serial.println(F(" T3...................pen up")); Serial.println(F(" T4...................pen down")); Serial.println(F(" T5...................draw ABC pattern")); Serial.println(F(" T6...................draw test pattern 1")); Serial.println(F(" T7...................draw test pattern 2")); Serial.println(F(" ------------------------------------------------------")); } //-------------------------------------------------------------------------- // PROCESS // // Extracts data from various code groups prior to performing an action. // // Inkscape output always has a Y-coordinate if there is an X-coordinate which // means the following simplifications are valid: // G00: pen-up. Use X,Y (if any) // G01: pen-down. Use X,Y (if any) // G02: pen-down. Use X,Y (if any) // G03: pen-down. Use X,Y (if any) //-------------------------------------------------------------------------- void process(){ // ------------------------ // G gcodes // ------------------------ int gcode = get_value('G'); switch (gcode){ // -----linear move with pen-up case 0:{ pen_up(); X = get_value('X'); //get_value() returns -9999 if X not found Y = get_value('Y'); //get_value() returns -9999 if Y not found if ((X>=0)&&(Y>=0)) move_to(X, Y); break; } // ----- linear move with pen-down case 1:{ pen_down(); X = get_value('X'); //get_value() returns -9999 if X not found Y = get_value('Y');; //get_value() returns -9999 if Y not found if ((X>=0)&&(Y>=0)) move_to(X, Y); break; } // ----- circular move (clockwise) with pen-down case 2:{ pen_down(); X = get_value('X'); //get_value() returns -9999 if X not found Y = get_value('Y'); //get_value() returns -9999 if Y not found I = get_value('I'); //get_value() returns -9999 if I not found J = get_value('J'); //get_value() returns -9999 if J not found // if ((X>=0) && (Y>=0) && (I>-9999) && (J>-9999)) move_to(X, Y); //line if ((X>=0) && (Y>=0) && (I>-9999) && (J>-9999)) draw_arc_cw(X, Y, I, J); //arc break; } // ----- circular move (counter-clockwise) with pen-down case 3:{ pen_down(); X = get_value('X'); //get_value() returns -9999 if X not found Y = get_value('Y'); //get_value() returns -9999 if Y not found I = get_value('I'); //get_value() returns -9999 if I not found J = get_value('J'); //get_value() returns -9999 if J not found //if ((X>=0) && (Y>=0) && (I>-9999) && (J>-9999)) move_to(X, Y); //line if ((X>=0) && (Y>=0) && (I>-9999) && (J>-9999)) draw_arc_ccw(X, Y, I, J); //arc break; } // ----- default default:{ break; } } // ------------------------ // M miscellaneous codes // ------------------------ int mcode = get_value('M'); switch (mcode){ // ----- display menu // M0 is normally used to stop a CNC machine // in which case you need the menu case 0:{ menu(); break; } // ----- default default:{ break; } } // ------------------------ // T (test) codes // T0 not used as incoming 'T' can trigger case T0 // ------------------------ int tcode = get_value('T'); int step; //motor-step counter switch (tcode){ // ----- pen position case 1:{ // ----- instructions Serial.println(F("")); Serial.println(F(" ----------------------------------------------")); Serial.println(F(" Position the pen over the 0,0 co-ordinate:")); Serial.println(F(" ----------------------------------------------")); Serial.println(F(" X-axis: Y-axis:")); Serial.println(F(" 'A' 'S' 'K' 'L'")); Serial.println(F(" <- -> <- ->")); Serial.println(F(" Exit = 'E'")); // ----- flush the buffer while (Serial.available() > 0) Serial.read(); // ----- control motors with 'A', 'S', 'K', and 'L' keys char keystroke = ' '; while (keystroke != 'E'){ //press 'E' key to exit // ----- check for keypress if (Serial.available() > 0){ keystroke = Serial.read(); } // ----- select task switch (keystroke){ case 'A':{ // ----- move pen 5mm left for (step=0; step<512; step++){ stepX(LEFT); } keystroke = ' '; //otherwise motor will continue to rotate break; } case 'S':{ // ------ move pen 5mm right for (step=0; step<512; step++){ stepX(RIGHT); } keystroke = ' '; break; } case 'K':{ // ----- move pen 5mm up for (step=0; step<72; step++){ stepY(UP); } keystroke = ' '; break; } case 'L':{ // ----- move pen 5mm down for (step=0; step<72; step++){ stepY(DOWN); } keystroke = ' '; break; } case 'E':{ // ----- exit Serial.println(F(" ")); Serial.println(F(" Calibration complete ...")); break; } // ----- default for keystroke default:{ break; } } } break; } case 2:{ float scale = get_value('S'); if (scale > 0){ Serial.print(F(" Drawing now ")); Serial.print(scale*100); Serial.println(F("%")); SCALE_FACTOR = scale; } else { Serial.println("Invalid scale factor ... try again. (1 = 100%)"); } break; } // ----- raise the pen case 3:{ pen_up(); break; } // ------ lower the pen case 4:{ pen_down(); break; } // ----- draw ABC case 5:{ abc(); break; } // ----- draw test pattern case 6:{ test_pattern(); break; } // ----- draw test pattern 2 case 7:{ test_pattern_2(); break; } //----- default default:{ break; } } } //---------------------------------------------------------------------------- // GET_VALUE // Returns the float that immediately follows a specific code // It assumes that there is a single ' ' between values. // If the gcode is not found then an error code (-999) is returned. //---------------------------------------------------------------------------- float get_value(char gcode){ char *ptr=MESSAGE; while ((ptr>=MESSAGE) && (ptr<(MESSAGE+MAX_LENGTH))){ //validate pointer if (*ptr==gcode){ return(atof(ptr+1)); } ptr=strchr(ptr,' ')+1; //ptr==NULL if ' ' not found } return(-9999); //negative value used to indicate an error } //---------------------------------------------------------------- // MOVE_TO // Move the pen to the next co-ordinate //-------------------------------------------------------------------------- void move_to(float x, float y ){ // ----- calculate motor steps int steps_x = (int)((x-LAST_X)*X_STEPS_PER_MM); int steps_y = (int)((y-LAST_Y)*Y_STEPS_PER_MM); // ----- draw the line draw_line(steps_x, steps_y); // ----- remember last position LAST_X = x; LAST_Y = y; } //------------------------------------------------ // DRAW LINE // The draw_line() function uses an optimised version of the Bresenham line // drawing algorithm described in a paper, "Bresenham Line and Circle Drawing", // W Randolf Franklin (WRF), https://www.ecse.rpi.edu/~ /Research/Short_Notes/bresenham.html. // This material may be used for non-profit research and education providing a // link is made to the above home page. // // pseudocode: // assumes current XY co-ordinate is 0,0 // m=dy/dx; //slope // e=0; //error // plot(0,0); // y=0; // for(x=1; x<=x1; x++){ // e+=m; //add the slope to e each time we increment x // if (e>=0.5){ // e-=0.5; //plot above the line // y++; // } // plot(x,y); // } // // The algorithm is converted to integer form by // scaling 'm' and 'e' by 2*x1 // // This is okay as 'm' and 'e' are only used in // the if statement and any branching will occur // at the same place. //------------------------------------------------ void draw_line(int steps_X, int steps_Y){ // ----- variables int X, Y, x1, y1, octant; // ----- scale drawing X = steps_X*SCALE_FACTOR; Y = steps_Y*SCALE_FACTOR; // ----- determine octant if ((X>=0) && (Y>=0)) {abs(Y)>abs(X)?octant=1:octant=0;} if ((X<0) && (Y>=0)) {abs(Y)>abs(X)?octant=2:octant=3;} if ((X<0) && (Y<0)) {abs(Y)>abs(X)?octant=5:octant=4;} if ((X>=0) && (Y<0)) {abs(Y)>abs(X)?octant=6:octant=7;} // ----- map input values to octant zero switch(octant){ case 0:{x1=X; y1=Y; break;} case 1:{x1=Y; y1=X; break;} case 2:{x1=Y; y1=-X; break;} case 3:{x1=-X; y1=Y; break;} case 4:{x1=-X; y1=-Y; break;} case 5:{x1=-Y; y1=-X; break;} case 6:{x1=-Y; y1=X; break;} case 7:{x1=X; y1=-Y; break;} } // ---- bresenham straight line for octant 0 int m=(y1<<1); //constant:slope scaled by 2*x1 int E=(x1<<1); //constant:2*x1 for use in loop int e=-x1; //add offset of -x1 so test uses zero int y=0; //start value for (int x=1; x<=x1; x++){ e+=m; if (e>=0){ //midpoint equals E/2+offset=0 e-=E; //E=2*x1 // ----- map Y increment to correct axis switch(octant){ case 0:{stepY(UP); break;} case 1:{stepX(RIGHT); break;} case 2:{stepX(LEFT); break;} case 3:{stepY(UP); break;} case 4:{stepY(DOWN); break;} case 5:{stepX(LEFT); break;} case 6:{stepX(RIGHT); break;} case 7:{stepY(DOWN); break;} } } // ----- map X increment to correct axis switch(octant){ case 0:{stepX(RIGHT); break;} case 1:{stepY(UP); break;} case 2:{stepY(UP); break;} case 3:{stepX(LEFT);break;} case 4:{stepX(LEFT);break;} case 5:{stepY(DOWN);break;} case 6:{stepY(DOWN);break;} case 7:{stepX(RIGHT);break;} } } } //------------------------------------------------ // STEPX // Controls the BJY-48 (X-axis) stepping-motor //------------------------------------------------ void stepX(bool direction){ // ----- local variables static int STEP = 0; //current motor step int pattern; //used for bitwisemotor control switch(STEP){ case 0: pattern = MOTOR1; pattern = pattern & B11110000; // erase motor current pattern pattern = pattern | B00000011; // create new motor pattern MOTOR1 = pattern; break; case 1: pattern =MOTOR1; pattern = pattern & B11110000; pattern = pattern | B00000110; MOTOR1 = pattern; break; case 2: pattern = MOTOR1; pattern = pattern & B11110000; pattern = pattern | B00001100; MOTOR1 = pattern; break; case 3: pattern = MOTOR1; pattern = pattern & B11110000; pattern = pattern | B00001001; MOTOR1 = pattern; break; default: pattern = MOTOR1; pattern = pattern & B11110000; pattern = pattern | B00000000; MOTOR1 = pattern; break; } if(direction){ STEP++; if(STEP>3){ //wrap-around STEP=0; } }else{ STEP--; if(STEP<0){ //wrap-around STEP=3; } } delay(2); //allow motor time to move } //------------------------------------------------ // STEPY // Controls the BJY-48 (Y-axis) stepping-motor //------------------------------------------------ void stepY(bool direction){ // ----- local variables static int STEP = 0; //current motor step int pattern; //used for bitwise motor control switch(STEP){ case 0: pattern = MOTOR2; pattern = pattern & B11110000; // erase motor current pattern pattern = pattern | B00000011; // create new motor pattern MOTOR2 = pattern; break; case 1: pattern =MOTOR2; pattern = pattern & B11110000; pattern = pattern | B00000110; MOTOR2 = pattern; break; case 2: pattern = MOTOR2; pattern = pattern & B11110000; pattern = pattern | B00001100; MOTOR2 = pattern; break; case 3: pattern = MOTOR2; pattern = pattern & B11110000; pattern = pattern | B00001001; MOTOR2 = pattern; break; default: pattern = MOTOR2; pattern = pattern & B11110000; pattern = pattern | B00000000; MOTOR2 = pattern; break; } if(direction){ STEP++; if(STEP>3){ //wrap-around STEP=0; } }else{ STEP--; if(STEP<0){ //wrap-around STEP=3; } } delay(14); //match drum speed to print-head } //---------------------------------------------------------------------------- // DRAW ARC CLOCKWISE (G02) //---------------------------------------------------------------------------- void draw_arc_cw(float x, float y, float i, float j){ // ----- variables float thisX = LAST_X, //current X co-ordinate thisY = LAST_Y, //current Y co-ordinate nextX = x, //next X co-ordinate nextY = y, //next Y co-ordinate newX, //interpolated X co-ordinate newY, //interpolated Y co-ordinate I = i, //horizontal distance thisX from circle center J = j, //vertical distance thisY from circle center circleX = thisX + I, //circle X co-ordinate circleY = thisY + J, //circle Y co-ordinate delta_x, //horizontal distance between thisX and nextX delta_y, //vertical distance between thisY and nextY chord, //line_length between lastXY and nextXY radius, //circle radius alpha, //interior angle of arc beta, //fraction of alpha arc, //subtended by alpha current_angle, //measured CCW from 3 o'clock next_angle; //measured CCW from 3 o'clock // ----- calculate arc delta_x = thisX - nextX; delta_y = thisY - nextY; chord = sqrt(delta_x*delta_x + delta_y*delta_y); radius = sqrt(I*I + J*J); alpha = 2*asin(chord/(2*radius)); //see construction lines arc = alpha*radius; //radians // ----- sub-divide alpha int segments=1; if (arc>LINE_MAX){ segments = (int)(arc/LINE_MAX); beta = alpha/segments; }else{ beta = alpha; } // ----- calculate current angle // atan2() angles between 0 and PI are CCW +ve from 3 o'clock. // atan2() angles between 2*PI and PI are CW -ve relative to 3 o'clock current_angle = atan2(-J, -I); if (current_angle<=0) current_angle+=2*PI; //angles now 360..0 degrees CW if (DEBUG_ON){ // ----- co-ordinates Serial.print(" startX:"); Serial.print(thisX,2); Serial.print(" startY:"); Serial.print(thisY,2); Serial.print(" start_angle:"); Serial.println((current_angle*RAD_TO_DEG),2); } // ----- plot intermediate CW co-ordinates next_angle = current_angle; //initialise angle for (int segment=1; segmentLINE_MAX){ segments = (int)(arc/LINE_MAX); beta = alpha/segments; }else{ beta = alpha; } // ----- calculate current angle // atan2() angles between 0 and PI are CCW +ve from 3 o'clock. // atan2() angles between 2*PI and PI are CW -ve relative to 3 o'clock current_angle = atan2(-J, -I); if (current_angle<=0) current_angle+=2*PI; //angles now 360..0 degrees CW if (DEBUG_ON){ // ----- co-ordinates Serial.print(" startX:"); Serial.print(thisX,2); Serial.print(" startY:"); Serial.print(thisY,2); Serial.print(" start_angle:"); Serial.println((current_angle*RAD_TO_DEG),2); } // ----- plot intermediate CCW co-ordinates next_angle = current_angle; //initialise angle for (int segment=1; segment2*PI) next_angle-=2*PI; //check if angle crosses zero newX = circleX + radius*cos(next_angle); //standard circle formula newY = circleY + radius*sin(next_angle); if (DEBUG_ON){ Serial.print(" segment:"); Serial.print(segment); Serial.print(" newX:"); Serial.print(newX); Serial.print(" newY:"); Serial.print(newY); Serial.print(" current_angle:"); Serial.println((next_angle*RAD_TO_DEG),2); } move_to(newX, newY); } // ----- draw final line move_to(nextX, nextY); } //--------------------------------------------------------------------------- // PEN_UP // Raise the pen // Changing the value in OCR2B changes the pulse-width to the SG-90 servo //--------------------------------------------------------------------------- void pen_up(){ OCR2B = 148; //1mS pulse delay(100); //give pen-lift time to respond } //--------------------------------------------------------------------------- // PEN_DOWN // Lower the pen // Changing the value in OCR2B changes the pulse-width to the SG-90 servo //--------------------------------------------------------------------------- void pen_down(){ OCR2B = 140; //2mS pulse delay(100); //give pen-lift time to respond } //---------------------------------------------------------------------------- // ABC test pattern //---------------------------------------------------------------------------- void abc(){ // ------ letter C pen_up(); move_to(71.607584,21.035879); pen_down(); move_to(70.163261, 20.392706); move_to(68.665833, 19.931058); move_to(67.123530, 19.654409); move_to(65.471170, 19.558347); move_to(60.824111, 20.352237); move_to(57.604313, 22.327055); move_to(55.521083, 25.458294); move_to(54.702496, 29.861135); move_to(55.523176, 34.275230); move_to(57.604313, 37.395213); move_to(60.826497, 39.380152); move_to(65.471170, 40.177231); move_to(67.123530, 40.081169); move_to(68.665833, 39.804521); move_to(70.163261, 39.342872); move_to(71.607584, 38.699700); move_to(71.607584, 34.586572); move_to(70.133934, 35.457974); move_to(68.798946, 36.010859); move_to(67.396000, 36.346751); move_to(65.883816, 36.463436); move_to(63.362672, 35.969576); move_to(61.571020, 34.706372); move_to(60.460591, 32.773568); move_to(60.000312, 29.861135); move_to(60.459740, 26.961684); move_to(61.571020, 25.029205); move_to(63.362672, 23.766000); move_to(65.883816, 23.272141); move_to(67.396000, 23.388826); move_to(68.798946, 23.724718); move_to(70.133934, 24.277603); move_to(71.607584, 25.149006); move_to(71.607584, 21.035879); pen_up(); // ------ top inside-loop in letter 'B' pen_up(); move_to(43.041974, 32.124019); pen_down(); move_to(44.193140, 32.287491); move_to(44.878907, 32.656463); move_to(45.321578, 33.273441); move_to(45.504526, 34.227172); move_to(45.322608, 35.168069); move_to(44.878907, 35.784570); move_to(44.190670, 36.163294); move_to(43.041974, 36.330325); move_to(40.206713, 36.330325); move_to(40.206713, 32.124019); move_to(43.041974, 32.124019); pen_up(); // ----- bottom inside-loop in letter 'B' pen_up(); move_to(43.215018, 23.431875); pen_down(); move_to(44.684832, 23.634884); move_to(45.531148, 24.084119); move_to(46.084429, 24.845298); move_to(46.316505, 26.054160); move_to(46.088504, 27.238072); move_to(45.544461, 27.984270); move_to(44.697894, 28.432828); move_to(43.215018, 28.636513); move_to(40.206713, 28.636513); move_to(40.206713, 23.431875); move_to(43.215018, 23.431875); pen_up(); // ----- outside of letter 'B' pen_up(); move_to(47.980391, 30.579932); pen_down(); move_to(49.467494, 29.872216); move_to(50.536123, 28.809558); move_to(51.189538, 27.438932); move_to(51.441274, 25.641517); move_to(50.881551, 23.051631); move_to(49.497855, 21.355344); move_to(47.408388, 20.394118); move_to(43.587730, 19.944368); move_to(35.081941, 19.944368); move_to(35.081941, 39.817832); move_to(42.775754, 39.817832); move_to(46.788467, 39.403201); move_to(48.765745, 38.566589); move_to(50.084134, 37.024736); move_to(50.629298, 34.559950); move_to(50.441596, 33.165564); move_to(49.950432, 32.084086); move_to(49.146555, 31.229561); move_to(47.980391, 30.579932); pen_up(); // ----- outside of letter 'A' pen_up(); move_to(26.057020, 23.564986); pen_down(); move_to(18.043741, 23.564986); move_to(16.779187, 19.944368); move_to(11.627794, 19.944368); move_to(18.988829, 39.817832); move_to(25.098621, 39.817832); move_to(32.459656, 19.944368); move_to(27.308262, 19.944368); move_to(26.057020, 23.564986); pen_up(); // ----- inside of letter 'A' pen_up(); move_to(19.321606, 27.252160); pen_down(); move_to(24.765843, 27.252160); move_to(22.050380, 35.158949); move_to(19.321606, 27.252160); pen_up(); // home -------------- move_to(0.0000, 0.0000); } /*************************************************************************** TEST_PATTERN ***************************************************************************/ void test_pattern(){ // circle ------------ pen_up(); move_to(136.738441, 145.187821); pen_down(); move_to(134.380298, 133.732203); move_to(127.595170, 123.920361); move_to(117.521703, 117.417222); move_to(105.521361, 115.111091); move_to(93.521020, 117.417222); move_to(83.447553, 123.920361); move_to(76.662425, 133.732203); move_to(74.304282, 145.187821); move_to(76.662425, 156.643438); move_to(83.447553, 166.455281); move_to(93.521020, 172.958420); move_to(105.521361, 175.264551); move_to(117.521703, 172.958420); move_to(127.595170, 166.455281); move_to(134.380298, 156.643438); move_to(136.738441, 145.187821); move_to(136.738441, 145.187821); pen_up(); // back-slash ----------- pen_up(); move_to(37.813081, 210.330315); pen_down(); move_to(174.084903, 79.190066); pen_up(); // slash ------------- pen_up(); move_to(37.527994, 79.190066); pen_down(); move_to(173.799816, 210.330315); pen_up(); // square ------------ pen_up(); move_to(37.656509, 210.457146); pen_down(); move_to(173.929525, 210.457146); move_to(173.929525, 79.022220); move_to(37.656509, 79.022220); move_to(37.656509, 210.457146); pen_up(); // home -------------- move_to(0.0000, 0.0000); } /*************************************************************************** TEST_PATTERN_2 ***************************************************************************/ void test_pattern_2(){ // ----- move to the centre of the square pen_up(); move_to(100,100); // ----- draw octant 0 radials pen_down(); move_to(150,100); pen_up(); move_to(100,100); pen_down(); move_to(150,125); pen_up(); move_to(100,100); pen_down(); move_to(150,150); pen_up(); move_to(100,100); // ----- draw octant 1 radials pen_down(); move_to(125,150); pen_up(); move_to(100,100); pen_down(); move_to(100,150); pen_up(); move_to(100,100); // ----- draw octant 2 radials pen_down(); move_to(75,150); pen_up(); move_to(100,100); pen_down(); move_to(50,150); pen_up(); move_to(100,100); // ----- draw octant 3 radials pen_down(); move_to(50,125); pen_up(); move_to(100,100); pen_down(); move_to(50,100); pen_up(); move_to(100,100); // ----- draw octant 4 radials pen_down(); move_to(50,75); pen_up(); move_to(100,100); pen_down(); move_to(50,50); pen_up(); move_to(100,100); // ----- draw octant 5 radials pen_down(); move_to(75,50); pen_up(); move_to(100,100); pen_down(); move_to(100,50); pen_up(); move_to(100,100); // ----- draw octant 6 radials pen_down(); move_to(125,50); pen_up(); move_to(100,100); pen_down(); move_to(150,50); pen_up(); move_to(100,100); // ----- draw octant 7 radials pen_down(); move_to(150,75); pen_up(); move_to(100,100); pen_up(); // ----- draw box move_to(50,50); pen_down(); move_to(50,150); move_to(150,150); move_to(150,50); move_to(50,50); pen_up(); // home -------------- move_to(0.0000, 0.0000); }