package bs.bs2d.slicer;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Locale;
import javax.vecmath.Vector2d;
/**
*
* @author Djen
*/
public class GCodeAssembler {
private static final String NEWLINE = System.getProperty("line.separator");
private StringBuilder gcode;
private int movePrecision, extrdPrecision;
private double x, y, z, f, e;
private double totalE;
private double temperature, bedTemperature;
private double retractFeedrate, retractLength, retractThrshSq;
private boolean retracted;
/**
* Constructs a new GCodeAssembler with empty gcode buffer and default
* output precision.
*/
public GCodeAssembler() {
this(3, 5);
}
/**
* Constructs a new GCodeAssembler with empty gcode buffer and the given
* output precision.
* @param movePrecision number of decimal digits for move commands
* @param extrdPrecision number of decimal digits for extrusion commands
*/
private GCodeAssembler(int movePrecision, int extrdPrecision) {
gcode = new StringBuilder();
retractFeedrate = 0;
retractLength = 0;
retractThrshSq = Double.NaN;
totalE = 0;
temperature = 0;
this.movePrecision = movePrecision;
this.extrdPrecision = extrdPrecision;
}
public void append(String s) {
getGcode().append(s);
}
public void append(CharSequence s){
gcode.append(s);
}
public void appendLine(String s) {
getGcode().append(s);
getGcode().append(NEWLINE);
}
public void appendLines(String[] s){
for (String line : s) {
appendLine(line);
}
}
/**
* Writes a print move to the output.
* @param x absolute x coordinate
* @param y absolute y coordinate
* @param z absolute z coordinate
* @param f the feedrate for this move
* @param e relative extrusion length
* @
*/
public void appendMove(double x, double y, double z, double f, double e) {
String s = "G1";
if(this.x != x || this.y != y){
s += " X" + formatNum(x, movePrecision);
this.x = x;
s += " Y" + formatNum(y, movePrecision);
this.y = y;
}
if(0 != z){
this.z += z;
s += " Z" + formatNum(this.z, movePrecision);
}
if(this.f != f){
s += " F" + formatNum(f, movePrecision);
this.f = f;
}
if(0 != e){
this.e += e;
totalE += e;
s += " E" + formatNum(this.e, extrdPrecision);
}
if(!s.equals("G1") && !s.contains("X") && !s.contains("Z") && !s.contains("E"))
throw new RuntimeException("stop!");
if(!s.equals("G1"))
appendLine(s);
}
/**
*
* @param x - absolute x coordinate
* @param y - absolute y coordinate
* @param e - relative extrusion length
* @
*/
public void print(double x, double y, double e) {
print(x, y, f, e);
}
public void print(double x, double y, double f, double e) {
restart();
appendMove(x, y, 0, f, e);
}
public void travel(double x, double y) {
travel(x, y, f);
}
public void travel(double x, double y, double f) {
Vector2d v = new Vector2d(x - this.x, y - this.y);
double length = v.lengthSquared();
if(length > retractThrshSq)
retract();
if(length > 0)
appendMove(x, y, 0, f, 0);
}
/**
* Adds a z axis move.
* @param z absolute z coordinate
* @param f feedrate
*/
public void zTravel(double z, double f) {
appendMove(x, y, z, f, 0);
}
/**
* Retracts a length of filament to minimize oozing during travel moves.
* This call will have no effect if no call to 'restart' has been made since
* the last retract (i.e. retract moves cannot accumulate).
*/
public void retract() {
if(!retracted){
appendMove(x, y, 0, retractFeedrate, -retractLength);
retracted = true;
}
}
/**
* Restart printing after travel move. Reverses prior retraction (if any).
*/
public void restart() {
if(!retracted)
return;
appendMove(x, y, 0, retractFeedrate, retractLength);
retracted = false;
}
public void resetOriginE(){
appendLine("G92 E0");
e = 0;
}
public void setAbsoluteDistances() {
appendLine("G90 ; use absolute coordinates");
appendLine("M89 ; use absolute extrusion distances");
}
public void setTemperature(double t){
if(t != temperature){
appendLine("M104 S" + (int)t);
temperature = t;
}
}
public void setBedTemperature(double t){
if(t != bedTemperature){
appendLine("M140 S" + (int)t);
bedTemperature = t;
}
}
/**
* Generates a formatted number string with the given precision.
* @param num the floating point value to be formatted
* @param precision the number of decimal digits
* @return a formatted number string
*/
private String formatNum(double num, int precision){
return String.format(Locale.US, "%." + precision + "f", num);
}
public void writeToFile(File f) throws IOException{
String filename = f.getPath();
String ext = ".gcode";
File outputFile;
if(filename.endsWith(ext)){
outputFile = f;
} else {
filename += ext;
outputFile = new File(filename);
}
try (Writer out = new FileWriter(outputFile)) {
out.append(getGcode());
}
}
/**
* @return the gcode
*/
public StringBuilder getGcode() {
return gcode;
}
/**
* Clears all gcode in this assembler. Leaves all other setting intact.
*/
public void clearGCode(){
gcode = new StringBuilder();
x = y = z = f = e = totalE = 0;
}
/**
* Sets the retraction properties.
* @param feedrate the retraction feedrate
* @param length the retraction length
* @param thrsh the travel distance threshold to trigger retraction
*/
public void setRetraction(double feedrate, double length, double thrsh){
retractFeedrate = feedrate;
retractLength = length;
retractThrshSq = thrsh * thrsh;
}
/**
* @return the total extrusion length
*/
public double getTotalExtrusionLength() {
return totalE;
}
}