1
0
mirror of https://github.com/FULU-Foundation/OrcaSlicer-bambulab.git synced 2026-07-27 10:35:51 +00:00

Merge branch 'master' of https://github.com/prusa3d/Slic3r into time_estimate

This commit is contained in:
Enrico Turri
2018-02-16 08:45:38 +01:00
103 changed files with 26308 additions and 3750 deletions
-29
View File
@@ -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)
+33 -3
View File
@@ -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>
+28 -18
View File
@@ -324,7 +324,7 @@ void ConfigBase::setenv_()
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);
}
@@ -349,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 ";
@@ -365,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, '=');
@@ -402,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;
}
@@ -411,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;
+9 -1
View File
@@ -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;
+13 -8
View File
@@ -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(); }
File diff suppressed because it is too large Load Diff
+19
View 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_ */
+209 -56
View File
@@ -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;
}
+7 -3
View File
@@ -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
+1 -1
View File
@@ -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)
+102 -25
View File
@@ -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>
@@ -348,10 +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);
@@ -363,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);
@@ -389,12 +392,14 @@ void GCode::do_export(Print *print, const char *path)
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();
@@ -402,6 +407,15 @@ void GCode::_do_export(Print &print, FILE *file)
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;
@@ -762,7 +776,7 @@ void GCode::_do_export(Print &print, FILE *file)
_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%
@@ -802,14 +816,15 @@ void GCode::_do_export(Print &print, FILE *file)
// Append full config.
_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")
_write_format(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)
@@ -1301,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)
@@ -1372,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[1024];
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);
@@ -1448,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";
};
}
@@ -1998,13 +2028,16 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
return gcode;
}
void GCode::_write(FILE* file, const char *what, size_t size)
void GCode::_write(FILE* file, const char *what)
{
if (size > 0) {
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(what, 1, size, file);
fwrite(gcode, 1, ::strlen(gcode), file);
// updates time estimator and gcode lines vector
m_time_estimator.add_gcode_block(what);
m_time_estimator.add_gcode_block(gcode);
}
}
@@ -2038,7 +2071,8 @@ void GCode::_write_format(FILE* file, const char* format, ...)
char *bufptr = buffer_dynamic ? (char*)malloc(buflen) : buffer;
int res = ::vsnprintf(bufptr, buflen, format, args);
if (res > 0)
_write(file, bufptr, res);
_write(file, bufptr);
if (buffer_dynamic)
free(bufptr);
@@ -2123,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()))
+23 -7
View File
@@ -17,6 +17,7 @@
#include "GCode/WipeTower.hpp"
#include "GCodeTimeEstimator.hpp"
#include "EdgeGrid.hpp"
#include "GCode/Analyzer.hpp"
#include <memory>
#include <string>
@@ -25,6 +26,7 @@ namespace Slic3r {
// Forward declarations.
class GCode;
class GCodePreviewData;
class AvoidCrossingPerimeters {
public:
@@ -118,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()))
@@ -132,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; }
@@ -155,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
@@ -241,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;
@@ -256,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;
@@ -277,9 +290,12 @@ protected:
// 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(), what.size()); }
void _write(FILE* file, const char *what, size_t size);
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.
File diff suppressed because it is too large Load Diff
+203 -128
View File
@@ -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
View 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
View 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_ */
@@ -6,6 +6,8 @@
#include <iostream>
#include <vector>
#include "Analyzer.hpp"
#if defined(__linux) || defined(__GNUC__ )
#include <strings.h>
#endif /* __linux */
@@ -419,6 +421,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
// Increase the extruder driver current to allow fast ramming.
.set_extruder_trimpot(750);
// 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) {
float y_end = 0.f;
for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) {
@@ -554,6 +561,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);
@@ -637,6 +649,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) {
// Prime the extruder 10*m_perimeter_width left along the vertical edge of the wipe tower.
writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0),
+70 -50
View File
@@ -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;
+10
View File
@@ -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
{
+15
View File
@@ -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:
+63 -4
View File
@@ -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)
+4
View File
@@ -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);
+55
View File
@@ -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);
+20
View File
@@ -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);
+23 -23
View File
@@ -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; };
+6
View File
@@ -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; }
+14
View File
@@ -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;
}
}
+16
View File
@@ -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
+4 -3
View File
@@ -997,7 +997,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 &lt = const_cast<ToolOrdering::LayerTools&>(m_tool_ordering.layer_tools()[i]);
@@ -1005,9 +1006,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;
File diff suppressed because it is too large Load Diff
+9
View File
@@ -375,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(
+1
View File
@@ -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;
+5
View File
@@ -20,6 +20,11 @@ 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.
+6 -6
View File
@@ -17,8 +17,8 @@
#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,
@@ -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
+12
View File
@@ -103,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)