Initial commit of gcode export toolchain (incomplete).

Passes tests so far as written, gcode output through test interface expects an ostream.
This commit is contained in:
Joseph Lenox 2018-07-15 14:48:56 -05:00
parent b2d6c47c53
commit 5b71940e60
9 changed files with 712 additions and 3 deletions

View File

@ -75,6 +75,7 @@ set(GUI_TESTDIR ${CMAKE_CURRENT_SOURCE_DIR}/test/GUI/)
# directory that contains the dependent non-source files, like models and configurations
set(TESTFILE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test/inputs/)
include_directories(${LIBDIR})
include_directories(${LIBDIR}/libslic3r)
include_directories(${LIBDIR}/slic3r/GUI/)
@ -112,6 +113,7 @@ add_library(libslic3r STATIC
${LIBDIR}/libslic3r/Fill/FillGyroid.cpp
${LIBDIR}/libslic3r/Flow.cpp
${LIBDIR}/libslic3r/GCode.cpp
${LIBDIR}/libslic3r/PrintGCode.cpp
${LIBDIR}/libslic3r/GCode/CoolingBuffer.cpp
${LIBDIR}/libslic3r/GCode/SpiralVase.cpp
${LIBDIR}/libslic3r/GCodeReader.cpp
@ -166,6 +168,7 @@ add_library(admesh STATIC
set_property(TARGET admesh PROPERTY C_STANDARD 99)
add_library(clipper STATIC ${LIBDIR}/clipper.cpp)
add_library(expat STATIC
${LIBDIR}/expat/xmlparse.c
${LIBDIR}/expat/xmlrole.c
@ -182,8 +185,6 @@ add_library(poly2tri STATIC
${LIBDIR}/poly2tri/sweep/sweep.cc
)
set(UI_TEST_SOURCES
${GUI_TESTDIR}/testableframe.cpp
${GUI_TESTDIR}/test_harness_gui.cpp
@ -196,13 +197,17 @@ set(UI_TEST_SOURCES
${GUI_TESTDIR}/test_field_point3.cpp
${GUI_TESTDIR}/test_misc_ui.cpp
)
set(SLIC3R_TEST_SOURCES
${TESTDIR}/test_harness.cpp
${TESTDIR}/test_data.cpp
${TESTDIR}/libslic3r/test_trianglemesh.cpp
${TESTDIR}/libslic3r/test_config.cpp
${TESTDIR}/libslic3r/test_support_material.cpp
${TESTDIR}/libslic3r/test_flow.cpp
${TESTDIR}/libslic3r/test_printgcode.cpp
)
add_executable(slic3r slic3r.cpp)
#set_target_properties(slic3r PROPERTIES LINK_SEARCH_START_STATIC 1)
#set_target_properties(slic3r PROPERTIES LINK_SEARCH_END_STATIC 1)

View File

@ -0,0 +1,127 @@
#include <catch.hpp>
#include <numeric>
#include <sstream>
#include "test_data.hpp" // get access to init_print, etc
#include "Config.hpp"
#include "Model.hpp"
#include "ConfigBase.hpp"
#include "GCodeReader.hpp"
#include "Flow.hpp"
using namespace Slic3r::Test;
using namespace Slic3r;
SCENARIO("Extrusion width specifics") {
GIVEN("A config with a skirt, brim, some fill density, 3 perimeters, and 1 bottom solid layer and a 20mm cube mesh") {
// this is a sharedptr
auto config {Slic3r::Config::new_from_defaults()};
config->set("skirts", 1);
config->set("brim_width", 2);
config->set("perimeters", 3);
config->set("fill_density", 0.4);
config->set("first_layer_height", "100%");
WHEN("first layer width set to 2mm") {
config->set("first_layer_extrusion_width", 2);
auto print {Slic3r::Test::init_print(std::make_tuple<int,int,int>(20,20,20), config)};
std::vector<double> E_per_mm_bottom;
auto gcode {std::stringstream("")};
Slic3r::Test::gcode(gcode, print);
auto parser {Slic3r::GCodeReader()};
const auto layer_height { config->getFloat("layer_height") };
parser.parse_stream(gcode, [&E_per_mm_bottom, layer_height] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
{
if (self.Z == layer_height) { // only consider first layer
if (line.extruding() && line.dist_XY() > 0) {
E_per_mm_bottom.emplace_back(line.dist_E() / line.dist_XY());
}
}
});
THEN(" First layer width applies to everything on first layer.") {
bool pass = false;
auto avg_E {std::accumulate(E_per_mm_bottom.cbegin(), E_per_mm_bottom.cend(), 0.0) / static_cast<double>(E_per_mm_bottom.size())};
pass = std::count_if(E_per_mm_bottom.cbegin(), E_per_mm_bottom.cend(), [avg_E] (double v) { return _equiv(v, avg_E, 0.015); }) == 0;
REQUIRE(pass == true);
REQUIRE(E_per_mm_bottom.size() > 0);
}
THEN(" First layer width does not apply to upper layer.") {
}
}
}
}
SCENARIO(" Bridge flow specifics.", "[!mayfail]") {
GIVEN("A default config with no cooling and a fixed bridge speed, flow ratio and an overhang mesh.") {
WHEN("bridge_flow_ratio is set to 1.0") {
THEN("Output flow is as expected.") {
}
}
WHEN("bridge_flow_ratio is set to 0.5") {
THEN("Output flow is as expected.") {
}
}
WHEN("bridge_flow_ratio is set to 2.0") {
THEN("Output flow is as expected.") {
}
}
}
GIVEN("A default config with no cooling and a fixed bridge speed, flow ratio, fixed extrusion width of 0.4mm and an overhang mesh.") {
WHEN("bridge_flow_ratio is set to 1.0") {
THEN("Output flow is as expected.") {
}
}
WHEN("bridge_flow_ratio is set to 0.5") {
THEN("Output flow is as expected.") {
}
}
WHEN("bridge_flow_ratio is set to 2.0") {
THEN("Output flow is as expected.") {
}
}
}
}
/// Test the expected behavior for auto-width,
/// spacing, etc
SCENARIO("Flow: Flow math for non-bridges", "[!mayfail]") {
GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
auto width {ConfigOptionFloat(1.0)};
auto spacing {0.4};
auto nozzle_diameter {0.4};
auto bridge_flow {1.0};
// Spacing for non-bridges is has some overlap
THEN("External perimeter flow has spacing of <>") {
}
THEN("Internal perimeter flow has spacing of <>") {
}
}
/// Check the min/max
GIVEN("Nozzle Diameter of 0.4") {
WHEN("AA") {
}
}
GIVEN("Nozzle Diameter of 0.4, a desired spacing of 1mm and layer height of 0.5") {
}
}
/// Spacing, width calculation for bridge extrusions
SCENARIO("Flow: Flow math for bridges", "[!mayfail]") {
GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
// auto width {ConfigOptionFloat(1.0)};
auto spacing {0.4};
auto nozzle_diameter {0.4};
auto bridge_flow {1.0};
}
GIVEN("Nozzle Diameter of 0.4, a desired spacing of 1mm and layer height of 0.5") {
}
}
SCENARIO("Flow: Auto spacing", "[!mayfail]") {
}

View File

@ -0,0 +1,91 @@
#include <catch.hpp>
#include <regex>
#include "test_data.hpp"
#include "libslic3r.h"
SCENARIO( "PrintGCode basic functionality", "[!mayfail]") {
GIVEN("A default configuration and a print test object") {
auto config {Slic3r::Config::new_from_defaults()};
auto gcode {std::stringstream("")};
WHEN("the output is executed with no support material") {
config->set("first_layer_extrusion_width", 0);
auto print {Slic3r::Test::init_print(std::make_tuple<int,int,int>(20,20,20), config)};
Slic3r::Test::gcode(gcode, print);
auto exported {gcode.str()};
THEN("Some text output is generated.") {
REQUIRE(exported.size() > 0);
}
THEN("Exported text contains slic3r version") {
REQUIRE(exported.find(SLIC3R_VERSION) != std::string::npos);
}
THEN("Exported text contains git commit id") {
REQUIRE(exported.find("; Git Commit") != std::string::npos);
REQUIRE(exported.find(BUILD_COMMIT) != std::string::npos);
}
THEN("Exported text contains extrusion statistics.") {
REQUIRE(exported.find("; external perimeters extrusion width") != std::string::npos);
REQUIRE(exported.find("; perimeters extrusion width") != std::string::npos);
REQUIRE(exported.find("; infill extrusion width") != std::string::npos);
REQUIRE(exported.find("; solid infill extrusion width") != std::string::npos);
REQUIRE(exported.find("; top solid infill extrusion width") != std::string::npos);
REQUIRE(exported.find("; support material extrusion width") == std::string::npos);
REQUIRE(exported.find("; first layer extrusion width") == std::string::npos);
}
THEN("GCode preamble is emitted.") {
REQUIRE(exported.find("G21 ; set units to millimeters") != std::string::npos);
}
}
WHEN("the output is executed with support material") {
config->set("first_layer_extrusion_width", 0);
config->set("support_material", true);
config->set("raft_layers", 3);
auto print {Slic3r::Test::init_print(std::make_tuple<int,int,int>(20,20,20), config)};
Slic3r::Test::gcode(gcode, print);
auto exported {gcode.str()};
THEN("Some text output is generated.") {
REQUIRE(exported.size() > 0);
}
THEN("Exported text contains extrusion statistics.") {
REQUIRE(exported.find("; external perimeters extrusion width") != std::string::npos);
REQUIRE(exported.find("; perimeters extrusion width") != std::string::npos);
REQUIRE(exported.find("; infill extrusion width") != std::string::npos);
REQUIRE(exported.find("; solid infill extrusion width") != std::string::npos);
REQUIRE(exported.find("; top solid infill extrusion width") != std::string::npos);
REQUIRE(exported.find("; support material extrusion width") != std::string::npos);
REQUIRE(exported.find("; first layer extrusion width") == std::string::npos);
}
}
WHEN("the output is executed with a separate first layer extrusion width") {
config->set("first_layer_extrusion_width", 0.5);
auto print {Slic3r::Test::init_print(std::make_tuple<int,int,int>(20,20,20), config)};
Slic3r::Test::gcode(gcode, print);
auto exported {gcode.str()};
THEN("Some text output is generated.") {
REQUIRE(exported.size() > 0);
}
THEN("Exported text contains extrusion statistics.") {
REQUIRE(exported.find("; external perimeters extrusion width") != std::string::npos);
REQUIRE(exported.find("; perimeters extrusion width") != std::string::npos);
REQUIRE(exported.find("; infill extrusion width") != std::string::npos);
REQUIRE(exported.find("; solid infill extrusion width") != std::string::npos);
REQUIRE(exported.find("; top solid infill extrusion width") != std::string::npos);
REQUIRE(exported.find("; support material extrusion width") == std::string::npos);
REQUIRE(exported.find("; first layer extrusion width") != std::string::npos);
}
}
WHEN("Cooling is enabled and the fan is disabled.") {
config->set("cooling", true);
config->set("disable_fan_first_layers", 5);
auto print {Slic3r::Test::init_print(std::make_tuple<int,int,int>(20,20,20), config)};
Slic3r::Test::gcode(gcode, print);
auto exported {gcode.str()};
THEN("GCode to disable fan is emitted."){
REQUIRE(exported.find("M107") != std::string::npos);
}
}
gcode.clear();
}
}

View File

@ -1,7 +1,39 @@
#include "test_data.hpp"
#include "TriangleMesh.hpp"
#include "GCodeReader.hpp"
#include "Config.hpp"
#include "Print.hpp"
#include <cstdlib>
#include <string>
using namespace std::string_literals;
using namespace std;
namespace Slic3r { namespace Test {
// Mesh enumeration to name mapping
const std::unordered_map<TestMesh, const char*> mesh_names {
std::make_pair<TestMesh, const char*>(TestMesh::A,"A"),
std::make_pair<TestMesh, const char*>(TestMesh::L,"L"),
std::make_pair<TestMesh, const char*>(TestMesh::V,"V"),
std::make_pair<TestMesh, const char*>(TestMesh::_40x10,"40x10"),
std::make_pair<TestMesh, const char*>(TestMesh::bridge,"bridge"),
std::make_pair<TestMesh, const char*>(TestMesh::bridge_with_hole,"bridge_with_hole"),
std::make_pair<TestMesh, const char*>(TestMesh::cube_with_concave_hole,"cube_with_concave_hole"),
std::make_pair<TestMesh, const char*>(TestMesh::cube_with_hole,"cube_with_hole"),
std::make_pair<TestMesh, const char*>(TestMesh::gt2_teeth,"gt2_teeth"),
std::make_pair<TestMesh, const char*>(TestMesh::ipadstand,"ipadstand"),
std::make_pair<TestMesh, const char*>(TestMesh::overhang,"overhang"),
std::make_pair<TestMesh, const char*>(TestMesh::pyramid,"pyramid"),
std::make_pair<TestMesh, const char*>(TestMesh::sloping_hole,"sloping_hole"),
std::make_pair<TestMesh, const char*>(TestMesh::slopy_cube,"slopy_cube"),
std::make_pair<TestMesh, const char*>(TestMesh::small_dorito,"small_dorito"),
std::make_pair<TestMesh, const char*>(TestMesh::step,"step"),
std::make_pair<TestMesh, const char*>(TestMesh::two_hollow_squares,"two_hollow_squares")
};
TriangleMesh mesh(TestMesh m) {
Point3s facets;
Pointf3s vertices;
@ -150,11 +182,71 @@ TriangleMesh mesh(TestMesh m) {
return _mesh;
}
TriangleMesh mesh(TestMesh m, Pointf3 translate, double scale) {
return mesh(m, translate, Pointf3(scale, scale, scale));
}
TriangleMesh mesh(TestMesh m, Pointf3 translate, Pointf3 scale) {
TriangleMesh _mesh {mesh(m)};
_mesh.scale(scale);
_mesh.translate(translate);
return _mesh;
}
/*
Slic3r::Model model(const std::string& model_name, TestMesh m, Pointf3 translate = Pointf3(0,0,0), double scale = 1.0) {
return model(model_name, m, translate, Pointf3(scale, scale, scale));
}
*/
Slic3r::Test::Print init_print(std::tuple<int,int,int> cube, config_ptr _config) {
std::stringstream s;
s << "cube_" << get<0>(cube) << "x" << get<1>(cube) << "x" << get<2>(cube);
auto config {Slic3r::Config::new_from_defaults()};
config->apply(_config);
const char* v {std::getenv("SLIC3R_TESTS_GCODE")};
auto tests_gcode {(v == nullptr ? ""s : std::string(v))};
if (tests_gcode != ""s)
config->set("gcode_comments", 1);
std::shared_ptr<Slic3r::Print> print = std::make_shared<Slic3r::Print>();
print->apply_config(config);
auto model {Slic3r::Test::model(s.str(), Slic3r::TriangleMesh::make_cube(get<0>(cube), get<1>(cube), get<1>(cube))) };
model.arrange_objects(print->config.min_object_distance());
model.center_instances_around_point(Slic3r::Pointf(100,100));
for (auto* mo : model.objects) {
print->auto_assign_extruders(mo);
print->add_model_object(mo);
}
print->validate();
std::vector<Slic3r::Model> models {};
models.emplace_back(model);
return Slic3r::Test::Print(print, models);
}
void gcode(std::stringstream& gcode, Slic3r::Test::Print& _print) {
Slic3r::Print& print {_print.print()};
print.process();
print.export_gcode(gcode, true);
}
Slic3r::Model model(const std::string& model_name, TriangleMesh&& _mesh) {
Slic3r::Model result;
auto* object {result.add_object()};
object->name += model_name + ".stl"s;
object->add_volume(_mesh);
auto* inst {object->add_instance()};
inst->rotation = 0;
inst->scaling_factor = 1.0;
return result;
}
} } // namespace Slic3r::Test

View File

@ -2,9 +2,15 @@
#include "Point.hpp"
#include "TriangleMesh.hpp"
#include "Geometry.hpp"
#include "Model.hpp"
#include "Print.hpp"
#include "Config.hpp"
#include <unordered_map>
namespace Slic3r { namespace Test {
/// Enumeration of test meshes
enum class TestMesh {
A,
L,
@ -25,16 +31,46 @@ enum class TestMesh {
two_hollow_squares
};
class Print {
public:
Print(std::shared_ptr<Slic3r::Print> _print, std::vector<Slic3r::Model> _models) : models(_models), _print(_print) {}
void process() { _print->process(); }
void apply_config(config_ptr _config) { _print->apply_config(_config); }
Slic3r::Print& print() { return *(_print); }
const std::vector<Slic3r::Model> models {};
private:
std::shared_ptr<Slic3r::Print> _print {nullptr};
};
/// Mesh enumeration to name mapping
extern const std::unordered_map<TestMesh, const char*> mesh_names;
/// Port of Slic3r::Test::mesh
/// Basic cubes/boxes should call TriangleMesh::make_cube() directly and rescale/translate it
TriangleMesh mesh(TestMesh m);
TriangleMesh mesh(TestMesh m, Pointf3 translate, Pointf3 scale = Pointf3(1.0, 1.0, 1.0));
TriangleMesh mesh(TestMesh m, Pointf3 translate, double scale = 1.0);
/// Templated function to see if two values are equivalent (+/- epsilon)
template <typename T, typename U>
bool _equiv(T a, U b) { return abs(a - b) < Slic3r::Geometry::epsilon; }
bool _equiv(const T& a, const U& b) { return abs(a - b) < Slic3r::Geometry::epsilon; }
template <typename T, typename U>
bool _equiv(const T& a, const U& b, double epsilon) { return abs(a - b) < epsilon; }
//Slic3r::Model model(const std::string& model_name, TestMesh m, Pointf3 translate = Pointf3(0,0,0), Pointf3 scale = Pointf3(1.0,1.0,1.0));
//Slic3r::Model model(const std::string& model_name, TestMesh m, Pointf3 translate = Pointf3(0,0,0), double scale = 1.0);
Slic3r::Model model(const std::string& model_name, TriangleMesh&& _mesh);
Slic3r::Test::Print init_print(std::tuple<int,int,int> cube, config_ptr _config = Slic3r::Config::new_from_defaults());
void gcode(std::stringstream& gcode, Slic3r::Test::Print& print);
} } // namespace Slic3r::Test
#endif // SLIC3R_TEST_DATA_HPP

View File

@ -1,4 +1,5 @@
#include "Print.hpp"
#include "PrintGCode.hpp"
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"
#include "Fill/Fill.hpp"
@ -91,6 +92,25 @@ Print::delete_object(size_t idx)
// TODO: purge unused regions
}
#ifndef SLIC3RXS
void
Print::process()
{
}
void
Print::make_brim()
{
}
void
Print::make_skirt()
{
}
#endif // SLIC3RXS
void
Print::reload_object(size_t idx)
{
@ -500,6 +520,27 @@ Print::add_model_object(ModelObject* model_object, int idx)
}
}
#ifndef SLIC3RXS
void
Print::export_gcode(std::ostream& output, bool quiet)
{
this->process();
if (this->status_cb != nullptr)
this->status_cb(90, "Exporting G-Code...");
auto export_handler {Slic3r::PrintGCode(*this, output)};
export_handler.output();
}
bool
Print::apply_config(config_ptr config) {
// dereference the stored pointer and pass the resulting data to apply_config()
return this->apply_config(config->config());
}
#endif
bool
Print::apply_config(DynamicPrintConfig config)
{

View File

@ -9,6 +9,7 @@
#include "BoundingBox.hpp"
#include "Flow.hpp"
#include "PrintConfig.hpp"
#include "Config.hpp"
#include "Point.hpp"
#include "Layer.hpp"
#include "Model.hpp"
@ -184,6 +185,13 @@ class Print
PrintRegionPtrs regions;
PlaceholderParser placeholder_parser;
// TODO: status_cb
#ifndef SLIC3RXS
std::function<void(int, const std::string&)> status_cb {nullptr};
/// Function pointer for the UI side to call post-processing scripts.
/// Vector is assumed to be the executable script and all arguments.
std::function<void(std::vector<std::string>)> post_process_cb {nullptr};
#endif
double total_used_filament, total_extruded_volume, total_cost, total_weight;
std::map<size_t,float> filament_stats;
PrintState<PrintStep> state;
@ -206,6 +214,21 @@ class Print
PrintRegion* get_region(size_t idx) { return this->regions.at(idx); };
const PrintRegion* get_region(size_t idx) const { return this->regions.at(idx); };
PrintRegion* add_region();
#ifndef SLIC3RXS
/// Triggers the rest of the print process
void process();
/// Performs a gcode export.
void export_gcode(std::ostream& output, bool quiet = false);
/// Performs a gcode export and then runs post-processing scripts (if any)
void export_gcode(const std::string& filename, bool quiet = false);
/// commands a gcode export to a temporary file and return its name
std::string export_gcode(bool quiet = false);
#endif // SLIC3RXS
// methods for handling state
bool invalidate_state_by_config(const PrintConfigBase &config);
@ -214,6 +237,10 @@ class Print
bool step_done(PrintObjectStep step) const;
void add_model_object(ModelObject* model_object, int idx = -1);
#ifndef SLIC3RXS
/// Apply a provided configuration to the internal copy
bool apply_config(config_ptr config);
#endif // SLIC3RXS
bool apply_config(DynamicPrintConfig config);
bool has_infinite_skirt() const;
bool has_skirt() const;
@ -225,6 +252,16 @@ class Print
Flow brim_flow() const;
Flow skirt_flow() const;
void _make_brim();
#ifndef SLIC3RXS
/// Generates a skirt around the union of all of
/// the objects in the print.
void make_skirt();
/// Generates a brim around all of the objects in the print.
void make_brim();
#endif // SLIC3RXS
std::set<size_t> object_extruders() const;
std::set<size_t> support_material_extruders() const;

View File

@ -0,0 +1,211 @@
#ifndef SLIC3RXS
#include "PrintGCode.hpp"
#include <ctime>
#include <iostream>
namespace Slic3r {
void
PrintGCode::output()
{
auto& gcodegen {this->_gcodegen};
auto& fh {this->fh};
auto& print {this->_print};
const auto& config {this->config};
// Write information about the generator.
time_t rawtime; tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
fh << "; generated by Slic3r " << SLIC3R_VERSION << " on ";
fh << asctime(timeinfo) << "\n";
fh << "; Git Commit: " << BUILD_COMMIT << "\n\n";
// Writes notes (content of all Settings tabs -> Notes)
fh << gcodegen.notes();
// Write some terse information on the slicing parameters.
auto& first_object {*(this->objects.at(0))};
auto layer_height {first_object.config.layer_height.getFloat()};
for (auto* region : print.regions) {
{
auto flow {region->flow(frExternalPerimeter, layer_height, false, false, -1, first_object)};
auto vol_speed {flow.mm3_per_mm() * region->config.get_abs_value("external_perimeter_speed")};
if (config.max_volumetric_speed.getInt() > 0)
vol_speed = std::min(vol_speed, config.max_volumetric_speed.getFloat());
fh << "; external perimeters extrusion width = ";
fh << std::fixed << std::setprecision(2) << flow.width << "mm ";
fh << "(" << vol_speed << "mm^3/s)\n";
}
{
auto flow {region->flow(frPerimeter, layer_height, false, false, -1, first_object)};
auto vol_speed {flow.mm3_per_mm() * region->config.get_abs_value("perimeter_speed")};
if (config.max_volumetric_speed.getInt() > 0)
vol_speed = std::min(vol_speed, config.max_volumetric_speed.getFloat());
fh << "; perimeters extrusion width = ";
fh << std::fixed << std::setprecision(2) << flow.width << "mm ";
fh << "(" << vol_speed << "mm^3/s)\n";
}
{
auto flow {region->flow(frInfill, layer_height, false, false, -1, first_object)};
auto vol_speed {flow.mm3_per_mm() * region->config.get_abs_value("infill_speed")};
if (config.max_volumetric_speed.getInt() > 0)
vol_speed = std::min(vol_speed, config.max_volumetric_speed.getFloat());
fh << "; infill extrusion width = ";
fh << std::fixed << std::setprecision(2) << flow.width << "mm ";
fh << "(" << vol_speed << "mm^3/s)\n";
}
{
auto flow {region->flow(frSolidInfill, layer_height, false, false, -1, first_object)};
auto vol_speed {flow.mm3_per_mm() * region->config.get_abs_value("solid_infill_speed")};
if (config.max_volumetric_speed.getInt() > 0)
vol_speed = std::min(vol_speed, config.max_volumetric_speed.getFloat());
fh << "; solid infill extrusion width = ";
fh << std::fixed << std::setprecision(2) << flow.width << "mm ";
fh << "(" << vol_speed << "mm^3/s)\n";
}
{
auto flow {region->flow(frTopSolidInfill, layer_height, false, false, -1, first_object)};
auto vol_speed {flow.mm3_per_mm() * region->config.get_abs_value("top_solid_infill_speed")};
if (config.max_volumetric_speed.getInt() > 0)
vol_speed = std::min(vol_speed, config.max_volumetric_speed.getFloat());
fh << "; top solid infill extrusion width = ";
fh << std::fixed << std::setprecision(2) << flow.width << "mm ";
fh << "(" << vol_speed << "mm^3/s)\n";
}
if (print.has_support_material()) {
auto flow {first_object._support_material_flow()};
auto vol_speed {flow.mm3_per_mm() * first_object.config.get_abs_value("support_material_speed")};
if (config.max_volumetric_speed.getInt() > 0)
vol_speed = std::min(vol_speed, config.max_volumetric_speed.getFloat());
fh << "; support material extrusion width = ";
fh << std::fixed << std::setprecision(2) << flow.width << "mm ";
fh << "(" << vol_speed << "mm^3/s)\n";
}
if (print.config.first_layer_extrusion_width.getFloat() > 0) {
auto flow {region->flow(frPerimeter, layer_height, false, false, -1, first_object)};
// auto vol_speed {flow.mm3_per_mm() * print.config.get_abs_value("first_layer_speed")};
// if (config.max_volumetric_speed.getInt() > 0)
// vol_speed = std::min(vol_speed, config.max_volumetric_speed.getFloat());
fh << "; first layer extrusion width = ";
fh << std::fixed << std::setprecision(2) << flow.width << "mm ";
// fh << "(" << vol_speed << "mm^3/s)\n";
}
fh << std::endl;
}
// Prepare the helper object for replacing placeholders in custom G-Code and output filename
print.placeholder_parser.update_timestamp();
// GCode sets this automatically when change_layer() is called, but needed for skirt/brim as well
gcodegen.first_layer = true;
// disable fan
if (config.cooling.getBool() && config.disable_fan_first_layers.getInt() > 0) {
fh << gcodegen.writer.set_fan(0,1) << "\n";
}
// set bed temperature
auto bed_temp_regex { std::regex("M(?:190|140)", std::regex_constants::icase)};
auto ex_temp_regex { std::regex("M(?:109|104)", std::regex_constants::icase)};
auto temp{config.first_layer_bed_temperature.getFloat()};
if (config.has_heatbed.getBool() && temp > 0 && std::regex_search(config.start_gcode.getString(), bed_temp_regex)) {
fh << gcodegen.writer.set_bed_temperature(temp, 1);
}
// Set extruder(s) temperature before and after start gcode.
auto include_start_extruder_temp {!std::regex_search(config.start_gcode.getString(), ex_temp_regex)};
for(const auto& start_gcode : config.start_filament_gcode.values) {
include_start_extruder_temp = include_start_extruder_temp && !std::regex_search(start_gcode, ex_temp_regex);
}
auto include_end_extruder_temp {!std::regex_search(config.end_gcode.getString(), ex_temp_regex)};
for(const auto& end_gcode : config.end_filament_gcode.values) {
include_end_extruder_temp = include_end_extruder_temp && !std::regex_search(end_gcode, ex_temp_regex);
}
if (include_start_extruder_temp) this->_print_first_layer_temperature(0);
// Apply gcode math to start and end gcode
// fh << apply_math(gcodegen.placeholder_parser->process(config.start_gcode.value));
for(const auto& start_gcode : config.start_filament_gcode.values) {
// fh << apply_math(gcodegen.placeholder_parser->process(start_gcode));
}
if (include_start_extruder_temp) this->_print_first_layer_temperature(1);
// Set other general things (preamble)
fh << gcodegen.preamble();
// initialize motion planner for object-to-object travel moves
if (config.avoid_crossing_perimeters.getBool()) {
auto distance_from_objects {scale_(1)};
// compute the offsetted convex hull for each object and repeat it for each copy
Polygons islands_p {};
for (auto object : this->objects) {
Polygons polygons {};
for (auto layer : object->layers) {
const auto& slice {ExPolygons(layer->slices)};
std::for_each(slice.cbegin(), slice.cend(), [&polygons] (const ExPolygon& a) { polygons.emplace_back(a.contour); });
}
if (polygons.size() == 0) continue;
for (auto copy : object->_shifted_copies) {
Polygons copy_islands_p {polygons};
std::for_each(copy_islands_p.begin(), copy_islands_p.end(), [copy] (Polygon& obj) { obj.translate(copy); });
islands_p.insert(islands_p.cend(), copy_islands_p.begin(), copy_islands_p.end());
}
}
gcodegen.avoid_crossing_perimeters.init_external_mp(union_ex(islands_p));
}
// Calculate wiping points if needed.
//
// Set initial extruder only after custom start gcode
// Do all objects for each layer.
if (config.complete_objects.getBool()) {
} else {
}
// Write end commands to file.
// set bed temperature
// Get filament stats
// Append full config
}
std::string
PrintGCode::filter(const std::string& in, bool wait)
{
return in;
}
void
PrintGCode::_print_first_layer_temperature(bool wait)
{
auto& gcodegen {this->_gcodegen};
auto& fh {this->fh};
const auto& print {this->_print};
const auto& config {this->config};
for (auto& t : print.extruders()) {
auto temp { config.first_layer_temperature.get_at(t) };
if (config.ooze_prevention.value) temp += config.standby_temperature_delta.value;
if (temp > 0) fh << gcodegen.writer.set_temperature(temp, wait, t);
}
}
} // namespace Slic3r
#endif //SLIC3RXS

View File

@ -0,0 +1,69 @@
#ifndef SLIC3RXS
#ifndef slic3r_PrintGCode_hpp
#define slic3r_PrintGCode_hpp
#include "GCode.hpp"
#include "GCode/CoolingBuffer.hpp"
#include "GCode/SpiralVase.hpp"
#include "Geometry.hpp"
#include "Flow.hpp"
#include "ExtrusionEntity.hpp"
#include "libslic3r.h"
#include <string>
#include <iostream>
namespace Slic3r {
class PrintGCode {
public:
PrintGCode(Slic3r::Print& print, std::ostream& _fh) :
_print(print),
config(print.config),
_gcodegen(Slic3r::GCode()),
objects(print.objects),
fh(_fh),
_cooling_buffer(Slic3r::CoolingBuffer(this->_gcodegen)),
_spiral_vase(Slic3r::SpiralVase(this->config))
{ };
/// Perform the export. export is a reserved name in C++, so changed to output
void output();
void flush_filters() { fh << this->filter(this->_cooling_buffer.flush(), 1); }
/// Applies various filters, if enabled.
std::string filter(const std::string& in, bool wait);
private:
Slic3r::Print& _print;
const Slic3r::PrintConfig& config;
Slic3r::GCode _gcodegen;
const PrintObjectPtrs& objects;
std::ostream& fh;
Slic3r::CoolingBuffer _cooling_buffer;
Slic3r::SpiralVase _spiral_vase;
// Slic3r::VibrationLimit _vibration_limit;
// Slic3r::ArcFitting _arc_fitting;
// Slic3r::PressureRegulator _pressure_regulator;
size_t _skirt_done {0};
bool _brim_done {false};
bool _second_layer_things_done {false};
std::string _last_obj_copy {""};
bool _autospeed {false};
void _print_first_layer_temperature(bool wait);
void _print_off_temperature(bool wait);
};
} // namespace Slic3r
#endif // slic3r_PrintGCode_hpp
#endif // SLIC3RXS