Arquivos
CuraEngine/main.cpp
T
2013-05-17 09:20:32 +02:00

599 linhas
24 KiB
C++

/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/time.h>
#include <signal.h>
#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__))
#include <sys/resource.h>
#endif
#include "utils/gettime.h"
#include "utils/logoutput.h"
#include "sliceDataStorage.h"
#include "modelFile/modelFile.h"
#include "optimizedModel.h"
#include "polygonOptimizer.h"
#include "slicer.h"
#include "layerPart.h"
#include "inset.h"
#include "skin.h"
#include "infill.h"
#include "bridge.h"
#include "support.h"
#include "pathOptimizer.h"
#include "skirt.h"
#include "raft.h"
#include "comb.h"
#include "gcodeExport.h"
#define VERSION "1.0"
class Config
{
public:
int layerThickness;
int initialLayerThickness;
int filamentDiameter;
int filamentFlow;
int extrusionWidth;
int insetCount;
int downSkinCount;
int upSkinCount;
int sparseInfillLineDistance;
int infillOverlap;
int skirtDistance;
int skirtLineCount;
int retractionAmount;
int retractionSpeed;
int initialSpeedupLayers;
int initialLayerSpeed;
int printSpeed;
int infillSpeed;
int moveSpeed;
int fanOnLayerNr;
//Support material
int supportAngle;
int supportEverywhere;
int supportLineWidth;
//Cool settings
int minimalLayerTime;
int minimalFeedrate;
int coolHeadLift;
int fanSpeedMin;
int fanSpeedMax;
//Raft settings
int raftMargin;
int raftLineSpacing;
int raftBaseThickness;
int raftBaseLinewidth;
int raftInterfaceThickness;
int raftInterfaceLinewidth;
FMatrix3x3 matrix;
Point objectPosition;
int objectSink;
int fixHorrible;
Point extruderOffset[16];
const char* startCode;
const char* endCode;
};
int verbose_level;
int maxObjectHeight;
void processFile(const char* input_filename, Config& config, GCodeExport& gcode, bool firstFile)
{
for(unsigned int n=1; n<16;n++)
gcode.setExtruderOffset(n, config.extruderOffset[n]);
double t = getTime();
log("Loading %s from disk...\n", input_filename);
SimpleModel* m = loadModel(input_filename, config.matrix);
if (!m)
{
log("Failed to load model: %s\n", input_filename);
return;
}
log("Loaded from disk in %5.3fs\n", timeElapsed(t));
log("Analyzing and optimizing model...\n");
OptimizedModel* om = new OptimizedModel(m, Point3(config.objectPosition.X, config.objectPosition.Y, -config.objectSink));
for(unsigned int v = 0; v < m->volumes.size(); v++)
{
log(" Face counts: %i -> %i %0.1f%%\n", (int)m->volumes[v].faces.size(), (int)om->volumes[v].faces.size(), float(om->volumes[v].faces.size()) / float(m->volumes[v].faces.size()) * 100);
log(" Vertex counts: %i -> %i %0.1f%%\n", (int)m->volumes[v].faces.size() * 3, (int)om->volumes[v].points.size(), float(om->volumes[v].points.size()) / float(m->volumes[v].faces.size() * 3) * 100);
}
delete m;
log("Optimize model %5.3fs \n", timeElapsed(t));
//om->saveDebugSTL("output.stl");
log("Slicing model...\n");
vector<Slicer*> slicerList;
for(unsigned int volumeIdx=0; volumeIdx < om->volumes.size(); volumeIdx++)
{
slicerList.push_back(new Slicer(&om->volumes[volumeIdx], config.initialLayerThickness / 2, config.layerThickness, config.fixHorrible));
//slicerList[volumeIdx]->dumpSegments("C:\\models\\output.html");
}
log("Sliced model in %5.3fs\n", timeElapsed(t));
SliceDataStorage storage;
if (config.supportAngle > -1)
{
fprintf(stdout,"Generating support map...\n");
generateSupportGrid(storage.support, om, config.initialLayerThickness / 2, config.layerThickness);
}
storage.modelSize = om->modelSize;
storage.modelMin = om->vMin;
storage.modelMax = om->vMax;
delete om;
log("Generating layer parts...\n");
for(unsigned int volumeIdx=0; volumeIdx < slicerList.size(); volumeIdx++)
{
storage.volumes.push_back(SliceVolumeStorage());
createLayerParts(storage.volumes[volumeIdx], slicerList[volumeIdx], config.fixHorrible);
delete slicerList[volumeIdx];
//Go trough all the volumes, and remove the previous volume outlines from our own outline, so we never have overlapped areas.
for(unsigned int volumeIdx2=0; volumeIdx2<volumeIdx; volumeIdx2++)
{
for(unsigned int layerNr=0; layerNr < storage.volumes[volumeIdx].layers.size(); layerNr++)
{
SliceLayer* layer1 = &storage.volumes[volumeIdx].layers[layerNr];
SliceLayer* layer2 = &storage.volumes[volumeIdx2].layers[layerNr];
for(unsigned int p1 = 0; p1 < layer1->parts.size(); p1++)
{
ClipperLib::Clipper clipper;
clipper.AddPolygons(layer1->parts[p1].outline, ClipperLib::ptSubject);
for(unsigned int p2 = 0; p2 < layer2->parts.size(); p2++)
{
clipper.AddPolygons(layer2->parts[p2].outline, ClipperLib::ptClip);
}
clipper.Execute(ClipperLib::ctDifference, layer1->parts[p1].outline);
}
}
}
}
log("Generated layer parts in %5.3fs\n", timeElapsed(t));
//dumpLayerparts(storage, "c:/models/output.html");
const unsigned int totalLayers = storage.volumes[0].layers.size();
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
{
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
{
generateInsets(&storage.volumes[volumeIdx].layers[layerNr], config.extrusionWidth, config.insetCount);
}
logProgress("inset",layerNr+1,totalLayers);
}
log("Generated inset in %5.3fs\n", timeElapsed(t));
//dumpLayerparts(storage, "c:/models/output.html");
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
{
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
{
generateSkins(layerNr, storage.volumes[volumeIdx], config.extrusionWidth, config.downSkinCount, config.upSkinCount, config.infillOverlap);
generateSparse(layerNr, storage.volumes[volumeIdx], config.extrusionWidth, config.downSkinCount, config.upSkinCount);
}
logProgress("skin",layerNr+1,totalLayers);
}
log("Generated up/down skin in %5.3fs\n", timeElapsed(t));
generateSkirt(storage, config.skirtDistance, config.extrusionWidth, config.skirtLineCount);
generateRaft(storage, config.raftMargin);
for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++)
{
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
{
for(unsigned int partNr=0; partNr<storage.volumes[volumeIdx].layers[layerNr].parts.size(); partNr++)
{
if (layerNr > 0)
storage.volumes[volumeIdx].layers[layerNr].parts[partNr].bridgeAngle = bridgeAngle(&storage.volumes[volumeIdx].layers[layerNr].parts[partNr], &storage.volumes[volumeIdx].layers[layerNr-1]);
else
storage.volumes[volumeIdx].layers[layerNr].parts[partNr].bridgeAngle = -1;
}
}
}
gcode.setRetractionSettings(config.retractionAmount, config.retractionSpeed);
if (firstFile)
{
gcode.addCode(config.startCode);
}else{
gcode.resetExtrusionValue();
gcode.addRetraction();
gcode.setZ(maxObjectHeight + 5000);
gcode.addMove(config.objectPosition, config.moveSpeed, 0);
}
gcode.addComment("total_layers=%d",totalLayers);
GCodePathConfig skirtConfig(config.printSpeed, config.extrusionWidth, "SKIRT");
GCodePathConfig inset0Config(config.printSpeed, config.extrusionWidth, "WALL-OUTER");
GCodePathConfig inset1Config(config.printSpeed, config.extrusionWidth, "WALL-INNER");
GCodePathConfig fillConfig(config.infillSpeed, config.extrusionWidth, "FILL");
GCodePathConfig supportConfig(config.printSpeed, config.supportLineWidth, "SUPPORT");
if (config.raftBaseThickness > 0 && config.raftInterfaceThickness > 0)
{
GCodePathConfig raftBaseConfig(config.initialLayerSpeed, config.raftBaseLinewidth, "SUPPORT");
GCodePathConfig raftInterfaceConfig(config.initialLayerSpeed, config.raftInterfaceLinewidth, "SUPPORT");
{
gcode.addComment("LAYER:-2");
gcode.addComment("RAFT");
GCodePlanner gcodeLayer(gcode, config.moveSpeed);
gcode.setZ(config.raftBaseThickness);
gcode.setExtrusion(config.raftBaseThickness, config.filamentDiameter, config.filamentFlow);
gcodeLayer.addPolygonsByOptimizer(storage.raftOutline, &raftBaseConfig);
Polygons raftLines;
generateLineInfill(storage.raftOutline, raftLines, config.raftBaseLinewidth, config.raftLineSpacing, config.infillOverlap, 0);
gcodeLayer.addPolygonsByOptimizer(raftLines, &raftBaseConfig);
gcodeLayer.writeGCode(false);
}
{
gcode.addComment("LAYER:-1");
gcode.addComment("RAFT");
GCodePlanner gcodeLayer(gcode, config.moveSpeed);
gcode.setZ(config.raftBaseThickness + config.raftInterfaceThickness);
gcode.setExtrusion(config.raftInterfaceThickness, config.filamentDiameter, config.filamentFlow);
Polygons raftLines;
generateLineInfill(storage.raftOutline, raftLines, config.raftInterfaceLinewidth, config.raftLineSpacing, config.infillOverlap, 90);
gcodeLayer.addPolygonsByOptimizer(raftLines, &raftInterfaceConfig);
gcodeLayer.writeGCode(false);
}
}
int volumeIdx = 0;
for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++)
{
logProgress("export", layerNr+1, totalLayers);
GCodePlanner gcodeLayer(gcode, config.moveSpeed);
gcode.addComment("LAYER:%d", layerNr);
int32_t z = config.initialLayerThickness + layerNr * config.layerThickness;
z += config.raftBaseThickness + config.raftInterfaceThickness;
gcode.setZ(z);
if (layerNr == 0)
gcodeLayer.addPolygonsByOptimizer(storage.skirt, &skirtConfig);
for(unsigned int volumeCnt = 0; volumeCnt < storage.volumes.size(); volumeCnt++)
{
if (volumeCnt > 0)
volumeIdx = (volumeIdx + 1) % storage.volumes.size();
SliceLayer* layer = &storage.volumes[volumeIdx].layers[layerNr];
gcodeLayer.setExtruder(volumeIdx);
PathOptimizer partOrderOptimizer(gcode.getPositionXY());
for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++)
{
partOrderOptimizer.addPolygon(layer->parts[partNr].insets[0][0]);
}
partOrderOptimizer.optimize();
for(unsigned int partCounter=0; partCounter<partOrderOptimizer.polyOrder.size(); partCounter++)
{
SliceLayerPart* part = &layer->parts[partOrderOptimizer.polyOrder[partCounter]];
gcodeLayer.setCombBoundary(&part->insets[0]);
for(int insetNr=part->insets.size()-1; insetNr>-1; insetNr--)
{
if (insetNr == 0)
gcodeLayer.addPolygonsByOptimizer(part->insets[insetNr], &inset0Config);
else
gcodeLayer.addPolygonsByOptimizer(part->insets[insetNr], &inset1Config);
}
Polygons fillPolygons;
int fillAngle = 45;
if (layerNr & 1) fillAngle += 90;
//int sparseSteps[1] = {config.extrusionWidth};
//generateConcentricInfill(part->skinOutline, fillPolygons, sparseSteps, 1);
generateLineInfill(part->skinOutline, fillPolygons, config.extrusionWidth, config.extrusionWidth, config.infillOverlap, (part->bridgeAngle > -1) ? part->bridgeAngle : fillAngle);
//int sparseSteps[2] = {config.extrusionWidth*5, config.extrusionWidth * 0.8};
//generateConcentricInfill(part->sparseOutline, fillPolygons, sparseSteps, 2);
if (config.sparseInfillLineDistance > 0)
{
if (config.sparseInfillLineDistance > config.extrusionWidth * 4)
{
generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance * 2, config.infillOverlap, 45);
generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance * 2, config.infillOverlap, 45 + 90);
}
else
{
generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance, config.infillOverlap, fillAngle);
}
}
gcodeLayer.addPolygonsByOptimizer(fillPolygons, &fillConfig);
}
gcodeLayer.setCombBoundary(NULL);
}
if (config.supportAngle > -1)
{
SupportPolyGenerator supportGenerator(storage.support, z, config.supportAngle, config.supportEverywhere > 0, true);
gcodeLayer.addPolygonsByOptimizer(supportGenerator.polygons, &supportConfig);
if (layerNr == 0)
{
SupportPolyGenerator supportGenerator2(storage.support, z, config.supportAngle, config.supportEverywhere > 0, false);
gcodeLayer.addPolygonsByOptimizer(supportGenerator2.polygons, &supportConfig);
}
}
//Finish the layer by applying speed corrections for minimal layer times and slowdown for the initial layer.
if (int(layerNr) < config.initialSpeedupLayers)
{
int n = config.initialSpeedupLayers;
int layer0Factor = config.initialLayerSpeed * 100 / config.printSpeed;
gcodeLayer.setSpeedFactor((layer0Factor * (n - layerNr) + 100 * (layerNr)) / n);
}
gcodeLayer.forceMinimalLayerTime(config.minimalLayerTime, config.minimalFeedrate);
if (layerNr == 0)
gcode.setExtrusion(config.initialLayerThickness, config.filamentDiameter, config.filamentFlow);
else
gcode.setExtrusion(config.layerThickness, config.filamentDiameter, config.filamentFlow);
if (int(layerNr) >= config.fanOnLayerNr)
{
int speed = config.fanSpeedMin;
if (gcodeLayer.getSpeedFactor() <= 50)
{
speed = config.fanSpeedMax;
}else{
int n = gcodeLayer.getSpeedFactor() - 50;
speed = config.fanSpeedMin * n / 50 + config.fanSpeedMax * (50 - n) / 50;
}
gcode.addFanCommand(speed);
}else{
gcode.addFanCommand(0);
}
gcodeLayer.writeGCode(config.coolHeadLift > 0);
}
/* support debug
for(int32_t y=0; y<storage.support.gridHeight; y++)
{
for(int32_t x=0; x<storage.support.gridWidth; x++)
{
unsigned int n = x+y*storage.support.gridWidth;
if (storage.support.grid[n].size() < 1) continue;
int32_t z = storage.support.grid[n][0].z;
gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, 0), 0);
gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, z), z);
gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, 0), 0);
}
}
//*/
log("Wrote layers in %5.2fs.\n", timeElapsed(t));
gcode.tellFileSize();
gcode.addFanCommand(0);
logProgress("process", 1, 1);
log("Total time elapsed %5.2fs.\n", timeElapsed(t,true));
//Store the object height for when we are printing multiple objects, as we need to clear every one of them when moving to the next position.
maxObjectHeight = std::max(maxObjectHeight, storage.modelSize.z);
}
void setConfig(Config& config, char* str)
{
char* valuePtr = strchr(str, '=');
if (!valuePtr) return;
*valuePtr++ = '\0';
#define STRINGIFY(_s) #_s
#define SETTING(longName, shortName) if (strcasecmp(str, STRINGIFY(longName)) == 0 || strcasecmp(str, STRINGIFY(shortName)) == 0) { config.longName = atoi(valuePtr); }
SETTING(layerThickness, lt);
SETTING(initialLayerThickness, ilt);
SETTING(filamentDiameter, fd);
SETTING(filamentFlow, ff);
SETTING(extrusionWidth, ew);
SETTING(insetCount, ic);
SETTING(downSkinCount, dsc);
SETTING(upSkinCount, usc);
SETTING(sparseInfillLineDistance, sild);
SETTING(infillOverlap, iover);
SETTING(skirtDistance, sd);
SETTING(skirtLineCount, slc);
SETTING(initialSpeedupLayers, isl);
SETTING(initialLayerSpeed, ils);
SETTING(printSpeed, ps);
SETTING(infillSpeed, is);
SETTING(moveSpeed, ms);
SETTING(fanOnLayerNr, fl);
SETTING(supportAngle, supa);
SETTING(supportEverywhere, supe);
SETTING(supportLineWidth, sulw);
SETTING(retractionAmount, reta);
SETTING(retractionSpeed, rets);
SETTING(objectPosition.X, posx);
SETTING(objectPosition.Y, posy);
SETTING(objectSink, objsink);
SETTING(raftMargin, raftMar);
SETTING(raftLineSpacing, raftLS);
SETTING(raftBaseThickness, raftBaseT);
SETTING(raftBaseLinewidth, raftBaseL);
SETTING(raftInterfaceThickness, raftInterfaceT);
SETTING(raftInterfaceLinewidth, raftInterfaceL);
SETTING(minimalLayerTime, minLayTime);
SETTING(minimalFeedrate, minFeed);
SETTING(coolHeadLift, coolLift);
SETTING(fanSpeedMin, fanMin);
SETTING(fanSpeedMax, fanMax);
SETTING(extruderOffset[1].X, eOff1X);
SETTING(extruderOffset[1].Y, eOff1Y);
SETTING(extruderOffset[2].X, eOff2X);
SETTING(extruderOffset[2].Y, eOff2Y);
SETTING(extruderOffset[3].X, eOff3X);
SETTING(extruderOffset[3].Y, eOff3Y);
#undef SETTING
if (strcasecmp(str, "startCode") == 0)
config.startCode = valuePtr;
if (strcasecmp(str, "endCode") == 0)
config.endCode = valuePtr;
}
void print_usage()
{
printf("TODO\n");
}
void signal_FPE(int n)
{
printf("Floating point exception\n");
exit(1);
}
int main(int argc, char **argv)
{
#if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__))
//Lower the process priority on linux and mac.
setpriority(PRIO_PROCESS, 0, 10);
#endif
signal(SIGFPE, signal_FPE);
GCodeExport gcode;
Config config;
int fileNr = 0;
config.filamentDiameter = 2890;
config.initialLayerThickness = 300;
config.layerThickness = 100;
config.extrusionWidth = 400;
config.insetCount = 2;
config.downSkinCount = 6;
config.upSkinCount = 6;
config.initialSpeedupLayers = 4;
config.initialLayerSpeed = 20;
config.printSpeed = 50;
config.infillSpeed = 50;
config.moveSpeed = 200;
config.fanOnLayerNr = 2;
config.skirtDistance = 6000;
config.skirtLineCount = 1;
config.sparseInfillLineDistance = 100 * config.extrusionWidth / 20;
config.infillOverlap = 15;
config.objectPosition = Point(102500, 102500);
config.objectSink = 0;
config.supportAngle = -1;
config.supportEverywhere = 0;
config.supportLineWidth = config.extrusionWidth;
config.retractionAmount = 4.5;
config.retractionSpeed = 45;
config.minimalLayerTime = 5;
config.minimalFeedrate = 10;
config.coolHeadLift = 1;
config.fanSpeedMin = 100;
config.fanSpeedMax = 100;
config.raftMargin = 5000;
config.raftLineSpacing = 1000;
config.raftBaseThickness = 0;
config.raftBaseLinewidth = 0;
config.raftInterfaceThickness = 0;
config.raftInterfaceLinewidth = 0;
config.fixHorrible = 0;
config.startCode =
"M109 S210 ;Heatup to 210C\n"
"G21 ;metric values\n"
"G90 ;absolute positioning\n"
"G28 ;Home\n"
"G1 Z15.0 F300 ;move the platform down 15mm\n"
"G92 E0 ;zero the extruded length\n"
"G1 F200 E5 ;extrude 5mm of feed stock\n"
"G92 E0 ;zero the extruded length again\n";
config.endCode =
"M104 S0 ;extruder heater off\n"
"M140 S0 ;heated bed heater off (if you have it)\n"
"G91 ;relative positioning\n"
"G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n"
"G1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\n"
"G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n"
"M84 ;steppers off\n"
"G90 ;absolute positioning\n";
fprintf(stdout,"Cura_SteamEngine version %s\n", VERSION);
for(int argn = 1; argn < argc; argn++)
{
char* str = argv[argn];
if (str[0] == '-')
{
for(str++; *str; str++)
{
switch(*str)
{
case 'h':
print_usage();
exit(1);
case 'v':
verbose_level++;
break;
case 'b':
argn++;
binaryMeshBlob = fopen(argv[argn], "rb");
break;
case 'o':
argn++;
gcode.setFilename(argv[argn]);
if (!gcode.isValid())
{
logError("Failed to open %s for output.\n", argv[argn]);
exit(1);
}
gcode.addComment("Generated with Cura_SteamEngine %s", VERSION);
break;
case 's':
argn++;
setConfig(config, argv[argn]);
break;
case 'm':
argn++;
sscanf(argv[argn], "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
&config.matrix.m[0][0], &config.matrix.m[0][1], &config.matrix.m[0][2],
&config.matrix.m[1][0], &config.matrix.m[1][1], &config.matrix.m[1][2],
&config.matrix.m[2][0], &config.matrix.m[2][1], &config.matrix.m[2][2]);
break;
default:
logError("Unknown option: %c\n", *str);
break;
}
}
}else{
if (!gcode.isValid())
{
logError("No output file specified\n");
return 1;
}
processFile(argv[argn], config, gcode, fileNr == 0);
fileNr ++;
}
}
if (gcode.isValid())
{
gcode.addFanCommand(0);
gcode.addCode(config.endCode);
log("Print time: %d\n", int(gcode.getTotalPrintTime()));
log("Filament: %d\n", int(gcode.getTotalFilamentUsed()));
}
}