/**
   * speed up for short lines
   *
   * @param p
   * @return
   * @throws ReprapException
   * @throws IOException
   */
  private boolean shortLine(Rr2Point p, boolean stopExtruder, boolean closeValve)
      throws ReprapException, IOException {
    Printer printer = layerConditions.getPrinter();
    double shortLen = printer.getExtruder().getShortLength();
    if (shortLen < 0) return false;
    Rr2Point a = Rr2Point.sub(posNow(), p);
    double amod = a.mod();
    if (amod > shortLen) {
      //			Debug.d("Long segment.  Current feedrate is: " + currentFeedrate);
      return false;
    }

    // printer.setFeedrate(printer.getExtruder().getShortLineFeedrate());
    // TODO: FIX THIS
    //		printer.setSpeed(LinePrinter.speedFix(printer.getExtruder().getXYSpeed(),
    //				printer.getExtruder().getShortSpeed()));
    printer.printTo(
        p.x(),
        p.y(),
        layerConditions.getMachineZ(),
        printer.getExtruder().getShortLineFeedrate(),
        stopExtruder,
        closeValve);
    // printer.setFeedrate(currentFeedrate);
    return true;
  }
  /** Destroy me and all that I point to */
  public void destroy() {
    if (beingDestroyed) // Prevent infinite loop
    return;
    beingDestroyed = true;

    // Keep the lower shell - the graphics system is using it

    // lowerShell = null;

    // Keep the printer; that's needed for the next layer

    // printer = null;

    //		if(hatchedPolygons != null)
    //			hatchedPolygons.destroy();
    //		hatchedPolygons = null;
    //
    //		if(borderPolygons != null)
    //			borderPolygons.destroy();
    //		borderPolygons = null;
    //
    //		//if(boolGrdSlice != null)
    //		//	boolGrdSlice.destroy();
    //		boolGrdSlice = null;
    //
    //		//if(offHatch != null)
    //			//offHatch.destroy();
    //		offHatch = null;

    if (startNearHere != null) startNearHere.destroy();
    startNearHere = null;
    allPolygons = null;
    beingDestroyed = false;
  }
  /**
   * @param first First point, the end of the line segment to be plotted to from the current
   *     position.
   * @param second Second point, the end of the next line segment; used for angle calculations
   * @param turnOff True if the extruder should be turned off at the end of this segment.
   * @throws ReprapException
   * @throws IOException
   */
  private void plot(Rr2Point first, Rr2Point second, boolean stopExtruder, boolean closeValve)
      throws ReprapException, IOException {
    Printer printer = layerConditions.getPrinter();
    if (printer.isCancelled()) return;

    // Don't call delay; this isn't controlling the printer
    while (paused) {
      try {
        Thread.sleep(200);
      } catch (Exception ex) {
      }
    }

    if (shortLine(first, stopExtruder, closeValve)) return;

    double z = layerConditions.getMachineZ();

    double speedUpLength = printer.getExtruder().getAngleSpeedUpLength();
    if (speedUpLength > 0) {
      segmentSpeeds ss = new segmentSpeeds(posNow(), first, second, speedUpLength);
      if (ss.abandon) return;

      printer.printTo(ss.p1.x(), ss.p1.y(), z, currentFeedrate, false, false);

      if (ss.plotMiddle) {
        // TODO: FIX THIS.
        //				int straightSpeed = LinePrinter.speedFix(currentSpeed, (1 -
        //						printer.getExtruder().getAngleSpeedFactor()));
        // printer.setFeedrate(printer.getExtruder().getAngleFeedrate());
        printer.printTo(
            ss.p2.x(), ss.p2.y(), z, printer.getExtruder().getAngleFeedrate(), false, false);
      }

      // printer.setSpeed(ss.speed(currentSpeed, printer.getExtruder().getAngleSpeedFactor()));

      // printer.setFeedrate(printer.getExtruder().getAngleFeedrate());
      printer.printTo(
          ss.p3.x(),
          ss.p3.y(),
          z,
          printer.getExtruder().getAngleFeedrate(),
          stopExtruder,
          closeValve);
      // pos = ss.p3;
      // Leave speed set for the start of the next line.
    } else printer.printTo(first.x(), first.y(), z, currentFeedrate, stopExtruder, closeValve);
  }
  /**
   * @param first
   * @param second
   * @param startUp
   * @param endUp
   * @throws ReprapException
   * @throws IOException
   */
  private void move(Rr2Point first, Rr2Point second, boolean startUp, boolean endUp, boolean fast)
      throws ReprapException, IOException {
    Printer printer = layerConditions.getPrinter();

    if (printer.isCancelled()) return;

    //		 Don't call delay; this isn't controlling the printer
    while (paused) {
      try {
        Thread.sleep(200);
      } catch (Exception ex) {
      }
    }

    double z = layerConditions.getMachineZ();

    // if(startUp)
    if (fast) {
      // printer.setFeedrate(printer.getFastFeedrateXY());
      printer.moveTo(
          first.x(), first.y(), z, printer.getExtruder().getFastXYFeedrate(), startUp, endUp);
      return;
    }

    double speedUpLength = printer.getExtruder().getAngleSpeedUpLength();
    if (speedUpLength > 0) {
      segmentSpeeds ss = new segmentSpeeds(posNow(), first, second, speedUpLength);
      if (ss.abandon) return;

      printer.moveTo(ss.p1.x(), ss.p1.y(), z, printer.getCurrentFeedrate(), startUp, startUp);

      if (ss.plotMiddle) {
        // printer.setFeedrate(currentFeedrate);
        printer.moveTo(ss.p2.x(), ss.p2.y(), z, currentFeedrate, startUp, startUp);
      }

      // TODO: FIX ME!
      // printer.setSpeed(ss.speed(currentSpeed, printer.getExtruder().getAngleSpeedFactor()));

      // printer.setFeedrate(printer.getExtruder().getAngleFeedrate());
      printer.moveTo(
          ss.p3.x(), ss.p3.y(), z, printer.getExtruder().getAngleFeedrate(), startUp, endUp);
      // pos = ss.p3;
      // Leave speed set for the start of the next movement.
    } else printer.moveTo(first.x(), first.y(), z, currentFeedrate, startUp, endUp);
  }
  // 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);
    }
  }
 private void singleMove(Rr2Point p) {
   Printer pt = layerConditions.getPrinter();
   pt.singleMove(p.x(), p.y(), pt.getZ(), pt.getFastXYFeedrate());
 }
 /**
  * @param before
  * @param now
  * @param after
  * @param fastLength
  */
 public segmentSpeeds(Rr2Point before, Rr2Point now, Rr2Point after, double fastLength) {
   Rr2Point a = Rr2Point.sub(now, before);
   double amod = a.mod();
   abandon = amod == 0;
   if (abandon) return;
   Rr2Point b = Rr2Point.sub(after, now);
   if (b.mod() == 0) ca = 0;
   else ca = Rr2Point.mul(a.norm(), b.norm());
   plotMiddle = true;
   if (amod <= 2 * fastLength) {
     fastLength = amod * 0.5;
     plotMiddle = false;
   }
   a = a.norm();
   p1 = Rr2Point.add(before, Rr2Point.mul(a, fastLength));
   p2 = Rr2Point.add(p1, Rr2Point.mul(a, amod - 2 * fastLength));
   p3 = Rr2Point.add(p2, Rr2Point.mul(a, fastLength));
 }