Produce G-Code for a Height Field

In this tutorial we will produce machine code for milling a height field surface using parallel style machining. A height-field is a special kind of surface where all points are on a regular xy-grid and only the z-coordinate is varying. Machining such surfaces is rather simple and instructive.



  1. You have studies the profiling code tutorial and understand that all machine paths are just polylines defining the sequence of tool center point locations.
  2. You have recreated the above pictured set of input parameters and output panels.
  3. We are working in a Rhino document with millimeters as units (all units are metric) and the top of material working plane coincides with the document’s World XY plane.



  1. Compute the grid dimensions by dividing the total part width and length (eg. 300 x 400mm) with the resolution factor (eg. 5mm).
  2. Scan the grid with two nested loops construct and produce a series of polylines also known as scan lines (this is how old monitors used to beam images on screen).
  3. Flip every other scan line using mod 2 arithmetic such that when we join scan lines into the master machine path polyline the points will appear in a zig-zag pattern.
  4. Apply a signal map from grid coordinates to physical coordinates, linear in XY and in addition perform as few non-linear transforms for computing the z-coordinate such that it appears on a undulating surface.
  5. When playing with transcendental functions (sine/cosine) it is useful to define the complete form including the phase, period and amplitude for better control the end design.
  6. After the path is generated, translate it below the top of material plane and add a lower bound slab for visual inspection of potentially machining clashes between tool and the router’s bed plate.
  7. Reuse the points generated during the path and code generations for constructing a proper nurbs surface from a point grid for end product visual evaluation.
  8. Finally, given a specific feed rate (machining speed in mm/min) and total machine path length (mm) approximately compute the machine production time!


private void RunScript(int DeltaX, int DeltaY, int DeltaZ, 
    int Delta, int MinZ, int MaxZ, 
    double PeriodX, double PeriodY, 
    double PhaseX, double PhaseY, 
    ref object Code, ref object Geometry)

    var geometry = new List<object>();

    //-- Number of Point per Direction
    var nx = DeltaX / Delta;
    var ny = DeltaY / Delta;
    var dz = MaxZ - MinZ;

    //-- Machine Path
    var path = new Polyline();
    var list = new List<Point3d>();
    var code = new List<string>();

    //-- Create Height Field Surface
    for( var ix = 0; ix < nx; ix++)
      var poly = new Polyline();

      for( var iy = 0; iy < ny; iy++)
        //-- XY Point Position (Absolute)
        var x = ix * Delta;
        var y = iy * Delta;

        //-- XY Normalized [0..1]
        var tx = (double) ix / (double) (nx - 1);
        var ty = (double) iy / (double) (ny - 1);

        //-- Map XY to Angle
        var ax = tx * Math.PI * PeriodX + PhaseX;
        var ay = ty * Math.PI * PeriodY + PhaseY;

        var z = 0.5 * dz * ( Math.Sin(ax + ay) * Math.Cos(ay * ax) );

        //-- Create the Point
        var p = new Point3d(x, y, z);

        //-- Store It

      //-- Flip Even Other polyline such that
      //-- when merged they for a zig-zag pattern
      if( (ix % 2) == 0)

    //-- To Move the Path Such that The Top is at Z=0
    var xform = Transform.Translation(0, 0, -path.BoundingBox.Max.Z);


    //-- To Make a Surface from a Point List
    var srf = NurbsSurface.CreateFromPoints(list, nx, ny, 3, 3);

    //-- To Visually Inspect if Andy is in Danger
    //-- If Surface below 50mm of material stock
    //-- use 5mm clearance from bottom of bed
    geometry.Add(new BoundingBox(0, 0, -DeltaZ, DeltaX, DeltaY, -DeltaZ + 5));

    //-- To Produce Machine Code from Polyline
    var speed = 1200; //Feed Rate: mm/min


    code.Add("( Reset )");
    code.Add("G00 G91 G28 Z0.000");
    code.Add("G80 G90 G49 G40");

    code.Add("( Setup )");
    code.Add("T1 M06(Tool)");
    code.Add("G90 G54 G00 X0.000 Y0.000");
    code.Add("S5000 M03");
    code.Add("G00 G43 H1 Z0.000");

    code.Add("(Clear to Entry and Feed Rate )");
    code.Add("G01 X0.000 Y0.000 Z50.0 F" + speed);

    code.Add("( Program )");

    foreach( var point in path )
        "X{0:0.000} Y{1:0.000} Z{2:0.000}",
        point.X, point.Y, point.Z));

    code.Add("( Conclusion )");
    code.Add("G00 Z50.0 ( Exit )");
    code.Add("G28 G90(Home)");

    //-- Production Time Based on Xmm/min Feed Rate
      "Speed    {0:0.0} mm/min\r\nLength   {1:0.000} m\r\nTime     {2:0.000} min\r\n",
      path.Length / 1000.0,
      path.Length / speed));

    Code = code;
    Geometry = geometry;



  1. The style of the procedure presented here is highly advisable. It is cleaner and better to separate the surface generation logic from the machine code production. It is even better to place high-level logic in separate function such that code is its own documentation (instead of adding all the information above).
  2. Using a list of strings seems to work better with Grasshopper with very large bodies of text. You can copy/paste the data into notepad and save the machine file as previously. A new line (\r\n) will be automatically appended for each line of text.
  3. It is very easy to go overboard using very fine resolution (eg. 0.1mm) producing fifilions of points and instructions. The machine may not be able to handle very large files. In that case you will have to split the machine code into multiple files.
  4. The resolution factor, delta, is also our machining step-over parameter. For a 6mm ball mill tool we have 0.5mm overlap laterally among consecutive parallel cuts. The scallop artifact will thus be real and perhaps beautiful.
  5. Question: Does this approach to machining a height field surface makes sense? Even if we reduce the step over (increase grid resolution) to sub-millimeter level. Or is there something inherently wrong about it geometrically?
  6. Even 10mm of cutting depth is sufficient to produce very powerful and evocative objects. It is the way the light and shadow that exaggerates the glyph.
  7. Typical construction grade blue/pink XPS foam (30Kg/m3) tears with speeds higher than 1,500mm/min and lower than 10,000 RPMs. The first sample was cut at 3,000mm/min while the second at 1,500mm/min.
  8. Note that the deep points of the surface show more tearing, which implies that material was not escaping up the flute fast enough. Perhaps the carbide tool was not cut for the job or we should have carved the surface in multiple depth passes.