Produce G-Code for Profiling

In this tutorial we will produce machine code, also known as g-code, for 3-axis CNC milling. In particular we will import an already drawn polygonal curve and convert the point data into machine motion instructions. This type of almost 2D operation is similar to laser cutting and it is known as profiling.



  1. Rhino’s World XY-Plane is the Work-Origin at the Top of Material and we are working in millimeter units.
  2. Cutting profiles drawn in Rhino as curves (lines, polylines, arcs, splines, nurbs, polycurves) on the top of material plane (blue curves).
  3. Profile curves have been offset outwards by 3mm, which is half the tool bit diameter (cyan underneath the green curves).
  4. Profile curves have been extended by 10mm both ends to meet the entry/exit boundary rectangle (orange curve).
  5. Profile curves have been converted to polylines, within ~1mm error tolerance (green curves)
  6. Polylines have been imported into Grasshopper as machine paths.



The procedure for converting drawn machine paths into machine code (re)introduces a few common tasks which are applicable to various other operations:

  1. Emitting geometry to Grasshopper using a generic object list (read the previous post on setting up).
  2. Composing formatted text documents using the StringBuilder object. The StringBuilder object is a collector of string data similar to defining a string s = “something” and continuously appending/concatenating more string with the s += “another string”, notation. It is much more efficient to build long documents in this fashion.
  3. Reading and writing points into polylines as if they are generic point lists. A very nice and useful property of polylines is that they behave like arrays/lists of points. We can Add points, select them with poly[index] notation as well as iterate using the foreach construct.
  4. Once the code is generated, select the output panel, copy the “data only” and paste the code into a notepad. Save the text file as (*.txt) or (*.nc) if you prefer.


    //-- Initialize ---------------------------------------------------------------------
    var geometry = new List<object>();
    var code = new System.Text.StringBuilder();
    var machine_path = new Polyline();

    //-- Write G-Code Header ------------------------------------------------------------
    code.AppendFormat("O{0}\r\n", TeamNumber);

    code.AppendFormat("(-- Reset Machine --)\r\n");
    code.AppendFormat("G00 G91 G28 Z0.0\r\n");
    code.AppendFormat("G80 G90 G49 G40       ( Cancel Previous States )\r\n");

    code.AppendFormat("(--Setup Machine--)\r\n");
    code.AppendFormat("G90 G54 G00 X0.0 Y0.0\r\n");
    code.AppendFormat("S{0} M03\r\n", SpindleSpeed);
    code.AppendFormat("G00 X0.0 Y0.0");
    code.AppendFormat("G43 H1 Z80.0\r\n");

    code.AppendFormat("(--Testing Sequence--)\r\n");
    code.AppendFormat("F{0}\r\n", FeedRate);
    code.AppendFormat("G00 G54 X0.0 Y0.0 Z{0:0.000}\r\n", TestPlane);

    //-- Machine Code from Machine Paths -------------------------------------------------
    foreach( Polyline path in MachinePaths )
      //-- Write Path Entry Data
      Point3d spoint = path[0];
      spoint.Z += FastPlane;

      code.AppendFormat("(-- Path Entry --)\r\n");
      code.AppendFormat("F{0}\r\n", JogRate);
      code.AppendFormat("G01 X{0:0.000} Y{1:0.000} Z{2:0.000}\r\n", 
        spoint.X, spoint.Y, spoint.Z);
      code.AppendFormat("F{0}\r\n", FeedRate);

      //-- Add Machine Path Entry

      //-- Write Path Position Data
      code.AppendFormat("(-- Path Route --)\r\n");

      foreach( Point3d point in path )
        code.AppendFormat("G01 X{0:0.000} Y{1:0.000} Z{2:0.000}\r\n", 
          point.X, point.Y, -MillingDepth);

        //-- Add Machine Path Step
        machine_path.Add(new Point3d(point.X, point.Y, -MillingDepth));

      //-- Write Path Exit Data
      Point3d epoint = path[path.Count - 1];
      epoint.Z += FastPlane;

      code.AppendFormat("(-- Path Exit --)\r\n");
      code.AppendFormat("F{0}\r\n", JogRate);
      code.AppendFormat("G01 X{0:0.000} Y{1:0.000} Z{2:0.000}\r\n", 
        epoint.X, epoint.Y, epoint.Z);

      //-- Add Machine Path Exit

    //-- Write G-Code Footer -------------------------------------------------------------
    code.AppendFormat("(-- Finish Program --)\r\n");
    code.AppendFormat("G00 Z{0:0.0}\r\n", FastPlane);
    code.AppendFormat("G28 G91 X0.0 Y0.0\r\n");

    //-- Finalize -------------------------------------------------------------------------

    Geometry = geometry;
    MachineCode = code;


  1. The machine code header (initialization instructions) is not general purpose. It was provided by Andy who has tested it on the Machino semi-auto mills. This is pretty much the case for the footer (finalization instructions).
  2. The motion pathing code is generic and can be used on multiple other systems. We will reuse the same logic and code for producing later G-Code for the HAAS large scale wood router.
  3. The assumptions about the feed rate (linear motion speed) and spindle rate (revolutions per minute) are indicative. Andy will modify them to fit the material we are machining. The rules of the thumbs: (a) Always ask Andy, he knows better, (b) Google it: you can find online recommended values, (c) Start slow and increase the speed later. Not recommended: (a) Plug random values for speeds and feeds, (b) Be greedy and try to max out the machine without consideration for the material, tool bit, the machine, yourself and Andy.
  4. The fast plane (where the machine is cutting air, therefore it can move really fast) is assumed 50mm above the top of material because of the physical design of the jig. If we use different positioning apparatus, say clamps, we have to make sure we are clear above all collisions so that we wont create hazards (for you, Andy and the machine). Imagine a drill bit clashing on a clamp or bolt.
  5. The StringBuilder object is nice and powerful but Grasshopper tends to crash if you try to load very large text files (strings) in a yellow panel. We will look at another approach in the next tutorial.