// public LayerProducer(BooleanGridList bgPols, LayerRules lc, RrGraphics simPlot) throws
  // Exception
  public LayerProducer(RrPolygonList ap[], LayerRules lc, RrGraphics simPlot) throws Exception {
    layerConditions = lc;
    startNearHere = null;
    // lowerShell = ls;
    // shellSet = false;
    simulationPlot = simPlot;

    allPolygons = ap;

    // boolGrdSlice = bgPols;

    // The next two are experimental for the moment...

    // inFillCalculations();
    // supportCalculations();

    // offHatch = boolGrdSlice.offset(layerConditions, false);

    //		if(layerConditions.getLayingSupport())
    //		{
    //			borderPolygons = null;
    //			offHatch = offHatch.union(lc.getPrinter().getExtruders());
    //		} else
    //		{
    //			BooleanGridList offBorder = boolGrdSlice.offset(layerConditions, true);
    //			borderPolygons = offBorder.borders();
    //		}
    //
    //		hatchedPolygons = offHatch.hatch(layerConditions);
    //
    //
    //		if(borderPolygons != null && borderPolygons.size() > 0)
    //		{
    //			borderPolygons.middleStarts(hatchedPolygons, layerConditions);
    //			if(Preferences.loadGlobalBool("Shield"))
    //			{
    //				RrRectangle rr = lc.getBox();
    //				Rr2Point corner = Rr2Point.add(rr.sw(), new Rr2Point(-3, -3));
    //				RrPolygon ell = new RrPolygon(borderPolygons.polygon(0).getAttributes(), false);
    //				ell.add(corner);
    //				ell.add(Rr2Point.add(corner, new Rr2Point(-2, 10)));
    //				ell.add(Rr2Point.add(corner, new Rr2Point(-2, -2)));
    //				ell.add(Rr2Point.add(corner, new Rr2Point(20, -2)));
    //				borderPolygons.add(0, ell);
    //			}
    //		}

    if (simulationPlot != null) {
      if (!simulationPlot.isInitialised()) {
        RrRectangle rec = lc.getBox();
        if (Preferences.loadGlobalBool("Shield"))
          rec.expand(
              Rr2Point.add(
                  rec.sw(), new Rr2Point(-7, -7))); // TODO: Yuk - this should be a parameter
        simulationPlot.init(rec, false);
      } else simulationPlot.cleanPolygons();
    }
  }
  /**
   * Plot a polygon
   *
   * @throws IOException
   * @throws ReprapException
   * @return
   */
  private void plot(RrPolygon p, boolean firstOneInLayer) throws ReprapException, IOException {
    Attributes att = p.getAttributes();
    Printer printer = layerConditions.getPrinter();
    double outlineFeedrate = att.getExtruder().getOutlineFeedrate();
    double infillFeedrate = att.getExtruder().getInfillFeedrate();

    boolean acc =
        att.getExtruder().getMaxAcceleration() > 0; // Preferences.loadGlobalBool("Accelerating");

    // int leng = p.size();

    if (p.size() <= 1) {
      startNearHere = null;
      return;
    }

    // If the length of the plot is <0.05mm, don't bother with it.
    // This will not spot an attempt to plot 10,000 points in 1mm.
    double plotDist = 0;
    Rr2Point lastPoint = p.point(0);
    for (int i = 1; i < p.size(); i++) {
      Rr2Point n = p.point(i);
      plotDist += Rr2Point.d(lastPoint, n);
      lastPoint = n;
    }
    if (plotDist < Preferences.machineResolution() * 0.5) {
      Debug.d("Rejected line with " + p.size() + " points, length: " + plotDist);
      startNearHere = null;
      return;
    }

    printer.selectExtruder(att);

    // Don't do these with mid-point starting

    //		if(p.isClosed() && att.getExtruder().incrementedStart())
    //			p = p.incrementedStart(layerConditions);
    //		else if(p.isClosed() && att.getExtruder().randomStart())
    //			p = p.randomStart();

    int stopExtruding = p.size() + 10;
    int stopValve = stopExtruding;
    double extrudeBackLength = att.getExtruder().getExtrusionOverRun();
    double valveBackLength = att.getExtruder().getValveOverRun();
    if (extrudeBackLength >= valveBackLength) {
      if (extrudeBackLength > 0) stopExtruding = p.backStep(extrudeBackLength);
      if (valveBackLength > 0) stopValve = p.backStep(valveBackLength);
    } else {
      if (valveBackLength > 0) stopValve = p.backStep(valveBackLength);
      if (extrudeBackLength > 0) stopExtruding = p.backStep(extrudeBackLength);
    }

    if (printer.isCancelled()) return;

    // If getMinLiftedZ() is negative, never lift the head

    Boolean lift = att.getExtruder().getMinLiftedZ() >= 0;

    if (acc)
      p.setSpeeds(
          att.getExtruder().getSlowXYFeedrate(),
          p.isClosed() ? outlineFeedrate : infillFeedrate,
          att.getExtruder().getMaxAcceleration());

    if (extrudeBackLength <= 0) stopExtruding = Integer.MAX_VALUE;
    else if (acc) stopExtruding = p.findBackPoint(extrudeBackLength);

    if (valveBackLength <= 0) stopValve = Integer.MAX_VALUE;
    else if (acc) stopValve = p.findBackPoint(valveBackLength);

    currentFeedrate = att.getExtruder().getFastXYFeedrate();
    singleMove(p.point(0));

    if (acc) currentFeedrate = p.speed(0);
    else {
      if (p.isClosed()) {
        currentFeedrate = outlineFeedrate;
      } else {
        currentFeedrate = infillFeedrate;
      }
    }

    plot(p.point(0), p.point(1), false, false);

    // Print any lead-in.
    printer.printStartDelay(firstOneInLayer);

    boolean extrudeOff = false;
    boolean valveOff = false;
    boolean oldexoff;

    //		if(p.isClosed())
    //		{
    //			for(int j = 1; j <= p.size(); j++)
    //			{
    //				int i = j%p.size();
    //				Rr2Point next = p.point((j+1)%p.size());
    //
    //				if (printer.isCancelled())
    //				{
    //					printer.stopMotor();
    //					singleMove(posNow());
    //					move(posNow(), posNow(), lift, true, true);
    //					return;
    //				}
    //				if(acc)
    //					currentFeedrate = p.speed(i);
    //
    //				oldexoff = extrudeOff;
    //				extrudeOff = j > stopExtruding || j == p.size();
    //				valveOff = j > stopValve || j == p.size();
    //
    //				plot(p.point(i), next, extrudeOff, valveOff);
    //				if(oldexoff ^ extrudeOff)
    //					printer.printEndReverse();
    //			}
    //		} else

    //		{
    for (int i = 1; i < p.size(); i++) {
      Rr2Point next = p.point((i + 1) % p.size());

      if (printer.isCancelled()) {
        printer.stopMotor();
        singleMove(posNow());
        move(posNow(), posNow(), lift, lift, true);
        return;
      }

      if (acc) currentFeedrate = p.speed(i);

      oldexoff = extrudeOff;
      extrudeOff = i > stopExtruding || i == p.size() - 1;
      valveOff = i > stopValve || i == p.size() - 1;
      //				if(oldexoff ^ extrudeOff)
      //					printer.printEndReverse();
      plot(p.point(i), next, extrudeOff, valveOff);
      if (oldexoff ^ extrudeOff) printer.printEndReverse();
    }
    //		}

    if (p.isClosed()) move(p.point(0), p.point(0), false, false, true);

    move(posNow(), posNow(), lift, lift, true);

    // The last point is near where we want to start next
    if (p.isClosed()) startNearHere = p.point(0);
    else startNearHere = p.point(p.size() - 1);

    if (simulationPlot != null) {
      RrPolygonList pgl = new RrPolygonList();
      pgl.add(p);
      simulationPlot.add(pgl);
    }
  }