/**
   * This method handles shuffling together two gcodes, it first executes preprocessing and then
   * hands the gcodes off to Layer_Helper
   */
  public void combine() {
    MutableGCodeSource left, right;

    // load our layers into something we can use
    left = new MutableGCodeSource(leftFile);
    right = new MutableGCodeSource(rightFile);

    // remove  some tags we don't want/get
    SkeinforgePostProcessor.stripNonLayerTagComments(left);
    SkeinforgePostProcessor.stripNonLayerTagComments(right);

    // load our layers into something we can *really* use
    LinkedList<Layer> leftLayers = parseLayers(left);
    LinkedList<Layer> rightLayers = parseLayers(right);

    // merge our layers into one list
    final LinkedList<Layer> layers = doMerge(leftLayers, rightLayers);

    // refresh and repopulate our result
    result = new MutableGCodeSource();
    for (Layer l : layers) {
      result.add(l.getCommands());
    }

    // add start gcode, updated based on settings
    SkeinforgePostProcessor.prependAndModifyStartCode(result, startGCode);
    // add end code
    Layer endLayer = new Layer(Double.MAX_VALUE, endGCode.asList());
    result.add(endLayer.toString());
    // interlace progress updates
    result.addProgressUpdates();
  }
 /**
  * This gets the first feedrate used in a layer
  *
  * @param l
  * @return
  */
 private String getFirstFeedrate(final Layer l) {
   final List<String> search = l.getCommands();
   GCodeCommand gcode;
   for (int i = 0; i < search.size(); i++) {
     gcode = new GCodeCommand(search.get(i));
     if (gcode.getCodeValue('F') != -1)
       return "F" + Base.getGcodeFormat().format(gcode.getCodeValue('F'));
   }
   return "";
 }
 /**
  * Apparently skeinforge does not have all the moves in a layer at the same height. The first one
  * is frequently lower than the following ones, so this function finds the last height listed in a
  * layer
  *
  * @param l the layer in which to look
  * @return the layer height (maybe)
  */
 private Double getLayerZ(final Layer l) {
   final List<String> search = l.getCommands();
   GCodeCommand gcode;
   for (int i = search.size() - 1; i >= 0; i--) {
     gcode = new GCodeCommand(search.get(i));
     if (gcode.getCodeValue('G') == 1 && gcode.hasCode('Z')) {
       return gcode.getCodeValue('Z');
     }
   }
   return null;
 }
 /**
  * gets the first G1 from a layer, returns the position of X, Y, Z axes
  *
  * @param l
  * @return
  */
 private Point5d getFirstPosition(final Layer l) {
   final List<String> search = l.getCommands();
   GCodeCommand gcode;
   for (int i = 0; i < search.size(); i++) {
     gcode = new GCodeCommand(search.get(i));
     if (gcode.getCodeValue('G') == 1) {
       Point5d result = new Point5d();
       result.setX(gcode.getCodeValue('X'));
       result.setY(gcode.getCodeValue('Y'));
       result.setZ(gcode.getCodeValue('Z'));
       return result;
     }
   }
   return null;
 }