Close

Drawing a line

A project log for RigTig's Big 3D Printer

A DIY 3D printer (big volume, inexpensive, lightweight and portable).

rigtigRigTig 09/22/2016 at 14:270 Comments

An object is divided into layers as thick as the printer lays down material in the vertical direction, and each layer is divided into lines as far apart as the width of the extrusion. So, we build our object using lines to make each layer, and multiple layers to make the vertical dimension. This means our simplest thing to print in a 3D printer is a straight line.

Pixels and stepper motors have a common characteristic in that a line has to be approximated into integral elements. Essentially, a line is represented by one or more steps in a direction and a step in a different (usually orthogonal) direction before continuing in the same fashion towards the end of the line. Mr. Bresenham figured this out a long time ago, and his algorithm is still widely applicable in fundamental graphics representation in digital media. [I'll leave it as an exercise for the reader to locate a suitable description of the Bresenham algorithm. I've used it for so long, I have no idea where I learnt it.]

A 3D printer actually uses four degrees of freedom (so a minimum of 4 motors). Three degrees of freedom provide the movement in our 3D world, and the fourth drives the extruder (thus determining the amount of material extruded). In a GCODE file, the four degrees of freedom are called X, Y, Z and E. Each GCODE line (of movement) moves from the position in the previous GCODE line to the one in this GCODE line, so the position described in the GCODE line is the state of the machine after the GCODE line is executed. The machine is supposed to move in a straight line from one position to the next. [Yes, I know there are GCODE commands for doing arcs, but we'll stick to the easier stuff first.]

The following Arduino code is the routine for drawing a single line in the rb3dp. Each motor moves one step at a time, and the Bresenham algorithm says which one to move to keep the actual movement as close to the intended line as possible.

static void line(float x,float y,float z, float e) {
  long l1,l2,l3,l4;
  IK(x,y,z,l1,l2,l3);
  l4 = e * e2steps;
  // in machine space
  long d1 = l1 - m1Pos_steps;
  long d2 = l2 - m2Pos_steps;
  long d3 = l3 - m3Pos_steps;
  long d4 = l4 - m4Pos_steps;

  long ad1=abs(d1);
  long ad2=abs(d2);
  long ad3=abs(d3);
  long ad4=abs(d4);

  long ad1x2=ad1*2;
  long ad2x2=ad2*2;
  long ad3x2=ad3*2;
  long ad4x2=ad4*2;
  
  int dir1=d1<0?M1_REEL_IN:M1_REEL_OUT;
  int dir2=d2<0?M2_REEL_IN:M2_REEL_OUT;
  int dir3=d3<0?M3_REEL_IN:M3_REEL_OUT;
  int dir4=d4<0?M4_REEL_IN:M4_REEL_OUT;

  long i, err1, err2, err3;
  
  if((ad1>=ad2) && (ad1>=ad3) && (ad1>=ad4)) {
    err1=ad2x2 - ad1;
    err2=ad3x2 - ad1;
    err3=ad4x2 - ad1;
    for(i=0; i0) {
        onestep(m2,dir2);
        err1 -= ad1x2;
      }
      if(err2>0) {
        onestep(m3,dir3);
        err2 -= ad1x2;
      }
      if(err3>0) {
        onestep(m4,dir4);
        err3 -= ad1x2;
      }
      err1 += ad2x2;
      err2 += ad3x2;
      err3 += ad4x2;
      onestep(m1,dir1);
    }  
    if(readSwitches()) return;
  }

  if((ad2>=ad1) && (ad2>=ad3) && (ad2>=ad4)) {
    err1=ad1x2 - ad2;
    err2=ad3x2 - ad2;
    err3=ad4x2 - ad2;
    for(i=0; i0) {
        onestep(m1,dir1);
        err1 -= ad2x2;
      }
      if(err2>0) {
        onestep(m3,dir3);
        err2 -= ad2x2;
      }
      if(err3>0) {
        onestep(m4,dir4);
        err3 -= ad2x2;
      }
      err1 += ad1x2;
      err2 += ad3x2;
      err3 += ad4x2;
      onestep(m2,dir2);
    }  
    if(readSwitches()) return;
  }

  if((ad3>=ad1) && (ad3>=ad2) && (ad3>=ad4)) {
    err1=ad1x2 - ad3;
    err2=ad2x2 - ad3;
    err3=ad4x2 - ad3;
    for(i=0; i0) {
        onestep(m1,dir1);
        err1 -= ad3x2;
      }
      if(err2>0) {
        onestep(m2,dir2);
        err2 -= ad3x2;
      }
      if(err3>0) {
        onestep(m4,dir4);
        err3 -= ad3x2;
      }
      err1 += ad1x2;
      err2 += ad2x2;
      err3 += ad4x2;
      onestep(m3,dir3);
    }  
    if(readSwitches()) return;
  }

  if((ad4>=ad1) && (ad4>=ad2) && (ad4>=ad3)) {
    err1=ad1x2 - ad4;
    err2=ad2x2 - ad4;
    err3=ad3x2 - ad4;
    for(i=0; i0) {
        onestep(m1,dir1);
        err1 -= ad4x2;
      }
      if(err2>0) {
        onestep(m2,dir2);
        err2 -= ad4x2;
      }
      if(err3>0) {
        onestep(m3,dir3);
        err3 -= ad4x2;
      }
      err1 += ad1x2;
      err2 += ad2x2;
      err3 += ad3x2;
      onestep(m4,dir4);
    }  
    if(readSwitches()) return;
  }

  m1Pos_steps=l1;
  m2Pos_steps=l2;
  m3Pos_steps=l3;
  m4Pos_steps=l4;
  
  // in object space
  posx=x;
  posy=y;
  posz=z;
  pose=e;
}

Discussions