mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-16 17:15:56 +08:00
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:
parent
b2d6c47c53
commit
5b71940e60
@ -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)
|
||||
|
127
src/test/libslic3r/test_flow.cpp
Normal file
127
src/test/libslic3r/test_flow.cpp
Normal 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]") {
|
||||
}
|
91
src/test/libslic3r/test_printgcode.cpp
Normal file
91
src/test/libslic3r/test_printgcode.cpp
Normal 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();
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
211
xs/src/libslic3r/PrintGCode.cpp
Normal file
211
xs/src/libslic3r/PrintGCode.cpp
Normal 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
|
69
xs/src/libslic3r/PrintGCode.hpp
Normal file
69
xs/src/libslic3r/PrintGCode.hpp
Normal 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
|
Loading…
x
Reference in New Issue
Block a user