mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-23 00:31:11 -06:00
Merge with master
This commit is contained in:
commit
de92f45eaf
125 changed files with 38665 additions and 4069 deletions
|
@ -4,38 +4,9 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
template <class PointClass>
|
||||
BoundingBoxBase<PointClass>::BoundingBoxBase(const std::vector<PointClass> &points)
|
||||
{
|
||||
if (points.empty())
|
||||
CONFESS("Empty point set supplied to BoundingBoxBase constructor");
|
||||
typename std::vector<PointClass>::const_iterator it = points.begin();
|
||||
this->min.x = this->max.x = it->x;
|
||||
this->min.y = this->max.y = it->y;
|
||||
for (++it; it != points.end(); ++it) {
|
||||
this->min.x = std::min(it->x, this->min.x);
|
||||
this->min.y = std::min(it->y, this->min.y);
|
||||
this->max.x = std::max(it->x, this->max.x);
|
||||
this->max.y = std::max(it->y, this->max.y);
|
||||
}
|
||||
this->defined = true;
|
||||
}
|
||||
template BoundingBoxBase<Point>::BoundingBoxBase(const std::vector<Point> &points);
|
||||
template BoundingBoxBase<Pointf>::BoundingBoxBase(const std::vector<Pointf> &points);
|
||||
|
||||
template <class PointClass>
|
||||
BoundingBox3Base<PointClass>::BoundingBox3Base(const std::vector<PointClass> &points)
|
||||
: BoundingBoxBase<PointClass>(points)
|
||||
{
|
||||
if (points.empty())
|
||||
CONFESS("Empty point set supplied to BoundingBox3Base constructor");
|
||||
typename std::vector<PointClass>::const_iterator it = points.begin();
|
||||
this->min.z = this->max.z = it->z;
|
||||
for (++it; it != points.end(); ++it) {
|
||||
this->min.z = std::min(it->z, this->min.z);
|
||||
this->max.z = std::max(it->z, this->max.z);
|
||||
}
|
||||
}
|
||||
template BoundingBox3Base<Pointf3>::BoundingBox3Base(const std::vector<Pointf3> &points);
|
||||
|
||||
BoundingBox::BoundingBox(const Lines &lines)
|
||||
|
|
|
@ -23,7 +23,23 @@ public:
|
|||
BoundingBoxBase() : defined(false) {};
|
||||
BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) :
|
||||
min(pmin), max(pmax), defined(pmin.x < pmax.x && pmin.y < pmax.y) {}
|
||||
BoundingBoxBase(const std::vector<PointClass> &points);
|
||||
BoundingBoxBase(const std::vector<PointClass>& points)
|
||||
{
|
||||
if (points.empty())
|
||||
CONFESS("Empty point set supplied to BoundingBoxBase constructor");
|
||||
|
||||
typename std::vector<PointClass>::const_iterator it = points.begin();
|
||||
this->min.x = this->max.x = it->x;
|
||||
this->min.y = this->max.y = it->y;
|
||||
for (++it; it != points.end(); ++it)
|
||||
{
|
||||
this->min.x = std::min(it->x, this->min.x);
|
||||
this->min.y = std::min(it->y, this->min.y);
|
||||
this->max.x = std::max(it->x, this->max.x);
|
||||
this->max.y = std::max(it->y, this->max.y);
|
||||
}
|
||||
this->defined = (this->min.x < this->max.x) && (this->min.y < this->max.y);
|
||||
}
|
||||
void merge(const PointClass &point);
|
||||
void merge(const std::vector<PointClass> &points);
|
||||
void merge(const BoundingBoxBase<PointClass> &bb);
|
||||
|
@ -54,7 +70,21 @@ public:
|
|||
BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) :
|
||||
BoundingBoxBase<PointClass>(pmin, pmax)
|
||||
{ if (pmin.z >= pmax.z) BoundingBoxBase<PointClass>::defined = false; }
|
||||
BoundingBox3Base(const std::vector<PointClass> &points);
|
||||
BoundingBox3Base(const std::vector<PointClass>& points)
|
||||
: BoundingBoxBase<PointClass>(points)
|
||||
{
|
||||
if (points.empty())
|
||||
CONFESS("Empty point set supplied to BoundingBox3Base constructor");
|
||||
|
||||
typename std::vector<PointClass>::const_iterator it = points.begin();
|
||||
this->min.z = this->max.z = it->z;
|
||||
for (++it; it != points.end(); ++it)
|
||||
{
|
||||
this->min.z = std::min(it->z, this->min.z);
|
||||
this->max.z = std::max(it->z, this->max.z);
|
||||
}
|
||||
this->defined &= (this->min.z < this->max.z);
|
||||
}
|
||||
void merge(const PointClass &point);
|
||||
void merge(const std::vector<PointClass> &points);
|
||||
void merge(const BoundingBox3Base<PointClass> &bb);
|
||||
|
@ -92,7 +122,7 @@ class BoundingBox3 : public BoundingBox3Base<Point3>
|
|||
public:
|
||||
BoundingBox3() : BoundingBox3Base<Point3>() {};
|
||||
BoundingBox3(const Point3 &pmin, const Point3 &pmax) : BoundingBox3Base<Point3>(pmin, pmax) {};
|
||||
BoundingBox3(const std::vector<Point3> &points) : BoundingBox3Base<Point3>(points) {};
|
||||
BoundingBox3(const Points3& points) : BoundingBox3Base<Point3>(points) {};
|
||||
};
|
||||
|
||||
class BoundingBoxf : public BoundingBoxBase<Pointf>
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(setenv) && defined(_putenv_s)
|
||||
#define setenv(k, v, o) _putenv_s(k, v)
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
std::string escape_string_cstyle(const std::string &str)
|
||||
|
@ -309,7 +305,6 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double rati
|
|||
|
||||
void ConfigBase::setenv_()
|
||||
{
|
||||
#ifdef setenv
|
||||
t_config_option_keys opt_keys = this->keys();
|
||||
for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) {
|
||||
// prepend the SLIC3R_ prefix
|
||||
|
@ -322,15 +317,14 @@ void ConfigBase::setenv_()
|
|||
for (size_t i = 0; i < envname.size(); ++i)
|
||||
envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i]-('a'-'A') : envname[i];
|
||||
|
||||
setenv(envname.c_str(), this->serialize(*it).c_str(), 1);
|
||||
boost::nowide::setenv(envname.c_str(), this->serialize(*it).c_str(), 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ConfigBase::load(const std::string &file)
|
||||
{
|
||||
if (boost::iends_with(file, ".gcode") || boost::iends_with(file, ".g"))
|
||||
this->load_from_gcode(file);
|
||||
this->load_from_gcode_file(file);
|
||||
else
|
||||
this->load_from_ini(file);
|
||||
}
|
||||
|
@ -355,10 +349,10 @@ void ConfigBase::load(const boost::property_tree::ptree &tree)
|
|||
}
|
||||
}
|
||||
|
||||
// Load the config keys from the tail of a G-code.
|
||||
void ConfigBase::load_from_gcode(const std::string &file)
|
||||
// Load the config keys from the tail of a G-code file.
|
||||
void ConfigBase::load_from_gcode_file(const std::string &file)
|
||||
{
|
||||
// 1) Read a 64k block from the end of the G-code.
|
||||
// Read a 64k block from the end of the G-code.
|
||||
boost::nowide::ifstream ifs(file);
|
||||
{
|
||||
const char slic3r_gcode_header[] = "; generated by Slic3r ";
|
||||
|
@ -371,30 +365,39 @@ void ConfigBase::load_from_gcode(const std::string &file)
|
|||
auto file_length = ifs.tellg();
|
||||
auto data_length = std::min<std::fstream::streampos>(65535, file_length);
|
||||
ifs.seekg(file_length - data_length, ifs.beg);
|
||||
std::vector<char> data(size_t(data_length) + 1, 0);
|
||||
ifs.read(data.data(), data_length);
|
||||
std::vector<char> data(size_t(data_length) + 1, 0);
|
||||
ifs.read(data.data(), data_length);
|
||||
ifs.close();
|
||||
|
||||
// 2) Walk line by line in reverse until a non-configuration key appears.
|
||||
char *data_start = data.data();
|
||||
load_from_gcode_string(data.data());
|
||||
}
|
||||
|
||||
// Load the config keys from the given string.
|
||||
void ConfigBase::load_from_gcode_string(const char* str)
|
||||
{
|
||||
if (str == nullptr)
|
||||
return;
|
||||
|
||||
// Walk line by line in reverse until a non-configuration key appears.
|
||||
char *data_start = const_cast<char*>(str);
|
||||
// boost::nowide::ifstream seems to cook the text data somehow, so less then the 64k of characters may be retrieved.
|
||||
char *end = data_start + strlen(data.data());
|
||||
char *end = data_start + strlen(str);
|
||||
size_t num_key_value_pairs = 0;
|
||||
for (;;) {
|
||||
// Extract next line.
|
||||
for (-- end; end > data_start && (*end == '\r' || *end == '\n'); -- end);
|
||||
for (--end; end > data_start && (*end == '\r' || *end == '\n'); --end);
|
||||
if (end == data_start)
|
||||
break;
|
||||
char *start = end;
|
||||
*(++ end) = 0;
|
||||
for (; start > data_start && *start != '\r' && *start != '\n'; -- start);
|
||||
*(++end) = 0;
|
||||
for (; start > data_start && *start != '\r' && *start != '\n'; --start);
|
||||
if (start == data_start)
|
||||
break;
|
||||
// Extracted a line from start to end. Extract the key = value pair.
|
||||
if (end - (++ start) < 10 || start[0] != ';' || start[1] != ' ')
|
||||
if (end - (++start) < 10 || start[0] != ';' || start[1] != ' ')
|
||||
break;
|
||||
char *key = start + 2;
|
||||
if (! (*key >= 'a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z'))
|
||||
if (!(*key >= 'a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z'))
|
||||
// A key must start with a letter.
|
||||
break;
|
||||
char *sep = strchr(key, '=');
|
||||
|
@ -408,8 +411,8 @@ void ConfigBase::load_from_gcode(const std::string &file)
|
|||
break;
|
||||
*key_end = 0;
|
||||
// The key may contain letters, digits and underscores.
|
||||
for (char *c = key; c != key_end; ++ c)
|
||||
if (! ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '_')) {
|
||||
for (char *c = key; c != key_end; ++c)
|
||||
if (!((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '_')) {
|
||||
key = nullptr;
|
||||
break;
|
||||
}
|
||||
|
@ -417,8 +420,9 @@ void ConfigBase::load_from_gcode(const std::string &file)
|
|||
break;
|
||||
try {
|
||||
this->set_deserialize(key, value);
|
||||
++ num_key_value_pairs;
|
||||
} catch (UnknownOptionException & /* e */) {
|
||||
++num_key_value_pairs;
|
||||
}
|
||||
catch (UnknownOptionException & /* e */) {
|
||||
// ignore
|
||||
}
|
||||
end = start;
|
||||
|
|
|
@ -582,6 +582,13 @@ public:
|
|||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionFloatOrPercent(*this); }
|
||||
ConfigOptionFloatOrPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOption &rhs) const override
|
||||
{
|
||||
if (rhs.type() != this->type())
|
||||
throw std::runtime_error("ConfigOptionFloatOrPercent: Comparing incompatible types");
|
||||
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(&rhs));
|
||||
return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs);
|
||||
}
|
||||
bool operator==(const ConfigOptionFloatOrPercent &rhs) const
|
||||
{ return this->value == rhs.value && this->percent == rhs.percent; }
|
||||
double get_abs_value(double ratio_over) const
|
||||
|
@ -1049,7 +1056,8 @@ public:
|
|||
void setenv_();
|
||||
void load(const std::string &file);
|
||||
void load_from_ini(const std::string &file);
|
||||
void load_from_gcode(const std::string &file);
|
||||
void load_from_gcode_file(const std::string &file);
|
||||
void load_from_gcode_string(const char* str);
|
||||
void load(const boost::property_tree::ptree &tree);
|
||||
void save(const std::string &file) const;
|
||||
|
||||
|
@ -1161,6 +1169,8 @@ public:
|
|||
const ConfigDef* def() const override { return nullptr; };
|
||||
template<class T> T* opt(const t_config_option_key &opt_key, bool create = false)
|
||||
{ return dynamic_cast<T*>(this->option(opt_key, create)); }
|
||||
template<class T> const T* opt(const t_config_option_key &opt_key) const
|
||||
{ return dynamic_cast<const T*>(this->option(opt_key)); }
|
||||
// Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name.
|
||||
ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override;
|
||||
// Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store.
|
||||
|
|
|
@ -25,6 +25,7 @@ enum ExtrusionRole {
|
|||
erSkirt,
|
||||
erSupportMaterial,
|
||||
erSupportMaterialInterface,
|
||||
erWipeTower,
|
||||
// Extrusion role for a collection with multiple extrusion roles.
|
||||
erMixed,
|
||||
};
|
||||
|
@ -104,15 +105,19 @@ public:
|
|||
float width;
|
||||
// Height of the extrusion, used for visualization purposed.
|
||||
float height;
|
||||
|
||||
ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {};
|
||||
ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {};
|
||||
ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
|
||||
ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {}
|
||||
// ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height) {};
|
||||
// Feedrate of the extrusion, used for visualization purposed.
|
||||
float feedrate;
|
||||
// Id of the extruder, used for visualization purposed.
|
||||
unsigned int extruder_id;
|
||||
|
||||
ExtrusionPath& operator=(const ExtrusionPath &rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = rhs.polyline; return *this; }
|
||||
ExtrusionPath& operator=(ExtrusionPath &&rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = std::move(rhs.polyline); return *this; }
|
||||
ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), extruder_id(0), m_role(role) {};
|
||||
ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), feedrate(0.0f), extruder_id(0), m_role(role) {};
|
||||
ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), m_role(rhs.m_role) {}
|
||||
ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), m_role(rhs.m_role) {}
|
||||
// ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(0) {};
|
||||
|
||||
ExtrusionPath& operator=(const ExtrusionPath &rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = rhs.polyline; return *this; }
|
||||
ExtrusionPath& operator=(ExtrusionPath &&rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = std::move(rhs.polyline); return *this; }
|
||||
|
||||
ExtrusionPath* clone() const { return new ExtrusionPath (*this); }
|
||||
void reverse() { this->polyline.reverse(); }
|
||||
|
|
1396
xs/src/libslic3r/Format/3mf.cpp
Normal file
1396
xs/src/libslic3r/Format/3mf.cpp
Normal file
File diff suppressed because it is too large
Load diff
19
xs/src/libslic3r/Format/3mf.hpp
Normal file
19
xs/src/libslic3r/Format/3mf.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef slic3r_Format_3mf_hpp_
|
||||
#define slic3r_Format_3mf_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Model;
|
||||
class Print;
|
||||
class PresetBundle;
|
||||
|
||||
// Load the content of a 3mf file into the given model and preset bundle.
|
||||
extern bool load_3mf(const char* path, PresetBundle* bundle, Model* model);
|
||||
|
||||
// Save the given model and the config data contained in the given Print into a 3mf file.
|
||||
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
||||
extern bool store_3mf(const char* path, Model* model, Print* print);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_3mf_hpp_ */
|
|
@ -7,8 +7,14 @@
|
|||
|
||||
#include "../libslic3r.h"
|
||||
#include "../Model.hpp"
|
||||
#include "../GCode.hpp"
|
||||
#include "../slic3r/GUI/PresetBundle.hpp"
|
||||
#include "AMF.hpp"
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <miniz/miniz_zip.h>
|
||||
|
||||
#if 0
|
||||
// Enable debugging and assert in this file.
|
||||
#define DEBUG
|
||||
|
@ -18,18 +24,22 @@
|
|||
|
||||
#include <assert.h>
|
||||
|
||||
const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config";
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
struct AMFParserContext
|
||||
{
|
||||
AMFParserContext(XML_Parser parser, Model *model) :
|
||||
AMFParserContext(XML_Parser parser, const std::string& archive_filename, PresetBundle* preset_bundle, Model *model) :
|
||||
m_parser(parser),
|
||||
m_model(*model),
|
||||
m_object(nullptr),
|
||||
m_volume(nullptr),
|
||||
m_material(nullptr),
|
||||
m_instance(nullptr)
|
||||
m_instance(nullptr),
|
||||
m_preset_bundle(preset_bundle),
|
||||
m_archive_filename(archive_filename)
|
||||
{
|
||||
m_path.reserve(12);
|
||||
}
|
||||
|
@ -149,6 +159,10 @@ struct AMFParserContext
|
|||
Instance *m_instance;
|
||||
// Generic string buffer for vertices, face indices, metadata etc.
|
||||
std::string m_value[3];
|
||||
// Pointer to preset bundle to update if config data are stored inside the amf file
|
||||
PresetBundle* m_preset_bundle;
|
||||
// Fullpath name of the amf file
|
||||
std::string m_archive_filename;
|
||||
|
||||
private:
|
||||
AMFParserContext& operator=(AMFParserContext&);
|
||||
|
@ -403,7 +417,10 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
break;
|
||||
|
||||
case NODE_TYPE_METADATA:
|
||||
if (strncmp(m_value[0].c_str(), "slic3r.", 7) == 0) {
|
||||
if ((m_preset_bundle != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0) {
|
||||
m_preset_bundle->load_config_string(m_value[1].c_str(), m_archive_filename.c_str());
|
||||
}
|
||||
else if (strncmp(m_value[0].c_str(), "slic3r.", 7) == 0) {
|
||||
const char *opt_key = m_value[0].c_str() + 7;
|
||||
if (print_config_def.options.find(opt_key) != print_config_def.options.end()) {
|
||||
DynamicPrintConfig *config = nullptr;
|
||||
|
@ -474,10 +491,13 @@ void AMFParserContext::endDocument()
|
|||
}
|
||||
|
||||
// Load an AMF file into a provided model.
|
||||
bool load_amf(const char *path, Model *model)
|
||||
bool load_amf_file(const char *path, PresetBundle* bundle, Model *model)
|
||||
{
|
||||
if ((path == nullptr) || (model == nullptr))
|
||||
return false;
|
||||
|
||||
XML_Parser parser = XML_ParserCreate(nullptr); // encoding
|
||||
if (! parser) {
|
||||
if (!parser) {
|
||||
printf("Couldn't allocate memory for parser\n");
|
||||
return false;
|
||||
}
|
||||
|
@ -488,7 +508,7 @@ bool load_amf(const char *path, Model *model)
|
|||
return false;
|
||||
}
|
||||
|
||||
AMFParserContext ctx(parser, model);
|
||||
AMFParserContext ctx(parser, path, bundle, model);
|
||||
XML_SetUserData(parser, (void*)&ctx);
|
||||
XML_SetElementHandler(parser, AMFParserContext::startElement, AMFParserContext::endElement);
|
||||
XML_SetCharacterDataHandler(parser, AMFParserContext::characters);
|
||||
|
@ -519,49 +539,163 @@ bool load_amf(const char *path, Model *model)
|
|||
|
||||
if (result)
|
||||
ctx.endDocument();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool store_amf(const char *path, Model *model)
|
||||
// Load an AMF archive into a provided model.
|
||||
bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
|
||||
{
|
||||
FILE *file = boost::nowide::fopen(path, "wb");
|
||||
if (file == nullptr)
|
||||
if ((path == nullptr) || (model == nullptr))
|
||||
return false;
|
||||
|
||||
fprintf(file, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
fprintf(file, "<amf unit=\"millimeter\">\n");
|
||||
fprintf(file, "<metadata type=\"cad\">Slic3r %s</metadata>\n", SLIC3R_VERSION);
|
||||
mz_zip_archive archive;
|
||||
mz_zip_zero_struct(&archive);
|
||||
|
||||
mz_bool res = mz_zip_reader_init_file(&archive, path, 0);
|
||||
if (res == 0)
|
||||
{
|
||||
printf("Unable to init zip reader\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
|
||||
if (num_entries != 1)
|
||||
{
|
||||
printf("Found invalid number of entries\n");
|
||||
mz_zip_reader_end(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
mz_zip_archive_file_stat stat;
|
||||
res = mz_zip_reader_file_stat(&archive, 0, &stat);
|
||||
if (res == 0)
|
||||
{
|
||||
printf("Unable to extract entry statistics\n");
|
||||
mz_zip_reader_end(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string internal_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(path).filename().string(), ".zip.amf", ".amf");
|
||||
if (internal_amf_filename != stat.m_filename)
|
||||
{
|
||||
printf("Found invalid internal filename\n");
|
||||
mz_zip_reader_end(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stat.m_uncomp_size == 0)
|
||||
{
|
||||
printf("Found invalid size\n");
|
||||
mz_zip_reader_end(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
XML_Parser parser = XML_ParserCreate(nullptr); // encoding
|
||||
if (!parser) {
|
||||
printf("Couldn't allocate memory for parser\n");
|
||||
mz_zip_reader_end(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
AMFParserContext ctx(parser, path, bundle, model);
|
||||
XML_SetUserData(parser, (void*)&ctx);
|
||||
XML_SetElementHandler(parser, AMFParserContext::startElement, AMFParserContext::endElement);
|
||||
XML_SetCharacterDataHandler(parser, AMFParserContext::characters);
|
||||
|
||||
void* parser_buffer = XML_GetBuffer(parser, (int)stat.m_uncomp_size);
|
||||
if (parser_buffer == nullptr)
|
||||
{
|
||||
printf("Unable to create buffer\n");
|
||||
mz_zip_reader_end(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0);
|
||||
if (res == 0)
|
||||
{
|
||||
printf("Error while reading model data to buffer\n");
|
||||
mz_zip_reader_end(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!XML_ParseBuffer(parser, (int)stat.m_uncomp_size, 1))
|
||||
{
|
||||
printf("Error (%s) while parsing xml file at line %d\n", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser));
|
||||
mz_zip_reader_end(&archive);
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx.endDocument();
|
||||
|
||||
mz_zip_reader_end(&archive);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load an AMF file into a provided model.
|
||||
// If bundle is not a null pointer, updates it if the amf file/archive contains config data
|
||||
bool load_amf(const char *path, PresetBundle* bundle, Model *model)
|
||||
{
|
||||
if (boost::iends_with(path, ".zip.amf"))
|
||||
return load_amf_archive(path, bundle, model);
|
||||
else if (boost::iends_with(path, ".amf") || boost::iends_with(path, ".amf.xml"))
|
||||
return load_amf_file(path, bundle, model);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool store_amf(const char *path, Model *model, Print* print)
|
||||
{
|
||||
if ((path == nullptr) || (model == nullptr) || (print == nullptr))
|
||||
return false;
|
||||
|
||||
mz_zip_archive archive;
|
||||
mz_zip_zero_struct(&archive);
|
||||
|
||||
mz_bool res = mz_zip_writer_init_file(&archive, path, 0);
|
||||
if (res == 0)
|
||||
return false;
|
||||
|
||||
std::stringstream stream;
|
||||
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
stream << "<amf unit=\"millimeter\">\n";
|
||||
stream << "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>\n";
|
||||
|
||||
std::string config = "\n";
|
||||
GCode::append_full_config(*print, config);
|
||||
stream << "<metadata type=\"" << SLIC3R_CONFIG_TYPE << "\">" << config << "</metadata>\n";
|
||||
|
||||
for (const auto &material : model->materials) {
|
||||
if (material.first.empty())
|
||||
continue;
|
||||
// note that material-id must never be 0 since it's reserved by the AMF spec
|
||||
fprintf(file, " <material id=\"%s\">\n", material.first.c_str());
|
||||
stream << " <material id=\"" << material.first << "\">\n";
|
||||
for (const auto &attr : material.second->attributes)
|
||||
fprintf(file, " <metadata type=\"%s\">%s</metadata>\n", attr.first.c_str(), attr.second.c_str());
|
||||
stream << " <metadata type=\"" << attr.first << "\">" << attr.second << "</metadata>\n";
|
||||
for (const std::string &key : material.second->config.keys())
|
||||
fprintf(file, " <metadata type=\"slic3r.%s\">%s</metadata>\n", key.c_str(), material.second->config.serialize(key).c_str());
|
||||
fprintf(file, " </material>\n");
|
||||
stream << " <metadata type=\"slic3r." << key << "\">" << material.second->config.serialize(key) << "</metadata>\n";
|
||||
stream << " </material>\n";
|
||||
}
|
||||
std::string instances;
|
||||
for (size_t object_id = 0; object_id < model->objects.size(); ++ object_id) {
|
||||
ModelObject *object = model->objects[object_id];
|
||||
fprintf(file, " <object id=\"" PRINTF_ZU "\">\n", object_id);
|
||||
stream << " <object id=\"" << object_id << "\">\n";
|
||||
for (const std::string &key : object->config.keys())
|
||||
fprintf(file, " <metadata type=\"slic3r.%s\">%s</metadata>\n", key.c_str(), object->config.serialize(key).c_str());
|
||||
if (! object->name.empty())
|
||||
fprintf(file, " <metadata type=\"name\">%s</metadata>\n", object->name.c_str());
|
||||
stream << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
|
||||
if (!object->name.empty())
|
||||
stream << " <metadata type=\"name\">" << object->name << "</metadata>\n";
|
||||
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
|
||||
if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) {
|
||||
// Store the layer height profile as a single semicolon separated list.
|
||||
fprintf(file, " <metadata type=\"slic3r.layer_height_profile\">");
|
||||
fprintf(file, "%f", layer_height_profile.front());
|
||||
for (size_t i = 1; i < layer_height_profile.size(); ++ i)
|
||||
fprintf(file, ";%f", layer_height_profile[i]);
|
||||
fprintf(file, "\n </metadata>\n");
|
||||
stream << " <metadata type=\"slic3r.layer_height_profile\">";
|
||||
stream << layer_height_profile.front();
|
||||
for (size_t i = 1; i < layer_height_profile.size(); ++i)
|
||||
stream << ";" << layer_height_profile[i];
|
||||
stream << "\n </metadata>\n";
|
||||
}
|
||||
//FIXME Store the layer height ranges (ModelObject::layer_height_ranges)
|
||||
fprintf(file, " <mesh>\n");
|
||||
fprintf(file, " <vertices>\n");
|
||||
stream << " <mesh>\n";
|
||||
stream << " <vertices>\n";
|
||||
std::vector<int> vertices_offsets;
|
||||
int num_vertices = 0;
|
||||
for (ModelVolume *volume : object->volumes) {
|
||||
|
@ -572,41 +706,41 @@ bool store_amf(const char *path, Model *model)
|
|||
if (stl.v_shared == nullptr)
|
||||
stl_generate_shared_vertices(&stl);
|
||||
for (size_t i = 0; i < stl.stats.shared_vertices; ++ i) {
|
||||
fprintf(file, " <vertex>\n");
|
||||
fprintf(file, " <coordinates>\n");
|
||||
fprintf(file, " <x>%f</x>\n", stl.v_shared[i].x);
|
||||
fprintf(file, " <y>%f</y>\n", stl.v_shared[i].y);
|
||||
fprintf(file, " <z>%f</z>\n", stl.v_shared[i].z);
|
||||
fprintf(file, " </coordinates>\n");
|
||||
fprintf(file, " </vertex>\n");
|
||||
stream << " <vertex>\n";
|
||||
stream << " <coordinates>\n";
|
||||
stream << " <x>" << stl.v_shared[i].x << "</x>\n";
|
||||
stream << " <y>" << stl.v_shared[i].y << "</y>\n";
|
||||
stream << " <z>" << stl.v_shared[i].z << "</z>\n";
|
||||
stream << " </coordinates>\n";
|
||||
stream << " </vertex>\n";
|
||||
}
|
||||
num_vertices += stl.stats.shared_vertices;
|
||||
}
|
||||
fprintf(file, " </vertices>\n");
|
||||
for (size_t i_volume = 0; i_volume < object->volumes.size(); ++ i_volume) {
|
||||
stream << " </vertices>\n";
|
||||
for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) {
|
||||
ModelVolume *volume = object->volumes[i_volume];
|
||||
int vertices_offset = vertices_offsets[i_volume];
|
||||
if (volume->material_id().empty())
|
||||
fprintf(file, " <volume>\n");
|
||||
stream << " <volume>\n";
|
||||
else
|
||||
fprintf(file, " <volume materialid=\"%s\">\n", volume->material_id().c_str());
|
||||
stream << " <volume materialid=\"" << volume->material_id() << "\">\n";
|
||||
for (const std::string &key : volume->config.keys())
|
||||
fprintf(file, " <metadata type=\"slic3r.%s\">%s</metadata>\n", key.c_str(), volume->config.serialize(key).c_str());
|
||||
if (! volume->name.empty())
|
||||
fprintf(file, " <metadata type=\"name\">%s</metadata>\n", volume->name.c_str());
|
||||
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
|
||||
if (!volume->name.empty())
|
||||
stream << " <metadata type=\"name\">" << volume->name << "</metadata>\n";
|
||||
if (volume->modifier)
|
||||
fprintf(file, " <metadata type=\"slic3r.modifier\">1</metadata>\n");
|
||||
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++ i) {
|
||||
fprintf(file, " <triangle>\n");
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
fprintf(file, " <v%d>%d</v%d>\n", j+1, volume->mesh.stl.v_indices[i].vertex[j] + vertices_offset, j+1);
|
||||
fprintf(file, " </triangle>\n");
|
||||
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
|
||||
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
|
||||
stream << " <triangle>\n";
|
||||
for (int j = 0; j < 3; ++j)
|
||||
stream << " <v" << j + 1 << ">" << volume->mesh.stl.v_indices[i].vertex[j] + vertices_offset << "</v" << j + 1 << ">\n";
|
||||
stream << " </triangle>\n";
|
||||
}
|
||||
fprintf(file, " </volume>\n");
|
||||
stream << " </volume>\n";
|
||||
}
|
||||
fprintf(file, " </mesh>\n");
|
||||
fprintf(file, " </object>\n");
|
||||
if (! object->instances.empty()) {
|
||||
stream << " </mesh>\n";
|
||||
stream << " </object>\n";
|
||||
if (!object->instances.empty()) {
|
||||
for (ModelInstance *instance : object->instances) {
|
||||
char buf[512];
|
||||
sprintf(buf,
|
||||
|
@ -627,12 +761,31 @@ bool store_amf(const char *path, Model *model)
|
|||
}
|
||||
}
|
||||
if (! instances.empty()) {
|
||||
fprintf(file, " <constellation id=\"1\">\n");
|
||||
fwrite(instances.data(), instances.size(), 1, file);
|
||||
fprintf(file, " </constellation>\n");
|
||||
stream << " <constellation id=\"1\">\n";
|
||||
stream << instances;
|
||||
stream << " </constellation>\n";
|
||||
}
|
||||
fprintf(file, "</amf>\n");
|
||||
fclose(file);
|
||||
stream << "</amf>\n";
|
||||
|
||||
std::string internal_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(path).filename().string(), ".zip.amf", ".amf");
|
||||
std::string out = stream.str();
|
||||
|
||||
if (!mz_zip_writer_add_mem(&archive, internal_amf_filename.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
|
||||
{
|
||||
mz_zip_writer_end(&archive);
|
||||
boost::filesystem::remove(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mz_zip_writer_finalize_archive(&archive))
|
||||
{
|
||||
mz_zip_writer_end(&archive);
|
||||
boost::filesystem::remove(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
mz_zip_writer_end(&archive);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,11 +4,15 @@
|
|||
namespace Slic3r {
|
||||
|
||||
class Model;
|
||||
class Print;
|
||||
class PresetBundle;
|
||||
|
||||
// Load an AMF file into a provided model.
|
||||
extern bool load_amf(const char *path, Model *model);
|
||||
// Load the content of an amf file into the given model and preset bundle.
|
||||
extern bool load_amf(const char *path, PresetBundle* bundle, Model *model);
|
||||
|
||||
extern bool store_amf(const char *path, Model *model);
|
||||
// Save the given model and the config data contained in the given Print into an amf file.
|
||||
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
||||
extern bool store_amf(const char *path, Model *model, Print* print);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ bool load_prus(const char *path, Model *model)
|
|||
if (scene_xml_data.size() < size_last + size_incr)
|
||||
scene_xml_data.resize(size_last + size_incr);
|
||||
}
|
||||
size_last += size_last + zip.LastRead();
|
||||
size_last += zip.LastRead();
|
||||
if (scene_xml_data.size() == size_last)
|
||||
scene_xml_data.resize(size_last + 1);
|
||||
else if (scene_xml_data.size() > size_last + 1)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/find.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
|
@ -20,6 +21,8 @@
|
|||
|
||||
#include "SVG.hpp"
|
||||
|
||||
#include <Shiny/Shiny.h>
|
||||
|
||||
#if 0
|
||||
// Enable debugging and asserts, even in the release build.
|
||||
#define DEBUG
|
||||
|
@ -267,22 +270,6 @@ std::string WipeTowerIntegration::finalize(GCode &gcodegen)
|
|||
|
||||
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id())
|
||||
|
||||
inline void write(FILE *file, const std::string &what)
|
||||
{
|
||||
fwrite(what.data(), 1, what.size(), file);
|
||||
}
|
||||
|
||||
// Write a string into a file. Add a newline, if the string does not end with a newline already.
|
||||
// Used to export a custom G-code section processed by the PlaceholderParser.
|
||||
inline void writeln(FILE *file, const std::string &what)
|
||||
{
|
||||
if (! what.empty()) {
|
||||
write(file, what);
|
||||
if (what.back() != '\n')
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Collect pairs of object_layer + support_layer sorted by print_z.
|
||||
// object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON.
|
||||
std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObject &object)
|
||||
|
@ -362,8 +349,12 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
|
|||
return layers_to_print;
|
||||
}
|
||||
|
||||
void GCode::do_export(Print *print, const char *path)
|
||||
void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_data)
|
||||
{
|
||||
PROFILE_CLEAR();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Exporting G-code...";
|
||||
|
||||
// Remove the old g-code if it exists.
|
||||
boost::nowide::remove(path);
|
||||
|
||||
|
@ -375,7 +366,7 @@ void GCode::do_export(Print *print, const char *path)
|
|||
throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
|
||||
|
||||
this->m_placeholder_parser_failed_templates.clear();
|
||||
this->_do_export(*print, file);
|
||||
this->_do_export(*print, file, preview_data);
|
||||
fflush(file);
|
||||
if (ferror(file)) {
|
||||
fclose(file);
|
||||
|
@ -395,14 +386,36 @@ void GCode::do_export(Print *print, const char *path)
|
|||
msg += " !!!!! End of an error report for the custom G-code template ...\n";
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
|
||||
if (boost::nowide::rename(path_tmp.c_str(), path) != 0)
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' +
|
||||
"Is " + path_tmp + " locked?" + '\n');
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished";
|
||||
|
||||
// Write the profiler measurements to file
|
||||
PROFILE_UPDATE();
|
||||
PROFILE_OUTPUT(debug_out_path("gcode-export-profile.txt").c_str());
|
||||
}
|
||||
|
||||
void GCode::_do_export(Print &print, FILE *file)
|
||||
void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
|
||||
// resets time estimator
|
||||
m_time_estimator.reset();
|
||||
m_time_estimator.set_dialect(print.config.gcode_flavor);
|
||||
|
||||
// resets analyzer
|
||||
m_analyzer.reset();
|
||||
m_enable_analyzer = preview_data != nullptr;
|
||||
|
||||
// resets analyzer's tracking data
|
||||
m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm;
|
||||
m_last_width = GCodeAnalyzer::Default_Width;
|
||||
m_last_height = GCodeAnalyzer::Default_Height;
|
||||
|
||||
// How many times will be change_layer() called?
|
||||
// change_layer() in turn increments the progress bar status.
|
||||
m_layer_count = 0;
|
||||
|
@ -486,7 +499,7 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
m_enable_extrusion_role_markers = (bool)m_pressure_equalizer;
|
||||
|
||||
// Write information on the generator.
|
||||
fprintf(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str());
|
||||
_write_format(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str());
|
||||
// Write notes (content of the Print Settings tab -> Notes)
|
||||
{
|
||||
std::list<std::string> lines;
|
||||
|
@ -495,10 +508,10 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
// Remove the trailing '\r' from the '\r\n' sequence.
|
||||
if (! line.empty() && line.back() == '\r')
|
||||
line.pop_back();
|
||||
fprintf(file, "; %s\n", line.c_str());
|
||||
_write_format(file, "; %s\n", line.c_str());
|
||||
}
|
||||
if (! lines.empty())
|
||||
fprintf(file, "\n");
|
||||
_write(file, "\n");
|
||||
}
|
||||
// Write some terse information on the slicing parameters.
|
||||
const PrintObject *first_object = print.objects.front();
|
||||
|
@ -506,16 +519,16 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height);
|
||||
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
|
||||
auto region = print.regions[region_id];
|
||||
fprintf(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
|
||||
fprintf(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width);
|
||||
fprintf(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width);
|
||||
fprintf(file, "; solid infill extrusion width = %.2fmm\n", region->flow(frSolidInfill, layer_height, false, false, -1., *first_object).width);
|
||||
fprintf(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; solid infill extrusion width = %.2fmm\n", region->flow(frSolidInfill, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width);
|
||||
if (print.has_support_material())
|
||||
fprintf(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
|
||||
_write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
|
||||
if (print.config.first_layer_extrusion_width.value > 0)
|
||||
fprintf(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width);
|
||||
fprintf(file, "\n");
|
||||
_write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width);
|
||||
_write_format(file, "\n");
|
||||
}
|
||||
|
||||
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
|
||||
|
@ -558,7 +571,7 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
|
||||
// Disable fan.
|
||||
if (! print.config.cooling.get_at(initial_extruder_id) || print.config.disable_fan_first_layers.get_at(initial_extruder_id))
|
||||
write(file, m_writer.set_fan(0, true));
|
||||
_write(file, m_writer.set_fan(0, true));
|
||||
|
||||
// Let the start-up script prime the 1st printing tool.
|
||||
m_placeholder_parser.set("initial_tool", initial_extruder_id);
|
||||
|
@ -575,24 +588,24 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
// Set extruder(s) temperature before and after start G-code.
|
||||
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false);
|
||||
// Write the custom start G-code
|
||||
writeln(file, start_gcode);
|
||||
_writeln(file, start_gcode);
|
||||
// Process filament-specific gcode in extruder order.
|
||||
if (print.config.single_extruder_multi_material) {
|
||||
if (has_wipe_tower) {
|
||||
// Wipe tower will control the extruder switching, it will call the start_filament_gcode.
|
||||
} else {
|
||||
// Only initialize the initial extruder.
|
||||
writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id));
|
||||
_writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id));
|
||||
}
|
||||
} else {
|
||||
for (const std::string &start_gcode : print.config.start_filament_gcode.values)
|
||||
writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front())));
|
||||
_writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front())));
|
||||
}
|
||||
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
|
||||
|
||||
// Set other general things.
|
||||
write(file, this->preamble());
|
||||
|
||||
_write(file, this->preamble());
|
||||
|
||||
// Initialize a motion planner for object-to-object travel moves.
|
||||
if (print.config.avoid_crossing_perimeters.value) {
|
||||
// Collect outer contours of all objects over all layers.
|
||||
|
@ -640,7 +653,7 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
}
|
||||
|
||||
// Set initial extruder only after custom start G-code.
|
||||
write(file, this->set_extruder(initial_extruder_id));
|
||||
_write(file, this->set_extruder(initial_extruder_id));
|
||||
|
||||
// Do all objects for each layer.
|
||||
if (print.config.complete_objects.value) {
|
||||
|
@ -670,8 +683,8 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
// This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
|
||||
m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
|
||||
m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
write(file, this->retract());
|
||||
write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
|
||||
_write(file, this->retract());
|
||||
_write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
|
||||
m_enable_cooling_markers = true;
|
||||
// Disable motion planner when traveling to first object point.
|
||||
m_avoid_crossing_perimeters.disable_once = true;
|
||||
|
@ -683,7 +696,7 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
|
||||
this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
|
||||
this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
|
||||
writeln(file, between_objects_gcode);
|
||||
_writeln(file, between_objects_gcode);
|
||||
}
|
||||
// Reset the cooling buffer internal state (the current position, feed rate, accelerations).
|
||||
m_cooling_buffer->reset();
|
||||
|
@ -696,7 +709,7 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), © - object._shifted_copies.data());
|
||||
}
|
||||
if (m_pressure_equalizer)
|
||||
write(file, m_pressure_equalizer->process("", true));
|
||||
_write(file, m_pressure_equalizer->process("", true));
|
||||
++ finished_objects;
|
||||
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
|
||||
// Reset it when starting another object from 1st layer.
|
||||
|
@ -716,8 +729,8 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
// Prusa Multi-Material wipe tower.
|
||||
if (has_wipe_tower && ! layers_to_print.empty()) {
|
||||
m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get()));
|
||||
write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
|
||||
write(file, m_wipe_tower->prime(*this));
|
||||
_write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
|
||||
_write(file, m_wipe_tower->prime(*this));
|
||||
// Verify, whether the print overaps the priming extrusions.
|
||||
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
|
||||
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
|
||||
|
@ -727,16 +740,17 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
|
||||
bbox_prime.offset(0.5f);
|
||||
// Beep for 500ms, tone 800Hz. Yet better, play some Morse.
|
||||
write(file, this->retract());
|
||||
fprintf(file, "M300 S800 P500\n");
|
||||
_write(file, this->retract());
|
||||
_write(file, "M300 S800 P500\n");
|
||||
if (bbox_prime.overlap(bbox_print)) {
|
||||
// Wait for the user to remove the priming extrusions, otherwise they would
|
||||
// get covered by the print.
|
||||
fprintf(file, "M1 Remove priming towers and click button.\n");
|
||||
} else {
|
||||
_write(file, "M1 Remove priming towers and click button.\n");
|
||||
}
|
||||
else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
fprintf(file, "M1 S10\n");
|
||||
_write(file, "M1 S10\n");
|
||||
}
|
||||
}
|
||||
// Extrude the layers.
|
||||
|
@ -747,26 +761,29 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
|
||||
}
|
||||
if (m_pressure_equalizer)
|
||||
write(file, m_pressure_equalizer->process("", true));
|
||||
_write(file, m_pressure_equalizer->process("", true));
|
||||
if (m_wipe_tower)
|
||||
// Purge the extruder, pull out the active filament.
|
||||
write(file, m_wipe_tower->finalize(*this));
|
||||
_write(file, m_wipe_tower->finalize(*this));
|
||||
}
|
||||
|
||||
// Write end commands to file.
|
||||
write(file, this->retract());
|
||||
write(file, m_writer.set_fan(false));
|
||||
_write(file, this->retract());
|
||||
_write(file, m_writer.set_fan(false));
|
||||
// Process filament-specific gcode in extruder order.
|
||||
if (print.config.single_extruder_multi_material) {
|
||||
// Process the end_filament_gcode for the active filament only.
|
||||
writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id()));
|
||||
_writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id()));
|
||||
} else {
|
||||
for (const std::string &end_gcode : print.config.end_filament_gcode.values)
|
||||
writeln(file, this->placeholder_parser_process("end_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front())));
|
||||
_writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front())));
|
||||
}
|
||||
writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id()));
|
||||
write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
|
||||
write(file, m_writer.postamble());
|
||||
_writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id()));
|
||||
_write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
|
||||
_write(file, m_writer.postamble());
|
||||
|
||||
// calculates estimated printing time
|
||||
m_time_estimator.calculate_time();
|
||||
|
||||
// Get filament stats.
|
||||
print.filament_stats.clear();
|
||||
|
@ -774,37 +791,40 @@ void GCode::_do_export(Print &print, FILE *file)
|
|||
print.total_extruded_volume = 0.;
|
||||
print.total_weight = 0.;
|
||||
print.total_cost = 0.;
|
||||
print.estimated_print_time = m_time_estimator.get_time_hms();
|
||||
for (const Extruder &extruder : m_writer.extruders()) {
|
||||
double used_filament = extruder.used_filament();
|
||||
double extruded_volume = extruder.extruded_volume();
|
||||
double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
|
||||
double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
|
||||
print.filament_stats.insert(std::pair<size_t,float>(extruder.id(), used_filament));
|
||||
fprintf(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001);
|
||||
_write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001);
|
||||
if (filament_weight > 0.) {
|
||||
print.total_weight = print.total_weight + filament_weight;
|
||||
fprintf(file, "; filament used = %.1lf\n", filament_weight);
|
||||
_write_format(file, "; filament used = %.1lf\n", filament_weight);
|
||||
if (filament_cost > 0.) {
|
||||
print.total_cost = print.total_cost + filament_cost;
|
||||
fprintf(file, "; filament cost = %.1lf\n", filament_cost);
|
||||
_write_format(file, "; filament cost = %.1lf\n", filament_cost);
|
||||
}
|
||||
}
|
||||
print.total_used_filament = print.total_used_filament + used_filament;
|
||||
print.total_used_filament = print.total_used_filament + used_filament;
|
||||
print.total_extruded_volume = print.total_extruded_volume + extruded_volume;
|
||||
}
|
||||
fprintf(file, "; total filament cost = %.1lf\n", print.total_cost);
|
||||
_write_format(file, "; total filament cost = %.1lf\n", print.total_cost);
|
||||
_write_format(file, "; estimated printing time = %s\n", m_time_estimator.get_time_hms().c_str());
|
||||
|
||||
// Append full config.
|
||||
fprintf(file, "\n");
|
||||
_write(file, "\n");
|
||||
{
|
||||
StaticPrintConfig *configs[] = { &print.config, &print.default_object_config, &print.default_region_config };
|
||||
for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++ i) {
|
||||
StaticPrintConfig *cfg = configs[i];
|
||||
for (const std::string &key : cfg->keys())
|
||||
if (key != "compatible_printers")
|
||||
fprintf(file, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str());
|
||||
}
|
||||
std::string full_config = "";
|
||||
append_full_config(print, full_config);
|
||||
if (!full_config.empty())
|
||||
_write(file, full_config);
|
||||
}
|
||||
|
||||
// starts analizer calculations
|
||||
if (preview_data != nullptr)
|
||||
m_analyzer.calc_gcode_preview_data(*preview_data);
|
||||
}
|
||||
|
||||
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
|
||||
|
@ -893,7 +913,7 @@ void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const s
|
|||
// the custom start G-code emited these.
|
||||
std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait);
|
||||
if (! temp_set_by_gcode)
|
||||
write(file, set_temp_gcode);
|
||||
_write(file, set_temp_gcode);
|
||||
}
|
||||
|
||||
// Write 1st layer extruder temperatures into the G-code.
|
||||
|
@ -916,7 +936,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
|
|||
// Set temperature of the first printing extruder only.
|
||||
int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id);
|
||||
if (temp > 0)
|
||||
write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id));
|
||||
_write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id));
|
||||
} else {
|
||||
// Set temperatures of all the printing extruders.
|
||||
for (unsigned int tool_id : print.extruders()) {
|
||||
|
@ -924,7 +944,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
|
|||
if (print.config.ooze_prevention.value)
|
||||
temp += print.config.standby_temperature_delta.value;
|
||||
if (temp > 0)
|
||||
write(file, m_writer.set_temperature(temp, wait, tool_id));
|
||||
_write(file, m_writer.set_temperature(temp, wait, tool_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1296,12 +1316,7 @@ void GCode::process_layer(
|
|||
if (print_object == nullptr)
|
||||
// This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
|
||||
continue;
|
||||
if (m_enable_analyzer_markers) {
|
||||
// Store the binary pointer to the layer object directly into the G-code to be accessed by the GCodeAnalyzer.
|
||||
char buf[64];
|
||||
sprintf(buf, ";_LAYEROBJ:%p\n", m_layer);
|
||||
gcode += buf;
|
||||
}
|
||||
|
||||
m_config.apply(print_object->config, true);
|
||||
m_layer = layers[layer_id].layer();
|
||||
if (m_config.avoid_crossing_perimeters)
|
||||
|
@ -1358,7 +1373,7 @@ void GCode::process_layer(
|
|||
gcode = m_pressure_equalizer->process(gcode.c_str(), false);
|
||||
// printf("G-code after filter:\n%s\n", out.c_str());
|
||||
|
||||
write(file, gcode);
|
||||
_write(file, gcode);
|
||||
}
|
||||
|
||||
void GCode::apply_print_config(const PrintConfig &print_config)
|
||||
|
@ -1367,6 +1382,24 @@ void GCode::apply_print_config(const PrintConfig &print_config)
|
|||
m_config.apply(print_config);
|
||||
}
|
||||
|
||||
void GCode::append_full_config(const Print& print, std::string& str)
|
||||
{
|
||||
char buff[4096];
|
||||
|
||||
const StaticPrintConfig *configs[] = { &print.config, &print.default_object_config, &print.default_region_config };
|
||||
for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) {
|
||||
const StaticPrintConfig *cfg = configs[i];
|
||||
for (const std::string &key : cfg->keys())
|
||||
{
|
||||
if (key != "compatible_printers")
|
||||
{
|
||||
sprintf(buff, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str());
|
||||
str += buff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCode::set_extruders(const std::vector<unsigned int> &extruder_ids)
|
||||
{
|
||||
m_writer.set_extruders(extruder_ids);
|
||||
|
@ -1443,7 +1476,9 @@ static inline const char* ExtrusionRole2String(const ExtrusionRole role)
|
|||
case erSkirt: return "erSkirt";
|
||||
case erSupportMaterial: return "erSupportMaterial";
|
||||
case erSupportMaterialInterface: return "erSupportMaterialInterface";
|
||||
case erWipeTower: return "erWipeTower";
|
||||
case erMixed: return "erMixed";
|
||||
|
||||
default: return "erInvalid";
|
||||
};
|
||||
}
|
||||
|
@ -1993,6 +2028,57 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
|
|||
return gcode;
|
||||
}
|
||||
|
||||
void GCode::_write(FILE* file, const char *what)
|
||||
{
|
||||
if (what != nullptr) {
|
||||
// apply analyzer, if enabled
|
||||
const char* gcode = m_enable_analyzer ? m_analyzer.process_gcode(what).c_str() : what;
|
||||
|
||||
// writes string to file
|
||||
fwrite(gcode, 1, ::strlen(gcode), file);
|
||||
// updates time estimator and gcode lines vector
|
||||
m_time_estimator.add_gcode_block(gcode);
|
||||
}
|
||||
}
|
||||
|
||||
void GCode::_writeln(FILE* file, const std::string &what)
|
||||
{
|
||||
if (! what.empty())
|
||||
_write(file, (what.back() == '\n') ? what : (what + '\n'));
|
||||
}
|
||||
|
||||
void GCode::_write_format(FILE* file, const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
int buflen;
|
||||
{
|
||||
va_list args2;
|
||||
va_copy(args2, args);
|
||||
buflen =
|
||||
#ifdef _MSC_VER
|
||||
::_vscprintf(format, args2)
|
||||
#else
|
||||
::vsnprintf(nullptr, 0, format, args2)
|
||||
#endif
|
||||
+ 1;
|
||||
va_end(args2);
|
||||
}
|
||||
|
||||
char buffer[1024];
|
||||
bool buffer_dynamic = buflen > 1024;
|
||||
char *bufptr = buffer_dynamic ? (char*)malloc(buflen) : buffer;
|
||||
int res = ::vsnprintf(bufptr, buflen, format, args);
|
||||
if (res > 0)
|
||||
_write(file, bufptr);
|
||||
|
||||
if (buffer_dynamic)
|
||||
free(bufptr);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed)
|
||||
{
|
||||
std::string gcode;
|
||||
|
@ -2071,14 +2157,57 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
double F = speed * 60; // convert mm/sec to mm/min
|
||||
|
||||
// extrude arc or line
|
||||
if (m_enable_extrusion_role_markers || m_enable_analyzer_markers) {
|
||||
if (path.role() != m_last_extrusion_role) {
|
||||
if (m_enable_extrusion_role_markers || m_enable_analyzer)
|
||||
{
|
||||
if (path.role() != m_last_extrusion_role)
|
||||
{
|
||||
m_last_extrusion_role = path.role();
|
||||
if (m_enable_extrusion_role_markers)
|
||||
{
|
||||
char buf[32];
|
||||
sprintf(buf, ";_EXTRUSION_ROLE:%d\n", int(m_last_extrusion_role));
|
||||
gcode += buf;
|
||||
}
|
||||
if (m_enable_analyzer)
|
||||
{
|
||||
char buf[32];
|
||||
sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), int(m_last_extrusion_role));
|
||||
gcode += buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adds analyzer tags and updates analyzer's tracking data
|
||||
if (m_enable_analyzer)
|
||||
{
|
||||
if (m_last_mm3_per_mm != path.mm3_per_mm)
|
||||
{
|
||||
m_last_mm3_per_mm = path.mm3_per_mm;
|
||||
|
||||
char buf[32];
|
||||
sprintf(buf, ";_EXTRUSION_ROLE:%d\n", int(path.role()));
|
||||
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), m_last_mm3_per_mm);
|
||||
gcode += buf;
|
||||
}
|
||||
|
||||
if (m_last_width != path.width)
|
||||
{
|
||||
m_last_width = path.width;
|
||||
|
||||
char buf[32];
|
||||
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), m_last_width);
|
||||
gcode += buf;
|
||||
}
|
||||
|
||||
if (m_last_height != path.height)
|
||||
{
|
||||
m_last_height = path.height;
|
||||
|
||||
char buf[32];
|
||||
sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_last_height);
|
||||
gcode += buf;
|
||||
}
|
||||
}
|
||||
|
||||
std::string comment;
|
||||
if (m_enable_cooling_markers) {
|
||||
if (is_bridge(path.role()))
|
||||
|
@ -2182,8 +2311,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCode::retract(bool toolchange)
|
||||
std::string GCode::retract(bool toolchange)
|
||||
{
|
||||
std::string gcode;
|
||||
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
#include "GCode/SpiralVase.hpp"
|
||||
#include "GCode/ToolOrdering.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "GCodeTimeEstimator.hpp"
|
||||
#include "EdgeGrid.hpp"
|
||||
#include "GCode/Analyzer.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
@ -24,6 +26,7 @@ namespace Slic3r {
|
|||
|
||||
// Forward declarations.
|
||||
class GCode;
|
||||
class GCodePreviewData;
|
||||
|
||||
class AvoidCrossingPerimeters {
|
||||
public:
|
||||
|
@ -117,13 +120,16 @@ public:
|
|||
m_enable_loop_clipping(true),
|
||||
m_enable_cooling_markers(false),
|
||||
m_enable_extrusion_role_markers(false),
|
||||
m_enable_analyzer_markers(false),
|
||||
m_enable_analyzer(false),
|
||||
m_layer_count(0),
|
||||
m_layer_index(-1),
|
||||
m_layer(nullptr),
|
||||
m_volumetric_speed(0),
|
||||
m_last_pos_defined(false),
|
||||
m_last_extrusion_role(erNone),
|
||||
m_last_mm3_per_mm(GCodeAnalyzer::Default_mm3_per_mm),
|
||||
m_last_width(GCodeAnalyzer::Default_Width),
|
||||
m_last_height(GCodeAnalyzer::Default_Height),
|
||||
m_brim_done(false),
|
||||
m_second_layer_things_done(false),
|
||||
m_last_obj_copy(nullptr, Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max()))
|
||||
|
@ -131,7 +137,7 @@ public:
|
|||
~GCode() {}
|
||||
|
||||
// throws std::runtime_exception
|
||||
void do_export(Print *print, const char *path);
|
||||
void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr);
|
||||
|
||||
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
|
||||
const Pointf& origin() const { return m_origin; }
|
||||
|
@ -154,8 +160,11 @@ public:
|
|||
void set_layer_count(unsigned int value) { m_layer_count = value; }
|
||||
void apply_print_config(const PrintConfig &print_config);
|
||||
|
||||
// append full config to the given string
|
||||
static void append_full_config(const Print& print, std::string& str);
|
||||
|
||||
protected:
|
||||
void _do_export(Print &print, FILE *file);
|
||||
void _do_export(Print &print, FILE *file, GCodePreviewData *preview_data);
|
||||
|
||||
// Object and support extrusions of the same PrintObject at the same print_z.
|
||||
struct LayerToPrint
|
||||
|
@ -240,9 +249,10 @@ protected:
|
|||
// Markers for the Pressure Equalizer to recognize the extrusion type.
|
||||
// The Pressure Equalizer removes the markers from the final G-code.
|
||||
bool m_enable_extrusion_role_markers;
|
||||
// Extended markers for the G-code Analyzer.
|
||||
// Enableds the G-code Analyzer.
|
||||
// Extended markers will be added during G-code generation.
|
||||
// The G-code Analyzer will remove these comments from the final G-code.
|
||||
bool m_enable_analyzer_markers;
|
||||
bool m_enable_analyzer;
|
||||
// How many times will change_layer() be called?
|
||||
// change_layer() will update the progress bar.
|
||||
unsigned int m_layer_count;
|
||||
|
@ -255,6 +265,10 @@ protected:
|
|||
double m_volumetric_speed;
|
||||
// Support for the extrusion role markers. Which marker is active?
|
||||
ExtrusionRole m_last_extrusion_role;
|
||||
// Support for G-Code Analyzer
|
||||
double m_last_mm3_per_mm;
|
||||
float m_last_width;
|
||||
float m_last_height;
|
||||
|
||||
Point m_last_pos;
|
||||
bool m_last_pos_defined;
|
||||
|
@ -273,6 +287,24 @@ protected:
|
|||
// Index of a last object copy extruded.
|
||||
std::pair<const PrintObject*, Point> m_last_obj_copy;
|
||||
|
||||
// Time estimator
|
||||
GCodeTimeEstimator m_time_estimator;
|
||||
|
||||
// Analyzer
|
||||
GCodeAnalyzer m_analyzer;
|
||||
|
||||
// Write a string into a file.
|
||||
void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str()); }
|
||||
void _write(FILE* file, const char *what);
|
||||
|
||||
// Write a string into a file.
|
||||
// Add a newline, if the string does not end with a newline already.
|
||||
// Used to export a custom G-code section processed by the PlaceholderParser.
|
||||
void _writeln(FILE* file, const std::string& what);
|
||||
|
||||
// Formats and write into a file the given data.
|
||||
void _write_format(FILE* file, const char* format, ...);
|
||||
|
||||
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
|
||||
void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
|
||||
void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,152 +1,227 @@
|
|||
#ifndef slic3r_GCode_PressureEqualizer_hpp_
|
||||
#define slic3r_GCode_PressureEqualizer_hpp_
|
||||
#ifndef slic3r_GCode_Analyzer_hpp_
|
||||
#define slic3r_GCode_Analyzer_hpp_
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../ExtrusionEntity.hpp"
|
||||
|
||||
#include "Point.hpp"
|
||||
#include "GCodeReader.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum GCodeMoveType
|
||||
{
|
||||
GCODE_MOVE_TYPE_NOOP,
|
||||
GCODE_MOVE_TYPE_RETRACT,
|
||||
GCODE_MOVE_TYPE_UNRETRACT,
|
||||
GCODE_MOVE_TYPE_TOOL_CHANGE,
|
||||
GCODE_MOVE_TYPE_MOVE,
|
||||
GCODE_MOVE_TYPE_EXTRUDE,
|
||||
};
|
||||
class GCodePreviewData;
|
||||
|
||||
// For visualization purposes, for the purposes of the G-code analysis and timing.
|
||||
// The size of this structure is 56B.
|
||||
// Keep the size of this structure as small as possible, because all moves of a complete print
|
||||
// may be held in RAM.
|
||||
struct GCodeMove
|
||||
{
|
||||
bool moving_xy(const float* pos_start) const { return fabs(pos_end[0] - pos_start[0]) > 0.f || fabs(pos_end[1] - pos_start[1]) > 0.f; }
|
||||
bool moving_xy() const { return moving_xy(get_pos_start()); }
|
||||
bool moving_z (const float* pos_start) const { return fabs(pos_end[2] - pos_start[2]) > 0.f; }
|
||||
bool moving_z () const { return moving_z(get_pos_start()); }
|
||||
bool extruding(const float* pos_start) const { return moving_xy() && pos_end[3] > pos_start[3]; }
|
||||
bool extruding() const { return extruding(get_pos_start()); }
|
||||
bool retracting(const float* pos_start) const { return pos_end[3] < pos_start[3]; }
|
||||
bool retracting() const { return retracting(get_pos_start()); }
|
||||
bool deretracting(const float* pos_start) const { return ! moving_xy() && pos_end[3] > pos_start[3]; }
|
||||
bool deretracting() const { return deretracting(get_pos_start()); }
|
||||
|
||||
float dist_xy2(const float* pos_start) const { return (pos_end[0] - pos_start[0]) * (pos_end[0] - pos_start[0]) + (pos_end[1] - pos_start[1]) * (pos_end[1] - pos_start[1]); }
|
||||
float dist_xy2() const { return dist_xy2(get_pos_start()); }
|
||||
float dist_xyz2(const float* pos_start) const { return (pos_end[0] - pos_start[0]) * (pos_end[0] - pos_start[0]) + (pos_end[1] - pos_start[1]) * (pos_end[1] - pos_start[1]) + (pos_end[2] - pos_start[2]) * (pos_end[2] - pos_start[2]); }
|
||||
float dist_xyz2() const { return dist_xyz2(get_pos_start()); }
|
||||
|
||||
float dist_xy(const float* pos_start) const { return sqrt(dist_xy2(pos_start)); }
|
||||
float dist_xy() const { return dist_xy(get_pos_start()); }
|
||||
float dist_xyz(const float* pos_start) const { return sqrt(dist_xyz2(pos_start)); }
|
||||
float dist_xyz() const { return dist_xyz(get_pos_start()); }
|
||||
|
||||
float dist_e(const float* pos_start) const { return fabs(pos_end[3] - pos_start[3]); }
|
||||
float dist_e() const { return dist_e(get_pos_start()); }
|
||||
|
||||
float feedrate() const { return pos_end[4]; }
|
||||
float time(const float* pos_start) const { return dist_xyz(pos_start) / feedrate(); }
|
||||
float time() const { return time(get_pos_start()); }
|
||||
float time_inv(const float* pos_start) const { return feedrate() / dist_xyz(pos_start); }
|
||||
float time_inv() const { return time_inv(get_pos_start()); }
|
||||
|
||||
const float* get_pos_start() const { assert(type != GCODE_MOVE_TYPE_NOOP); return this[-1].pos_end; }
|
||||
|
||||
// Pack the enums to conserve space. With C++x11 the allocation size could be declared for enums, but for old C++ this is the only portable way.
|
||||
// GCodeLineType
|
||||
uint8_t type;
|
||||
// Index of the active extruder.
|
||||
uint8_t extruder_id;
|
||||
// ExtrusionRole
|
||||
uint8_t extrusion_role;
|
||||
// For example, is it a bridge flow? Is the fan on?
|
||||
uint8_t flags;
|
||||
// X,Y,Z,E,F. Storing the state of the currently active extruder only.
|
||||
float pos_end[5];
|
||||
// Extrusion width, height for this segment in um.
|
||||
uint16_t extrusion_width;
|
||||
uint16_t extrusion_height;
|
||||
};
|
||||
|
||||
typedef std::vector<GCodeMove> GCodeMoves;
|
||||
|
||||
struct GCodeLayer
|
||||
{
|
||||
// Index of an object printed.
|
||||
size_t object_idx;
|
||||
// Index of an object instance printed.
|
||||
size_t object_instance_idx;
|
||||
// Index of the layer printed.
|
||||
size_t layer_idx;
|
||||
// Top z coordinate of the layer printed.
|
||||
float layer_z_top;
|
||||
|
||||
// Moves over this layer. The 0th move is always of type GCODELINETYPE_NOOP and
|
||||
// it sets the initial position and tool for the layer.
|
||||
GCodeMoves moves;
|
||||
|
||||
// Indices into m_moves, where the tool changes happen.
|
||||
// This is useful, if one wants to display just only a piece of the path quickly.
|
||||
std::vector<size_t> tool_changes;
|
||||
};
|
||||
|
||||
typedef std::vector<GCodeLayer*> GCodeLayerPtrs;
|
||||
|
||||
class GCodeMovesDB
|
||||
{
|
||||
public:
|
||||
GCodeMovesDB() {};
|
||||
~GCodeMovesDB() { reset(); }
|
||||
void reset();
|
||||
GCodeLayerPtrs m_layers;
|
||||
};
|
||||
|
||||
// Processes a G-code to extract moves and their types.
|
||||
// This information is then used to render the print simulation colored by the extrusion type
|
||||
// or various speeds.
|
||||
// The GCodeAnalyzer is employed as a G-Code filter. It reads the G-code as it is generated,
|
||||
// parses the comments generated by Slic3r just for the analyzer, and removes these comments.
|
||||
class GCodeAnalyzer
|
||||
{
|
||||
public:
|
||||
GCodeAnalyzer(const Slic3r::GCodeConfig *config);
|
||||
~GCodeAnalyzer();
|
||||
static const std::string Extrusion_Role_Tag;
|
||||
static const std::string Mm3_Per_Mm_Tag;
|
||||
static const std::string Width_Tag;
|
||||
static const std::string Height_Tag;
|
||||
|
||||
void reset();
|
||||
static const double Default_mm3_per_mm;
|
||||
static const float Default_Width;
|
||||
static const float Default_Height;
|
||||
|
||||
// Process a next batch of G-code lines. Flush the internal buffers if asked for.
|
||||
const char* process(const char *szGCode, bool flush);
|
||||
// Length of the buffer returned by process().
|
||||
size_t get_output_buffer_length() const { return output_buffer_length; }
|
||||
enum EUnits : unsigned char
|
||||
{
|
||||
Millimeters,
|
||||
Inches
|
||||
};
|
||||
|
||||
enum EAxis : unsigned char
|
||||
{
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
E,
|
||||
Num_Axis
|
||||
};
|
||||
|
||||
enum EPositioningType : unsigned char
|
||||
{
|
||||
Absolute,
|
||||
Relative
|
||||
};
|
||||
|
||||
struct Metadata
|
||||
{
|
||||
ExtrusionRole extrusion_role;
|
||||
unsigned int extruder_id;
|
||||
double mm3_per_mm;
|
||||
float width; // mm
|
||||
float height; // mm
|
||||
float feedrate; // mm/s
|
||||
|
||||
Metadata();
|
||||
Metadata(ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate);
|
||||
|
||||
bool operator != (const Metadata& other) const;
|
||||
};
|
||||
|
||||
struct GCodeMove
|
||||
{
|
||||
enum EType : unsigned char
|
||||
{
|
||||
Noop,
|
||||
Retract,
|
||||
Unretract,
|
||||
Tool_change,
|
||||
Move,
|
||||
Extrude,
|
||||
Num_Types
|
||||
};
|
||||
|
||||
EType type;
|
||||
Metadata data;
|
||||
Pointf3 start_position;
|
||||
Pointf3 end_position;
|
||||
float delta_extruder;
|
||||
|
||||
GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder);
|
||||
GCodeMove(EType type, const Metadata& data, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder);
|
||||
};
|
||||
|
||||
typedef std::vector<GCodeMove> GCodeMovesList;
|
||||
typedef std::map<GCodeMove::EType, GCodeMovesList> TypeToMovesMap;
|
||||
|
||||
private:
|
||||
// Keeps the reference, does not own the config.
|
||||
const Slic3r::GCodeConfig *m_config;
|
||||
struct State
|
||||
{
|
||||
EUnits units;
|
||||
EPositioningType positioning_xyz_type;
|
||||
EPositioningType positioning_e_type;
|
||||
Metadata data;
|
||||
Pointf3 start_position;
|
||||
float start_extrusion;
|
||||
float position[Num_Axis];
|
||||
};
|
||||
|
||||
// Internal data.
|
||||
// X,Y,Z,E,F
|
||||
float m_current_pos[5];
|
||||
size_t m_current_extruder;
|
||||
ExtrusionRole m_current_extrusion_role;
|
||||
uint16_t m_current_extrusion_width;
|
||||
uint16_t m_current_extrusion_height;
|
||||
bool m_retracted;
|
||||
private:
|
||||
State m_state;
|
||||
GCodeReader m_parser;
|
||||
TypeToMovesMap m_moves_map;
|
||||
|
||||
GCodeMovesDB *m_moves;
|
||||
// The output of process_layer()
|
||||
std::string m_process_output;
|
||||
|
||||
// Output buffer will only grow. It will not be reallocated over and over.
|
||||
std::vector<char> output_buffer;
|
||||
size_t output_buffer_length;
|
||||
public:
|
||||
GCodeAnalyzer();
|
||||
|
||||
bool process_line(const char *line, const size_t len);
|
||||
// Reinitialize the analyzer
|
||||
void reset();
|
||||
|
||||
// Push the text to the end of the output_buffer.
|
||||
void push_to_output(const char *text, const size_t len, bool add_eol = true);
|
||||
// Adds the gcode contained in the given string to the analysis and returns it after removing the workcodes
|
||||
const std::string& process_gcode(const std::string& gcode);
|
||||
|
||||
// Calculates all data needed for gcode visualization
|
||||
void calc_gcode_preview_data(GCodePreviewData& preview_data);
|
||||
|
||||
static bool is_valid_extrusion_role(ExtrusionRole role);
|
||||
|
||||
private:
|
||||
// Processes the given gcode line
|
||||
void _process_gcode_line(GCodeReader& reader, const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Move
|
||||
void _processG1(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Firmware controlled Retract
|
||||
void _processG22(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Firmware controlled Unretract
|
||||
void _processG23(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set to Absolute Positioning
|
||||
void _processG90(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set to Relative Positioning
|
||||
void _processG91(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Position
|
||||
void _processG92(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extruder to absolute mode
|
||||
void _processM82(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extruder to relative mode
|
||||
void _processM83(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Processes T line (Select Tool)
|
||||
void _processT(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Processes the tags
|
||||
// Returns true if any tag has been processed
|
||||
bool _process_tags(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Processes extrusion role tag
|
||||
void _process_extrusion_role_tag(const std::string& comment, size_t pos);
|
||||
|
||||
// Processes mm3_per_mm tag
|
||||
void _process_mm3_per_mm_tag(const std::string& comment, size_t pos);
|
||||
|
||||
// Processes width tag
|
||||
void _process_width_tag(const std::string& comment, size_t pos);
|
||||
|
||||
// Processes height tag
|
||||
void _process_height_tag(const std::string& comment, size_t pos);
|
||||
|
||||
void _set_units(EUnits units);
|
||||
EUnits _get_units() const;
|
||||
|
||||
void _set_positioning_xyz_type(EPositioningType type);
|
||||
EPositioningType _get_positioning_xyz_type() const;
|
||||
|
||||
void _set_positioning_e_type(EPositioningType type);
|
||||
EPositioningType _get_positioning_e_type() const;
|
||||
|
||||
void _set_extrusion_role(ExtrusionRole extrusion_role);
|
||||
ExtrusionRole _get_extrusion_role() const;
|
||||
|
||||
void _set_extruder_id(unsigned int id);
|
||||
unsigned int _get_extruder_id() const;
|
||||
|
||||
void _set_mm3_per_mm(double value);
|
||||
double _get_mm3_per_mm() const;
|
||||
|
||||
void _set_width(float width);
|
||||
float _get_width() const;
|
||||
|
||||
void _set_height(float height);
|
||||
float _get_height() const;
|
||||
|
||||
void _set_feedrate(float feedrate_mm_sec);
|
||||
float _get_feedrate() const;
|
||||
|
||||
void _set_axis_position(EAxis axis, float position);
|
||||
float _get_axis_position(EAxis axis) const;
|
||||
|
||||
// Sets axes position to zero
|
||||
void _reset_axes_position();
|
||||
|
||||
void _set_start_position(const Pointf3& position);
|
||||
const Pointf3& _get_start_position() const;
|
||||
|
||||
void _set_start_extrusion(float extrusion);
|
||||
float _get_start_extrusion() const;
|
||||
float _get_delta_extrusion() const;
|
||||
|
||||
// Returns current xyz position (from m_state.position[])
|
||||
Pointf3 _get_end_position() const;
|
||||
|
||||
// Adds a new move with the given data
|
||||
void _store_move(GCodeMove::EType type);
|
||||
|
||||
// Checks if the given int is a valid extrusion role (contained into enum ExtrusionRole)
|
||||
bool _is_valid_extrusion_role(int value) const;
|
||||
|
||||
void _calc_gcode_preview_extrusion_layers(GCodePreviewData& preview_data);
|
||||
void _calc_gcode_preview_travel(GCodePreviewData& preview_data);
|
||||
void _calc_gcode_preview_retractions(GCodePreviewData& preview_data);
|
||||
void _calc_gcode_preview_unretractions(GCodePreviewData& preview_data);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_GCode_PressureEqualizer_hpp_ */
|
||||
#endif /* slic3r_GCode_Analyzer_hpp_ */
|
||||
|
|
408
xs/src/libslic3r/GCode/PreviewData.cpp
Normal file
408
xs/src/libslic3r/GCode/PreviewData.cpp
Normal file
|
@ -0,0 +1,408 @@
|
|||
#include "Analyzer.hpp"
|
||||
#include "PreviewData.hpp"
|
||||
#include <float.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
const GCodePreviewData::Color GCodePreviewData::Color::Dummy(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
GCodePreviewData::Color::Color()
|
||||
{
|
||||
rgba[0] = 1.0f;
|
||||
rgba[1] = 1.0f;
|
||||
rgba[2] = 1.0f;
|
||||
rgba[3] = 1.0f;
|
||||
}
|
||||
|
||||
GCodePreviewData::Color::Color(float r, float g, float b, float a)
|
||||
{
|
||||
rgba[0] = r;
|
||||
rgba[1] = g;
|
||||
rgba[2] = b;
|
||||
rgba[3] = a;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> GCodePreviewData::Color::as_bytes() const
|
||||
{
|
||||
std::vector<unsigned char> ret;
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
{
|
||||
ret.push_back((unsigned char)(255.0f * rgba[i]));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
GCodePreviewData::Extrusion::Layer::Layer(float z, const ExtrusionPaths& paths)
|
||||
: z(z)
|
||||
, paths(paths)
|
||||
{
|
||||
}
|
||||
|
||||
GCodePreviewData::Travel::Polyline::Polyline(EType type, EDirection direction, float feedrate, unsigned int extruder_id, const Polyline3& polyline)
|
||||
: type(type)
|
||||
, direction(direction)
|
||||
, feedrate(feedrate)
|
||||
, extruder_id(extruder_id)
|
||||
, polyline(polyline)
|
||||
{
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color GCodePreviewData::Range::Default_Colors[Colors_Count] =
|
||||
{
|
||||
Color(0.043f, 0.173f, 0.478f, 1.0f),
|
||||
Color(0.075f, 0.349f, 0.522f, 1.0f),
|
||||
Color(0.110f, 0.533f, 0.569f, 1.0f),
|
||||
Color(0.016f, 0.839f, 0.059f, 1.0f),
|
||||
Color(0.667f, 0.949f, 0.000f, 1.0f),
|
||||
Color(0.988f, 0.975f, 0.012f, 1.0f),
|
||||
Color(0.961f, 0.808f, 0.039f, 1.0f),
|
||||
Color(0.890f, 0.533f, 0.125f, 1.0f),
|
||||
Color(0.820f, 0.408f, 0.188f, 1.0f),
|
||||
Color(0.761f, 0.322f, 0.235f, 1.0f)
|
||||
};
|
||||
|
||||
GCodePreviewData::Range::Range()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void GCodePreviewData::Range::reset()
|
||||
{
|
||||
min = FLT_MAX;
|
||||
max = -FLT_MAX;
|
||||
}
|
||||
|
||||
bool GCodePreviewData::Range::empty() const
|
||||
{
|
||||
return min == max;
|
||||
}
|
||||
|
||||
void GCodePreviewData::Range::update_from(float value)
|
||||
{
|
||||
min = std::min(min, value);
|
||||
max = std::max(max, value);
|
||||
}
|
||||
|
||||
void GCodePreviewData::Range::set_from(const Range& other)
|
||||
{
|
||||
min = other.min;
|
||||
max = other.max;
|
||||
}
|
||||
|
||||
float GCodePreviewData::Range::step_size() const
|
||||
{
|
||||
return (max - min) / (float)Colors_Count;
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color& GCodePreviewData::Range::get_color_at_max() const
|
||||
{
|
||||
return colors[Colors_Count - 1];
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color& GCodePreviewData::Range::get_color_at(float value) const
|
||||
{
|
||||
return empty() ? get_color_at_max() : colors[clamp((unsigned int)0, Colors_Count - 1, (unsigned int)((value - min) / step_size()))];
|
||||
}
|
||||
|
||||
GCodePreviewData::LegendItem::LegendItem(const std::string& text, const GCodePreviewData::Color& color)
|
||||
: text(text)
|
||||
, color(color)
|
||||
{
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color GCodePreviewData::Extrusion::Default_Extrusion_Role_Colors[Num_Extrusion_Roles] =
|
||||
{
|
||||
Color(0.0f, 0.0f, 0.0f, 1.0f), // erNone
|
||||
Color(1.0f, 0.0f, 0.0f, 1.0f), // erPerimeter
|
||||
Color(0.0f, 1.0f, 0.0f, 1.0f), // erExternalPerimeter
|
||||
Color(0.0f, 0.0f, 1.0f, 1.0f), // erOverhangPerimeter
|
||||
Color(1.0f, 1.0f, 0.0f, 1.0f), // erInternalInfill
|
||||
Color(1.0f, 0.0f, 1.0f, 1.0f), // erSolidInfill
|
||||
Color(0.0f, 1.0f, 1.0f, 1.0f), // erTopSolidInfill
|
||||
Color(0.5f, 0.5f, 0.5f, 1.0f), // erBridgeInfill
|
||||
Color(1.0f, 1.0f, 1.0f, 1.0f), // erGapFill
|
||||
Color(0.5f, 0.0f, 0.0f, 1.0f), // erSkirt
|
||||
Color(0.0f, 0.5f, 0.0f, 1.0f), // erSupportMaterial
|
||||
Color(0.0f, 0.0f, 0.5f, 1.0f), // erSupportMaterialInterface
|
||||
Color(0.7f, 0.89f, 0.67f, 1.0f), // erWipeTower
|
||||
Color(0.0f, 0.0f, 0.0f, 1.0f) // erMixed
|
||||
};
|
||||
|
||||
// todo: merge with Slic3r::ExtrusionRole2String() from GCode.cpp
|
||||
const std::string GCodePreviewData::Extrusion::Default_Extrusion_Role_Names[Num_Extrusion_Roles]
|
||||
{
|
||||
"None",
|
||||
"Perimeter",
|
||||
"External perimeter",
|
||||
"Overhang perimeter",
|
||||
"Internal infill",
|
||||
"Solid infill",
|
||||
"Top solid infill",
|
||||
"Bridge infill",
|
||||
"Gap fill",
|
||||
"Skirt",
|
||||
"Support material",
|
||||
"Support material interface",
|
||||
"Wipe tower",
|
||||
"Mixed"
|
||||
};
|
||||
|
||||
const GCodePreviewData::Extrusion::EViewType GCodePreviewData::Extrusion::Default_View_Type = GCodePreviewData::Extrusion::FeatureType;
|
||||
|
||||
void GCodePreviewData::Extrusion::set_default()
|
||||
{
|
||||
view_type = Default_View_Type;
|
||||
|
||||
::memcpy((void*)role_colors, (const void*)Default_Extrusion_Role_Colors, Num_Extrusion_Roles * sizeof(Color));
|
||||
::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
|
||||
::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
|
||||
::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
|
||||
|
||||
for (unsigned int i = 0; i < Num_Extrusion_Roles; ++i)
|
||||
{
|
||||
role_names[i] = Default_Extrusion_Role_Names[i];
|
||||
}
|
||||
|
||||
role_flags = 0;
|
||||
for (unsigned int i = 0; i < Num_Extrusion_Roles; ++i)
|
||||
{
|
||||
role_flags |= 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
bool GCodePreviewData::Extrusion::is_role_flag_set(ExtrusionRole role) const
|
||||
{
|
||||
return is_role_flag_set(role_flags, role);
|
||||
}
|
||||
|
||||
bool GCodePreviewData::Extrusion::is_role_flag_set(unsigned int flags, ExtrusionRole role)
|
||||
{
|
||||
return GCodeAnalyzer::is_valid_extrusion_role(role) && (flags & (1 << (role - erPerimeter))) != 0;
|
||||
}
|
||||
|
||||
const float GCodePreviewData::Travel::Default_Width = 0.075f;
|
||||
const float GCodePreviewData::Travel::Default_Height = 0.075f;
|
||||
const GCodePreviewData::Color GCodePreviewData::Travel::Default_Type_Colors[Num_Types] =
|
||||
{
|
||||
Color(0.0f, 0.0f, 0.75f, 1.0f), // Move
|
||||
Color(0.0f, 0.75f, 0.0f, 1.0f), // Extrude
|
||||
Color(0.75f, 0.0f, 0.0f, 1.0f), // Retract
|
||||
};
|
||||
|
||||
void GCodePreviewData::Travel::set_default()
|
||||
{
|
||||
width = Default_Width;
|
||||
height = Default_Height;
|
||||
::memcpy((void*)type_colors, (const void*)Default_Type_Colors, Num_Types * sizeof(Color));
|
||||
is_visible = false;
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color GCodePreviewData::Retraction::Default_Color = GCodePreviewData::Color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
GCodePreviewData::Retraction::Position::Position(const Point3& position, float width, float height)
|
||||
: position(position)
|
||||
, width(width)
|
||||
, height(height)
|
||||
{
|
||||
}
|
||||
|
||||
void GCodePreviewData::Retraction::set_default()
|
||||
{
|
||||
color = Default_Color;
|
||||
is_visible = false;
|
||||
}
|
||||
|
||||
void GCodePreviewData::Shell::set_default()
|
||||
{
|
||||
is_visible = false;
|
||||
}
|
||||
|
||||
GCodePreviewData::GCodePreviewData()
|
||||
{
|
||||
set_default();
|
||||
}
|
||||
|
||||
void GCodePreviewData::set_default()
|
||||
{
|
||||
extrusion.set_default();
|
||||
travel.set_default();
|
||||
retraction.set_default();
|
||||
unretraction.set_default();
|
||||
shell.set_default();
|
||||
}
|
||||
|
||||
void GCodePreviewData::reset()
|
||||
{
|
||||
extrusion.layers.clear();
|
||||
travel.polylines.clear();
|
||||
retraction.positions.clear();
|
||||
unretraction.positions.clear();
|
||||
}
|
||||
|
||||
bool GCodePreviewData::empty() const
|
||||
{
|
||||
return extrusion.layers.empty() && travel.polylines.empty() && retraction.positions.empty() && unretraction.positions.empty();
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color& GCodePreviewData::get_extrusion_role_color(ExtrusionRole role) const
|
||||
{
|
||||
return extrusion.role_colors[role];
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color& GCodePreviewData::get_extrusion_height_color(float height) const
|
||||
{
|
||||
return extrusion.ranges.height.get_color_at(height);
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color& GCodePreviewData::get_extrusion_width_color(float width) const
|
||||
{
|
||||
return extrusion.ranges.width.get_color_at(width);
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color& GCodePreviewData::get_extrusion_feedrate_color(float feedrate) const
|
||||
{
|
||||
return extrusion.ranges.feedrate.get_color_at(feedrate);
|
||||
}
|
||||
|
||||
void GCodePreviewData::set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha)
|
||||
{
|
||||
for (unsigned int i = 0; i < Extrusion::Num_Extrusion_Roles; ++i)
|
||||
{
|
||||
if (role_name == extrusion.role_names[i])
|
||||
{
|
||||
extrusion.role_colors[i] = Color(red, green, blue, alpha);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GCodePreviewData::set_extrusion_paths_colors(const std::vector<std::string>& colors)
|
||||
{
|
||||
unsigned int size = (unsigned int)colors.size();
|
||||
|
||||
if (size % 2 != 0)
|
||||
return;
|
||||
|
||||
for (unsigned int i = 0; i < size; i += 2)
|
||||
{
|
||||
const std::string& color_str = colors[i + 1];
|
||||
|
||||
if (color_str.size() == 6)
|
||||
{
|
||||
bool valid = true;
|
||||
for (int c = 0; c < 6; ++c)
|
||||
{
|
||||
if (::isxdigit(color_str[c]) == 0)
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid)
|
||||
{
|
||||
unsigned int color;
|
||||
std::stringstream ss;
|
||||
ss << std::hex << color_str;
|
||||
ss >> color;
|
||||
|
||||
float den = 1.0f / 255.0f;
|
||||
|
||||
float r = (float)((color & 0xFF0000) >> 16) * den;
|
||||
float g = (float)((color & 0x00FF00) >> 8) * den;
|
||||
float b = (float)(color & 0x0000FF) * den;
|
||||
|
||||
this->set_extrusion_role_color(colors[i], r, g, b, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GCodePreviewData::get_legend_title() const
|
||||
{
|
||||
switch (extrusion.view_type)
|
||||
{
|
||||
case Extrusion::FeatureType:
|
||||
return "Feature type";
|
||||
case Extrusion::Height:
|
||||
return "Height (mm)";
|
||||
case Extrusion::Width:
|
||||
return "Width (mm)";
|
||||
case Extrusion::Feedrate:
|
||||
return "Speed (mm/s)";
|
||||
case Extrusion::Tool:
|
||||
return "Tool";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::vector<float>& tool_colors) const
|
||||
{
|
||||
struct Helper
|
||||
{
|
||||
static void FillListFromRange(LegendItemsList& list, const Range& range, unsigned int decimals, float scale_factor)
|
||||
{
|
||||
list.reserve(Range::Colors_Count);
|
||||
float step = range.step_size();
|
||||
for (unsigned int i = 0; i < Range::Colors_Count; ++i)
|
||||
{
|
||||
char buf[32];
|
||||
sprintf(buf, "%.*f/%.*f", decimals, scale_factor * (range.min + (float)i * step), decimals, scale_factor * (range.min + (float)(i + 1) * step));
|
||||
list.emplace_back(buf, range.colors[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
LegendItemsList items;
|
||||
|
||||
switch (extrusion.view_type)
|
||||
{
|
||||
case Extrusion::FeatureType:
|
||||
{
|
||||
items.reserve(erMixed - erPerimeter + 1);
|
||||
for (unsigned int i = (unsigned int)erPerimeter; i < (unsigned int)erMixed; ++i)
|
||||
{
|
||||
items.emplace_back(extrusion.role_names[i], extrusion.role_colors[i]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Extrusion::Height:
|
||||
{
|
||||
Helper::FillListFromRange(items, extrusion.ranges.height, 3, 1.0f);
|
||||
break;
|
||||
}
|
||||
case Extrusion::Width:
|
||||
{
|
||||
Helper::FillListFromRange(items, extrusion.ranges.width, 3, 1.0f);
|
||||
break;
|
||||
}
|
||||
case Extrusion::Feedrate:
|
||||
{
|
||||
Helper::FillListFromRange(items, extrusion.ranges.feedrate, 0, 1.0f);
|
||||
break;
|
||||
}
|
||||
case Extrusion::Tool:
|
||||
{
|
||||
unsigned int tools_colors_count = tool_colors.size() / 4;
|
||||
items.reserve(tools_colors_count);
|
||||
for (unsigned int i = 0; i < tools_colors_count; ++i)
|
||||
{
|
||||
char buf[32];
|
||||
sprintf(buf, "Extruder %d", i + 1);
|
||||
|
||||
GCodePreviewData::Color color;
|
||||
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
|
||||
|
||||
items.emplace_back(buf, color);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
205
xs/src/libslic3r/GCode/PreviewData.hpp
Normal file
205
xs/src/libslic3r/GCode/PreviewData.hpp
Normal file
|
@ -0,0 +1,205 @@
|
|||
#ifndef slic3r_GCode_PreviewData_hpp_
|
||||
#define slic3r_GCode_PreviewData_hpp_
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../ExtrusionEntity.hpp"
|
||||
|
||||
#include "Point.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class GCodePreviewData
|
||||
{
|
||||
public:
|
||||
struct Color
|
||||
{
|
||||
float rgba[4];
|
||||
|
||||
Color();
|
||||
Color(float r, float g, float b, float a);
|
||||
|
||||
std::vector<unsigned char> as_bytes() const;
|
||||
|
||||
static const Color Dummy;
|
||||
};
|
||||
|
||||
struct Range
|
||||
{
|
||||
static const unsigned int Colors_Count = 10;
|
||||
static const Color Default_Colors[Colors_Count];
|
||||
|
||||
Color colors[Colors_Count];
|
||||
float min;
|
||||
float max;
|
||||
|
||||
Range();
|
||||
|
||||
void reset();
|
||||
bool empty() const;
|
||||
void update_from(float value);
|
||||
void set_from(const Range& other);
|
||||
float step_size() const;
|
||||
|
||||
const Color& get_color_at(float value) const;
|
||||
const Color& get_color_at_max() const;
|
||||
};
|
||||
|
||||
struct LegendItem
|
||||
{
|
||||
std::string text;
|
||||
Color color;
|
||||
|
||||
LegendItem(const std::string& text, const Color& color);
|
||||
};
|
||||
|
||||
typedef std::vector<LegendItem> LegendItemsList;
|
||||
|
||||
struct Extrusion
|
||||
{
|
||||
enum EViewType : unsigned char
|
||||
{
|
||||
FeatureType,
|
||||
Height,
|
||||
Width,
|
||||
Feedrate,
|
||||
Tool,
|
||||
Num_View_Types
|
||||
};
|
||||
|
||||
static const unsigned int Num_Extrusion_Roles = (unsigned int)erMixed + 1;
|
||||
static const Color Default_Extrusion_Role_Colors[Num_Extrusion_Roles];
|
||||
static const std::string Default_Extrusion_Role_Names[Num_Extrusion_Roles];
|
||||
static const EViewType Default_View_Type;
|
||||
|
||||
struct Ranges
|
||||
{
|
||||
Range height;
|
||||
Range width;
|
||||
Range feedrate;
|
||||
};
|
||||
|
||||
struct Layer
|
||||
{
|
||||
float z;
|
||||
ExtrusionPaths paths;
|
||||
|
||||
Layer(float z, const ExtrusionPaths& paths);
|
||||
};
|
||||
|
||||
typedef std::vector<Layer> LayersList;
|
||||
|
||||
EViewType view_type;
|
||||
Color role_colors[Num_Extrusion_Roles];
|
||||
std::string role_names[Num_Extrusion_Roles];
|
||||
Ranges ranges;
|
||||
LayersList layers;
|
||||
unsigned int role_flags;
|
||||
|
||||
void set_default();
|
||||
bool is_role_flag_set(ExtrusionRole role) const;
|
||||
|
||||
static bool is_role_flag_set(unsigned int flags, ExtrusionRole role);
|
||||
};
|
||||
|
||||
struct Travel
|
||||
{
|
||||
enum EType : unsigned char
|
||||
{
|
||||
Move,
|
||||
Extrude,
|
||||
Retract,
|
||||
Num_Types
|
||||
};
|
||||
|
||||
static const float Default_Width;
|
||||
static const float Default_Height;
|
||||
static const Color Default_Type_Colors[Num_Types];
|
||||
|
||||
struct Polyline
|
||||
{
|
||||
enum EDirection
|
||||
{
|
||||
Vertical,
|
||||
Generic,
|
||||
Num_Directions
|
||||
};
|
||||
|
||||
EType type;
|
||||
EDirection direction;
|
||||
float feedrate;
|
||||
unsigned int extruder_id;
|
||||
Polyline3 polyline;
|
||||
|
||||
Polyline(EType type, EDirection direction, float feedrate, unsigned int extruder_id, const Polyline3& polyline);
|
||||
};
|
||||
|
||||
typedef std::vector<Polyline> PolylinesList;
|
||||
|
||||
PolylinesList polylines;
|
||||
float width;
|
||||
float height;
|
||||
Color type_colors[Num_Types];
|
||||
bool is_visible;
|
||||
|
||||
void set_default();
|
||||
};
|
||||
|
||||
struct Retraction
|
||||
{
|
||||
static const Color Default_Color;
|
||||
|
||||
struct Position
|
||||
{
|
||||
Point3 position;
|
||||
float width;
|
||||
float height;
|
||||
|
||||
Position(const Point3& position, float width, float height);
|
||||
};
|
||||
|
||||
typedef std::vector<Position> PositionsList;
|
||||
|
||||
PositionsList positions;
|
||||
Color color;
|
||||
bool is_visible;
|
||||
|
||||
void set_default();
|
||||
};
|
||||
|
||||
struct Shell
|
||||
{
|
||||
bool is_visible;
|
||||
|
||||
void set_default();
|
||||
};
|
||||
|
||||
Extrusion extrusion;
|
||||
Travel travel;
|
||||
Retraction retraction;
|
||||
Retraction unretraction;
|
||||
Shell shell;
|
||||
|
||||
GCodePreviewData();
|
||||
|
||||
void set_default();
|
||||
void reset();
|
||||
bool empty() const;
|
||||
|
||||
const Color& get_extrusion_role_color(ExtrusionRole role) const;
|
||||
const Color& get_extrusion_height_color(float height) const;
|
||||
const Color& get_extrusion_width_color(float width) const;
|
||||
const Color& get_extrusion_feedrate_color(float feedrate) const;
|
||||
|
||||
void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha);
|
||||
void set_extrusion_paths_colors(const std::vector<std::string>& colors);
|
||||
|
||||
std::string get_legend_title() const;
|
||||
LegendItemsList get_legend_items(const std::vector<float>& tool_colors) const;
|
||||
};
|
||||
|
||||
GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2);
|
||||
GCodePreviewData::Color operator * (float f, const GCodePreviewData::Color& color);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_GCode_PreviewData_hpp_ */
|
|
@ -4,17 +4,7 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
std::string
|
||||
_format_z(float z)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << std::fixed << std::setprecision(3)
|
||||
<< z;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
SpiralVase::process_layer(const std::string &gcode)
|
||||
std::string SpiralVase::process_layer(const std::string &gcode)
|
||||
{
|
||||
/* This post-processor relies on several assumptions:
|
||||
- all layers are processed through it, including those that are not supposed
|
||||
|
@ -27,7 +17,7 @@ SpiralVase::process_layer(const std::string &gcode)
|
|||
// If we're not going to modify G-code, just feed it to the reader
|
||||
// in order to update positions.
|
||||
if (!this->enable) {
|
||||
this->_reader.parse(gcode, {});
|
||||
this->_reader.parse_buffer(gcode);
|
||||
return gcode;
|
||||
}
|
||||
|
||||
|
@ -38,16 +28,17 @@ SpiralVase::process_layer(const std::string &gcode)
|
|||
bool set_z = false;
|
||||
|
||||
{
|
||||
//FIXME Performance warning: This copies the GCodeConfig of the reader.
|
||||
GCodeReader r = this->_reader; // clone
|
||||
r.parse(gcode, [&total_layer_length, &layer_height, &z, &set_z]
|
||||
(GCodeReader &, const GCodeReader::GCodeLine &line) {
|
||||
if (line.cmd == "G1") {
|
||||
if (line.extruding()) {
|
||||
total_layer_length += line.dist_XY();
|
||||
} else if (line.has('Z')) {
|
||||
layer_height += line.dist_Z();
|
||||
r.parse_buffer(gcode, [&total_layer_length, &layer_height, &z, &set_z]
|
||||
(GCodeReader &reader, const GCodeReader::GCodeLine &line) {
|
||||
if (line.cmd_is("G1")) {
|
||||
if (line.extruding(reader)) {
|
||||
total_layer_length += line.dist_XY(reader);
|
||||
} else if (line.has(Z)) {
|
||||
layer_height += line.dist_Z(reader);
|
||||
if (!set_z) {
|
||||
z = line.new_Z();
|
||||
z = line.new_Z(reader);
|
||||
set_z = true;
|
||||
}
|
||||
}
|
||||
|
@ -59,23 +50,23 @@ SpiralVase::process_layer(const std::string &gcode)
|
|||
z -= layer_height;
|
||||
|
||||
std::string new_gcode;
|
||||
this->_reader.parse(gcode, [&new_gcode, &z, &layer_height, &total_layer_length]
|
||||
(GCodeReader &, GCodeReader::GCodeLine line) {
|
||||
if (line.cmd == "G1") {
|
||||
if (line.has('Z')) {
|
||||
this->_reader.parse_buffer(gcode, [&new_gcode, &z, &layer_height, &total_layer_length]
|
||||
(GCodeReader &reader, GCodeReader::GCodeLine line) {
|
||||
if (line.cmd_is("G1")) {
|
||||
if (line.has_z()) {
|
||||
// If this is the initial Z move of the layer, replace it with a
|
||||
// (redundant) move to the last Z of previous layer.
|
||||
line.set('Z', _format_z(z));
|
||||
new_gcode += line.raw + '\n';
|
||||
line.set(reader, Z, z);
|
||||
new_gcode += line.raw() + '\n';
|
||||
return;
|
||||
} else {
|
||||
float dist_XY = line.dist_XY();
|
||||
float dist_XY = line.dist_XY(reader);
|
||||
if (dist_XY > 0) {
|
||||
// horizontal move
|
||||
if (line.extruding()) {
|
||||
if (line.extruding(reader)) {
|
||||
z += dist_XY * layer_height / total_layer_length;
|
||||
line.set('Z', _format_z(z));
|
||||
new_gcode += line.raw + '\n';
|
||||
line.set(reader, Z, z);
|
||||
new_gcode += line.raw() + '\n';
|
||||
}
|
||||
return;
|
||||
|
||||
|
@ -87,7 +78,7 @@ SpiralVase::process_layer(const std::string &gcode)
|
|||
}
|
||||
}
|
||||
}
|
||||
new_gcode += line.raw + '\n';
|
||||
new_gcode += line.raw() + '\n';
|
||||
});
|
||||
|
||||
return new_gcode;
|
||||
|
|
|
@ -13,7 +13,7 @@ class SpiralVase {
|
|||
SpiralVase(const PrintConfig &config)
|
||||
: enable(false), _config(&config)
|
||||
{
|
||||
this->_reader.Z = this->_config->z_offset;
|
||||
this->_reader.z() = this->_config->z_offset;
|
||||
this->_reader.apply_config(*this->_config);
|
||||
};
|
||||
std::string process_layer(const std::string &gcode);
|
||||
|
|
|
@ -21,6 +21,8 @@ TODO LIST
|
|||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "Analyzer.hpp"
|
||||
|
||||
#if defined(__linux) || defined(__GNUC__ )
|
||||
#include <strings.h>
|
||||
#endif /* __linux */
|
||||
|
@ -510,6 +512,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
|||
.travel(cleaning_box.ld, 7200)
|
||||
.set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming.
|
||||
|
||||
// adds tag for analyzer
|
||||
char buf[32];
|
||||
sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
|
||||
writer.append(buf);
|
||||
|
||||
if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
|
||||
for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) {
|
||||
unsigned int tool = tools[idx_tool];
|
||||
|
@ -658,6 +665,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
|||
writer.set_initial_position(initial_position);
|
||||
}
|
||||
|
||||
// adds tag for analyzer
|
||||
char buf[32];
|
||||
sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
|
||||
writer.append(buf);
|
||||
|
||||
if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
|
||||
// Increase the extruder driver current to allow fast ramming.
|
||||
writer.set_extruder_trimpot(750);
|
||||
|
@ -735,6 +747,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b
|
|||
else
|
||||
writer.set_initial_position(initial_position);
|
||||
|
||||
// adds tag for analyzer
|
||||
char buf[32];
|
||||
sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
|
||||
writer.append(buf);
|
||||
|
||||
if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
|
||||
|
||||
writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower.
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <Shiny/Shiny.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void GCodeReader::apply_config(const GCodeConfig &config)
|
||||
|
@ -18,66 +20,89 @@ void GCodeReader::apply_config(const DynamicPrintConfig &config)
|
|||
m_extrusion_axis = m_config.get_extrusion_axis()[0];
|
||||
}
|
||||
|
||||
void GCodeReader::parse(const std::string &gcode, callback_t callback)
|
||||
const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command)
|
||||
{
|
||||
std::istringstream ss(gcode);
|
||||
std::string line;
|
||||
while (std::getline(ss, line))
|
||||
this->parse_line(line, callback);
|
||||
}
|
||||
|
||||
void GCodeReader::parse_line(std::string line, callback_t callback)
|
||||
{
|
||||
GCodeLine gline(this);
|
||||
gline.raw = line;
|
||||
if (this->verbose)
|
||||
std::cout << line << std::endl;
|
||||
|
||||
// strip comment
|
||||
{
|
||||
size_t pos = line.find(';');
|
||||
if (pos != std::string::npos) {
|
||||
gline.comment = line.substr(pos+1);
|
||||
line.erase(pos);
|
||||
}
|
||||
}
|
||||
PROFILE_FUNC();
|
||||
|
||||
// command and args
|
||||
const char *c = ptr;
|
||||
{
|
||||
std::vector<std::string> args;
|
||||
boost::split(args, line, boost::is_any_of(" "));
|
||||
|
||||
// first one is cmd
|
||||
gline.cmd = args.front();
|
||||
args.erase(args.begin());
|
||||
|
||||
for (std::string &arg : args) {
|
||||
if (arg.size() < 2) continue;
|
||||
gline.args.insert(std::make_pair(arg[0], arg.substr(1)));
|
||||
PROFILE_BLOCK(command_and_args);
|
||||
// Skip the whitespaces.
|
||||
command.first = skip_whitespaces(c);
|
||||
// Skip the command.
|
||||
c = command.second = skip_word(command.first);
|
||||
// Up to the end of line or comment.
|
||||
while (! is_end_of_gcode_line(*c)) {
|
||||
// Skip whitespaces.
|
||||
c = skip_whitespaces(c);
|
||||
if (is_end_of_gcode_line(*c))
|
||||
break;
|
||||
// Check the name of the axis.
|
||||
Axis axis = NUM_AXES;
|
||||
switch (*c) {
|
||||
case 'X': axis = X; break;
|
||||
case 'Y': axis = Y; break;
|
||||
case 'Z': axis = Z; break;
|
||||
case 'F': axis = F; break;
|
||||
default:
|
||||
if (*c == m_extrusion_axis)
|
||||
axis = E;
|
||||
break;
|
||||
}
|
||||
if (axis != NUM_AXES) {
|
||||
// Try to parse the numeric value.
|
||||
char *pend = nullptr;
|
||||
double v = strtod(++ c, &pend);
|
||||
if (pend != nullptr && is_end_of_word(*pend)) {
|
||||
// The axis value has been parsed correctly.
|
||||
gline.m_axis[int(axis)] = float(v);
|
||||
gline.m_mask |= 1 << int(axis);
|
||||
c = pend;
|
||||
} else
|
||||
// Skip the rest of the word.
|
||||
c = skip_word(c);
|
||||
} else
|
||||
// Skip the rest of the word.
|
||||
c = skip_word(c);
|
||||
}
|
||||
}
|
||||
|
||||
// convert extrusion axis
|
||||
if (m_extrusion_axis != 'E') {
|
||||
const auto it = gline.args.find(m_extrusion_axis);
|
||||
if (it != gline.args.end()) {
|
||||
std::swap(gline.args['E'], it->second);
|
||||
gline.args.erase(it);
|
||||
}
|
||||
if (gline.has(E) && m_config.use_relative_e_distances)
|
||||
m_position[E] = 0;
|
||||
|
||||
// Skip the rest of the line.
|
||||
for (; ! is_end_of_line(*c); ++ c);
|
||||
|
||||
// Copy the raw string including the comment, without the trailing newlines.
|
||||
if (c > ptr) {
|
||||
PROFILE_BLOCK(copy_raw_string);
|
||||
gline.m_raw.assign(ptr, c);
|
||||
}
|
||||
|
||||
if (gline.has('E') && m_config.use_relative_e_distances)
|
||||
this->E = 0;
|
||||
|
||||
if (callback) callback(*this, gline);
|
||||
|
||||
// update coordinates
|
||||
if (gline.cmd == "G0" || gline.cmd == "G1" || gline.cmd == "G92") {
|
||||
this->X = gline.new_X();
|
||||
this->Y = gline.new_Y();
|
||||
this->Z = gline.new_Z();
|
||||
this->E = gline.new_E();
|
||||
this->F = gline.new_F();
|
||||
|
||||
// Skip the trailing newlines.
|
||||
if (*c == '\r')
|
||||
++ c;
|
||||
if (*c == '\n')
|
||||
++ c;
|
||||
|
||||
if (m_verbose)
|
||||
std::cout << gline.m_raw << std::endl;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void GCodeReader::update_coordinates(GCodeLine &gline, std::pair<const char*, const char*> &command)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
if (*command.first == 'G') {
|
||||
int cmd_len = int(command.second - command.first);
|
||||
if ((cmd_len == 2 && (command.first[1] == '0' || command.first[1] == '1')) ||
|
||||
(cmd_len == 3 && command.first[1] == '9' && command.first[2] == '2')) {
|
||||
for (size_t i = 0; i < NUM_AXES; ++ i)
|
||||
if (gline.has(Axis(i)))
|
||||
this->m_position[i] = gline.value(Axis(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,22 +114,64 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback)
|
|||
this->parse_line(line, callback);
|
||||
}
|
||||
|
||||
void GCodeReader::GCodeLine::set(char arg, std::string value)
|
||||
bool GCodeReader::GCodeLine::has_value(char axis, float &value) const
|
||||
{
|
||||
const std::string space(" ");
|
||||
if (this->has(arg)) {
|
||||
size_t pos = this->raw.find(space + arg)+2;
|
||||
size_t end = this->raw.find(' ', pos+1);
|
||||
this->raw = this->raw.replace(pos, end-pos, value);
|
||||
} else {
|
||||
size_t pos = this->raw.find(' ');
|
||||
if (pos == std::string::npos) {
|
||||
this->raw += space + arg + value;
|
||||
} else {
|
||||
this->raw = this->raw.replace(pos, 0, space + arg + value);
|
||||
const char *c = m_raw.c_str();
|
||||
// Skip the whitespaces.
|
||||
c = skip_whitespaces(c);
|
||||
// Skip the command.
|
||||
c = skip_word(c);
|
||||
// Up to the end of line or comment.
|
||||
while (! is_end_of_gcode_line(*c)) {
|
||||
// Skip whitespaces.
|
||||
c = skip_whitespaces(c);
|
||||
if (is_end_of_gcode_line(*c))
|
||||
break;
|
||||
// Check the name of the axis.
|
||||
if (*c == axis) {
|
||||
// Try to parse the numeric value.
|
||||
char *pend = nullptr;
|
||||
double v = strtod(++ c, &pend);
|
||||
if (pend != nullptr && is_end_of_word(*pend)) {
|
||||
// The axis value has been parsed correctly.
|
||||
value = float(v);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Skip the rest of the word.
|
||||
c = skip_word(c);
|
||||
}
|
||||
this->args[arg] = value;
|
||||
return false;
|
||||
}
|
||||
|
||||
void GCodeReader::GCodeLine::set(const GCodeReader &reader, const Axis axis, const float new_value, const int decimal_digits)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << std::fixed << std::setprecision(decimal_digits) << new_value;
|
||||
|
||||
char match[3] = " X";
|
||||
if (int(axis) < 3)
|
||||
match[1] += int(axis);
|
||||
else if (axis == F)
|
||||
match[1] = 'F';
|
||||
else {
|
||||
assert(axis == E);
|
||||
match[1] = reader.extrusion_axis();
|
||||
}
|
||||
|
||||
if (this->has(axis)) {
|
||||
size_t pos = m_raw.find(match)+2;
|
||||
size_t end = m_raw.find(' ', pos+1);
|
||||
m_raw = m_raw.replace(pos, end-pos, ss.str());
|
||||
} else {
|
||||
size_t pos = m_raw.find(' ');
|
||||
if (pos == std::string::npos)
|
||||
m_raw += std::string(match) + ss.str();
|
||||
else
|
||||
m_raw = m_raw.replace(pos, 0, std::string(match) + ss.str());
|
||||
}
|
||||
m_axis[axis] = new_value;
|
||||
m_mask |= 1 << int(axis);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,54 +11,127 @@
|
|||
namespace Slic3r {
|
||||
|
||||
class GCodeReader {
|
||||
public:
|
||||
public:
|
||||
class GCodeLine {
|
||||
public:
|
||||
GCodeReader* reader;
|
||||
std::string raw;
|
||||
std::string cmd;
|
||||
std::string comment;
|
||||
std::map<char,std::string> args;
|
||||
|
||||
GCodeLine(GCodeReader* _reader) : reader(_reader) {};
|
||||
|
||||
bool has(char arg) const { return this->args.count(arg) > 0; };
|
||||
float get_float(char arg) const { return float(atof(this->args.at(arg).c_str())); };
|
||||
float new_X() const { return this->has('X') ? float(atof(this->args.at('X').c_str())) : this->reader->X; };
|
||||
float new_Y() const { return this->has('Y') ? float(atof(this->args.at('Y').c_str())) : this->reader->Y; };
|
||||
float new_Z() const { return this->has('Z') ? float(atof(this->args.at('Z').c_str())) : this->reader->Z; };
|
||||
float new_E() const { return this->has('E') ? float(atof(this->args.at('E').c_str())) : this->reader->E; };
|
||||
float new_F() const { return this->has('F') ? float(atof(this->args.at('F').c_str())) : this->reader->F; };
|
||||
float dist_X() const { return this->new_X() - this->reader->X; };
|
||||
float dist_Y() const { return this->new_Y() - this->reader->Y; };
|
||||
float dist_Z() const { return this->new_Z() - this->reader->Z; };
|
||||
float dist_E() const { return this->new_E() - this->reader->E; };
|
||||
float dist_XY() const {
|
||||
float x = this->dist_X();
|
||||
float y = this->dist_Y();
|
||||
GCodeLine() { reset(); }
|
||||
void reset() { m_mask = 0; memset(m_axis, 0, sizeof(m_axis)); m_raw.clear(); }
|
||||
|
||||
const std::string& raw() const { return m_raw; }
|
||||
const std::string cmd() const {
|
||||
const char *cmd = GCodeReader::skip_whitespaces(m_raw.c_str());
|
||||
return std::string(cmd, GCodeReader::skip_word(cmd));
|
||||
}
|
||||
const std::string comment() const
|
||||
{ size_t pos = m_raw.find(';'); return (pos == std::string::npos) ? "" : m_raw.substr(pos + 1); }
|
||||
|
||||
bool has(Axis axis) const { return (m_mask & (1 << int(axis))) != 0; }
|
||||
float value(Axis axis) const { return m_axis[axis]; }
|
||||
bool has_value(char axis, float &value) const;
|
||||
float new_Z(const GCodeReader &reader) const { return this->has(Z) ? this->z() : reader.z(); }
|
||||
float new_E(const GCodeReader &reader) const { return this->has(E) ? this->e() : reader.e(); }
|
||||
float new_F(const GCodeReader &reader) const { return this->has(F) ? this->f() : reader.f(); }
|
||||
float dist_X(const GCodeReader &reader) const { return this->has(X) ? (this->x() - reader.x()) : 0; }
|
||||
float dist_Y(const GCodeReader &reader) const { return this->has(Y) ? (this->y() - reader.y()) : 0; }
|
||||
float dist_Z(const GCodeReader &reader) const { return this->has(Z) ? (this->z() - reader.z()) : 0; }
|
||||
float dist_E(const GCodeReader &reader) const { return this->has(E) ? (this->e() - reader.e()) : 0; }
|
||||
float dist_XY(const GCodeReader &reader) const {
|
||||
float x = this->has(X) ? (this->x() - reader.x()) : 0;
|
||||
float y = this->has(Y) ? (this->y() - reader.y()) : 0;
|
||||
return sqrt(x*x + y*y);
|
||||
};
|
||||
bool extruding() const { return this->cmd == "G1" && this->dist_E() > 0; };
|
||||
bool retracting() const { return this->cmd == "G1" && this->dist_E() < 0; };
|
||||
bool travel() const { return this->cmd == "G1" && ! this->has('E'); };
|
||||
void set(char arg, std::string value);
|
||||
}
|
||||
bool cmd_is(const char *cmd_test) const {
|
||||
const char *cmd = GCodeReader::skip_whitespaces(m_raw.c_str());
|
||||
int len = strlen(cmd_test);
|
||||
return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]);
|
||||
}
|
||||
bool extruding(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) > 0; }
|
||||
bool retracting(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) < 0; }
|
||||
bool travel() const { return this->cmd_is("G1") && ! this->has(E); }
|
||||
void set(const GCodeReader &reader, const Axis axis, const float new_value, const int decimal_digits = 3);
|
||||
|
||||
bool has_x() const { return this->has(X); }
|
||||
bool has_y() const { return this->has(Y); }
|
||||
bool has_z() const { return this->has(Z); }
|
||||
bool has_e() const { return this->has(E); }
|
||||
bool has_f() const { return this->has(F); }
|
||||
float x() const { return m_axis[X]; }
|
||||
float y() const { return m_axis[Y]; }
|
||||
float z() const { return m_axis[Z]; }
|
||||
float e() const { return m_axis[E]; }
|
||||
float f() const { return m_axis[F]; }
|
||||
|
||||
private:
|
||||
std::string m_raw;
|
||||
float m_axis[NUM_AXES];
|
||||
uint32_t m_mask;
|
||||
friend class GCodeReader;
|
||||
};
|
||||
|
||||
typedef std::function<void(GCodeReader&, const GCodeLine&)> callback_t;
|
||||
|
||||
float X, Y, Z, E, F;
|
||||
bool verbose;
|
||||
callback_t callback;
|
||||
|
||||
GCodeReader() : X(0), Y(0), Z(0), E(0), F(0), verbose(false), m_extrusion_axis('E') {};
|
||||
GCodeReader() : m_verbose(false), m_extrusion_axis('E') { memset(m_position, 0, sizeof(m_position)); }
|
||||
void apply_config(const GCodeConfig &config);
|
||||
void apply_config(const DynamicPrintConfig &config);
|
||||
void parse(const std::string &gcode, callback_t callback);
|
||||
void parse_line(std::string line, callback_t callback);
|
||||
|
||||
template<typename Callback>
|
||||
void parse_buffer(const std::string &buffer, Callback callback)
|
||||
{
|
||||
const char *ptr = buffer.c_str();
|
||||
GCodeLine gline;
|
||||
while (*ptr != 0) {
|
||||
gline.reset();
|
||||
ptr = this->parse_line(ptr, gline, callback);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_buffer(const std::string &buffer)
|
||||
{ this->parse_buffer(buffer, [](GCodeReader&, const GCodeReader::GCodeLine&){}); }
|
||||
|
||||
template<typename Callback>
|
||||
const char* parse_line(const char *ptr, GCodeLine &gline, Callback &callback)
|
||||
{
|
||||
std::pair<const char*, const char*> cmd;
|
||||
const char *end = parse_line_internal(ptr, gline, cmd);
|
||||
callback(*this, gline);
|
||||
update_coordinates(gline, cmd);
|
||||
return end;
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void parse_line(const std::string &line, Callback callback)
|
||||
{ GCodeLine gline; this->parse_line(line.c_str(), gline, callback); }
|
||||
|
||||
void parse_file(const std::string &file, callback_t callback);
|
||||
|
||||
|
||||
float& x() { return m_position[X]; }
|
||||
float x() const { return m_position[X]; }
|
||||
float& y() { return m_position[Y]; }
|
||||
float y() const { return m_position[Y]; }
|
||||
float& z() { return m_position[Z]; }
|
||||
float z() const { return m_position[Z]; }
|
||||
float& e() { return m_position[E]; }
|
||||
float e() const { return m_position[E]; }
|
||||
float& f() { return m_position[F]; }
|
||||
float f() const { return m_position[F]; }
|
||||
|
||||
char extrusion_axis() const { return m_extrusion_axis; }
|
||||
|
||||
private:
|
||||
const char* parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command);
|
||||
void update_coordinates(GCodeLine &gline, std::pair<const char*, const char*> &command);
|
||||
|
||||
static bool is_whitespace(char c) { return c == ' ' || c == '\t'; }
|
||||
static bool is_end_of_line(char c) { return c == '\r' || c == '\n' || c == 0; }
|
||||
static bool is_end_of_gcode_line(char c) { return c == ';' || is_end_of_line(c); }
|
||||
static bool is_end_of_word(char c) { return is_whitespace(c) || is_end_of_gcode_line(c); }
|
||||
static const char* skip_whitespaces(const char *c) { for (; is_whitespace(*c); ++ c); return c; }
|
||||
static const char* skip_word(const char *c) { for (; ! is_end_of_word(*c); ++ c); return c; }
|
||||
|
||||
GCodeConfig m_config;
|
||||
char m_extrusion_axis;
|
||||
char m_extrusion_axis;
|
||||
float m_position[NUM_AXES];
|
||||
bool m_verbose;
|
||||
};
|
||||
|
||||
} /* namespace Slic3r */
|
||||
|
|
|
@ -7,16 +7,36 @@
|
|||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#if defined(__APPLE__) || defined(__linux) || defined(__OpenBSD__)
|
||||
#if defined(__APPLE__) || defined(__OpenBSD__)
|
||||
#include <termios.h>
|
||||
#endif
|
||||
#if __APPLE__
|
||||
#ifdef __APPLE__
|
||||
#include <sys/ioctl.h>
|
||||
#include <IOKit/serial/ioss.h>
|
||||
#endif
|
||||
#ifdef __linux
|
||||
#ifdef __linux__
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/serial.h>
|
||||
#include <fcntl.h>
|
||||
#include "/usr/include/asm-generic/ioctls.h"
|
||||
|
||||
/* The following definitions are kindly borrowed from:
|
||||
/usr/include/asm-generic/termbits.h
|
||||
Unfortunately we cannot just include that one because
|
||||
it would redefine the "struct termios" already defined
|
||||
the <termios.h> already included by Boost.ASIO. */
|
||||
#define K_NCCS 19
|
||||
struct termios2 {
|
||||
tcflag_t c_iflag;
|
||||
tcflag_t c_oflag;
|
||||
tcflag_t c_cflag;
|
||||
tcflag_t c_lflag;
|
||||
cc_t c_line;
|
||||
cc_t c_cc[K_NCCS];
|
||||
speed_t c_ispeed;
|
||||
speed_t c_ospeed;
|
||||
};
|
||||
#define BOTHER CBAUDEX
|
||||
|
||||
#endif
|
||||
|
||||
//#define DEBUG_SERIAL
|
||||
|
@ -47,26 +67,26 @@ GCodeSender::connect(std::string devname, unsigned int baud_rate)
|
|||
this->set_error_status(false);
|
||||
try {
|
||||
this->serial.open(devname);
|
||||
|
||||
this->serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::odd));
|
||||
this->serial.set_option(boost::asio::serial_port_base::character_size(boost::asio::serial_port_base::character_size(8)));
|
||||
this->serial.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
|
||||
this->serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
|
||||
this->set_baud_rate(baud_rate);
|
||||
|
||||
this->serial.close();
|
||||
this->serial.open(devname);
|
||||
this->serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
|
||||
|
||||
// set baud rate again because set_option overwrote it
|
||||
this->set_baud_rate(baud_rate);
|
||||
this->open = true;
|
||||
this->reset();
|
||||
} catch (boost::system::system_error &) {
|
||||
this->set_error_status(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::odd));
|
||||
this->serial.set_option(boost::asio::serial_port_base::character_size(boost::asio::serial_port_base::character_size(8)));
|
||||
this->serial.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
|
||||
this->serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
|
||||
this->set_baud_rate(baud_rate);
|
||||
|
||||
this->serial.close();
|
||||
this->serial.open(devname);
|
||||
this->serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
|
||||
|
||||
// set baud rate again because set_option overwrote it
|
||||
this->set_baud_rate(baud_rate);
|
||||
this->open = true;
|
||||
this->reset();
|
||||
|
||||
// a reset firmware expect line numbers to start again from 1
|
||||
this->sent = 0;
|
||||
this->last_sent.clear();
|
||||
|
@ -84,6 +104,11 @@ GCodeSender::connect(std::string devname, unsigned int baud_rate)
|
|||
boost::thread t(boost::bind(&boost::asio::io_service::run, &this->io));
|
||||
this->background_thread.swap(t);
|
||||
|
||||
// always send a M105 to check for connection because firmware might be silent on connect
|
||||
//FIXME Vojtech: This is being sent too early, leading to line number synchronization issues,
|
||||
// from which the GCodeSender never recovers.
|
||||
// this->send("M105", true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -104,27 +129,17 @@ GCodeSender::set_baud_rate(unsigned int baud_rate)
|
|||
ioctl(handle, IOSSIOSPEED, &newSpeed);
|
||||
::tcsetattr(handle, TCSANOW, &ios);
|
||||
#elif __linux
|
||||
termios ios;
|
||||
::tcgetattr(handle, &ios);
|
||||
::cfsetispeed(&ios, B38400);
|
||||
::cfsetospeed(&ios, B38400);
|
||||
::tcflush(handle, TCIFLUSH);
|
||||
::tcsetattr(handle, TCSANOW, &ios);
|
||||
|
||||
struct serial_struct ss;
|
||||
ioctl(handle, TIOCGSERIAL, &ss);
|
||||
ss.flags = (ss.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST;
|
||||
ss.custom_divisor = (ss.baud_base + (baud_rate / 2)) / baud_rate;
|
||||
//cout << "bbase " << ss.baud_base << " div " << ss.custom_divisor;
|
||||
long closestSpeed = ss.baud_base / ss.custom_divisor;
|
||||
//cout << " Closest speed " << closestSpeed << endl;
|
||||
ss.reserved_char[0] = 0;
|
||||
if (closestSpeed < baud_rate * 98 / 100 || closestSpeed > baud_rate * 102 / 100) {
|
||||
printf("Failed to set baud rate\n");
|
||||
}
|
||||
|
||||
ioctl(handle, TIOCSSERIAL, &ss);
|
||||
printf("< set_baud_rate: %u\n", baud_rate);
|
||||
termios2 ios;
|
||||
if (ioctl(handle, TCGETS2, &ios))
|
||||
printf("Error in TCGETS2: %s\n", strerror(errno));
|
||||
ios.c_ispeed = ios.c_ospeed = baud_rate;
|
||||
ios.c_cflag &= ~CBAUD;
|
||||
ios.c_cflag |= BOTHER | CLOCAL | CREAD;
|
||||
ios.c_cc[VMIN] = 1; // Minimum of characters to read, prevents eof errors when 0 bytes are read
|
||||
ios.c_cc[VTIME] = 1;
|
||||
if (ioctl(handle, TCSETS2, &ios))
|
||||
printf("Error in TCSETS2: %s\n", strerror(errno));
|
||||
|
||||
#elif __OpenBSD__
|
||||
struct termios ios;
|
||||
::tcgetattr(handle, &ios);
|
||||
|
@ -154,6 +169,7 @@ GCodeSender::disconnect()
|
|||
*/
|
||||
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs << "DISCONNECTED" << std::endl << std::flush;
|
||||
fs.close();
|
||||
#endif
|
||||
}
|
||||
|
@ -292,17 +308,20 @@ GCodeSender::on_read(const boost::system::error_code& error,
|
|||
{
|
||||
this->set_error_status(false);
|
||||
if (error) {
|
||||
#ifdef __APPLE__
|
||||
if (error.value() == 45) {
|
||||
// OS X bug: http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html
|
||||
this->do_read();
|
||||
} else {
|
||||
// printf("ERROR: [%d] %s\n", error.value(), error.message().c_str());
|
||||
// error can be true even because the serial port was closed.
|
||||
// In this case it is not a real error, so ignore.
|
||||
if (this->open) {
|
||||
this->do_close();
|
||||
this->set_error_status(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// printf("ERROR: [%d] %s\n", error.value(), error.message().c_str());
|
||||
// error can be true even because the serial port was closed.
|
||||
// In this case it is not a real error, so ignore.
|
||||
if (this->open) {
|
||||
this->do_close();
|
||||
this->set_error_status(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -339,7 +358,8 @@ GCodeSender::on_read(const boost::system::error_code& error,
|
|||
// extract the first number from line
|
||||
boost::algorithm::trim_left_if(line, !boost::algorithm::is_digit());
|
||||
size_t toresend = boost::lexical_cast<size_t>(line.substr(0, line.find_first_not_of("0123456789")));
|
||||
if (toresend >= this->sent - this->last_sent.size()) {
|
||||
++ toresend; // N is 0-based
|
||||
if (toresend >= this->sent - this->last_sent.size() && toresend < this->last_sent.size()) {
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
|
||||
|
@ -457,8 +477,8 @@ GCodeSender::do_send()
|
|||
if (line.empty()) return;
|
||||
|
||||
// compute full line
|
||||
this->sent++;
|
||||
std::string full_line = "N" + boost::lexical_cast<std::string>(this->sent) + " " + line;
|
||||
++ this->sent;
|
||||
|
||||
// calculate checksum
|
||||
int cs = 0;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,22 +2,323 @@
|
|||
#define slic3r_GCodeTimeEstimator_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "GCodeReader.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class GCodeTimeEstimator : public GCodeReader {
|
||||
//
|
||||
// Some of the algorithms used by class GCodeTimeEstimator were inpired by
|
||||
// Cura Engine's class TimeEstimateCalculator
|
||||
// https://github.com/Ultimaker/CuraEngine/blob/master/src/timeEstimate.h
|
||||
//
|
||||
class GCodeTimeEstimator
|
||||
{
|
||||
public:
|
||||
float time = 0; // in seconds
|
||||
|
||||
void parse(const std::string &gcode);
|
||||
void parse_file(const std::string &file);
|
||||
|
||||
protected:
|
||||
float acceleration = 9000;
|
||||
void _parser(GCodeReader&, const GCodeReader::GCodeLine &line);
|
||||
static float _accelerated_move(double length, double v, double acceleration);
|
||||
};
|
||||
enum EUnits : unsigned char
|
||||
{
|
||||
Millimeters,
|
||||
Inches
|
||||
};
|
||||
|
||||
enum EAxis : unsigned char
|
||||
{
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
E,
|
||||
Num_Axis
|
||||
};
|
||||
|
||||
enum EPositioningType : unsigned char
|
||||
{
|
||||
Absolute,
|
||||
Relative
|
||||
};
|
||||
|
||||
private:
|
||||
struct Axis
|
||||
{
|
||||
float position; // mm
|
||||
float max_feedrate; // mm/s
|
||||
float max_acceleration; // mm/s^2
|
||||
float max_jerk; // mm/s
|
||||
};
|
||||
|
||||
struct Feedrates
|
||||
{
|
||||
float feedrate; // mm/s
|
||||
float axis_feedrate[Num_Axis]; // mm/s
|
||||
float abs_axis_feedrate[Num_Axis]; // mm/s
|
||||
float safe_feedrate; // mm/s
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
struct State
|
||||
{
|
||||
GCodeFlavor dialect;
|
||||
EUnits units;
|
||||
EPositioningType positioning_xyz_type;
|
||||
EPositioningType positioning_e_type;
|
||||
Axis axis[Num_Axis];
|
||||
float feedrate; // mm/s
|
||||
float acceleration; // mm/s^2
|
||||
float retract_acceleration; // mm/s^2
|
||||
float additional_time; // s
|
||||
float minimum_feedrate; // mm/s
|
||||
float minimum_travel_feedrate; // mm/s
|
||||
float extrude_factor_override_percentage;
|
||||
};
|
||||
|
||||
public:
|
||||
struct Block
|
||||
{
|
||||
struct FeedrateProfile
|
||||
{
|
||||
float entry; // mm/s
|
||||
float cruise; // mm/s
|
||||
float exit; // mm/s
|
||||
};
|
||||
|
||||
struct Trapezoid
|
||||
{
|
||||
float distance; // mm
|
||||
float accelerate_until; // mm
|
||||
float decelerate_after; // mm
|
||||
FeedrateProfile feedrate;
|
||||
|
||||
float acceleration_time(float acceleration) const;
|
||||
float cruise_time() const;
|
||||
float deceleration_time(float acceleration) const;
|
||||
float cruise_distance() const;
|
||||
|
||||
// This function gives the time needed to accelerate from an initial speed to reach a final distance.
|
||||
static float acceleration_time_from_distance(float initial_feedrate, float distance, float acceleration);
|
||||
|
||||
// This function gives the final speed while accelerating at the given constant acceleration from the given initial speed along the given distance.
|
||||
static float speed_from_distance(float initial_feedrate, float distance, float acceleration);
|
||||
};
|
||||
|
||||
struct Flags
|
||||
{
|
||||
bool recalculate;
|
||||
bool nominal_length;
|
||||
};
|
||||
|
||||
Flags flags;
|
||||
|
||||
float delta_pos[Num_Axis]; // mm
|
||||
float acceleration; // mm/s^2
|
||||
float max_entry_speed; // mm/s
|
||||
float safe_feedrate; // mm/s
|
||||
|
||||
FeedrateProfile feedrate;
|
||||
Trapezoid trapezoid;
|
||||
|
||||
// Returns the length of the move covered by this block, in mm
|
||||
float move_length() const;
|
||||
|
||||
// Returns true if this block is a retract/unretract move only
|
||||
float is_extruder_only_move() const;
|
||||
|
||||
// Returns true if this block is a move with no extrusion
|
||||
float is_travel_move() const;
|
||||
|
||||
// Returns the time spent accelerating toward cruise speed, in seconds
|
||||
float acceleration_time() const;
|
||||
|
||||
// Returns the time spent at cruise speed, in seconds
|
||||
float cruise_time() const;
|
||||
|
||||
// Returns the time spent decelerating from cruise speed, in seconds
|
||||
float deceleration_time() const;
|
||||
|
||||
// Returns the distance covered at cruise speed, in mm
|
||||
float cruise_distance() const;
|
||||
|
||||
// Calculates this block's trapezoid
|
||||
void calculate_trapezoid();
|
||||
|
||||
// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the
|
||||
// acceleration within the allotted distance.
|
||||
static float max_allowable_speed(float acceleration, float target_velocity, float distance);
|
||||
|
||||
// Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the given acceleration:
|
||||
static float estimate_acceleration_distance(float initial_rate, float target_rate, float acceleration);
|
||||
|
||||
// This function gives you the point at which you must start braking (at the rate of -acceleration) if
|
||||
// you started at speed initial_rate and accelerated until this point and want to end at the final_rate after
|
||||
// a total travel of distance. This can be used to compute the intersection point between acceleration and
|
||||
// deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed)
|
||||
static float intersection_distance(float initial_rate, float final_rate, float acceleration, float distance);
|
||||
};
|
||||
|
||||
typedef std::vector<Block> BlocksList;
|
||||
|
||||
private:
|
||||
GCodeReader _parser;
|
||||
State _state;
|
||||
Feedrates _curr;
|
||||
Feedrates _prev;
|
||||
BlocksList _blocks;
|
||||
float _time; // s
|
||||
|
||||
public:
|
||||
GCodeTimeEstimator();
|
||||
|
||||
// Calculates the time estimate from the given gcode in string format
|
||||
void calculate_time_from_text(const std::string& gcode);
|
||||
|
||||
// Calculates the time estimate from the gcode contained in the file with the given filename
|
||||
void calculate_time_from_file(const std::string& file);
|
||||
|
||||
// Calculates the time estimate from the gcode contained in given list of gcode lines
|
||||
void calculate_time_from_lines(const std::vector<std::string>& gcode_lines);
|
||||
|
||||
// Adds the given gcode line
|
||||
void add_gcode_line(const std::string& gcode_line);
|
||||
|
||||
void add_gcode_block(const char *ptr);
|
||||
void add_gcode_block(const std::string &str) { this->add_gcode_block(str.c_str()); }
|
||||
|
||||
// Calculates the time estimate from the gcode lines added using add_gcode_line()
|
||||
void calculate_time();
|
||||
|
||||
// Set current position on the given axis with the given value
|
||||
void set_axis_position(EAxis axis, float position);
|
||||
|
||||
void set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec);
|
||||
void set_axis_max_acceleration(EAxis axis, float acceleration);
|
||||
void set_axis_max_jerk(EAxis axis, float jerk);
|
||||
|
||||
// Returns current position on the given axis
|
||||
float get_axis_position(EAxis axis) const;
|
||||
|
||||
float get_axis_max_feedrate(EAxis axis) const;
|
||||
float get_axis_max_acceleration(EAxis axis) const;
|
||||
float get_axis_max_jerk(EAxis axis) const;
|
||||
|
||||
void set_feedrate(float feedrate_mm_sec);
|
||||
float get_feedrate() const;
|
||||
|
||||
void set_acceleration(float acceleration_mm_sec2);
|
||||
float get_acceleration() const;
|
||||
|
||||
void set_retract_acceleration(float acceleration_mm_sec2);
|
||||
float get_retract_acceleration() const;
|
||||
|
||||
void set_minimum_feedrate(float feedrate_mm_sec);
|
||||
float get_minimum_feedrate() const;
|
||||
|
||||
void set_minimum_travel_feedrate(float feedrate_mm_sec);
|
||||
float get_minimum_travel_feedrate() const;
|
||||
|
||||
void set_extrude_factor_override_percentage(float percentage);
|
||||
float get_extrude_factor_override_percentage() const;
|
||||
|
||||
void set_dialect(GCodeFlavor dialect);
|
||||
GCodeFlavor get_dialect() const;
|
||||
|
||||
void set_units(EUnits units);
|
||||
EUnits get_units() const;
|
||||
|
||||
void set_positioning_xyz_type(EPositioningType type);
|
||||
EPositioningType get_positioning_xyz_type() const;
|
||||
|
||||
void set_positioning_e_type(EPositioningType type);
|
||||
EPositioningType get_positioning_e_type() const;
|
||||
|
||||
void add_additional_time(float timeSec);
|
||||
void set_additional_time(float timeSec);
|
||||
float get_additional_time() const;
|
||||
|
||||
void set_default();
|
||||
|
||||
// Call this method before to start adding lines using add_gcode_line() when reusing an instance of GCodeTimeEstimator
|
||||
void reset();
|
||||
|
||||
// Returns the estimated time, in seconds
|
||||
float get_time() const;
|
||||
|
||||
// Returns the estimated time, in format HHh MMm SSs
|
||||
std::string get_time_hms() const;
|
||||
|
||||
private:
|
||||
void _reset();
|
||||
void _reset_blocks();
|
||||
|
||||
// Calculates the time estimate
|
||||
void _calculate_time();
|
||||
|
||||
// Processes the given gcode line
|
||||
void _process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Move
|
||||
void _processG1(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Dwell
|
||||
void _processG4(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Units to Inches
|
||||
void _processG20(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Units to Millimeters
|
||||
void _processG21(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Move to Origin (Home)
|
||||
void _processG28(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set to Absolute Positioning
|
||||
void _processG90(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set to Relative Positioning
|
||||
void _processG91(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Position
|
||||
void _processG92(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Sleep or Conditional stop
|
||||
void _processM1(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extruder to absolute mode
|
||||
void _processM82(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extruder to relative mode
|
||||
void _processM83(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set Extruder Temperature and Wait
|
||||
void _processM109(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set max printing acceleration
|
||||
void _processM201(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set maximum feedrate
|
||||
void _processM203(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set default acceleration
|
||||
void _processM204(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Advanced settings
|
||||
void _processM205(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set extrude factor override percentage
|
||||
void _processM221(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Set allowable instantaneous speed change
|
||||
void _processM566(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Simulates firmware st_synchronize() call
|
||||
void _simulate_st_synchronize();
|
||||
|
||||
void _forward_pass();
|
||||
void _reverse_pass();
|
||||
|
||||
void _planner_forward_pass_kernel(Block& prev, Block& curr);
|
||||
void _planner_reverse_pass_kernel(Block& curr, Block& next);
|
||||
|
||||
void _recalculate_trapezoids();
|
||||
};
|
||||
|
||||
} /* namespace Slic3r */
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ std::string GCodeWriter::preamble()
|
|||
gcode << "G21 ; set units to millimeters\n";
|
||||
gcode << "G90 ; use absolute coordinates\n";
|
||||
}
|
||||
if (FLAVOR_IS(gcfRepRap) || FLAVOR_IS(gcfTeacup) || FLAVOR_IS(gcfRepetier) || FLAVOR_IS(gcfSmoothie)) {
|
||||
if (FLAVOR_IS(gcfRepRap) || FLAVOR_IS(gcfMarlin) || FLAVOR_IS(gcfTeacup) || FLAVOR_IS(gcfRepetier) || FLAVOR_IS(gcfSmoothie)) {
|
||||
if (this->config.use_relative_e_distances) {
|
||||
gcode << "M83 ; use relative distances for extrusion\n";
|
||||
} else {
|
||||
|
|
|
@ -218,6 +218,16 @@ Line::ccw(const Point& point) const
|
|||
return point.ccw(*this);
|
||||
}
|
||||
|
||||
double Line3::length() const
|
||||
{
|
||||
return a.distance_to(b);
|
||||
}
|
||||
|
||||
Vector3 Line3::vector() const
|
||||
{
|
||||
return Vector3(b.x - a.x, b.y - a.y, b.z - a.z);
|
||||
}
|
||||
|
||||
Pointf3
|
||||
Linef3::intersect_plane(double z) const
|
||||
{
|
||||
|
|
|
@ -7,10 +7,12 @@
|
|||
namespace Slic3r {
|
||||
|
||||
class Line;
|
||||
class Line3;
|
||||
class Linef3;
|
||||
class Polyline;
|
||||
class ThickLine;
|
||||
typedef std::vector<Line> Lines;
|
||||
typedef std::vector<Line3> Lines3;
|
||||
typedef std::vector<ThickLine> ThickLines;
|
||||
|
||||
class Line
|
||||
|
@ -56,6 +58,19 @@ class ThickLine : public Line
|
|||
ThickLine(Point _a, Point _b) : Line(_a, _b), a_width(0), b_width(0) {};
|
||||
};
|
||||
|
||||
class Line3
|
||||
{
|
||||
public:
|
||||
Point3 a;
|
||||
Point3 b;
|
||||
|
||||
Line3() {}
|
||||
Line3(const Point3& _a, const Point3& _b) : a(_a), b(_b) {}
|
||||
|
||||
double length() const;
|
||||
Vector3 vector() const;
|
||||
};
|
||||
|
||||
class Linef
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "Format/OBJ.hpp"
|
||||
#include "Format/PRUS.hpp"
|
||||
#include "Format/STL.hpp"
|
||||
#include "Format/3mf.hpp"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
|
@ -46,16 +47,16 @@ Model Model::read_from_file(const std::string &input_file, bool add_default_inst
|
|||
result = load_stl(input_file.c_str(), &model);
|
||||
else if (boost::algorithm::iends_with(input_file, ".obj"))
|
||||
result = load_obj(input_file.c_str(), &model);
|
||||
else if (boost::algorithm::iends_with(input_file, ".amf") ||
|
||||
boost::algorithm::iends_with(input_file, ".amf.xml"))
|
||||
result = load_amf(input_file.c_str(), &model);
|
||||
else if (!boost::algorithm::iends_with(input_file, ".zip.amf") && (boost::algorithm::iends_with(input_file, ".amf") ||
|
||||
boost::algorithm::iends_with(input_file, ".amf.xml")))
|
||||
result = load_amf(input_file.c_str(), nullptr, &model);
|
||||
#ifdef SLIC3R_PRUS
|
||||
else if (boost::algorithm::iends_with(input_file, ".prusa"))
|
||||
result = load_prus(input_file.c_str(), &model);
|
||||
#endif /* SLIC3R_PRUS */
|
||||
else
|
||||
throw std::runtime_error("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
|
||||
|
||||
|
||||
if (! result)
|
||||
throw std::runtime_error("Loading of a model file failed.");
|
||||
|
||||
|
@ -71,6 +72,33 @@ Model Model::read_from_file(const std::string &input_file, bool add_default_inst
|
|||
return model;
|
||||
}
|
||||
|
||||
Model Model::read_from_archive(const std::string &input_file, PresetBundle* bundle, bool add_default_instances)
|
||||
{
|
||||
Model model;
|
||||
|
||||
bool result = false;
|
||||
if (boost::algorithm::iends_with(input_file, ".3mf"))
|
||||
result = load_3mf(input_file.c_str(), bundle, &model);
|
||||
else if (boost::algorithm::iends_with(input_file, ".zip.amf"))
|
||||
result = load_amf(input_file.c_str(), bundle, &model);
|
||||
else
|
||||
throw std::runtime_error("Unknown file format. Input file must have .3mf or .zip.amf extension.");
|
||||
|
||||
if (!result)
|
||||
throw std::runtime_error("Loading of a model file failed.");
|
||||
|
||||
if (model.objects.empty())
|
||||
throw std::runtime_error("The supplied file couldn't be read because it's empty");
|
||||
|
||||
for (ModelObject *o : model.objects)
|
||||
o->input_file = input_file;
|
||||
|
||||
if (add_default_instances)
|
||||
model.add_default_instances();
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
ModelObject* Model::add_object()
|
||||
{
|
||||
this->objects.emplace_back(new ModelObject(this));
|
||||
|
@ -115,6 +143,23 @@ void Model::delete_object(size_t idx)
|
|||
this->objects.erase(i);
|
||||
}
|
||||
|
||||
void Model::delete_object(ModelObject* object)
|
||||
{
|
||||
if (object == nullptr)
|
||||
return;
|
||||
|
||||
for (ModelObjectPtrs::iterator it = objects.begin(); it != objects.end(); ++it)
|
||||
{
|
||||
ModelObject* obj = *it;
|
||||
if (obj == object)
|
||||
{
|
||||
delete obj;
|
||||
objects.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Model::clear_objects()
|
||||
{
|
||||
for (ModelObject *o : this->objects)
|
||||
|
@ -607,6 +652,20 @@ void ModelObject::rotate(float angle, const Axis &axis)
|
|||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
void ModelObject::transform(const float* matrix3x4)
|
||||
{
|
||||
if (matrix3x4 == nullptr)
|
||||
return;
|
||||
|
||||
for (ModelVolume* v : volumes)
|
||||
{
|
||||
v->mesh.transform(matrix3x4);
|
||||
}
|
||||
|
||||
origin_translation = Pointf3(0.0f, 0.0f, 0.0f);
|
||||
invalidate_bounding_box();
|
||||
}
|
||||
|
||||
void ModelObject::mirror(const Axis &axis)
|
||||
{
|
||||
for (ModelVolume *v : this->volumes)
|
||||
|
|
|
@ -19,6 +19,7 @@ class ModelInstance;
|
|||
class ModelMaterial;
|
||||
class ModelObject;
|
||||
class ModelVolume;
|
||||
class PresetBundle;
|
||||
|
||||
typedef std::string t_model_material_id;
|
||||
typedef std::string t_model_material_attribute;
|
||||
|
@ -119,6 +120,7 @@ public:
|
|||
void translate(coordf_t x, coordf_t y, coordf_t z);
|
||||
void scale(const Pointf3 &versor);
|
||||
void rotate(float angle, const Axis &axis);
|
||||
void transform(const float* matrix3x4);
|
||||
void mirror(const Axis &axis);
|
||||
size_t materials_count() const;
|
||||
size_t facets_count() const;
|
||||
|
@ -238,12 +240,14 @@ public:
|
|||
~Model() { this->clear_objects(); this->clear_materials(); }
|
||||
|
||||
static Model read_from_file(const std::string &input_file, bool add_default_instances = true);
|
||||
static Model read_from_archive(const std::string &input_file, PresetBundle* bundle, bool add_default_instances = true);
|
||||
|
||||
ModelObject* add_object();
|
||||
ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh);
|
||||
ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh);
|
||||
ModelObject* add_object(const ModelObject &other, bool copy_volumes = true);
|
||||
void delete_object(size_t idx);
|
||||
void delete_object(ModelObject* object);
|
||||
void clear_objects();
|
||||
|
||||
ModelMaterial* add_material(t_model_material_id material_id);
|
||||
|
|
|
@ -214,6 +214,61 @@ MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
|
|||
return results;
|
||||
}
|
||||
|
||||
void MultiPoint3::translate(double x, double y)
|
||||
{
|
||||
for (Point3& p : points)
|
||||
{
|
||||
p.translate(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiPoint3::translate(const Point& vector)
|
||||
{
|
||||
translate(vector.x, vector.y);
|
||||
}
|
||||
|
||||
double MultiPoint3::length() const
|
||||
{
|
||||
Lines3 lines = this->lines();
|
||||
double len = 0.0;
|
||||
for (const Line3& line : lines)
|
||||
{
|
||||
len += line.length();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
BoundingBox3 MultiPoint3::bounding_box() const
|
||||
{
|
||||
return BoundingBox3(points);
|
||||
}
|
||||
|
||||
bool MultiPoint3::remove_duplicate_points()
|
||||
{
|
||||
size_t j = 0;
|
||||
for (size_t i = 1; i < points.size(); ++i)
|
||||
{
|
||||
if (points[j].coincides_with(points[i]))
|
||||
{
|
||||
// Just increase index i.
|
||||
}
|
||||
else
|
||||
{
|
||||
++j;
|
||||
if (j < i)
|
||||
points[j] = points[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (++j < points.size())
|
||||
{
|
||||
points.erase(points.begin() + j, points.end());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const MultiPoint &mp)
|
||||
{
|
||||
return BoundingBox(mp.points);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
namespace Slic3r {
|
||||
|
||||
class BoundingBox;
|
||||
class BoundingBox3;
|
||||
|
||||
class MultiPoint
|
||||
{
|
||||
|
@ -79,6 +80,25 @@ public:
|
|||
static Points _douglas_peucker(const Points &points, const double tolerance);
|
||||
};
|
||||
|
||||
class MultiPoint3
|
||||
{
|
||||
public:
|
||||
Points3 points;
|
||||
|
||||
void append(const Point3& point) { this->points.push_back(point); }
|
||||
|
||||
void translate(double x, double y);
|
||||
void translate(const Point& vector);
|
||||
virtual Lines3 lines() const = 0;
|
||||
double length() const;
|
||||
bool is_valid() const { return this->points.size() >= 2; }
|
||||
|
||||
BoundingBox3 bounding_box() const;
|
||||
|
||||
// Remove exact duplicates, return true if any duplicate has been removed.
|
||||
bool remove_duplicate_points();
|
||||
};
|
||||
|
||||
extern BoundingBox get_extents(const MultiPoint &mp);
|
||||
extern BoundingBox get_extents_rotated(const std::vector<Point> &points, double angle);
|
||||
extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle);
|
||||
|
|
|
@ -483,8 +483,9 @@ PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRo
|
|||
if (path.polyline.points.empty()) {
|
||||
path.polyline.append(line.a);
|
||||
path.polyline.append(line.b);
|
||||
|
||||
flow.width = unscale(w);
|
||||
// Convert from spacing to extrusion width based on the extrusion model
|
||||
// of a square extrusion ended with semi circles.
|
||||
flow.width = unscale(w) + flow.height * (1. - 0.25 * PI);
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf(" filling %f gap\n", flow.width);
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#ifdef _MSC_VER
|
||||
#include <stdlib.h> // provides **_environ
|
||||
#else
|
||||
|
@ -520,6 +521,9 @@ namespace client
|
|||
bool just_boolean_expression = false;
|
||||
std::string error_message;
|
||||
|
||||
// Table to translate symbol tag to a human readable error message.
|
||||
static std::map<std::string, std::string> tag_to_error_message;
|
||||
|
||||
static void evaluate_full_macro(const MyContext *ctx, bool &result) { result = ! ctx->just_boolean_expression; }
|
||||
|
||||
const ConfigOption* resolve_symbol(const std::string &opt_key) const
|
||||
|
@ -619,7 +623,7 @@ namespace client
|
|||
expr<Iterator> &output)
|
||||
{
|
||||
if (opt.opt->is_vector())
|
||||
ctx->throw_exception("Referencing a scalar variable in a vector context", opt.it_range);
|
||||
ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range);
|
||||
switch (opt.opt->type()) {
|
||||
case coFloat: output.set_d(opt.opt->getFloat()); break;
|
||||
case coInt: output.set_i(opt.opt->getInt()); break;
|
||||
|
@ -644,7 +648,7 @@ namespace client
|
|||
expr<Iterator> &output)
|
||||
{
|
||||
if (opt.opt->is_scalar())
|
||||
ctx->throw_exception("Referencing a vector variable in a scalar context", opt.it_range);
|
||||
ctx->throw_exception("Referencing a scalar variable when vector is expected", opt.it_range);
|
||||
const ConfigOptionVectorBase *vec = static_cast<const ConfigOptionVectorBase*>(opt.opt);
|
||||
if (vec->empty())
|
||||
ctx->throw_exception("Indexing an empty vector variable", opt.it_range);
|
||||
|
@ -707,9 +711,16 @@ namespace client
|
|||
msg += ": ";
|
||||
msg += info.tag.substr(1);
|
||||
} else {
|
||||
// A generic error report based on the nonterminal or terminal symbol name.
|
||||
msg += ". Expecting tag ";
|
||||
msg += info.tag;
|
||||
auto it = tag_to_error_message.find(info.tag);
|
||||
if (it == tag_to_error_message.end()) {
|
||||
// A generic error report based on the nonterminal or terminal symbol name.
|
||||
msg += ". Expecting tag ";
|
||||
msg += info.tag;
|
||||
} else {
|
||||
// Use the human readable error message.
|
||||
msg += ". ";
|
||||
msg + it->second;
|
||||
}
|
||||
}
|
||||
msg += '\n';
|
||||
msg += error_line;
|
||||
|
@ -720,6 +731,31 @@ namespace client
|
|||
}
|
||||
};
|
||||
|
||||
// Table to translate symbol tag to a human readable error message.
|
||||
std::map<std::string, std::string> MyContext::tag_to_error_message = {
|
||||
{ "eoi", "Unknown syntax error" },
|
||||
{ "start", "Unknown syntax error" },
|
||||
{ "text", "Invalid text." },
|
||||
{ "text_block", "Invalid text block." },
|
||||
{ "macro", "Invalid macro." },
|
||||
{ "if_else_output", "Not an {if}{else}{endif} macro." },
|
||||
{ "switch_output", "Not a {switch} macro." },
|
||||
{ "legacy_variable_expansion", "Expecting a legacy variable expansion format" },
|
||||
{ "identifier", "Expecting an identifier." },
|
||||
{ "conditional_expression", "Expecting a conditional expression." },
|
||||
{ "logical_or_expression", "Expecting a boolean expression." },
|
||||
{ "logical_and_expression", "Expecting a boolean expression." },
|
||||
{ "equality_expression", "Expecting an expression." },
|
||||
{ "bool_expr_eval", "Expecting a boolean expression."},
|
||||
{ "relational_expression", "Expecting an expression." },
|
||||
{ "additive_expression", "Expecting an expression." },
|
||||
{ "multiplicative_expression", "Expecting an expression." },
|
||||
{ "unary_expression", "Expecting an expression." },
|
||||
{ "scalar_variable_reference", "Expecting a scalar variable reference."},
|
||||
{ "variable_reference", "Expecting a variable reference."},
|
||||
{ "regular_expression", "Expecting a regular expression."}
|
||||
};
|
||||
|
||||
// For debugging the boost::spirit parsers. Print out the string enclosed in it_range.
|
||||
template<typename Iterator>
|
||||
std::ostream& operator<<(std::ostream& os, const boost::iterator_range<Iterator> &it_range)
|
||||
|
@ -822,7 +858,8 @@ namespace client
|
|||
spirit::int_type int_;
|
||||
spirit::double_type double_;
|
||||
spirit::ascii::string_type string;
|
||||
spirit::repository::qi::iter_pos_type iter_pos;
|
||||
spirit::eoi_type eoi;
|
||||
spirit::repository::qi::iter_pos_type iter_pos;
|
||||
auto kw = spirit::repository::qi::distinct(qi::copy(alnum | '_'));
|
||||
|
||||
qi::_val_type _val;
|
||||
|
@ -843,7 +880,7 @@ namespace client
|
|||
start = eps[px::bind(&MyContext::evaluate_full_macro, _r1, _a)] >
|
||||
( eps(_a==true) > text_block(_r1) [_val=_1]
|
||||
| conditional_expression(_r1) [ px::bind(&expr<Iterator>::evaluate_boolean_to_string, _1, _val) ]
|
||||
);
|
||||
) > eoi;
|
||||
start.name("start");
|
||||
qi::on_error<qi::fail>(start, px::bind(&MyContext::process_error_message<Iterator>, _r1, _4, _1, _2, _3));
|
||||
|
||||
|
@ -866,7 +903,7 @@ namespace client
|
|||
// The macro expansion may contain numeric or string expressions, ifs and cases.
|
||||
macro =
|
||||
(kw["if"] > if_else_output(_r1) [_val = _1])
|
||||
| (kw["switch"] > switch_output(_r1) [_val = _1])
|
||||
// | (kw["switch"] > switch_output(_r1) [_val = _1])
|
||||
| additive_expression(_r1) [ px::bind(&expr<Iterator>::to_string2, _1, _val) ];
|
||||
macro.name("macro");
|
||||
|
||||
|
@ -908,14 +945,17 @@ namespace client
|
|||
conditional_expression =
|
||||
logical_or_expression(_r1) [_val = _1]
|
||||
>> -('?' > conditional_expression(_r1) > ':' > conditional_expression(_r1)) [px::bind(&expr<Iterator>::ternary_op, _val, _1, _2)];
|
||||
conditional_expression.name("conditional_expression");
|
||||
|
||||
logical_or_expression =
|
||||
logical_and_expression(_r1) [_val = _1]
|
||||
>> *( ((kw["or"] | "||") > logical_and_expression(_r1) ) [px::bind(&expr<Iterator>::logical_or, _val, _1)] );
|
||||
logical_or_expression.name("logical_or_expression");
|
||||
|
||||
logical_and_expression =
|
||||
equality_expression(_r1) [_val = _1]
|
||||
>> *( ((kw["and"] | "&&") > equality_expression(_r1) ) [px::bind(&expr<Iterator>::logical_and, _val, _1)] );
|
||||
logical_and_expression.name("logical_and_expression");
|
||||
|
||||
equality_expression =
|
||||
relational_expression(_r1) [_val = _1]
|
||||
|
@ -934,11 +974,12 @@ namespace client
|
|||
|
||||
relational_expression =
|
||||
additive_expression(_r1) [_val = _1]
|
||||
>> *( (lit('<') > additive_expression(_r1) ) [px::bind(&expr<Iterator>::lower, _val, _1)]
|
||||
| (lit('>') > additive_expression(_r1) ) [px::bind(&expr<Iterator>::greater, _val, _1)]
|
||||
| ("<=" > additive_expression(_r1) ) [px::bind(&expr<Iterator>::leq, _val, _1)]
|
||||
>> *( ("<=" > additive_expression(_r1) ) [px::bind(&expr<Iterator>::leq, _val, _1)]
|
||||
| (">=" > additive_expression(_r1) ) [px::bind(&expr<Iterator>::geq, _val, _1)]
|
||||
| (lit('<') > additive_expression(_r1) ) [px::bind(&expr<Iterator>::lower, _val, _1)]
|
||||
| (lit('>') > additive_expression(_r1) ) [px::bind(&expr<Iterator>::greater, _val, _1)]
|
||||
);
|
||||
relational_expression.name("relational_expression");
|
||||
|
||||
additive_expression =
|
||||
multiplicative_expression(_r1) [_val = _1]
|
||||
|
@ -1020,7 +1061,7 @@ namespace client
|
|||
debug(text_block);
|
||||
debug(macro);
|
||||
debug(if_else_output);
|
||||
debug(switch_output);
|
||||
// debug(switch_output);
|
||||
debug(legacy_variable_expansion);
|
||||
debug(identifier);
|
||||
debug(conditional_expression);
|
||||
|
@ -1079,7 +1120,7 @@ namespace client
|
|||
qi::rule<Iterator, OptWithPos<Iterator>(const MyContext*), spirit::ascii::space_type> variable_reference;
|
||||
|
||||
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, bool>, spirit::ascii::space_type> if_else_output;
|
||||
qi::rule<Iterator, std::string(const MyContext*), qi::locals<expr<Iterator>, bool, std::string>, spirit::ascii::space_type> switch_output;
|
||||
// qi::rule<Iterator, std::string(const MyContext*), qi::locals<expr<Iterator>, bool, std::string>, spirit::ascii::space_type> switch_output;
|
||||
|
||||
qi::symbols<char> keywords;
|
||||
};
|
||||
|
@ -1102,7 +1143,7 @@ static std::string process_macro(const std::string &templ, client::MyContext &co
|
|||
// Accumulator for the processed template.
|
||||
std::string output;
|
||||
bool res = phrase_parse(iter, end, macro_processor_instance(&context), space, output);
|
||||
if (! context.error_message.empty()) {
|
||||
if (!context.error_message.empty()) {
|
||||
if (context.error_message.back() != '\n' && context.error_message.back() != '\r')
|
||||
context.error_message += '\n';
|
||||
throw std::runtime_error(context.error_message);
|
||||
|
|
|
@ -14,14 +14,17 @@ class Line;
|
|||
class Linef;
|
||||
class MultiPoint;
|
||||
class Point;
|
||||
class Point3;
|
||||
class Pointf;
|
||||
class Pointf3;
|
||||
typedef Point Vector;
|
||||
typedef Point3 Vector3;
|
||||
typedef Pointf Vectorf;
|
||||
typedef Pointf3 Vectorf3;
|
||||
typedef std::vector<Point> Points;
|
||||
typedef std::vector<Point*> PointPtrs;
|
||||
typedef std::vector<const Point*> PointConstPtrs;
|
||||
typedef std::vector<Point3> Points3;
|
||||
typedef std::vector<Pointf> Pointfs;
|
||||
typedef std::vector<Pointf3> Pointf3s;
|
||||
|
||||
|
@ -32,8 +35,7 @@ public:
|
|||
coord_t x;
|
||||
coord_t y;
|
||||
Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_y) {};
|
||||
Point(int _x, int _y): x(_x), y(_y) {};
|
||||
Point(long long _x, long long _y): x(coord_t(_x)), y(coord_t(_y)) {}; // for Clipper
|
||||
Point(int64_t _x, int64_t _y): x(coord_t(_x)), y(coord_t(_y)) {}; // for Clipper
|
||||
Point(double x, double y);
|
||||
static Point new_scale(coordf_t x, coordf_t y) { return Point(coord_t(scale_(x)), coord_t(scale_(y))); }
|
||||
|
||||
|
@ -186,10 +188,11 @@ public:
|
|||
static Point3 new_scale(coordf_t x, coordf_t y, coordf_t z) { return Point3(coord_t(scale_(x)), coord_t(scale_(y)), coord_t(scale_(z))); }
|
||||
bool operator==(const Point3 &rhs) const { return this->x == rhs.x && this->y == rhs.y && this->z == rhs.z; }
|
||||
bool operator!=(const Point3 &rhs) const { return ! (*this == rhs); }
|
||||
bool coincides_with(const Point3& rhs) const { return this->x == rhs.x && this->y == rhs.y && this->z == rhs.z; }
|
||||
private:
|
||||
// Hide the following inherited methods:
|
||||
bool operator==(const Point &rhs);
|
||||
bool operator!=(const Point &rhs);
|
||||
bool operator==(const Point &rhs) const;
|
||||
bool operator!=(const Point &rhs) const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream &stm, const Pointf &pointf);
|
||||
|
@ -244,6 +247,7 @@ public:
|
|||
static Pointf3 new_unscale(coord_t x, coord_t y, coord_t z) {
|
||||
return Pointf3(unscale(x), unscale(y), unscale(z));
|
||||
};
|
||||
static Pointf3 new_unscale(const Point3& p) { return Pointf3(unscale(p.x), unscale(p.y), unscale(p.z)); }
|
||||
void scale(double factor);
|
||||
void translate(const Vectorf3 &vector);
|
||||
void translate(double x, double y, double z);
|
||||
|
@ -256,10 +260,23 @@ public:
|
|||
|
||||
private:
|
||||
// Hide the following inherited methods:
|
||||
bool operator==(const Pointf &rhs);
|
||||
bool operator!=(const Pointf &rhs);
|
||||
bool operator==(const Pointf &rhs) const;
|
||||
bool operator!=(const Pointf &rhs) const;
|
||||
};
|
||||
|
||||
inline Pointf3 operator+(const Pointf3& p1, const Pointf3& p2) { return Pointf3(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z); }
|
||||
inline Pointf3 operator-(const Pointf3& p1, const Pointf3& p2) { return Pointf3(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z); }
|
||||
inline Pointf3 operator-(const Pointf3& p) { return Pointf3(-p.x, -p.y, -p.z); }
|
||||
inline Pointf3 operator*(double scalar, const Pointf3& p) { return Pointf3(scalar * p.x, scalar * p.y, scalar * p.z); }
|
||||
inline Pointf3 operator*(const Pointf3& p, double scalar) { return Pointf3(scalar * p.x, scalar * p.y, scalar * p.z); }
|
||||
inline Pointf3 cross(const Pointf3& v1, const Pointf3& v2) { return Pointf3(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x); }
|
||||
inline coordf_t dot(const Pointf3& v1, const Pointf3& v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; }
|
||||
inline Pointf3 normalize(const Pointf3& v)
|
||||
{
|
||||
coordf_t len = ::sqrt(sqr(v.x) + sqr(v.y) + sqr(v.z));
|
||||
return (len != 0.0) ? 1.0 / len * v : Pointf3(0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
template<typename TO> inline TO convert_to(const Point &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y)); }
|
||||
template<typename TO> inline TO convert_to(const Pointf &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y)); }
|
||||
template<typename TO> inline TO convert_to(const Point3 &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y), typename TO::coord_type(src.z)); }
|
||||
|
@ -271,23 +288,6 @@ template<typename TO> inline TO convert_to(const Pointf3 &src) { return TO(typen
|
|||
#include <boost/version.hpp>
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
namespace boost { namespace polygon {
|
||||
template <>
|
||||
struct geometry_concept<coord_t> { typedef coordinate_concept type; };
|
||||
|
||||
/* Boost.Polygon already defines a specialization for coordinate_traits<long> as of 1.60:
|
||||
https://github.com/boostorg/polygon/commit/0ac7230dd1f8f34cb12b86c8bb121ae86d3d9b97 */
|
||||
#if BOOST_VERSION < 106000
|
||||
template <>
|
||||
struct coordinate_traits<coord_t> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef long double area_type;
|
||||
typedef long long manhattan_area_type;
|
||||
typedef unsigned long long unsigned_area_type;
|
||||
typedef long long coordinate_difference;
|
||||
typedef long double coordinate_distance;
|
||||
};
|
||||
#endif
|
||||
|
||||
template <>
|
||||
struct geometry_concept<Slic3r::Point> { typedef point_concept type; };
|
||||
|
||||
|
|
|
@ -24,6 +24,12 @@ public:
|
|||
explicit Polygon(const Points &points): MultiPoint(points) {}
|
||||
Polygon(const Polygon &other) : MultiPoint(other.points) {}
|
||||
Polygon(Polygon &&other) : MultiPoint(std::move(other.points)) {}
|
||||
static Polygon new_scale(std::vector<Pointf> points) {
|
||||
Points int_points;
|
||||
for (auto pt : points)
|
||||
int_points.push_back(Point::new_scale(pt.x, pt.y));
|
||||
return Polygon(int_points);
|
||||
}
|
||||
Polygon& operator=(const Polygon &other) { points = other.points; return *this; }
|
||||
Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; }
|
||||
|
||||
|
|
|
@ -278,4 +278,18 @@ ThickPolyline::reverse()
|
|||
std::swap(this->endpoints.first, this->endpoints.second);
|
||||
}
|
||||
|
||||
Lines3 Polyline3::lines() const
|
||||
{
|
||||
Lines3 lines;
|
||||
if (points.size() >= 2)
|
||||
{
|
||||
lines.reserve(points.size() - 1);
|
||||
for (Points3::const_iterator it = points.begin(); it != points.end() - 1; ++it)
|
||||
{
|
||||
lines.emplace_back(*it, *(it + 1));
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,14 @@ public:
|
|||
Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {}
|
||||
Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
|
||||
Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; }
|
||||
static Polyline new_scale(std::vector<Pointf> points) {
|
||||
Polyline pl;
|
||||
Points int_points;
|
||||
for (auto pt : points)
|
||||
int_points.push_back(Point::new_scale(pt.x, pt.y));
|
||||
pl.append(int_points);
|
||||
return pl;
|
||||
}
|
||||
|
||||
void append(const Point &point) { this->points.push_back(point); }
|
||||
void append(const Points &src) { this->append(src.begin(), src.end()); }
|
||||
|
@ -129,6 +137,14 @@ class ThickPolyline : public Polyline {
|
|||
void reverse();
|
||||
};
|
||||
|
||||
class Polyline3 : public MultiPoint3
|
||||
{
|
||||
public:
|
||||
virtual Lines3 lines() const;
|
||||
};
|
||||
|
||||
typedef std::vector<Polyline3> Polylines3;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -568,8 +568,8 @@ std::string Print::validate() const
|
|||
if (std::abs(dmr - 0.4) > EPSILON)
|
||||
return "The Wipe Tower is currently only supported for the 0.4mm nozzle diameter.";
|
||||
#endif
|
||||
if (this->config.gcode_flavor != gcfRepRap)
|
||||
return "The Wipe Tower is currently only supported for the RepRap (Marlin / Sprinter) G-code flavor.";
|
||||
if (this->config.gcode_flavor != gcfRepRap && this->config.gcode_flavor != gcfMarlin)
|
||||
return "The Wipe Tower is currently only supported for the Marlin and RepRap/Sprinter G-code flavors.";
|
||||
if (! this->config.use_relative_e_distances)
|
||||
return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).";
|
||||
SlicingParameters slicing_params0 = this->objects.front()->slicing_parameters();
|
||||
|
@ -998,7 +998,8 @@ void Print::_make_wipe_tower()
|
|||
// Find the position in this->objects.first()->support_layers to insert these new support layers.
|
||||
double wipe_tower_new_layer_print_z_first = m_tool_ordering.layer_tools()[idx_begin].print_z;
|
||||
SupportLayerPtrs::iterator it_layer = this->objects.front()->support_layers.begin();
|
||||
for (; (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer) ;
|
||||
SupportLayerPtrs::iterator it_end = this->objects.front()->support_layers.end();
|
||||
for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer);
|
||||
// Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer.
|
||||
for (size_t i = idx_begin; i < idx_end; ++ i) {
|
||||
ToolOrdering::LayerTools < = const_cast<ToolOrdering::LayerTools&>(m_tool_ordering.layer_tools()[i]);
|
||||
|
@ -1006,9 +1007,9 @@ void Print::_make_wipe_tower()
|
|||
break;
|
||||
lt.has_support = true;
|
||||
// Insert the new support layer.
|
||||
//FIXME the support layer ID is duplicated, but Vojtech hopes it is not being used anywhere anyway.
|
||||
double height = lt.print_z - m_tool_ordering.layer_tools()[i-1].print_z;
|
||||
auto *new_layer = new SupportLayer((*it_layer)->id(), this->objects.front(),
|
||||
//FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway.
|
||||
auto *new_layer = new SupportLayer(size_t(-1), this->objects.front(),
|
||||
height, lt.print_z, lt.print_z - 0.5 * height);
|
||||
it_layer = this->objects.front()->support_layers.insert(it_layer, new_layer);
|
||||
++ it_layer;
|
||||
|
|
|
@ -233,8 +233,9 @@ public:
|
|||
PrintRegionPtrs regions;
|
||||
PlaceholderParser placeholder_parser;
|
||||
// TODO: status_cb
|
||||
std::string estimated_print_time;
|
||||
double total_used_filament, total_extruded_volume, total_cost, total_weight;
|
||||
std::map<size_t,float> filament_stats;
|
||||
std::map<size_t, float> filament_stats;
|
||||
PrintState<PrintStep, psCount> state;
|
||||
|
||||
// ordered collections of extrusion paths to build skirt loops and brim
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -23,7 +23,8 @@
|
|||
namespace Slic3r {
|
||||
|
||||
enum GCodeFlavor {
|
||||
gcfRepRap, gcfTeacup, gcfMakerWare, gcfSailfish, gcfMach3, gcfMachinekit, gcfNoExtrusion, gcfSmoothie, gcfRepetier,
|
||||
gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit,
|
||||
gcfSmoothie, gcfNoExtrusion,
|
||||
};
|
||||
|
||||
enum InfillPattern {
|
||||
|
@ -50,6 +51,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_
|
|||
keys_map["repetier"] = gcfRepetier;
|
||||
keys_map["teacup"] = gcfTeacup;
|
||||
keys_map["makerware"] = gcfMakerWare;
|
||||
keys_map["marlin"] = gcfMarlin;
|
||||
keys_map["sailfish"] = gcfSailfish;
|
||||
keys_map["smoothie"] = gcfSmoothie;
|
||||
keys_map["mach3"] = gcfMach3;
|
||||
|
|
|
@ -197,10 +197,11 @@ TriangleMesh::repair() {
|
|||
stl_fill_holes(&stl);
|
||||
stl_clear_error(&stl);
|
||||
}
|
||||
|
||||
// normal_directions
|
||||
stl_fix_normal_directions(&stl);
|
||||
|
||||
|
||||
// commenting out the following call fixes: #574, #413, #269, #262, #259, #230, #228, #206
|
||||
// // normal_directions
|
||||
// stl_fix_normal_directions(&stl);
|
||||
|
||||
// normal_values
|
||||
stl_fix_normal_values(&stl);
|
||||
|
||||
|
@ -374,6 +375,15 @@ void TriangleMesh::mirror_z()
|
|||
this->mirror(Z);
|
||||
}
|
||||
|
||||
void TriangleMesh::transform(const float* matrix3x4)
|
||||
{
|
||||
if (matrix3x4 == nullptr)
|
||||
return;
|
||||
|
||||
stl_transform(&stl, const_cast<float*>(matrix3x4));
|
||||
stl_invalidate_shared_vertices(&stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::align_to_origin()
|
||||
{
|
||||
this->translate(
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
void mirror_x();
|
||||
void mirror_y();
|
||||
void mirror_z();
|
||||
void transform(const float* matrix3x4);
|
||||
void align_to_origin();
|
||||
void rotate(double angle, Point* center);
|
||||
TriangleMeshPtrs split() const;
|
||||
|
|
|
@ -20,21 +20,49 @@ void set_resources_dir(const std::string &path);
|
|||
// Return a full path to the resources directory.
|
||||
const std::string& resources_dir();
|
||||
|
||||
// Set a path with GUI localization files.
|
||||
void set_local_dir(const std::string &path);
|
||||
// Return a full path to the localization directory.
|
||||
const std::string& localization_dir();
|
||||
|
||||
// Set a path with preset files.
|
||||
void set_data_dir(const std::string &path);
|
||||
// Return a full path to the GUI resource files.
|
||||
const std::string& data_dir();
|
||||
|
||||
extern std::string encode_path(const char *src);
|
||||
// A special type for strings encoded in the local Windows 8-bit code page.
|
||||
// This type is only needed for Perl bindings to relay to Perl that the string is raw, not UTF-8 encoded.
|
||||
typedef std::string local_encoded_string;
|
||||
|
||||
// Convert an UTF-8 encoded string into local coding.
|
||||
// On Windows, the UTF-8 string is converted to a local 8-bit code page.
|
||||
// On OSX and Linux, this function does no conversion and returns a copy of the source string.
|
||||
extern local_encoded_string encode_path(const char *src);
|
||||
extern std::string decode_path(const char *src);
|
||||
extern std::string normalize_utf8_nfc(const char *src);
|
||||
|
||||
// File path / name / extension splitting utilities, working with UTF-8,
|
||||
// to be published to Perl.
|
||||
namespace PerlUtils {
|
||||
// Get a file name including the extension.
|
||||
extern std::string path_to_filename(const char *src);
|
||||
// Get a file name without the extension.
|
||||
extern std::string path_to_stem(const char *src);
|
||||
// Get just the extension.
|
||||
extern std::string path_to_extension(const char *src);
|
||||
// Get a directory without the trailing slash.
|
||||
extern std::string path_to_parent_path(const char *src);
|
||||
};
|
||||
|
||||
// Timestamp formatted for header_slic3r_generated().
|
||||
extern std::string timestamp_str();
|
||||
// Standard "generated by Slic3r version xxx timestamp xxx" header string,
|
||||
// to be placed at the top of Slic3r generated files.
|
||||
inline std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_FORK_NAME " " SLIC3R_VERSION " " ) + timestamp_str(); }
|
||||
|
||||
// Encode a file into a multi-part HTTP response with a given boundary.
|
||||
std::string octoprint_encode_file_send_request_content(const char *path, bool select, bool print, const char *boundary);
|
||||
|
||||
// Compute the next highest power of 2 of 32-bit v
|
||||
// http://graphics.stanford.edu/~seander/bithacks.html
|
||||
template<typename T>
|
||||
|
|
|
@ -14,11 +14,11 @@
|
|||
#include <boost/thread.hpp>
|
||||
|
||||
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
|
||||
#define SLIC3R_VERSION "1.38.4"
|
||||
#define SLIC3R_VERSION "1.39.0"
|
||||
#define SLIC3R_BUILD "UNKNOWN"
|
||||
|
||||
typedef long coord_t;
|
||||
typedef double coordf_t;
|
||||
typedef int32_t coord_t;
|
||||
typedef double coordf_t;
|
||||
|
||||
//FIXME This epsilon value is used for many non-related purposes:
|
||||
// For a threshold of a squared Euclidean distance,
|
||||
|
@ -102,7 +102,7 @@ inline std::string debug_out_path(const char *name, ...)
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
enum Axis { X=0, Y, Z };
|
||||
enum Axis { X=0, Y, Z, E, F, NUM_AXES };
|
||||
|
||||
template <class T>
|
||||
inline void append_to(std::vector<T> &dst, const std::vector<T> &src)
|
||||
|
@ -163,11 +163,11 @@ static inline T clamp(const T low, const T high, const T value)
|
|||
return std::max(low, std::min(high, value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T lerp(const T a, const T b, const T t)
|
||||
template <typename T, typename Number>
|
||||
static inline T lerp(const T& a, const T& b, Number t)
|
||||
{
|
||||
assert(t >= T(-EPSILON) && t <= T(1.+EPSILON));
|
||||
return (1. - t) * a + t * b;
|
||||
assert((t >= Number(-EPSILON)) && (t <= Number(1) + Number(EPSILON)));
|
||||
return (Number(1) - t) * a + t * b;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/date_time/local_time/local_time.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/nowide/integration/filesystem.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
|
@ -101,6 +103,18 @@ const std::string& resources_dir()
|
|||
return g_resources_dir;
|
||||
}
|
||||
|
||||
static std::string g_local_dir;
|
||||
|
||||
void set_local_dir(const std::string &dir)
|
||||
{
|
||||
g_local_dir = dir;
|
||||
}
|
||||
|
||||
const std::string& localization_dir()
|
||||
{
|
||||
return g_local_dir;
|
||||
}
|
||||
|
||||
static std::string g_data_dir;
|
||||
|
||||
void set_data_dir(const std::string &dir)
|
||||
|
@ -235,6 +249,17 @@ std::string normalize_utf8_nfc(const char *src)
|
|||
return boost::locale::normalize(src, boost::locale::norm_nfc, locale_utf8);
|
||||
}
|
||||
|
||||
namespace PerlUtils {
|
||||
// Get a file name including the extension.
|
||||
std::string path_to_filename(const char *src) { return boost::filesystem::path(src).filename().string(); }
|
||||
// Get a file name without the extension.
|
||||
std::string path_to_stem(const char *src) { return boost::filesystem::path(src).stem().string(); }
|
||||
// Get just the extension.
|
||||
std::string path_to_extension(const char *src) { return boost::filesystem::path(src).extension().string(); }
|
||||
// Get a directory without the trailing slash.
|
||||
std::string path_to_parent_path(const char *src) { return boost::filesystem::path(src).parent_path().string(); }
|
||||
};
|
||||
|
||||
std::string timestamp_str()
|
||||
{
|
||||
const auto now = boost::posix_time::second_clock::local_time();
|
||||
|
@ -247,4 +272,31 @@ std::string timestamp_str()
|
|||
return buf;
|
||||
}
|
||||
|
||||
std::string octoprint_encode_file_send_request_content(const char *cpath, bool select, bool print, const char *boundary)
|
||||
{
|
||||
// Read the complete G-code string into a string buffer.
|
||||
// It will throw if the file cannot be open or read.
|
||||
std::stringstream str_stream;
|
||||
{
|
||||
boost::nowide::ifstream ifs(cpath);
|
||||
str_stream << ifs.rdbuf();
|
||||
}
|
||||
|
||||
boost::filesystem::path path(cpath);
|
||||
std::string request = boundary + '\n';
|
||||
request += "Content-Disposition: form-data; name=\"";
|
||||
request += path.stem().string() + "\"; filename=\"" + path.filename().string() + "\"\n";
|
||||
request += "Content-Type: application/octet-stream\n\n";
|
||||
request += str_stream.str();
|
||||
request += boundary + '\n';
|
||||
request += "Content-Disposition: form-data; name=\"select\"\n\n";
|
||||
request += select ? "true\n" : "false\n";
|
||||
request += boundary + '\n';
|
||||
request += "Content-Disposition: form-data; name=\"print\"\n\n";
|
||||
request += print ? "true\n" : "false\n";
|
||||
request += boundary + '\n';
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue