/**********************************************************************
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);
}