package org.reprap.geometry;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.PrintStream;
import org.reprap.Printer;
import org.reprap.Extruder;
import org.reprap.geometry.polygons.HalfPlane;
import org.reprap.geometry.polygons.Rectangle;
import org.reprap.geometry.polygons.Point2D;
import org.reprap.geometry.polygons.PolygonList;
import org.reprap.geometry.polyhedra.AllSTLsToBuild;
import org.reprap.Preferences;
import org.reprap.utilities.Debug;
/**
* This stores a set of facts about the layer currently being made, and the
* rules for such things as infill patterns, support patterns etc.
*/
public class LayerRules
{
/**
* The coordinates of the first point plotted in a layer
*/
private Point2D[] firstPoint;
/**
* The extruder first used in a layer
*/
private int[] firstExtruder;
/**
* The coordinates of the last point plotted in a layer
*/
private Point2D[] lastPoint;
/**
* The extruder last used in a layer
*/
private int[] lastExtruder;
/**
* The heights of the layers
*/
private double[] layerZ;
/**
* The name of the first file of output
*/
private String prologueFileName;
/**
* The name of the last file of output
*/
private String epilogueFileName;
/**
* The names of all the files for all the layers
*/
private String [] layerFileNames;
/**
* Are we reversing the layer orders?
*/
private boolean reversing;
/**
* Flag to remember if we have reversed the layer order in the output file
*/
private boolean alreadyReversed;
/**
* The machine
*/
private Printer printer;
/**
* How far up the model we are in mm
*/
private double modelZ;
/**
* How far we are up from machine Z=0
*/
private double machineZ;
/**
* The count of layers up the model
*/
private int modelLayer;
/**
* The number of layers the machine has done
*/
private int machineLayer;
/**
* The top of the model in model coordinates
*/
private double modelZMax;
/**
* The highest the machine should go this build
*/
private double machineZMax;
/**
* The number of the last model layer (first = 0)
*/
private int modelLayerMax;
/**
* The number of the last machine layer (first = 0)
*/
private int machineLayerMax;
/**
* Putting down foundations?
*/
private boolean layingSupport;
/**
* The smallest step height of all the extruders
*/
private double zStep;
/**
* The biggest step height of all the extruders
*/
private double thickestZStep;
/**
* If we take a short step, remember it and add it on next time
*/
private double addToStep = 0;
/**
* Are we going top to bottom or ground up?
*/
private boolean topDown = false;
/**
* This is true until it is first read, when it becomes false
*/
private boolean notStartedYet;
/**
* The XY rectangle that bounds the build
*/
private Rectangle bBox;
/**
* The maximum number of surface layers requested by any extruder
*/
private int maxSurfaceLayers = 2;
/**
*
* @param p
* @param modZMax
* @param macZMax
* @param modLMax
* @param macLMax
* @param found
*/
public LayerRules(Printer p, AllSTLsToBuild astls, boolean found)
{
printer = p;
reversing = false;
alreadyReversed = false;
notStartedYet = true;
astls.setBoxes();
astls.setLayerRules(this);
Rectangle gp = astls.ObjectPlanRectangle();
bBox = new Rectangle(new Point2D(gp.x().low() - 6, gp.y().low() - 6),
new Point2D(gp.x().high() + 6, gp.y().high() + 6));
modelZMax = astls.maxZ();
topDown = printer.getTopDown();
if(!topDown)
{
Debug.e("LayerRules(): Bottom-up slice calculations no longer supported.");
topDown = true;
}
// Run through the extruders checking their layer heights
layingSupport = found;
Extruder[] es = printer.getExtruders();
zStep = es[0].getExtrusionHeight();
thickestZStep = zStep;
int fineLayers = es[0].getLowerFineLayers();
if(es.length > 1)
{
for(int i = 1; i < es.length; i++)
{
if(es[i].getLowerFineLayers() > fineLayers)
fineLayers = es[i].getLowerFineLayers();
if(es[i].getExtrusionHeight() > thickestZStep)
thickestZStep = es[i].getExtrusionHeight();
if(es[i].getExtrusionHeight() < zStep)
zStep = es[i].getExtrusionHeight();
if(es[i].getSurfaceLayers() > maxSurfaceLayers)
maxSurfaceLayers = es[i].getSurfaceLayers();
/*
if(Math.abs(es[i].getExtrusionHeight() - zStep) > Preferences.tiny())
Debug.e("Not all extruders extrude the same height of filament: " +
zStep + " and " + es[i].getExtrusionHeight());
*/
}
}
long thick = Math.round(thickestZStep*1000.0);
for(int i = 0; i < es.length; i++)
{
long thin = Math.round(es[i].getExtrusionHeight()*1000.0);
if(thick%thin != 0)
Debug.e("LayerRules(): the layer height for extruder " + i + "(" + es[i].getLowerFineLayers() +
") is not an integer divisor of the layer height for layer height " + thickestZStep);
}
int foundationLayers = Math.max(0, printer.getFoundationLayers());
modelLayerMax = (int)(modelZMax/zStep) + 1;
machineLayerMax = modelLayerMax + foundationLayers;
machineZMax = modelZMax + foundationLayers*zStep;
if(topDown)
{
modelZ = modelZMax;
machineZ = machineZMax;
modelLayer = modelLayerMax;
machineLayer = machineLayerMax;
} else
{
modelZ = 0;
machineZ = 0;
modelLayer = -1;
machineLayer = 0;
}
addToStep = 0;
// Set up the records of the layers for later reversing (top->down ==>> bottom->up)
firstPoint = new Point2D[machineLayerMax+1];
firstExtruder = new int[machineLayerMax+1];
lastPoint = new Point2D[machineLayerMax+1];
lastExtruder = new int[machineLayerMax+1];
layerZ = new double[machineLayerMax+1];
layerFileNames = new String[machineLayerMax+1];
for(int i = 0; i < machineLayerMax+1; i++)
layerFileNames[i] = null;
prologueFileName = null;
epilogueFileName = null;
}
public Rectangle getBox()
{
return new Rectangle(bBox); // Something horrible happens to return by reference here; hence copy...
}
public boolean getTopDown() { return topDown; }
public void setPrinter(Printer p) { printer = p; }
public Printer getPrinter() { return printer; }
public double getModelZ() { return modelZ; }
public boolean getReversing() { return reversing; }
public double getModelZ(int layer)
{
return zStep*layer;
}
public double getMachineZ() { return machineZ; }
public int getModelLayer() { return modelLayer; }
public boolean extruderActiveThisLayer(int e)
{
Extruder[] es = printer.getExtruders();
double myHeight = es[e].getExtrusionHeight();
double eFraction = machineZ/myHeight;
double delta = eFraction - Math.floor(eFraction);
if(delta > 0.5)
delta = Math.ceil(eFraction) - eFraction;
delta = myHeight*delta;
return (delta < zStep*0.5);
}
public int sliceCacheSize()
{
return (int)Math.ceil(2*(maxSurfaceLayers*2 + 1)*thickestZStep/zStep);
}
public void setFirstAndLast(PolygonList[] pl)
{
firstPoint[machineLayer] = null;
lastPoint[machineLayer] = null;
firstExtruder[machineLayer] = -1;
lastExtruder[machineLayer] = -1;
layerZ[machineLayer] = machineZ;
if(pl == null)
return;
if(pl.length <= 0)
return;
int bottom = -1;
int top = -1;
for(int i = 0; i < pl.length; i++)
{
if(pl[i] != null)
if(pl[i].size() > 0)
{
if(bottom < 0)
bottom = i;
top = i;
}
}
if(bottom < 0)
return;
firstPoint[machineLayer] = pl[bottom].polygon(0).point(0);
firstExtruder[machineLayer] = pl[bottom].polygon(0).getAttributes().getExtruder().getID();
lastPoint[machineLayer] = pl[top].polygon(pl[top].size()-1).point(pl[top].polygon(pl[top].size()-1).size() - 1);
lastExtruder[machineLayer] = pl[top].polygon(pl[top].size()-1).getAttributes().getExtruder().getID();
}
public int realTopLayer()
{
int rtl = machineLayerMax;
while(firstPoint[rtl] == null && rtl > 0)
{
String s = "LayerRules.realTopLayer(): layer " + rtl + " from " + machineLayerMax + " is empty!";
if(machineLayerMax - rtl > 1)
Debug.e(s);
else
Debug.d(s);
rtl--;
}
return rtl;
}
public String getPrologueFileName() { return prologueFileName; }
public String getEpilogueFileName() { return epilogueFileName; }
public void setPrologueFileName(String s) { prologueFileName = s; }
public void setEpilogueFileName(String s) { epilogueFileName = s; }
public String getLayerFileName(int layer) { return layerFileNames[layer]; }
public String getLayerFileName() { return layerFileNames[machineLayer]; }
public void setLayerFileName(String s) { layerFileNames[machineLayer] = s; }
public Point2D getFirstPoint(int layer)
{
return firstPoint[layer];
}
public Point2D getLastPoint(int layer)
{
return lastPoint[layer];
}
public int getFirstExtruder(int layer)
{
return firstExtruder[layer];
}
public int getLastExtruder(int layer)
{
return lastExtruder[layer];
}
public double getLayerZ(int layer)
{
return layerZ[layer];
}
public int getModelLayerMax() { return modelLayerMax; }
public int getMachineLayerMax() { return machineLayerMax; }
public int getMachineLayer() { return machineLayer; }
public int getFoundationLayers() { return machineLayerMax - modelLayerMax; }
public double getModelZMAx() { return modelZMax; }
public double getMachineZMAx() { return machineZMax; }
public double getZStep() { return zStep; }
public boolean notStartedYet()
{
if(notStartedYet)
{
notStartedYet = false;
return true;
}
return false;
}
public void setLayingSupport(boolean lf) { layingSupport = lf; }
public boolean getLayingSupport() { return layingSupport; }
/**
* Does the layer about to be produced need to be recomputed?
* @return
*/
public boolean recomputeLayer()
{
return getFoundationLayers() - getMachineLayer() <= 2;
}
/**
* The hatch pattern is:
*
* Foundation:
* X and Y rectangle
*
* Model:
* Alternate even then odd (which can be set to the same angle if wanted).
*
* @return
*/
public HalfPlane getHatchDirection(Extruder e, boolean support)
{
double myHeight = e.getExtrusionHeight();
double eFraction = machineZ/myHeight;
int mylayer = (int)Math.round(eFraction);
double angle;
if(getMachineLayer() < getFoundationLayers())
{
// if(getMachineLayer() == getFoundationLayers() - 2)
// angle = e.getEvenHatchDirection();
// else
angle = e.getOddHatchDirection();
} else
{
if(mylayer%2 == 0)
angle = e.getEvenHatchDirection();
else
angle = e.getOddHatchDirection();
}
angle = angle*Math.PI/180;
HalfPlane result = new HalfPlane(new Point2D(0.0, 0.0), new Point2D(Math.sin(angle), Math.cos(angle)));
if(((mylayer/2)%2 == 0) && !support)
{
result = result.offset(0.5*getHatchWidth(e));
}
return result;
}
/**
* The gap in the layer zig-zag is:
*
* Foundation:
* The foundation width for all but...
* ...the penultimate foundation layer, which is half that and..
* ...the last foundation layer, which is the model fill width
*
* Model:
* The model fill width
*
* @param e
* @return
*/
public double getHatchWidth(Extruder e)
{
if(getMachineLayer() < getFoundationLayers())
return e.getExtrusionFoundationWidth();
return e.getExtrusionInfillWidth();
}
/**
* Move the machine up/down, but leave the model's layer where it is.
*
* @param e
*/
public void stepMachine()
{
int ld;
if(topDown)
{
//machineZ -= (sZ + addToStep);
machineLayer--;
machineZ = zStep*machineLayer + addToStep;
// ld = getFoundationLayers() - getMachineLayer();
// if(ld == 2)
// addToStep = zStep*(1 - e.getSeparationFraction());
// else if(ld == 1)
// addToStep = -zStep*(1 - e.getSeparationFraction());
// else
// addToStep = 0;
} else
{
//machineZ += (sZ + addToStep);
machineLayer++;
machineZ = zStep*machineLayer + addToStep;
// ld = getFoundationLayers() - getMachineLayer();
// if(ld == 2)
// addToStep = -zStep*(1 - e.getSeparationFraction());
// else if(ld == 1)
// addToStep = zStep*(1 - e.getSeparationFraction());
// else
// addToStep = 0;
}
}
public void moveZAtStartOfLayer(boolean really)
{
double z = getMachineZ();
if(topDown)
{
printer.setZ(z - (zStep + addToStep));
}
printer.singleMove(printer.getX(), printer.getY(), z, printer.getFastFeedrateZ(), really);
}
/**
* Move both the model and the machine up/down a layer
* @param e
*/
public void step()
{
if(topDown)
{
//modelZ -= (sZ + addToStep);
modelLayer--;
} else
{
//modelZ += (sZ + addToStep);
modelLayer++;
}
modelZ = modelLayer*zStep + addToStep;
addToStep = 0;
stepMachine();
}
public void setFractionDone()
{
// Set -ve to force the system to query the layer rules
org.reprap.gui.botConsole.BotConsoleFrame.getBotConsoleFrame().setFractionDone(-1, -1, -1);
}
private void copyFile(PrintStream ps, String ip)
{
File f = null;
try
{
f = new File(ip);
FileReader fr = new FileReader(f);
int character;
while ((character = fr.read()) >= 0)
{
ps.print((char)character);
//System.out.print((char)character);
}
ps.flush();
fr.close();
} catch (Exception e)
{
Debug.e("Error copying file: " + e.toString());
e.printStackTrace();
}
}
public void reverseLayers()
{
//if(opFileArray == null || alreadyReversed)
//if(getPrologueFileName() == null)
// return;
// Stop this being called twice...
if(alreadyReversed)
{
Debug.d("LayerRules.reverseLayers(): called twice.");
return;
}
alreadyReversed = true;
reversing = true;
String fileName = getPrinter().getOutputFilename();
PrintStream fileOutStream = null;
try
{
FileOutputStream fileStream = new FileOutputStream(fileName);
fileOutStream = new PrintStream(fileStream);
} catch (Exception e)
{
Debug.e("Can't write to file " + fileName);
return;
}
getPrinter().forceOutputFile(fileOutStream);
//copyFile(fileOutStream,getPrologueFileName());
try
{
getPrinter().startRun(this); // Sets current X, Y, Z to 0
int top = realTopLayer();
for(machineLayer = 1; machineLayer <= top; machineLayer++)
{
machineZ = layerZ[machineLayer];
getPrinter().startingLayer(this);
getPrinter().singleMove(getFirstPoint(machineLayer).x(), getFirstPoint(machineLayer).y(), machineZ, getPrinter().getFastXYFeedrate(), true);
copyFile(fileOutStream, getLayerFileName(machineLayer));
//System.out.println("Layer: " + machineLayer + " z: " + machineZ +
// " first point: " + getFirstPoint(machineLayer) + " last point: " + getLastPoint(machineLayer)
// + " " + getLayerFileName(machineLayer));
if(Preferences.loadGlobalBool("RepRapAccelerations"))
getPrinter().singleMove(getLastPoint(machineLayer).x(), getLastPoint(machineLayer).y(), machineZ, getPrinter().getSlowXYFeedrate(), false);
else
getPrinter().singleMove(getLastPoint(machineLayer).x(), getLastPoint(machineLayer).y(), machineZ, getPrinter().getFastXYFeedrate(), false);
getPrinter().finishedLayer(this);
getPrinter().betweenLayers(this);
}
getPrinter().terminate(this);
} catch (Exception e)
{
e.printStackTrace();
}
fileOutStream.close();
reversing = false;
//copyFile(fileOutStream, getEpilogueFileName());
//System.out.println("layerRules.reverseLayers(): exception at layer: " + i + " " + e.toString());
}
}