Merge branch 'master' into lm_tm_hollowing

This commit is contained in:
Lukas Matena 2020-01-21 13:00:07 +01:00
commit 63b0eec5a9
104 changed files with 3378 additions and 2140 deletions

View file

@ -289,13 +289,13 @@ public:
throw std::runtime_error("ConfigOptionVector::set_at(): Assigning an incompatible type");
}
T& get_at(size_t i)
const T& get_at(size_t i) const
{
assert(! this->values.empty());
return (i < this->values.size()) ? this->values[i] : this->values.front();
}
const T& get_at(size_t i) const { return const_cast<ConfigOptionVector<T>*>(this)->get_at(i); }
T& get_at(size_t i) { return const_cast<T&>(std::as_const(*this).get_at(i)); }
// Resize this vector by duplicating the /*last*/first value.
// If the current vector is empty, the default value is used instead.
@ -500,7 +500,7 @@ public:
if (NULLABLE)
this->values.push_back(nil_value());
else
std::runtime_error("Deserializing nil into a non-nullable object");
throw std::runtime_error("Deserializing nil into a non-nullable object");
} else {
std::istringstream iss(item_str);
double value;
@ -525,9 +525,9 @@ protected:
if (NULLABLE)
ss << "nil";
else
std::runtime_error("Serializing NaN");
throw std::runtime_error("Serializing NaN");
} else
std::runtime_error("Serializing invalid number");
throw std::runtime_error("Serializing invalid number");
}
static bool vectors_equal(const std::vector<double> &v1, const std::vector<double> &v2) {
if (NULLABLE) {
@ -646,7 +646,7 @@ public:
if (NULLABLE)
this->values.push_back(nil_value());
else
std::runtime_error("Deserializing nil into a non-nullable object");
throw std::runtime_error("Deserializing nil into a non-nullable object");
} else {
std::istringstream iss(item_str);
int value;
@ -663,7 +663,7 @@ private:
if (NULLABLE)
ss << "nil";
else
std::runtime_error("Serializing NaN");
throw std::runtime_error("Serializing NaN");
} else
ss << v;
}
@ -1126,7 +1126,7 @@ public:
if (NULLABLE)
this->values.push_back(nil_value());
else
std::runtime_error("Deserializing nil into a non-nullable object");
throw std::runtime_error("Deserializing nil into a non-nullable object");
} else
this->values.push_back(item_str.compare("1") == 0);
}
@ -1139,7 +1139,7 @@ protected:
if (NULLABLE)
ss << "nil";
else
std::runtime_error("Serializing NaN");
throw std::runtime_error("Serializing NaN");
} else
ss << (v ? "1" : "0");
}
@ -1638,7 +1638,7 @@ class DynamicConfig : public virtual ConfigBase
public:
DynamicConfig() {}
DynamicConfig(const DynamicConfig &rhs) { *this = rhs; }
DynamicConfig(DynamicConfig &&rhs) : options(std::move(rhs.options)) { rhs.options.clear(); }
DynamicConfig(DynamicConfig &&rhs) noexcept : options(std::move(rhs.options)) { rhs.options.clear(); }
explicit DynamicConfig(const ConfigBase &rhs, const t_config_option_keys &keys);
explicit DynamicConfig(const ConfigBase& rhs) : DynamicConfig(rhs, rhs.keys()) {}
virtual ~DynamicConfig() override { clear(); }
@ -1656,7 +1656,7 @@ public:
// Move a content of one DynamicConfig to another DynamicConfig.
// If rhs.def() is not null, then it has to be equal to this->def().
DynamicConfig& operator=(DynamicConfig &&rhs)
DynamicConfig& operator=(DynamicConfig &&rhs) noexcept
{
assert(this->def() == nullptr || this->def() == rhs.def());
this->clear();

View file

@ -19,7 +19,7 @@ class ExPolygon
public:
ExPolygon() {}
ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {}
ExPolygon(ExPolygon &&other) : contour(std::move(other.contour)), holes(std::move(other.holes)) {}
ExPolygon(ExPolygon &&other) noexcept : contour(std::move(other.contour)), holes(std::move(other.holes)) {}
explicit ExPolygon(const Polygon &contour) : contour(contour) {}
explicit ExPolygon(Polygon &&contour) : contour(std::move(contour)) {}
explicit ExPolygon(const Points &contour) : contour(contour) {}
@ -32,7 +32,7 @@ public:
ExPolygon(std::initializer_list<Point> contour, std::initializer_list<Point> hole) : contour(contour), holes({ hole }) {}
ExPolygon& operator=(const ExPolygon &other) { contour = other.contour; holes = other.holes; return *this; }
ExPolygon& operator=(ExPolygon &&other) { contour = std::move(other.contour); holes = std::move(other.holes); return *this; }
ExPolygon& operator=(ExPolygon &&other) noexcept { contour = std::move(other.contour); holes = std::move(other.holes); return *this; }
Polygon contour;
Polygons holes;

View file

@ -48,9 +48,6 @@ public:
double retract_length_toolchange() const;
double retract_restart_extra_toolchange() const;
// Constructor for a key object, to be used by the stdlib search functions.
static Extruder key(unsigned int id) { return Extruder(id); }
private:
// Private constructor to create a key for a search in std::set.
Extruder(unsigned int id) : m_id(id) {}

View file

@ -6,6 +6,26 @@
namespace Slic3r {
void filter_by_extrusion_role_in_place(ExtrusionEntitiesPtr &extrusion_entities, ExtrusionRole role)
{
if (role != erMixed) {
auto first = extrusion_entities.begin();
auto last = extrusion_entities.end();
auto result = first;
while (first != last) {
// The caller wants only paths with a specific extrusion role.
auto role2 = (*first)->role();
if (role != role2) {
// This extrusion entity does not match the role asked.
assert(role2 != erMixed);
*result = *first;
++ result;
}
++ first;
}
}
}
ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths)
: no_sort(false)
{
@ -74,31 +94,16 @@ void ExtrusionEntityCollection::remove(size_t i)
this->entities.erase(this->entities.begin() + i);
}
ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const Point &start_near, ExtrusionRole role) const
ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const ExtrusionEntitiesPtr& extrusion_entities, const Point &start_near, ExtrusionRole role)
{
ExtrusionEntityCollection out;
if (this->no_sort) {
out = *this;
} else {
if (role == erMixed)
out = *this;
else {
for (const ExtrusionEntity *ee : this->entities) {
if (role != erMixed) {
// The caller wants only paths with a specific extrusion role.
auto role2 = ee->role();
if (role != role2) {
// This extrusion entity does not match the role asked.
assert(role2 != erMixed);
continue;
}
}
out.entities.emplace_back(ee->clone());
}
}
chain_and_reorder_extrusion_entities(out.entities, &start_near);
}
return out;
// Return a filtered copy of the collection.
ExtrusionEntityCollection out;
out.entities = filter_by_extrusion_role(extrusion_entities, role);
// Clone the extrusion entities.
for (auto &ptr : out.entities)
ptr = ptr->clone();
chain_and_reorder_extrusion_entities(out.entities, &start_near);
return out;
}
void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const

View file

@ -6,6 +6,21 @@
namespace Slic3r {
// Remove those items from extrusion_entities, that do not match role.
// Do nothing if role is mixed.
// Removed elements are NOT being deleted.
void filter_by_extrusion_role_in_place(ExtrusionEntitiesPtr &extrusion_entities, ExtrusionRole role);
// Return new vector of ExtrusionEntities* with only those items from input extrusion_entities, that match role.
// Return all extrusion entities if role is mixed.
// Returned extrusion entities are shared with the source vector, they are NOT cloned, they are considered to be owned by extrusion_entities.
inline ExtrusionEntitiesPtr filter_by_extrusion_role(const ExtrusionEntitiesPtr &extrusion_entities, ExtrusionRole role)
{
ExtrusionEntitiesPtr out { extrusion_entities };
filter_by_extrusion_role_in_place(out, role);
return out;
}
class ExtrusionEntityCollection : public ExtrusionEntity
{
public:
@ -65,7 +80,9 @@ public:
}
void replace(size_t i, const ExtrusionEntity &entity);
void remove(size_t i);
ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const;
static ExtrusionEntityCollection chained_path_from(const ExtrusionEntitiesPtr &extrusion_entities, const Point &start_near, ExtrusionRole role = erMixed);
ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const
{ return this->no_sort ? *this : chained_path_from(this->entities, start_near, role); }
void reverse();
const Point& first_point() const { return this->entities.front()->first_point(); }
const Point& last_point() const { return this->entities.back()->last_point(); }
@ -105,6 +122,6 @@ public:
}
};
}
} // namespace Slic3r
#endif

View file

@ -107,9 +107,9 @@ double Flow::mm3_per_mm() const
{
float res = this->bridge ?
// Area of a circle with dmr of this->width.
(this->width * this->width) * 0.25 * PI :
float((this->width * this->width) * 0.25 * PI) :
// Rectangle with semicircles at the ends. ~ h (w - 0.215 h)
this->height * (this->width - this->height * (1. - 0.25 * PI));
float(this->height * (this->width - this->height * (1. - 0.25 * PI)));
//assert(res > 0.);
if (res <= 0.)
throw std::runtime_error("Flow::mm3_per_mm() produced negative flow. Did you set some extrusion width too small?");

View file

@ -1187,7 +1187,7 @@ namespace Slic3r {
return;
pt::ptree code_tree = main_tree.front().second;
m_model->custom_gcode_per_print_z.clear();
m_model->custom_gcode_per_print_z.gcodes.clear();
for (const auto& code : code_tree)
{
@ -1199,7 +1199,7 @@ namespace Slic3r {
int extruder = tree.get<int> ("<xmlattr>.extruder" );
std::string color = tree.get<std::string> ("<xmlattr>.color" );
m_model->custom_gcode_per_print_z.push_back(Model::CustomGCode{print_z, gcode, extruder, color}) ;
m_model->custom_gcode_per_print_z.gcodes.push_back(Model::CustomGCode{print_z, gcode, extruder, color}) ;
}
}
}
@ -1816,9 +1816,6 @@ namespace Slic3r {
break;
}
}
#if !ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
Transform3d inv_matrix = volume_matrix_to_object.inverse();
#endif // !ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
// splits volume out of imported geometry
TriangleMesh triangle_mesh;
@ -1838,15 +1835,7 @@ namespace Slic3r {
for (unsigned int v = 0; v < 3; ++v)
{
unsigned int tri_id = geometry.triangles[src_start_id + ii + v] * 3;
#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
facet.vertex[v] = Vec3f(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]);
#else
Vec3f vertex(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]);
facet.vertex[v] = has_transform ?
// revert the vertices to the original mesh reference system
(inv_matrix * vertex.cast<double>()).cast<float>() :
vertex;
#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
}
}
@ -1854,15 +1843,9 @@ namespace Slic3r {
triangle_mesh.repair();
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
// stores the volume matrix taken from the metadata, if present
if (has_transform)
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
#else
// apply the volume matrix taken from the metadata, if present
if (has_transform)
volume->set_transformation(Slic3r::Geometry::Transformation(volume_matrix_to_object));
#endif //ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
volume->calculate_convex_hull();
// apply the remaining volume's metadata
@ -1975,12 +1958,24 @@ namespace Slic3r {
typedef std::vector<BuildItem> BuildItemsList;
typedef std::map<int, ObjectData> IdToObjectDataMap;
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
bool m_fullpath_sources{ true };
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
public:
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr);
#else
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources);
#endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data = nullptr);
#else
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config);
#endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
private:
#if ENABLE_THUMBNAIL_GENERATOR
@ -2006,6 +2001,22 @@ namespace Slic3r {
bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model);
};
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data)
{
clear_errors();
m_fullpath_sources = fullpath_sources;
return _save_model_to_file(filename, model, config, thumbnail_data);
}
#else
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources)
{
clear_errors();
return _save_model_to_file(filename, model, config);
}
#endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data)
{
@ -2019,6 +2030,7 @@ namespace Slic3r {
return _save_model_to_file(filename, model, config);
}
#endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR
bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data)
@ -2690,11 +2702,7 @@ namespace Slic3r {
// stores volume's local matrix
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\"";
#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
Transform3d matrix = volume->get_matrix() * volume->source.transform.get_matrix();
#else
const Transform3d& matrix = volume->get_matrix();
#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
for (int r = 0; r < 4; ++r)
{
for (int c = 0; c < 4; ++c)
@ -2709,7 +2717,12 @@ namespace Slic3r {
// stores volume's source data
if (!volume->source.input_file.empty())
{
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
std::string input_file = xml_escape(m_fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string());
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n";
#else
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->source.input_file) << "\"/>\n";
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
@ -2749,12 +2762,12 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
{
std::string out = "";
if (!model.custom_gcode_per_print_z.empty())
if (!model.custom_gcode_per_print_z.gcodes.empty())
{
pt::ptree tree;
pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", "");
for (const Model::CustomGCode& code : model.custom_gcode_per_print_z)
for (const Model::CustomGCode& code : model.custom_gcode_per_print_z.gcodes)
{
pt::ptree& code_tree = main_tree.add("code", "");
// store minX and maxZ
@ -2798,21 +2811,37 @@ bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool c
return res;
}
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data)
#else
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources)
#endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data)
#else
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config)
#endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
{
if ((path == nullptr) || (model == nullptr))
return false;
_3MF_Exporter exporter;
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR
bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources, thumbnail_data);
#else
bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources);
#endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR
bool res = exporter.save_model_to_file(path, *model, config, thumbnail_data);
#else
bool res = exporter.save_model_to_file(path, *model, config);
#endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
if (!res)
exporter.log_errors();

View file

@ -35,11 +35,19 @@ namespace Slic3r {
// 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
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr);
#else
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources);
#endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data = nullptr);
#else
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config);
#endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
}; // namespace Slic3r

View file

@ -585,36 +585,20 @@ void AMFParserContext::endElement(const char * /* name */)
stl_allocate(&stl);
bool has_transform = ! m_volume_transform.isApprox(Transform3d::Identity(), 1e-10);
#if !ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
Transform3d inv_matrix = m_volume_transform.inverse();
#endif // !ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
for (size_t i = 0; i < m_volume_facets.size();) {
stl_facet &facet = stl.facet_start[i/3];
for (unsigned int v = 0; v < 3; ++v)
{
unsigned int tri_id = m_volume_facets[i++] * 3;
#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
facet.vertex[v] = Vec3f(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]);
#else
Vec3f vertex(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]);
facet.vertex[v] = has_transform ?
// revert the vertices to the original mesh reference system
(inv_matrix * vertex.cast<double>()).cast<float>() :
vertex;
#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
}
}
stl_get_size(&stl);
mesh.repair();
m_volume->set_mesh(std::move(mesh));
#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
// stores the volume matrix taken from the metadata, if present
if (has_transform)
m_volume->source.transform = Slic3r::Geometry::Transformation(m_volume_transform);
#else
if (has_transform)
m_volume->set_transformation(m_volume_transform);
#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART))
{
m_volume->source.object_idx = (int)m_model.objects.size() - 1;
@ -653,7 +637,7 @@ void AMFParserContext::endElement(const char * /* name */)
int extruder = atoi(m_value[2].c_str());
const std::string& color = m_value[3];
m_model.custom_gcode_per_print_z.push_back(Model::CustomGCode{height, gcode, extruder, color});
m_model.custom_gcode_per_print_z.gcodes.push_back(Model::CustomGCode{height, gcode, extruder, color});
for (std::string& val: m_value)
val.clear();
@ -1019,7 +1003,11 @@ bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, bool c
return false;
}
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources)
#else
bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
{
if ((path == nullptr) || (model == nullptr))
return false;
@ -1159,11 +1147,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
stream << " <metadata type=\"slic3r.matrix\">";
#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
const Transform3d& matrix = volume->get_matrix() * volume->source.transform.get_matrix();
#else
const Transform3d& matrix = volume->get_matrix();
#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
stream << std::setprecision(std::numeric_limits<double>::max_digits10);
for (int r = 0; r < 4; ++r)
{
@ -1177,7 +1161,12 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << "</metadata>\n";
if (!volume->source.input_file.empty())
{
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
std::string input_file = xml_escape(fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string());
stream << " <metadata type=\"slic3r.source_file\">" << input_file << "</metadata>\n";
#else
stream << " <metadata type=\"slic3r.source_file\">" << xml_escape(volume->source.input_file) << "</metadata>\n";
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
stream << " <metadata type=\"slic3r.source_object_id\">" << volume->source.object_idx << "</metadata>\n";
stream << " <metadata type=\"slic3r.source_volume_id\">" << volume->source.volume_idx << "</metadata>\n";
stream << " <metadata type=\"slic3r.source_offset_x\">" << volume->source.mesh_offset(0) << "</metadata>\n";
@ -1241,14 +1230,14 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << " </constellation>\n";
}
if (!model->custom_gcode_per_print_z.empty())
if (!model->custom_gcode_per_print_z.gcodes.empty())
{
std::string out = "";
pt::ptree tree;
pt::ptree& main_tree = tree.add("custom_gcodes_per_height", "");
for (const Model::CustomGCode& code : model->custom_gcode_per_print_z)
for (const Model::CustomGCode& code : model->custom_gcode_per_print_z.gcodes)
{
pt::ptree& code_tree = main_tree.add("code", "");
// store minX and maxZ

View file

@ -11,7 +11,11 @@ extern bool load_amf(const char* path, DynamicPrintConfig* config, Model* model,
// Save the given model and the config data into an amf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
extern bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources);
#else
extern bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config);
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
}; // namespace Slic3r

File diff suppressed because it is too large Load diff

View file

@ -197,23 +197,25 @@ public:
// append full config to the given string
static void append_full_config(const Print& print, std::string& str);
protected:
// Object and support extrusions of the same PrintObject at the same print_z.
// public, so that it could be accessed by free helper functions from GCode.cpp
struct LayerToPrint
{
LayerToPrint() : object_layer(nullptr), support_layer(nullptr) {}
const Layer* object_layer;
const SupportLayer* support_layer;
const Layer* layer() const { return (object_layer != nullptr) ? object_layer : support_layer; }
const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; }
coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; }
};
private:
#if ENABLE_THUMBNAIL_GENERATOR
void _do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb);
void _do_export(Print &print, FILE *file, ThumbnailsGeneratorCallback thumbnail_cb);
#else
void _do_export(Print &print, FILE *file);
#endif //ENABLE_THUMBNAIL_GENERATOR
// Object and support extrusions of the same PrintObject at the same print_z.
struct LayerToPrint
{
LayerToPrint() : object_layer(nullptr), support_layer(nullptr) {}
const Layer *object_layer;
const SupportLayer *support_layer;
const Layer* layer() const { return (object_layer != nullptr) ? object_layer : support_layer; }
const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; }
coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; }
};
static std::vector<LayerToPrint> collect_layers_to_print(const PrintObject &object);
static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print);
void process_layer(
@ -239,7 +241,6 @@ protected:
std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.);
std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.);
typedef std::vector<int> ExtruderPerCopy;
// Extruding multiple objects with soluble / non-soluble / combined supports
// on a multi-material printer, trying to minimize tool switches.
// Following structures sort extrusions by the extruder ID, by an order of objects and object islands.
@ -253,21 +254,29 @@ protected:
struct Island
{
struct Region {
ExtrusionEntityCollection perimeters;
ExtrusionEntityCollection infills;
// Non-owned references to LayerRegion::perimeters::entities
// std::vector<const ExtrusionEntity*> would be better here, but there is no way in C++ to convert from std::vector<T*> std::vector<const T*> without copying.
ExtrusionEntitiesPtr perimeters;
// Non-owned references to LayerRegion::fills::entities
ExtrusionEntitiesPtr infills;
std::vector<const ExtruderPerCopy*> infills_overrides;
std::vector<const ExtruderPerCopy*> perimeters_overrides;
std::vector<const WipingExtrusions::ExtruderPerCopy*> infills_overrides;
std::vector<const WipingExtrusions::ExtruderPerCopy*> perimeters_overrides;
enum Type {
PERIMETERS,
INFILL,
};
// Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping
void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, size_t object_copies_num);
void append(const Type type, const ExtrusionEntityCollection* eec, const WipingExtrusions::ExtruderPerCopy* copy_extruders);
};
std::vector<Region> by_region; // all extrusions for this island, grouped by regions
const std::vector<Region>& by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities = false); // returns reference to subvector of by_region
private:
std::vector<Region> by_region_per_copy_cache; // caches vector generated by function above to avoid copying and recalculating
std::vector<Region> by_region; // all extrusions for this island, grouped by regions
// Fills in by_region_per_copy_cache and returns its reference.
const std::vector<Region>& by_region_per_copy(std::vector<Region> &by_region_per_copy_cache, unsigned int copy, unsigned int extruder, bool wiping_entities = false) const;
};
std::vector<Island> islands;
};
@ -277,7 +286,9 @@ protected:
InstanceToPrint(ObjectByExtruder &object_by_extruder, size_t layer_id, const PrintObject &print_object, size_t instance_id) :
object_by_extruder(object_by_extruder), layer_id(layer_id), print_object(print_object), instance_id(instance_id) {}
ObjectByExtruder &object_by_extruder;
// Repository
ObjectByExtruder &object_by_extruder;
// Index into std::vector<LayerToPrint>, which contains Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances.
const size_t layer_id;
const PrintObject &print_object;
// Instance idx of the copy of a print object.
@ -285,7 +296,8 @@ protected:
};
std::vector<InstanceToPrint> sort_print_object_instances(
std::vector<ObjectByExtruder> &objects_by_extruder,
std::vector<ObjectByExtruder> &objects_by_extruder,
// Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances.
const std::vector<LayerToPrint> &layers,
// Ordering must be defined for normal (non-sequential print).
const std::vector<std::pair<size_t, size_t>> *ordering,
@ -354,7 +366,7 @@ protected:
#endif /* HAS_PRESSURE_EQUALIZER */
std::unique_ptr<WipeTowerIntegration> m_wipe_tower;
// Heights at which the skirt has already been extruded.
// Heights (print_z) at which the skirt has already been extruded.
std::vector<coordf_t> m_skirt_done;
// Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print.
bool m_brim_done;
@ -362,11 +374,6 @@ protected:
bool m_second_layer_things_done;
// Index of a last object copy extruded.
std::pair<const PrintObject*, Point> m_last_obj_copy;
// Extensions for colorprint - now it's not a just color_print_heights,
// there can be some custom gcode.
// Updated before the export and erased during the process,
// so no toolchange occurs twice.
std::vector<Model::CustomGCode> m_custom_gcode_per_print_z;
// Time estimators
GCodeTimeEstimator m_normal_time_estimator;

View file

@ -311,24 +311,22 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line)
{
auto axis_absolute_position = [this](GCodeAnalyzer::EAxis axis, const GCodeReader::GCodeLine& lineG1) -> float
{
float current_absolute_position = _get_axis_position(axis);
float current_origin = _get_axis_origin(axis);
float lengthsScaleFactor = (_get_units() == GCodeAnalyzer::Inches) ? INCHES_TO_MM : 1.0f;
bool is_relative = (_get_global_positioning_type() == Relative);
if (axis == E)
is_relative |= (_get_e_local_positioning_type() == Relative);
if (lineG1.has(Slic3r::Axis(axis)))
{
float lengthsScaleFactor = (_get_units() == GCodeAnalyzer::Inches) ? INCHES_TO_MM : 1.0f;
float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
return is_relative ? current_absolute_position + ret : ret + current_origin;
return is_relative ? _get_axis_position(axis) + ret : _get_axis_origin(axis) + ret;
}
else
return current_absolute_position;
return _get_axis_position(axis);
};
// updates axes positions from line
float new_pos[Num_Axis];
for (unsigned char a = X; a < Num_Axis; ++a)
{
@ -352,7 +350,7 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line)
if (delta_pos[E] < 0.0f)
{
if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f))
type = GCodeMove::Move;
type = GCodeMove::Move;
else
type = GCodeMove::Retract;
}
@ -440,7 +438,9 @@ void GCodeAnalyzer::_processG92(const GCodeReader::GCodeLine& line)
if (line.has_e())
{
_set_axis_origin(E, _get_axis_position(E) - line.e() * lengthsScaleFactor);
// extruder coordinate can grow to the point where its float representation does not allow for proper addition with small increments,
// we set the value taken from the G92 line as the new current position for it
_set_axis_position(E, line.e() * lengthsScaleFactor);
anyFound = true;
}
@ -956,7 +956,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
GCodePreviewData::Extrusion::Path &path = paths.back();
path.polyline = polyline;
path.extrusion_role = data.extrusion_role;
path.mm3_per_mm = data.mm3_per_mm;
path.mm3_per_mm = float(data.mm3_per_mm);
path.width = data.width;
path.height = data.height;
path.feedrate = data.feedrate;
@ -978,7 +978,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
float volumetric_rate = FLT_MAX;
GCodePreviewData::Range height_range;
GCodePreviewData::Range width_range;
GCodePreviewData::Range feedrate_range;
GCodePreviewData::MultiRange<GCodePreviewData::FeedrateKind> feedrate_range;
GCodePreviewData::Range volumetric_rate_range;
GCodePreviewData::Range fan_speed_range;
@ -1013,7 +1013,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
volumetric_rate = move.data.feedrate * (float)move.data.mm3_per_mm;
height_range.update_from(move.data.height);
width_range.update_from(move.data.width);
feedrate_range.update_from(move.data.feedrate);
feedrate_range.update_from(move.data.feedrate, GCodePreviewData::FeedrateKind::EXTRUSION);
volumetric_rate_range.update_from(volumetric_rate);
fan_speed_range.update_from(move.data.fan_speed);
}
@ -1066,7 +1066,7 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data, s
GCodePreviewData::Range height_range;
GCodePreviewData::Range width_range;
GCodePreviewData::Range feedrate_range;
GCodePreviewData::MultiRange<GCodePreviewData::FeedrateKind> feedrate_range;
// to avoid to call the callback too often
unsigned int cancel_callback_threshold = (unsigned int)std::max((int)travel_moves->second.size() / 25, 1);
@ -1106,7 +1106,7 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data, s
extruder_id = move.data.extruder_id;
height_range.update_from(move.data.height);
width_range.update_from(move.data.width);
feedrate_range.update_from(move.data.feedrate);
feedrate_range.update_from(move.data.feedrate, GCodePreviewData::FeedrateKind::TRAVEL);
}
// store last polyline

View file

@ -303,8 +303,8 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
unsigned int extruder_id = extruders[i].id();
adj.extruder_id = extruder_id;
adj.cooling_slow_down_enabled = config.cooling.get_at(extruder_id);
adj.slowdown_below_layer_time = config.slowdown_below_layer_time.get_at(extruder_id);
adj.min_print_speed = config.min_print_speed.get_at(extruder_id);
adj.slowdown_below_layer_time = float(config.slowdown_below_layer_time.get_at(extruder_id));
adj.min_print_speed = float(config.min_print_speed.get_at(extruder_id));
map_extruder_to_per_extruder_adjustment[extruder_id] = i;
}

View file

@ -1,6 +1,5 @@
#include "Analyzer.hpp"
#include "PreviewData.hpp"
#include <float.h>
#include <I18N.hpp>
#include "Utils.hpp"
@ -11,9 +10,7 @@
namespace Slic3r {
const GCodePreviewData::Color GCodePreviewData::Color::Dummy(0.0f, 0.0f, 0.0f, 0.0f);
std::vector<unsigned char> GCodePreviewData::Color::as_bytes() const
std::vector<unsigned char> Color::as_bytes() const
{
std::vector<unsigned char> ret;
for (unsigned int i = 0; i < 4; ++i)
@ -38,20 +35,6 @@ GCodePreviewData::Travel::Polyline::Polyline(EType type, EDirection direction, f
{
}
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();
@ -59,69 +42,73 @@ GCodePreviewData::Range::Range()
void GCodePreviewData::Range::reset()
{
min = FLT_MAX;
max = -FLT_MAX;
min_val = FLT_MAX;
max_val = -FLT_MAX;
}
bool GCodePreviewData::Range::empty() const
{
return min == max;
return min_val >= max_val;
}
void GCodePreviewData::Range::update_from(float value)
{
min = std::min(min, value);
max = std::max(max, value);
min_val = std::min(min_val, value);
max_val = std::max(max_val, value);
}
void GCodePreviewData::Range::update_from(const Range& other)
void GCodePreviewData::Range::update_from(const RangeBase& other)
{
min = std::min(min, other.min);
max = std::max(max, other.max);
min_val = std::min(min_val, other.min());
max_val = std::max(max_val, other.max());
}
void GCodePreviewData::Range::set_from(const Range& other)
float GCodePreviewData::RangeBase::step_size() const
{
min = other.min;
max = other.max;
return (max() - min()) / static_cast<float>(range_rainbow_colors.size() - 1);
}
float GCodePreviewData::Range::step_size() const
Color GCodePreviewData::RangeBase::get_color_at(float value) const
{
return (max - min) / (float)(Colors_Count - 1);
}
// Input value scaled to the color range
float step = step_size();
const float global_t = (step != 0.0f) ? std::max(0.0f, value - min()) / step_size() : 0.0f; // lower limit of 0.0f
GCodePreviewData::Color GCodePreviewData::Range::get_color_at(float value) const
{
if (empty())
return Color::Dummy;
constexpr std::size_t color_max_idx = range_rainbow_colors.size() - 1;
float global_t = (value - min) / step_size();
// Compute the two colors just below (low) and above (high) the input value
const std::size_t color_low_idx = std::clamp(static_cast<std::size_t>(global_t), std::size_t{ 0 }, color_max_idx);
const std::size_t color_high_idx = std::clamp(color_low_idx + 1, std::size_t{ 0 }, color_max_idx);
unsigned int low = (unsigned int)global_t;
unsigned int high = clamp((unsigned int)0, Colors_Count - 1, low + 1);
// Compute how far the value is between the low and high colors so that they can be interpolated
const float local_t = std::min(global_t - static_cast<float>(color_low_idx), 1.0f); // upper limit of 1.0f
Color color_low = colors[low];
Color color_high = colors[high];
float local_t = global_t - (float)low;
// interpolate in RGB space
// Interpolate between the low and high colors in RGB space to find exactly which color the input value should get
Color ret;
for (unsigned int i = 0; i < 4; ++i)
{
ret.rgba[i] = lerp(color_low.rgba[i], color_high.rgba[i], local_t);
ret.rgba[i] = lerp(range_rainbow_colors[color_low_idx].rgba[i], range_rainbow_colors[color_high_idx].rgba[i], local_t);
}
return ret;
}
GCodePreviewData::LegendItem::LegendItem(const std::string& text, const GCodePreviewData::Color& color)
float GCodePreviewData::Range::min() const
{
return min_val;
}
float GCodePreviewData::Range::max() const
{
return max_val;
}
GCodePreviewData::LegendItem::LegendItem(const std::string& text, const Color& color)
: text(text)
, color(color)
{
}
const GCodePreviewData::Color GCodePreviewData::Extrusion::Default_Extrusion_Role_Colors[erCount] =
const Color GCodePreviewData::Extrusion::Default_Extrusion_Role_Colors[erCount] =
{
Color(0.0f, 0.0f, 0.0f, 1.0f), // erNone
Color(1.0f, 0.0f, 0.0f, 1.0f), // erPerimeter
@ -180,7 +167,7 @@ size_t GCodePreviewData::Extrusion::memory_used() const
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] =
const 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
@ -206,7 +193,7 @@ size_t GCodePreviewData::Travel::memory_used() const
return out;
}
const GCodePreviewData::Color GCodePreviewData::Retraction::Default_Color = GCodePreviewData::Color(1.0f, 1.0f, 1.0f, 1.0f);
const Color GCodePreviewData::Retraction::Default_Color = Color(1.0f, 1.0f, 1.0f, 1.0f);
GCodePreviewData::Retraction::Position::Position(const Vec3crd& position, float width, float height)
: position(position)
@ -238,17 +225,15 @@ GCodePreviewData::GCodePreviewData()
void GCodePreviewData::set_default()
{
::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));
::memcpy((void*)ranges.fan_speed.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
::memcpy((void*)ranges.volumetric_rate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
extrusion.set_default();
travel.set_default();
retraction.set_default();
unretraction.set_default();
shell.set_default();
// Configure the color range for feedrate to match the default for travels and to enable extrusions since they are always visible
ranges.feedrate.set_mode(FeedrateKind::TRAVEL, travel.is_visible);
ranges.feedrate.set_mode(FeedrateKind::EXTRUSION, true);
}
void GCodePreviewData::reset()
@ -268,32 +253,32 @@ bool GCodePreviewData::empty() const
return extrusion.layers.empty() && travel.polylines.empty() && retraction.positions.empty() && unretraction.positions.empty();
}
GCodePreviewData::Color GCodePreviewData::get_extrusion_role_color(ExtrusionRole role) const
Color GCodePreviewData::get_extrusion_role_color(ExtrusionRole role) const
{
return extrusion.role_colors[role];
}
GCodePreviewData::Color GCodePreviewData::get_height_color(float height) const
Color GCodePreviewData::get_height_color(float height) const
{
return ranges.height.get_color_at(height);
}
GCodePreviewData::Color GCodePreviewData::get_width_color(float width) const
Color GCodePreviewData::get_width_color(float width) const
{
return ranges.width.get_color_at(width);
}
GCodePreviewData::Color GCodePreviewData::get_feedrate_color(float feedrate) const
Color GCodePreviewData::get_feedrate_color(float feedrate) const
{
return ranges.feedrate.get_color_at(feedrate);
}
GCodePreviewData::Color GCodePreviewData::get_fan_speed_color(float fan_speed) const
Color GCodePreviewData::get_fan_speed_color(float fan_speed) const
{
return ranges.fan_speed.get_color_at(fan_speed);
}
GCodePreviewData::Color GCodePreviewData::get_volumetric_rate_color(float rate) const
Color GCodePreviewData::get_volumetric_rate_color(float rate) const
{
return ranges.volumetric_rate.get_color_at(rate);
}
@ -384,16 +369,25 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
{
struct Helper
{
static void FillListFromRange(LegendItemsList& list, const Range& range, unsigned int decimals, float scale_factor)
static void FillListFromRange(LegendItemsList& list, const RangeBase& range, unsigned int decimals, float scale_factor)
{
list.reserve(Range::Colors_Count);
list.reserve(range_rainbow_colors.size());
float step = range.step_size();
for (int i = Range::Colors_Count - 1; i >= 0; --i)
if (step == 0.0f)
{
char buf[1024];
sprintf(buf, "%.*f", decimals, scale_factor * (range.min + (float)i * step));
list.emplace_back(buf, range.colors[i]);
sprintf(buf, "%.*f", decimals, scale_factor * range.min());
list.emplace_back(buf, range_rainbow_colors[0]);
}
else
{
for (int i = static_cast<int>(range_rainbow_colors.size()) - 1; i >= 0; --i)
{
char buf[1024];
sprintf(buf, "%.*f", decimals, scale_factor * (range.min() + (float)i * step));
list.emplace_back(buf, range_rainbow_colors[i]);
}
}
}
};
@ -446,8 +440,8 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
items.reserve(tools_colors_count);
for (unsigned int i = 0; i < tools_colors_count; ++i)
{
GCodePreviewData::Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
Color color;
::memcpy((void*)color.rgba.data(), (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
items.emplace_back((boost::format(Slic3r::I18N::translate(L("Extruder %d"))) % (i + 1)).str(), color);
}
@ -460,7 +454,7 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
if (color_print_cnt == 1) // means "Default print color"
{
Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data()), 4 * sizeof(float));
::memcpy((void*)color.rgba.data(), (const void*)(tool_colors.data()), 4 * sizeof(float));
items.emplace_back(cp_items[0], color);
break;
@ -472,7 +466,7 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
for (int i = 0 ; i < color_print_cnt; ++i)
{
Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
::memcpy((void*)color.rgba.data(), (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
items.emplace_back(cp_items[i], color);
}
@ -502,20 +496,20 @@ const std::vector<std::string>& GCodePreviewData::ColorPrintColors()
return color_print;
}
GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2)
Color operator + (const Color& c1, const Color& c2)
{
return GCodePreviewData::Color(clamp(0.0f, 1.0f, c1.rgba[0] + c2.rgba[0]),
clamp(0.0f, 1.0f, c1.rgba[1] + c2.rgba[1]),
clamp(0.0f, 1.0f, c1.rgba[2] + c2.rgba[2]),
clamp(0.0f, 1.0f, c1.rgba[3] + c2.rgba[3]));
return Color(std::clamp(c1.rgba[0] + c2.rgba[0], 0.0f, 1.0f),
std::clamp(c1.rgba[1] + c2.rgba[1], 0.0f, 1.0f),
std::clamp(c1.rgba[2] + c2.rgba[2], 0.0f, 1.0f),
std::clamp(c1.rgba[3] + c2.rgba[3], 0.0f, 1.0f));
}
GCodePreviewData::Color operator * (float f, const GCodePreviewData::Color& color)
Color operator * (float f, const Color& color)
{
return GCodePreviewData::Color(clamp(0.0f, 1.0f, f * color.rgba[0]),
clamp(0.0f, 1.0f, f * color.rgba[1]),
clamp(0.0f, 1.0f, f * color.rgba[2]),
clamp(0.0f, 1.0f, f * color.rgba[3]));
return Color(std::clamp(f * color.rgba[0], 0.0f, 1.0f),
std::clamp(f * color.rgba[1], 0.0f, 1.0f),
std::clamp(f * color.rgba[2], 0.0f, 1.0f),
std::clamp(f * color.rgba[3], 0.0f, 1.0f));
}
} // namespace Slic3r

View file

@ -5,43 +5,190 @@
#include "../ExtrusionEntity.hpp"
#include "../Point.hpp"
#include <tuple>
#include <array>
#include <vector>
#include <bitset>
#include <cstddef>
#include <algorithm>
#include <string>
#include <float.h>
namespace Slic3r {
// Represents an RGBA color
struct Color
{
std::array<float,4> rgba;
Color(const float *argba)
{
memcpy(this->rgba.data(), argba, sizeof(float) * 4);
}
constexpr Color(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f) : rgba{r,g,b,a}
{
// Intentionally empty
}
std::vector<unsigned char> as_bytes() const;
};
Color operator + (const Color& c1, const Color& c2);
Color operator * (float f, const Color& color);
// Default colors for Ranges
constexpr std::array<Color, 10> range_rainbow_colors{
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}};
class GCodePreviewData
{
public:
struct Color
// Color mapping to convert a float into a smooth rainbow of 10 colors.
class RangeBase
{
float rgba[4];
public:
Color(const float *argba) { memcpy(this->rgba, argba, sizeof(float) * 4); }
Color(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f) { rgba[0] = r; rgba[1] = g; rgba[2] = b; rgba[3] = a; }
std::vector<unsigned char> as_bytes() const;
static const Color Dummy;
virtual void reset() = 0;
virtual bool empty() const = 0;
virtual float min() const = 0;
virtual float max() const = 0;
// Gets the step size using min(), max() and colors
float step_size() const;
// Gets the color at a value using colors, min(), and max()
Color get_color_at(float value) const;
};
// Color mapping from a <min, max> range into a smooth rainbow of 10 colors.
struct Range
// Color mapping converting a float in a range between a min and a max into a smooth rainbow of 10 colors.
class Range : public RangeBase
{
static const unsigned int Colors_Count = 10;
static const Color Default_Colors[Colors_Count];
Color colors[Colors_Count];
float min;
float max;
public:
Range();
void reset();
bool empty() const;
// RangeBase Overrides
void reset() override;
bool empty() const override;
float min() const override;
float max() const override;
// Range-specific methods
void update_from(float value);
void update_from(const Range& other);
void set_from(const Range& other);
float step_size() const;
void update_from(const RangeBase& other);
Color get_color_at(float value) const;
private:
float min_val;
float max_val;
};
// Like Range, but stores multiple ranges internally that are used depending on mode.
// Template param EnumRangeType must be an enum with values for each type of range that needs to be tracked in this MultiRange.
// The last enum value should be num_values. The numerical values of all enum values should range from 0 to num_values.
template <typename EnumRangeType>
class MultiRange : public RangeBase
{
public:
void reset() override
{
bounds = decltype(bounds){};
}
bool empty() const override
{
for (std::size_t i = 0; i < bounds.size(); ++i)
{
if (bounds[i].min != bounds[i].max)
return false;
}
return true;
}
float min() const override
{
float min = FLT_MAX;
for (std::size_t i = 0; i < bounds.size(); ++i)
{
// Only use bounds[i] if the current mode includes it
if (mode.test(i))
{
min = std::min(min, bounds[i].min);
}
}
return min;
}
float max() const override
{
float max = -FLT_MAX;
for (std::size_t i = 0; i < bounds.size(); ++i)
{
// Only use bounds[i] if the current mode includes it
if (mode.test(i))
{
max = std::max(max, bounds[i].max);
}
}
return max;
}
void update_from(const float value, EnumRangeType range_type_value)
{
bounds[static_cast<std::size_t>(range_type_value)].update_from(value);
}
void update_from(const MultiRange& other)
{
for (std::size_t i = 0; i < bounds.size(); ++i)
{
bounds[i].update_from(other.bounds[i]);
}
}
void set_mode(const EnumRangeType range_type_value, const bool enable)
{
mode.set(static_cast<std::size_t>(range_type_value), enable);
}
private:
// Interval bounds
struct Bounds
{
float min{FLT_MAX};
float max{-FLT_MAX};
void update_from(const float value)
{
min = std::min(min, value);
max = std::max(max, value);
}
void update_from(const Bounds other_bounds)
{
min = std::min(min, other_bounds.min);
max = std::max(max, other_bounds.max);
}
};
std::array<Bounds, static_cast<std::size_t>(EnumRangeType::num_values)> bounds;
std::bitset<static_cast<std::size_t>(EnumRangeType::num_values)> mode;
};
// Enum distinguishing different kinds of feedrate data
enum class FeedrateKind
{
EXTRUSION = 0, // values must go from 0 up to num_values
TRAVEL,
num_values //must be last in the list of values
};
struct Ranges
@ -51,7 +198,7 @@ public:
// Color mapping by extrusion width.
Range width;
// Color mapping by feedrate.
Range feedrate;
MultiRange<FeedrateKind> feedrate;
// Color mapping by fan speed.
Range fan_speed;
// Color mapping by volumetric extrusion rate.
@ -245,9 +392,6 @@ public:
static const std::vector<std::string>& ColorPrintColors();
};
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_ */

View file

@ -29,7 +29,7 @@ static inline BoundingBox extrusion_polyline_extents(const Polyline &polyline, c
static inline BoundingBoxf extrusionentity_extents(const ExtrusionPath &extrusion_path)
{
BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width));
BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width)));
BoundingBoxf bboxf;
if (! empty(bbox)) {
bboxf.min = unscale(bbox.min);
@ -43,7 +43,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionLoop &extrusio
{
BoundingBox bbox;
for (const ExtrusionPath &extrusion_path : extrusion_loop.paths)
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width)));
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width))));
BoundingBoxf bboxf;
if (! empty(bbox)) {
bboxf.min = unscale(bbox.min);
@ -57,7 +57,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionMultiPath &ext
{
BoundingBox bbox;
for (const ExtrusionPath &extrusion_path : extrusion_multi_path.paths)
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width)));
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width))));
BoundingBoxf bboxf;
if (! empty(bbox)) {
bboxf.min = unscale(bbox.min);

View file

@ -13,13 +13,17 @@
#include <cassert>
#include <limits>
#include <libslic3r.h>
#include "../GCodeWriter.hpp"
namespace Slic3r {
// Returns true in case that extruder a comes before b (b does not have to be present). False otherwise.
bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
{
if (a==b)
if (a == b)
return false;
for (auto extruder : extruders) {
@ -32,6 +36,39 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
return false;
}
// Return a zero based extruder from the region, or extruder_override if overriden.
unsigned int LayerTools::perimeter_extruder(const PrintRegion &region) const
{
assert(region.config().perimeter_extruder.value > 0);
return ((this->extruder_override == 0) ? region.config().perimeter_extruder.value : this->extruder_override) - 1;
}
unsigned int LayerTools::infill_extruder(const PrintRegion &region) const
{
assert(region.config().infill_extruder.value > 0);
return ((this->extruder_override == 0) ? region.config().infill_extruder.value : this->extruder_override) - 1;
}
unsigned int LayerTools::solid_infill_extruder(const PrintRegion &region) const
{
assert(region.config().solid_infill_extruder.value > 0);
return ((this->extruder_override == 0) ? region.config().solid_infill_extruder.value : this->extruder_override) - 1;
}
// Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden.
unsigned int LayerTools::extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion &region) const
{
assert(region.config().perimeter_extruder.value > 0);
assert(region.config().infill_extruder.value > 0);
assert(region.config().solid_infill_extruder.value > 0);
// 1 based extruder ID.
unsigned int extruder = ((this->extruder_override == 0) ?
(is_infill(extrusions.role()) ?
(is_solid_infill(extrusions.entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) :
region.config().perimeter_extruder.value) :
this->extruder_override);
return (extruder == 0) ? 0 : extruder - 1;
}
// For the use case when each object is printed separately
// (print.config().complete_objects is true).
@ -52,7 +89,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
}
// Collect extruders reuqired to print the layers.
this->collect_extruders(object);
this->collect_extruders(object, std::vector<std::pair<double, unsigned int>>());
// Reorder the extruders to minimize tool switches.
this->reorder_extruders(first_extruder);
@ -89,9 +126,19 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
this->initialize_layers(zs);
}
// Use the extruder switches from Model::custom_gcode_per_print_z to override the extruder to print the object.
// Do it only if all the objects were configured to be printed with a single extruder.
std::vector<std::pair<double, unsigned int>> per_layer_extruder_switches;
if (auto num_extruders = unsigned(print.config().nozzle_diameter.size());
num_extruders > 1 && print.object_extruders().size() == 1) {
// Printing a single extruder platter on a printer with more than 1 extruder (or single-extruder multi-material).
// There may be custom per-layer tool changes available at the model.
per_layer_extruder_switches = custom_tool_changes(print.model(), num_extruders);
}
// Collect extruders reuqired to print the layers.
for (auto object : print.objects())
this->collect_extruders(*object);
this->collect_extruders(*object, per_layer_extruder_switches);
// Reorder the extruders to minimize tool switches.
this->reorder_extruders(first_extruder);
@ -111,13 +158,13 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
coordf_t zmax = zs[i] + EPSILON;
for (; j < zs.size() && zs[j] <= zmax; ++ j) ;
// Assign an average print_z to the set of layers with nearly equal print_z.
m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1]), m_print_config_ptr));
m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1])));
i = j;
}
}
// Collect extruders reuqired to print layers.
void ToolOrdering::collect_extruders(const PrintObject &object)
void ToolOrdering::collect_extruders(const PrintObject &object, const std::vector<std::pair<double, unsigned int>> &per_layer_extruder_switches)
{
// Collect the support extruders.
for (auto support_layer : object.support_layers()) {
@ -134,9 +181,23 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
if (has_support || has_interface)
layer_tools.has_support = true;
}
// Extruder overrides are ordered by print_z.
std::vector<std::pair<double, unsigned int>>::const_iterator it_per_layer_extruder_override;
it_per_layer_extruder_override = per_layer_extruder_switches.begin();
unsigned int extruder_override = 0;
// Collect the object extruders.
for (auto layer : object.layers()) {
LayerTools &layer_tools = this->tools_for_layer(layer->print_z);
// Override extruder with the next
for (; it_per_layer_extruder_override != per_layer_extruder_switches.end() && it_per_layer_extruder_override->first < layer->print_z + EPSILON; ++ it_per_layer_extruder_override)
extruder_override = (int)it_per_layer_extruder_override->second;
// Store the current extruder override (set to zero if no overriden), so that layer_tools.wiping_extrusions().is_overridable_and_mark() will use it.
layer_tools.extruder_override = extruder_override;
// What extruders are required to print this object layer?
for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) {
const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr;
@ -150,19 +211,18 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors)
something_nonoverriddable = false;
for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
if (!layer_tools.wiping_extrusions().is_overriddable(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) {
if (!layer_tools.wiping_extrusions().is_overriddable_and_mark(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) {
something_nonoverriddable = true;
break;
}
}
if (something_nonoverriddable)
layer_tools.extruders.push_back(region.config().perimeter_extruder.value);
layer_tools.extruders.emplace_back((extruder_override == 0) ? region.config().perimeter_extruder.value : extruder_override);
layer_tools.has_object = true;
}
bool has_infill = false;
bool has_solid_infill = false;
bool something_nonoverriddable = false;
@ -176,17 +236,19 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
has_infill = true;
if (m_print_config_ptr) {
if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable(*fill, *m_print_config_ptr, object, region))
if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable_and_mark(*fill, *m_print_config_ptr, object, region))
something_nonoverriddable = true;
}
}
if (something_nonoverriddable || !m_print_config_ptr)
{
if (has_solid_infill)
layer_tools.extruders.push_back(region.config().solid_infill_extruder);
if (has_infill)
layer_tools.extruders.push_back(region.config().infill_extruder);
if (something_nonoverriddable || !m_print_config_ptr) {
if (extruder_override == 0) {
if (has_solid_infill)
layer_tools.extruders.emplace_back(region.config().solid_infill_extruder);
if (has_infill)
layer_tools.extruders.emplace_back(region.config().infill_extruder);
} else if (has_solid_infill || has_infill)
layer_tools.extruders.emplace_back(extruder_override);
}
if (has_solid_infill || has_infill)
layer_tools.has_object = true;
@ -199,7 +261,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
// make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
if (layer.extruders.empty() && layer.has_object)
layer.extruders.push_back(0); // 0="dontcare" extruder - it will be taken care of in reorder_extruders
layer.extruders.emplace_back(0); // 0="dontcare" extruder - it will be taken care of in reorder_extruders
}
}
@ -254,11 +316,9 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
for (unsigned int &extruder_id : lt.extruders) {
assert(extruder_id > 0);
-- extruder_id;
}
}
}
void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z)
{
if (m_layer_tools.empty())
@ -394,17 +454,76 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
}
}
// Assign a pointer to a custom G-code to the respective ToolOrdering::LayerTools.
// Ignore color changes, which are performed on a layer and for such an extruder, that the extruder will not be printing above that layer.
// If multiple events are planned over a span of a single layer, use the last one.
void ToolOrdering::assign_custom_gcodes(const Print &print)
{
// Only valid for non-sequential print.
assert(! print.config().complete_objects.value);
const Model::CustomGCodeInfo &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
if (custom_gcode_per_print_z.gcodes.empty())
return;
unsigned int num_extruders = *std::max_element(m_all_printing_extruders.begin(), m_all_printing_extruders.end()) + 1;
std::vector<unsigned char> extruder_printing_above(num_extruders, false);
auto custom_gcode_it = custom_gcode_per_print_z.gcodes.rbegin();
// If printing on a single extruder machine, make the tool changes trigger color change (M600) events.
bool tool_changes_as_color_changes = num_extruders == 1;
// From the last layer to the first one:
for (auto it_lt = m_layer_tools.rbegin(); it_lt != m_layer_tools.rend(); ++ it_lt) {
LayerTools &lt = *it_lt;
// Add the extruders of the current layer to the set of extruders printing at and above this print_z.
for (unsigned int i : lt.extruders)
extruder_printing_above[i] = true;
// Skip all custom G-codes above this layer and skip all extruder switches.
for (; custom_gcode_it != custom_gcode_per_print_z.gcodes.rend() && (custom_gcode_it->print_z > lt.print_z + EPSILON || custom_gcode_it->gcode == ToolChangeCode); ++ custom_gcode_it);
if (custom_gcode_it == custom_gcode_per_print_z.gcodes.rend())
// Custom G-codes were processed.
break;
// Some custom G-code is configured for this layer or a layer below.
const Model::CustomGCode &custom_gcode = *custom_gcode_it;
// print_z of the layer below the current layer.
coordf_t print_z_below = 0.;
if (auto it_lt_below = it_lt; ++ it_lt_below != m_layer_tools.rend())
print_z_below = it_lt_below->print_z;
if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) {
// The custom G-code applies to the current layer.
if ( tool_changes_as_color_changes || custom_gcode.gcode != ColorChangeCode ||
(custom_gcode.extruder <= num_extruders && extruder_printing_above[unsigned(custom_gcode.extruder - 1)]))
// If it is color change, it will actually be useful as the exturder above will print.
lt.custom_gcode = &custom_gcode;
// Consume that custom G-code event.
++ custom_gcode_it;
}
}
}
const LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z) const
{
auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), LayerTools(print_z - EPSILON));
assert(it_layer_tools != m_layer_tools.end());
coordf_t dist_min = std::abs(it_layer_tools->print_z - print_z);
for (++ it_layer_tools; it_layer_tools != m_layer_tools.end(); ++ it_layer_tools) {
coordf_t d = std::abs(it_layer_tools->print_z - print_z);
if (d >= dist_min)
break;
dist_min = d;
}
-- it_layer_tools;
assert(dist_min < EPSILON);
return *it_layer_tools;
}
// This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual)
void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies)
void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, size_t copy_id, int extruder, size_t num_of_copies)
{
something_overridden = true;
auto entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first; // (add and) return iterator
auto& copies_vector = entity_map_it->second;
if (copies_vector.size() < num_of_copies)
copies_vector.resize(num_of_copies, -1);
auto entity_map_it = (entity_map.emplace(entity, ExtruderPerCopy())).first; // (add and) return iterator
ExtruderPerCopy& copies_vector = entity_map_it->second;
copies_vector.resize(num_of_copies, -1);
if (copies_vector[copy_id] != -1)
std::cout << "ERROR: Entity extruder overriden multiple times!!!\n"; // A debugging message - this must never happen.
@ -412,7 +531,6 @@ void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsi
copies_vector[copy_id] = extruder;
}
// Finds first non-soluble extruder on the layer
int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
{
@ -435,11 +553,10 @@ int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print
return (-1);
}
// Decides whether this entity could be overridden
bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const
{
if (print_config.filament_soluble.get_at(Print::get_extruder(eec, region)))
if (print_config.filament_soluble.get_at(m_layer_tools->extruder(eec, region)))
return false;
if (object.config().wipe_into_objects)
@ -451,7 +568,6 @@ bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, con
return true;
}
// Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange
// and returns volume that is left to be wiped on the wipe tower.
float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe)
@ -459,8 +575,8 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
const LayerTools& lt = *m_layer_tools;
const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
if (print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder))
return volume_to_wipe; // Soluble filament cannot be wiped in a random infill, neither the filament after it
if (! this->something_overridable || volume_to_wipe <= 0. || print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder))
return std::max(0.f, volume_to_wipe); // Soluble filament cannot be wiped in a random infill, neither the filament after it
// we will sort objects so that dedicated for wiping are at the beginning:
PrintObjectPtrs object_list = print.objects();
@ -483,13 +599,13 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
const PrintObject* object = object_list[i];
// Finds this layer:
auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
if (this_layer_it == object->layers().end())
continue;
const Layer* this_layer = *this_layer_it;
const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON);
if (this_layer == nullptr)
continue;
size_t num_of_copies = object->copies().size();
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
// iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves
for (unsigned int copy = 0; copy < num_of_copies; ++copy) {
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
const auto& region = *object->print()->regions()[region_id];
@ -497,51 +613,48 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
continue;
if ((!print.config().infill_first ? perimeters_done : !perimeters_done) || (!object->config().wipe_into_objects && region.config().wipe_into_infill)) {
bool wipe_into_infill_only = ! object->config().wipe_into_objects && region.config().wipe_into_infill;
if (print.config().infill_first != perimeters_done || wipe_into_infill_only) {
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config(), *object, region))
continue;
if (volume_to_wipe<=0)
continue;
if (!object->config().wipe_into_objects && !print.config().infill_first && region.config().wipe_into_infill)
if (wipe_into_infill_only && ! print.config().infill_first)
// In this case we must check that the original extruder is used on this layer before the one we are overridding
// (and the perimeters will be finished before the infill is printed):
if (!lt.is_extruder_order(region.config().perimeter_extruder - 1, new_extruder))
if (!lt.is_extruder_order(lt.perimeter_extruder(region), new_extruder))
continue;
if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder
set_extruder_override(fill, copy, new_extruder, num_of_copies);
volume_to_wipe -= float(fill->total_volume());
if ((volume_to_wipe -= float(fill->total_volume())) <= 0.f)
// More material was purged already than asked for.
return 0.f;
}
}
}
// Now the same for perimeters - see comments above for explanation:
if (object->config().wipe_into_objects && (print.config().infill_first ? perimeters_done : !perimeters_done))
if (object->config().wipe_into_objects && print.config().infill_first == perimeters_done)
{
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) {
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config(), *object, region))
continue;
if (volume_to_wipe<=0)
continue;
if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {
if (is_overriddable(*fill, print.config(), *object, region) && !is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {
set_extruder_override(fill, copy, new_extruder, num_of_copies);
volume_to_wipe -= float(fill->total_volume());
if ((volume_to_wipe -= float(fill->total_volume())) <= 0.f)
// More material was purged already than asked for.
return 0.f;
}
}
}
}
}
}
return std::max(0.f, volume_to_wipe);
// Some purge remains to be done on the Wipe Tower.
assert(volume_to_wipe > 0.);
return volume_to_wipe;
}
@ -552,16 +665,18 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
// them again and make sure we override it.
void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
{
if (! this->something_overridable)
return;
const LayerTools& lt = *m_layer_tools;
unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config());
unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config());
for (const PrintObject* object : print.objects()) {
// Finds this layer:
auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
if (this_layer_it == object->layers().end())
continue;
const Layer* this_layer = *this_layer_it;
const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON);
if (this_layer == nullptr)
continue;
size_t num_of_copies = object->copies().size();
for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
@ -584,9 +699,8 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
// Either way, we will now force-override it with something suitable:
if (print.config().infill_first
|| object->config().wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely
|| lt.is_extruder_order(region.config().perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
|| std::find(lt.extruders.begin(), lt.extruders.end(), region.config().infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME)
)
|| lt.is_extruder_order(lt.perimeter_extruder(region), last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
|| ! lt.has_extruder(lt.infill_extruder(region)))) // we have to force override - this could violate infill_first (FIXME)
set_extruder_override(fill, copy, (print.config().infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
else {
// In this case we can (and should) leave it to be printed normally.
@ -597,42 +711,31 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
// Now the same for perimeters - see comments above for explanation:
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { // iterate through all perimeter Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config(), *object, region)
|| is_entity_overridden(fill, copy) )
continue;
set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
if (is_overriddable(*fill, print.config(), *object, region) && ! is_entity_overridden(fill, copy))
set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
}
}
}
}
}
// Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
// It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy
// It also modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known,
// so -1 was used as "print as usual".
// The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
// its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies)
// Following function is called from GCode::process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
// If this extrusion does not have any override, nullptr is returned.
// Otherwise it modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known,
// so -1 was used as "print as usual").
// The resulting vector therefore keeps track of which extrusions are the ones that were overridden and which were not. If the extruder used is overridden,
// its number is saved as is (zero-based index). Regular extrusions are saved as -number-1 (unfortunately there is no negative zero).
const WipingExtrusions::ExtruderPerCopy* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies)
{
ExtruderPerCopy *overrides = nullptr;
auto entity_map_it = entity_map.find(entity);
if (entity_map_it == entity_map.end())
entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first;
// Now the entity_map_it should be valid, let's make sure the vector is long enough:
entity_map_it->second.resize(num_of_copies, -1);
// Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information):
std::replace(entity_map_it->second.begin(), entity_map_it->second.end(), -1, -correct_extruder_id-1);
return &(entity_map_it->second);
if (entity_map_it != entity_map.end()) {
overrides = &entity_map_it->second;
overrides->resize(num_of_copies, -1);
// Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information):
std::replace(overrides->begin(), overrides->end(), -1, -correct_extruder_id-1);
}
return overrides;
}

View file

@ -5,6 +5,10 @@
#include "../libslic3r.h"
#include <utility>
#include <boost/container/small_vector.hpp>
namespace Slic3r {
class Print;
@ -23,8 +27,19 @@ public:
return something_overridden;
}
// When allocating extruder overrides of an object's ExtrusionEntity, overrides for maximum 3 copies are allocated in place.
typedef boost::container::small_vector<int32_t, 3> ExtruderPerCopy;
class ExtruderOverrides
{
public:
ExtruderOverrides(const ExtruderPerCopy *overrides, const int correct_extruder_id) : m_overrides(overrides) {}
private:
const ExtruderPerCopy *m_overrides;
};
// This is called from GCode::process_layer - see implementation for further comments:
const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies);
const ExtruderPerCopy* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies);
// This function goes through all infill entities, decides which ones will be used for wiping and
// marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
@ -33,6 +48,11 @@ public:
void ensure_perimeters_infills_order(const Print& print);
bool is_overriddable(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const;
bool is_overriddable_and_mark(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) {
bool out = this->is_overriddable(ee, print_config, object, region);
this->something_overridable |= out;
return out;
}
void set_layer_tools_ptr(const LayerTools* lt) { m_layer_tools = lt; }
@ -41,14 +61,16 @@ private:
int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
// This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies);
void set_extruder_override(const ExtrusionEntity* entity, size_t copy_id, int extruder, size_t num_of_copies);
// Returns true in case that entity is not printed with its usual extruder for a given copy:
bool is_entity_overridden(const ExtrusionEntity* entity, size_t copy_id) const {
return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1);
auto it = entity_map.find(entity);
return it == entity_map.end() ? false : it->second[copy_id] != -1;
}
std::map<const ExtrusionEntity*, std::vector<int>> entity_map; // to keep track of who prints what
std::map<const ExtrusionEntity*, ExtruderPerCopy> entity_map; // to keep track of who prints what
bool something_overridable = false;
bool something_overridden = false;
const LayerTools* m_layer_tools; // so we know which LayerTools object this belongs to
};
@ -58,13 +80,7 @@ private:
class LayerTools
{
public:
LayerTools(const coordf_t z, const PrintConfig* print_config_ptr = nullptr) :
print_z(z),
has_object(false),
has_support(false),
has_wipe_tower(false),
wipe_tower_partitions(0),
wipe_tower_layer_height(0.) {}
LayerTools(const coordf_t z) : print_z(z) {}
// Changing these operators to epsilon version can make a problem in cases where support and object layers get close to each other.
// In case someone tries to do it, make sure you know what you're doing and test it properly (slice multiple objects at once with supports).
@ -72,20 +88,33 @@ public:
bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
bool is_extruder_order(unsigned int a, unsigned int b) const;
bool has_extruder(unsigned int extruder) const { return std::find(this->extruders.begin(), this->extruders.end(), extruder) != this->extruders.end(); }
coordf_t print_z;
bool has_object;
bool has_support;
// Return a zero based extruder from the region, or extruder_override if overriden.
unsigned int perimeter_extruder(const PrintRegion &region) const;
unsigned int infill_extruder(const PrintRegion &region) const;
unsigned int solid_infill_extruder(const PrintRegion &region) const;
// Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden.
unsigned int extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion &region) const;
coordf_t print_z = 0.;
bool has_object = false;
bool has_support = false;
// Zero based extruder IDs, ordered to minimize tool switches.
std::vector<unsigned int> extruders;
// If per layer extruder switches are inserted by the G-code preview slider, this value contains the new (1 based) extruder, with which the whole object layer is being printed with.
// If not overriden, it is set to 0.
unsigned int extruder_override = 0;
// Will there be anything extruded on this layer for the wipe tower?
// Due to the support layers possibly interleaving the object layers,
// wipe tower will be disabled for some support only layers.
bool has_wipe_tower;
bool has_wipe_tower = false;
// Number of wipe tower partitions to support the required number of tool switches
// and to support the wipe tower partitions above this one.
size_t wipe_tower_partitions;
coordf_t wipe_tower_layer_height;
size_t wipe_tower_partitions = 0;
coordf_t wipe_tower_layer_height = 0.;
// Custom G-code (color change, extruder switch, pause) to be performed before this layer starts to print.
const Model::CustomGCode *custom_gcode = nullptr;
WipingExtrusions& wiping_extrusions() {
m_wiping_extrusions.set_layer_tools_ptr(this);
@ -106,14 +135,20 @@ public:
// For the use case when each object is printed separately
// (print.config.complete_objects is true).
ToolOrdering(const PrintObject &object, unsigned int first_extruder = (unsigned int)-1, bool prime_multi_material = false);
ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material = false);
// For the use case when all objects are printed at once.
// (print.config.complete_objects is false).
ToolOrdering(const Print &print, unsigned int first_extruder = (unsigned int)-1, bool prime_multi_material = false);
ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material = false);
void clear() { m_layer_tools.clear(); }
// Only valid for non-sequential print:
// Assign a pointer to a custom G-code to the respective ToolOrdering::LayerTools.
// Ignore color changes, which are performed on a layer and for such an extruder, that the extruder will not be printing above that layer.
// If multiple events are planned over a span of a single layer, use the last one.
void assign_custom_gcodes(const Print &print);
// Get the first extruder printing, including the extruder priming areas, returns -1 if there is no layer printed.
unsigned int first_extruder() const { return m_first_printing_extruder; }
@ -123,25 +158,9 @@ public:
// For a multi-material print, the printing extruders are ordered in the order they shall be primed.
const std::vector<unsigned int>& all_extruders() const { return m_all_printing_extruders; }
template<class Self> static auto tools_for_layer(Self& self, coordf_t print_z) -> decltype (*self.m_layer_tools.begin())
{
auto it_layer_tools = std::lower_bound(self.m_layer_tools.begin(), self.m_layer_tools.end(), LayerTools(print_z - EPSILON));
assert(it_layer_tools != self.m_layer_tools.end());
coordf_t dist_min = std::abs(it_layer_tools->print_z - print_z);
for (++ it_layer_tools; it_layer_tools != self.m_layer_tools.end(); ++it_layer_tools) {
coordf_t d = std::abs(it_layer_tools->print_z - print_z);
if (d >= dist_min)
break;
dist_min = d;
}
-- it_layer_tools;
assert(dist_min < EPSILON);
return *it_layer_tools;
}
// Find LayerTools with the closest print_z.
LayerTools& tools_for_layer(coordf_t print_z) { return tools_for_layer(*this, print_z); }
const LayerTools& tools_for_layer(coordf_t print_z) const { return tools_for_layer(*this, print_z); }
const LayerTools& tools_for_layer(coordf_t print_z) const;
LayerTools& tools_for_layer(coordf_t print_z) { return const_cast<LayerTools&>(std::as_const(*this).tools_for_layer(print_z)); }
const LayerTools& front() const { return m_layer_tools.front(); }
const LayerTools& back() const { return m_layer_tools.back(); }
@ -153,7 +172,7 @@ public:
private:
void initialize_layers(std::vector<coordf_t> &zs);
void collect_extruders(const PrintObject &object);
void collect_extruders(const PrintObject &object, const std::vector<std::pair<double, unsigned int>> &per_layer_extruder_switches);
void reorder_extruders(unsigned int last_extruder_id);
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z);
void collect_extruder_statistics(bool prime_multi_material);
@ -166,7 +185,6 @@ private:
// All extruders, which extrude some material over m_layer_tools.
std::vector<unsigned int> m_all_printing_extruders;
const PrintConfig* m_print_config_ptr = nullptr;
};

View file

@ -1261,7 +1261,9 @@ namespace Slic3r {
if (line.has_e())
{
set_axis_origin(E, get_axis_position(E) - line.e() * lengthsScaleFactor);
// extruder coordinate can grow to the point where its float representation does not allow for proper addition with small increments,
// we set the value taken from the G92 line as the new current position for it
set_axis_position(E, line.e() * lengthsScaleFactor);
anyFound = true;
}
else

View file

@ -19,12 +19,13 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
this->config.apply(print_config, true);
m_extrusion_axis = this->config.get_extrusion_axis();
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
m_max_acceleration = (print_config.gcode_flavor.value == gcfMarlin) ?
print_config.machine_max_acceleration_extruding.values.front() : 0;
m_max_acceleration = std::lrint((print_config.gcode_flavor.value == gcfMarlin) ?
print_config.machine_max_acceleration_extruding.values.front() : 0);
}
void GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids)
void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids)
{
std::sort(extruder_ids.begin(), extruder_ids.end());
m_extruders.clear();
m_extruders.reserve(extruder_ids.size());
for (unsigned int extruder_id : extruder_ids)
@ -247,9 +248,9 @@ std::string GCodeWriter::toolchange_prefix() const
std::string GCodeWriter::toolchange(unsigned int extruder_id)
{
// set the new extruder
auto it_extruder = std::lower_bound(m_extruders.begin(), m_extruders.end(), Extruder::key(extruder_id));
assert(it_extruder != m_extruders.end());
m_extruder = const_cast<Extruder*>(&*it_extruder);
auto it_extruder = Slic3r::lower_bound_by_predicate(m_extruders.begin(), m_extruders.end(), [extruder_id](const Extruder &e) { return e.id() < extruder_id; });
assert(it_extruder != m_extruders.end() && it_extruder->id() == extruder_id);
m_extruder = &*it_extruder;
// return the toolchange command
// if we are running a single-extruder setup, just set the extruder and return nothing

View file

@ -13,7 +13,7 @@ namespace Slic3r {
// Additional Codes which can be set by user using DoubleSlider
static constexpr char ColorChangeCode[] = "M600";
static constexpr char PausePrintCode[] = "M601";
static constexpr char ExtruderChangeCode[] = "tool_change";
static constexpr char ToolChangeCode[] = "tool_change";
class GCodeWriter {
public:
@ -33,7 +33,7 @@ public:
std::string extrusion_axis() const { return m_extrusion_axis; }
void apply_print_config(const PrintConfig &print_config);
// Extruders are expected to be sorted in an increasing order.
void set_extruders(const std::vector<unsigned int> &extruder_ids);
void set_extruders(std::vector<unsigned int> extruder_ids);
const std::vector<Extruder>& extruders() const { return m_extruders; }
std::vector<unsigned int> extruder_ids() const {
std::vector<unsigned int> out;
@ -74,7 +74,8 @@ public:
Vec3d get_position() const { return m_pos; }
private:
std::vector<Extruder> m_extruders;
// Extruders are sorted by their ID, so that binary search is possible.
std::vector<Extruder> m_extruders;
std::string m_extrusion_axis;
bool m_single_extruder_multi_material;
Extruder* m_extruder;

View file

@ -126,7 +126,7 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
if (add_default_instances)
model.add_default_instances();
update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z.gcodes, config);
return model;
}
@ -163,7 +163,7 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
if (add_default_instances)
model.add_default_instances();
update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z.gcodes, config);
return model;
}
@ -598,21 +598,6 @@ std::string Model::propose_export_file_name_and_path(const std::string &new_exte
return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string();
}
std::vector<std::pair<double, DynamicPrintConfig>> Model::get_custom_tool_changes(double default_layer_height, size_t num_extruders) const
{
std::vector<std::pair<double, DynamicPrintConfig>> custom_tool_changes;
for (const CustomGCode& custom_gcode : custom_gcode_per_print_z)
if (custom_gcode.gcode == ExtruderChangeCode) {
DynamicPrintConfig config;
// If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
config.set_key_value("extruder", new ConfigOptionInt(custom_gcode.extruder > num_extruders ? 0 : custom_gcode.extruder));
// For correct extruders(tools) changing, we should decrease custom_gcode.height value by one default layer height
custom_tool_changes.push_back({ custom_gcode.print_z - default_layer_height, config });
}
return custom_tool_changes;
}
ModelObject::~ModelObject()
{
this->clear_volumes();
@ -860,7 +845,7 @@ TriangleMesh ModelObject::mesh() const
}
// Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
// Currently used by ModelObject::mesh(), to calculate the 2D envelope for 2D platter
// Currently used by ModelObject::mesh(), to calculate the 2D envelope for 2D plater
// and to display the object statistics at ModelObject::print_info().
TriangleMesh ModelObject::raw_mesh() const
{
@ -1863,6 +1848,19 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
return ret;
}
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Model &model, size_t num_extruders)
{
std::vector<std::pair<double, unsigned int>> custom_tool_changes;
for (const Model::CustomGCode &custom_gcode : model.custom_gcode_per_print_z.gcodes)
if (custom_gcode.gcode == ToolChangeCode) {
// If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast<unsigned int>(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder));
}
return custom_tool_changes;
}
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing.
bool model_object_list_equal(const Model &model_old, const Model &model_new)

View file

@ -240,7 +240,7 @@ public:
// A mesh containing all transformed instances of this object.
TriangleMesh mesh() const;
// Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
// Currently used by ModelObject::mesh() and to calculate the 2D envelope for 2D platter.
// Currently used by ModelObject::mesh() and to calculate the 2D envelope for 2D plater.
TriangleMesh raw_mesh() const;
// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
TriangleMesh full_raw_mesh() const;
@ -403,13 +403,9 @@ public:
int object_idx{ -1 };
int volume_idx{ -1 };
Vec3d mesh_offset{ Vec3d::Zero() };
#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
Geometry::Transformation transform;
template<class Archive> void serialize(Archive& ar) { ar(input_file, object_idx, volume_idx, mesh_offset, transform); }
#else
template<class Archive> void serialize(Archive& ar) { ar(input_file, object_idx, volume_idx, mesh_offset); }
#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
};
Source source;
@ -771,12 +767,34 @@ public:
double print_z;
std::string gcode;
int extruder; // 0 - "gcode" will be applied for whole print
// else - "gcode" will be applied only for "extruder" print
int extruder; // Informative value for ColorChangeCode and ToolChangeCode
// "gcode" == ColorChangeCode => M600 will be applied for "extruder" extruder
// "gcode" == ToolChangeCode => for whole print tool will be switched to "extruder" extruder
std::string color; // if gcode is equal to PausePrintCode,
// this field is used for save a short message shown on Printer display
};
std::vector<CustomGCode> custom_gcode_per_print_z;
struct CustomGCodeInfo
{
enum MODE
{
SingleExtruder, // single extruder printer preset is selected
MultiAsSingle, // multiple extruder printer preset is selected, but
// this mode works just for Single extruder print
// (For all print from objects settings is used just one extruder)
MultiExtruder // multiple extruder printer preset is selected
} mode;
std::vector<CustomGCode> gcodes;
bool operator==(const CustomGCodeInfo& rhs) const
{
return (rhs.mode == this->mode ) &&
(rhs.gcodes == this->gcodes );
}
bool operator!=(const CustomGCodeInfo& rhs) const { return !(*this == rhs); }
}
custom_gcode_per_print_z;
// Default constructor assigns a new ID to the model.
Model() { assert(this->id().valid()); }
@ -842,9 +860,6 @@ public:
// Propose an output path, replace extension. The new_extension shall contain the initial dot.
std::string propose_export_file_name_and_path(const std::string &new_extension) const;
// from custom_gcode_per_print_z get just tool_change codes
std::vector<std::pair<double, DynamicPrintConfig>> get_custom_tool_changes(double default_layer_height, size_t num_extruders) const;
private:
explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); };
void assign_new_unique_ids_recursive();
@ -861,6 +876,10 @@ private:
#undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE
#undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
extern std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Model &model, size_t num_extruders);
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing.
extern bool model_object_list_equal(const Model &model_old, const Model &model_new);

View file

@ -332,6 +332,21 @@ namespace client
return expr();
}
expr unary_integer(const Iterator start_pos) const
{
switch (this->type) {
case TYPE_INT :
return expr<Iterator>(this->i(), start_pos, this->it_range.end());
case TYPE_DOUBLE:
return expr<Iterator>((int)(this->d()), start_pos, this->it_range.end());
default:
this->throw_exception("Cannot convert to integer.");
}
assert(false);
// Suppress compiler warnings.
return expr();
}
expr unary_not(const Iterator start_pos) const
{
switch (this->type) {
@ -415,6 +430,22 @@ namespace client
return *this;
}
expr &operator%=(const expr &rhs)
{
this->throw_if_not_numeric("Cannot divide a non-numeric type.");
rhs.throw_if_not_numeric("Cannot divide with a non-numeric type.");
if ((this->type == TYPE_INT) ? (rhs.i() == 0) : (rhs.d() == 0.))
rhs.throw_exception("Division by zero");
if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) {
double d = std::fmod(this->as_d(), rhs.as_d());
this->data.d = d;
this->type = TYPE_DOUBLE;
} else
this->data.i %= rhs.i();
this->it_range = boost::iterator_range<Iterator>(this->it_range.begin(), rhs.it_range.end());
return *this;
}
static void to_string2(expr &self, std::string &out)
{
out = self.to_string();
@ -1087,6 +1118,7 @@ namespace client
unary_expression(_r1) [_val = _1]
>> *( (lit('*') > unary_expression(_r1) ) [_val *= _1]
| (lit('/') > unary_expression(_r1) ) [_val /= _1]
| (lit('%') > unary_expression(_r1) ) [_val %= _1]
);
multiplicative_expression.name("multiplicative_expression");
@ -1107,6 +1139,8 @@ namespace client
{ out = value.unary_minus(out.it_range.begin()); }
static void not_(expr<Iterator> &value, expr<Iterator> &out)
{ out = value.unary_not(out.it_range.begin()); }
static void to_int(expr<Iterator> &value, expr<Iterator> &out)
{ out = value.unary_integer(out.it_range.begin()); }
};
unary_expression = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> (
scalar_variable_reference(_r1) [ _val = _1 ]
@ -1118,6 +1152,8 @@ namespace client
[ px::bind(&expr<Iterator>::min, _val, _2) ]
| (kw["max"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')')
[ px::bind(&expr<Iterator>::max, _val, _2) ]
//FIXME this is likley not correct
| (kw["int"] > '(' > unary_expression(_r1) /* > ')' */ ) [ px::bind(&FactorActions::to_int, _1, _val) ]
| (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ]
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
| (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ]

View file

@ -41,7 +41,7 @@ public:
// Fill in the template using a macro processing language.
// Throws std::runtime_error on syntax or runtime error.
std::string process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr) const;
std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr) const;
// Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax.
// Throws std::runtime_error on syntax or runtime error.

View file

@ -478,7 +478,7 @@ static std::vector<PrintInstances> print_objects_from_model_object(const ModelOb
// Compare just the layer ranges and their layer heights, not the associated configs.
// Ignore the layer heights if check_layer_heights is false.
bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
{
if (lr1.size() != lr2.size())
return false;
@ -493,6 +493,37 @@ bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_c
return true;
}
// Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored.
static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<Model::CustomGCode> &va, const std::vector<Model::CustomGCode> &vb)
{
auto it_a = va.begin();
auto it_b = vb.begin();
while (it_a != va.end() && it_b != vb.end()) {
if (it_a != va.end() && it_a->gcode != ToolChangeCode) {
// Skip any CustomGCode items, which are not tool changes.
++ it_a;
continue;
}
if (it_b != vb.end() && it_b->gcode != ToolChangeCode) {
// Skip any CustomGCode items, which are not tool changes.
++ it_b;
continue;
}
if (it_a == va.end() || it_b == vb.end())
// va or vb contains more Tool Changes than the other.
return true;
assert(it_a->gcode == ToolChangeCode);
assert(it_b->gcode == ToolChangeCode);
if (*it_a != *it_b)
// The two Tool Changes differ.
return true;
++ it_a;
++ it_b;
}
// There is no change in custom Tool Changes.
return false;
}
// Collect diffs of configuration values at various containers,
// resolve the filament rectract overrides of extruder retract values.
void Print::config_diffs(
@ -638,48 +669,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
}
// Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs,
// considering custom_tool_change values
void assign(const t_layer_config_ranges &in, const std::vector<std::pair<double, DynamicPrintConfig>> &custom_tool_changes) {
m_ranges.clear();
m_ranges.reserve(in.size());
// Input ranges are sorted lexicographically. First range trims the other ranges.
coordf_t last_z = 0;
for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range : in)
if (range.first.second > last_z) {
coordf_t min_z = std::max(range.first.first, 0.);
if (min_z > last_z + EPSILON) {
m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
last_z = min_z;
}
if (range.first.second > last_z + EPSILON) {
const DynamicPrintConfig* cfg = &range.second;
m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
last_z = range.first.second;
}
}
// add ranges for extruder changes from custom_tool_changes
for (size_t i = 0; i < custom_tool_changes.size(); i++) {
const DynamicPrintConfig* cfg = &custom_tool_changes[i].second;
coordf_t cur_Z = custom_tool_changes[i].first;
coordf_t next_Z = i == custom_tool_changes.size()-1 ? DBL_MAX : custom_tool_changes[i+1].first;
if (cur_Z > last_z + EPSILON) {
if (i==0)
m_ranges.emplace_back(t_layer_height_range(last_z, cur_Z), nullptr);
m_ranges.emplace_back(t_layer_height_range(cur_Z, next_Z), cfg);
}
else if (next_Z > last_z + EPSILON)
m_ranges.emplace_back(t_layer_height_range(last_z, next_Z), cfg);
}
if (m_ranges.empty())
m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
else if (m_ranges.back().second == nullptr)
m_ranges.back().first.second = DBL_MAX;
else if (m_ranges.back().first.second != DBL_MAX)
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
}
const DynamicPrintConfig* config(const t_layer_height_range &range) const {
auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
// #ys_FIXME_COLOR
@ -733,17 +722,18 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
} else {
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
update_apply_status(custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes) ?
// The Tool Ordering and the Wipe Tower are no more valid.
this->invalidate_steps({ psWipeTower, psGCodeExport }) :
// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
this->invalidate_step(psGCodeExport));
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
}
if (model_object_list_equal(m_model, model)) {
// The object list did not change.
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
// But if custom gcode per layer height was changed
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
// we should stop background processing
update_apply_status(this->invalidate_step(psGCodeExport));
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
}
} else if (model_object_list_extended(m_model, model)) {
// Add new objects. Their volumes and configs will be synchronized later.
update_apply_status(this->invalidate_step(psGCodeExport));
@ -835,9 +825,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
for (PrintObject *print_object : m_objects)
print_object_status.emplace(PrintObjectStatus(print_object));
std::vector<std::pair<double, DynamicPrintConfig>> custom_tool_changes =
m_model.get_custom_tool_changes(m_default_object_config.layer_height, num_extruders);
// 3) Synchronize ModelObjects & PrintObjects.
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
ModelObject &model_object = *m_model.objects[idx_model_object];
@ -845,9 +832,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
const ModelObject& model_object_new = *model.objects[idx_model_object];
// ys_FIXME_COLOR
// const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges, custom_tool_changes);
const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
if (it_status->status == ModelObjectStatus::New)
// PrintObject instances will be added in the next loop.
continue;
@ -1015,8 +1000,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
PrintRegionConfig this_region_config;
bool this_region_config_set = false;
for (PrintObject *print_object : m_objects) {
if(m_force_update_print_regions && !custom_tool_changes.empty())
goto print_object_end;
const LayerRanges *layer_ranges;
{
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
@ -1572,9 +1555,13 @@ void Print::process()
obj->generate_support_material();
if (this->set_started(psWipeTower)) {
m_wipe_tower_data.clear();
m_tool_ordering.clear();
if (this->has_wipe_tower()) {
//this->set_status(95, L("Generating wipe tower"));
this->_make_wipe_tower();
} else if (! this->config().complete_objects.value) {
// Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches.
m_tool_ordering = ToolOrdering(*this, -1, false);
}
this->set_done(psWipeTower);
}
@ -1963,8 +1950,8 @@ const WipeTowerData& Print::wipe_tower_data(size_t extruders_cnt, double first_l
// If the wipe tower wasn't created yet, make sure the depth and brim_width members are set to default.
if (! is_step_done(psWipeTower) && extruders_cnt !=0) {
float width = m_config.wipe_tower_width;
float brim_spacing = nozzle_diameter * 1.25f - first_layer_height * (1. - M_PI_4);
float width = float(m_config.wipe_tower_width);
float brim_spacing = float(nozzle_diameter * 1.25f - first_layer_height * (1. - M_PI_4));
const_cast<Print*>(this)->m_wipe_tower_data.depth = (900.f/width) * float(extruders_cnt - 1);
const_cast<Print*>(this)->m_wipe_tower_data.brim_width = 4.5f * brim_spacing;
@ -1990,6 +1977,7 @@ void Print::_make_wipe_tower()
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, true);
if (! m_wipe_tower_data.tool_ordering.has_wipe_tower())
// Don't generate any wipe tower.
return;
@ -2107,13 +2095,6 @@ void Print::_make_wipe_tower()
m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
}
// Returns extruder this eec should be printed with, according to PrintRegion config
int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region)
{
return is_infill(fill.role()) ? std::max<int>(0, (is_solid_infill(fill.entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1) :
std::max<int>(region.config().perimeter_extruder.value - 1, 0);
}
// Generate a recommended G-code output file name based on the format template, default extension, and template parameters
// (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics.
// Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before G-code is finalized).

View file

@ -15,6 +15,8 @@
#include "GCode/ThumbnailData.hpp"
#endif // ENABLE_THUMBNAIL_GENERATOR
#include "libslic3r.h"
namespace Slic3r {
class Print;
@ -25,8 +27,19 @@ class GCodePreviewData;
// Print step IDs for keeping track of the print state.
enum PrintStep {
psSkirt, psBrim, psWipeTower, psGCodeExport, psCount,
psSkirt,
psBrim,
// Synonym for the last step before the Wipe Tower / Tool Ordering, for the G-code preview slider to understand that
// all the extrusions are there for the layer slider to add color changes etc.
psExtrusionPaths = psBrim,
psWipeTower,
// psToolOrdering is a synonym to psWipeTower, as the Wipe Tower calculates and modifies the ToolOrdering,
// while if printing without the Wipe Tower, the ToolOrdering is calculated as well.
psToolOrdering = psWipeTower,
psGCodeExport,
psCount,
};
enum PrintObjectStep {
posSlice, posPerimeters, posPrepareInfill,
posInfill, posSupportMaterial, posCount,
@ -50,7 +63,7 @@ public:
// Average diameter of nozzles participating on extruding this region.
coordf_t bridging_height_avg(const PrintConfig &print_config) const;
// Collect extruder indices used to print this region's object.
// Collect 0-based extruder indices used to print this region's object.
void collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const;
static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig &region_config, std::vector<unsigned int> &object_extruders);
@ -116,8 +129,21 @@ public:
size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); }
size_t layer_count() const { return m_layers.size(); }
void clear_layers();
Layer* get_layer(int idx) { return m_layers[idx]; }
const Layer* get_layer(int idx) const { return m_layers[idx]; }
const Layer* get_layer(int idx) const { return m_layers[idx]; }
Layer* get_layer(int idx) { return m_layers[idx]; }
// Get a layer exactly at print_z.
const Layer* get_layer_at_printz(coordf_t print_z) const {
auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [print_z](const Layer *layer) { return layer->print_z < print_z; });
return (it == m_layers.end() || (*it)->print_z != print_z) ? nullptr : *it;
}
Layer* get_layer_at_printz(coordf_t print_z) { return const_cast<Layer*>(std::as_const(*this).get_layer_at_printz(print_z)); }
// Get a layer approximately at print_z.
const Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon) const {
coordf_t limit = print_z + epsilon;
auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [limit](const Layer *layer) { return layer->print_z < limit; });
return (it == m_layers.end() || (*it)->print_z < print_z - epsilon) ? nullptr : *it;
}
Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon) { return const_cast<Layer*>(std::as_const(*this).get_layer_at_printz(print_z, epsilon)); }
// print_z: top of the layer; slice_z: center of the layer.
Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
@ -219,7 +245,7 @@ struct WipeTowerData
// Following section will be consumed by the GCodeGenerator.
// Tool ordering of a non-sequential print has to be known to calculate the wipe tower.
// Cache it here, so it does not need to be recalculated during the G-code generation.
ToolOrdering tool_ordering;
ToolOrdering &tool_ordering;
// Cache of tool changes per print layer.
std::unique_ptr<std::vector<WipeTower::ToolChangeResult>> priming;
std::vector<std::vector<WipeTower::ToolChangeResult>> tool_changes;
@ -232,7 +258,6 @@ struct WipeTowerData
float brim_width;
void clear() {
tool_ordering.clear();
priming.reset(nullptr);
tool_changes.clear();
final_purge.reset(nullptr);
@ -241,6 +266,14 @@ struct WipeTowerData
depth = 0.f;
brim_width = 0.f;
}
private:
// Only allow the WipeTowerData to be instantiated internally by Print,
// as this WipeTowerData shares reference to Print::m_tool_ordering.
friend class Print;
WipeTowerData(ToolOrdering &tool_ordering) : tool_ordering(tool_ordering) { clear(); }
WipeTowerData(const WipeTowerData & /* rhs */) = delete;
WipeTowerData &operator=(const WipeTowerData & /* rhs */) = delete;
};
struct PrintStatistics
@ -345,6 +378,7 @@ public:
const PrintConfig& config() const { return m_config; }
const PrintObjectConfig& default_object_config() const { return m_default_object_config; }
const PrintRegionConfig& default_region_config() const { return m_default_region_config; }
//FIXME returning const vector to non-const PrintObject*, caller could modify PrintObjects!
const PrintObjectPtrs& objects() const { return m_objects; }
PrintObject* get_object(size_t idx) { return m_objects[idx]; }
const PrintObject* get_object(size_t idx) const { return m_objects[idx]; }
@ -353,9 +387,6 @@ public:
// If zero, then the print is empty and the print shall not be executed.
unsigned int num_object_instances() const;
// Returns extruder this eec should be printed with, according to PrintRegion config:
static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region);
const ExtrusionEntityCollection& skirt() const { return m_skirt; }
const ExtrusionEntityCollection& brim() const { return m_brim; }
@ -364,14 +395,13 @@ public:
// Wipe tower support.
bool has_wipe_tower() const;
const WipeTowerData& wipe_tower_data(size_t extruders_cnt = 0, double first_layer_height = 0., double nozzle_diameter = 0.) const;
const ToolOrdering& tool_ordering() const { return m_tool_ordering; }
std::string output_filename(const std::string &filename_base = std::string()) const override;
// Accessed by SupportMaterial
const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; }
// force update of PrintRegions, when custom_tool_change is not empty and (Re)Slicing is started
void set_force_update_print_regions(bool force_update_print_regions) { m_force_update_print_regions = force_update_print_regions; }
const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } // #ys_FIXME just for testing
protected:
// methods for handling regions
@ -410,14 +440,12 @@ private:
ExtrusionEntityCollection m_brim;
// Following section will be consumed by the GCodeGenerator.
WipeTowerData m_wipe_tower_data;
ToolOrdering m_tool_ordering;
WipeTowerData m_wipe_tower_data {m_tool_ordering};
// Estimated print time, filament consumed.
PrintStatistics m_print_statistics;
// flag used
bool m_force_update_print_regions = false;
// To allow GCode to set the Print's GCodeExport step status.
friend class GCode;
// Allow PrintObject to access m_mutex and m_cancel_callback.

View file

@ -37,7 +37,7 @@ void PrintBase::update_object_placeholders(DynamicConfig &config, const std::str
}
}
config.set_key_value("year", new ConfigOptionStrings(v_scale));
config.set_key_value("scale", new ConfigOptionStrings(v_scale));
if (! input_file.empty()) {
// get basename with and without suffix
const std::string input_filename = boost::filesystem::path(input_file).filename().string();

View file

@ -1337,7 +1337,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back("OctoPrint");
def->enum_labels.push_back("Duet");
def->enum_labels.push_back("FlashAir");
def->enum_values.push_back("AstroBox");
def->enum_labels.push_back("AstroBox");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<PrintHostType>(htOctoPrint));

View file

@ -300,26 +300,14 @@ std::vector<double> layer_height_profile_adaptive(const SlicingParameters& slici
layer_height_profile.push_back(print_z);
layer_height_profile.push_back(height);
print_z += height;
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
layer_height_profile.push_back(print_z);
layer_height_profile.push_back(height);
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
}
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
double z_gap = slicing_params.object_print_z_height() - layer_height_profile[layer_height_profile.size() - 2];
if (z_gap > 0.0)
{
layer_height_profile.push_back(slicing_params.object_print_z_height());
layer_height_profile.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, z_gap));
}
#else
double last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]);
layer_height_profile.push_back(last);
layer_height_profile.push_back(slicing_params.first_object_layer_height);
layer_height_profile.push_back(slicing_params.object_print_z_height());
layer_height_profile.push_back(slicing_params.first_object_layer_height);
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
return layer_height_profile;
}

View file

@ -41,24 +41,6 @@
#define ENABLE_THUMBNAIL_GENERATOR (1 && ENABLE_2_2_0_ALPHA1)
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG (0 && ENABLE_THUMBNAIL_GENERATOR)
// Enable adaptive layer height profile
#define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE (1 && ENABLE_2_2_0_ALPHA1)
// Enable grayed variant for gizmos icons in non activable state
#define ENABLE_GIZMO_ICONS_NON_ACTIVABLE_STATE (1 && ENABLE_2_2_0_ALPHA1)
// Enable fix for view toolbar background not showing up on Mac with dark mode
#define ENABLE_VIEW_TOOLBAR_BACKGROUND_FIX (1 && ENABLE_2_2_0_ALPHA1)
// Enable selection for missing files in reload from disk command
#define ENABLE_RELOAD_FROM_DISK_MISSING_SELECTION (1 && ENABLE_2_2_0_ALPHA1)
// Enable closing 3Dconnextion imgui settings dialog by clicking on [X] and [Close] buttons
#define ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG (1 && ENABLE_2_2_0_ALPHA1)
// Enable not applying volume transformation during 3mf and amf loading, but keeping it as a ModelVolume member
#define ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE (1 && ENABLE_2_2_0_ALPHA1)
//==================
// 2.2.0.beta1 techs
@ -68,4 +50,13 @@
// Enable using Y axis of 3Dconnexion devices as zoom
#define ENABLE_3DCONNEXION_Y_AS_ZOOM (1 && ENABLE_2_2_0_BETA1)
// Enable a modified version of the toolbar textures where all the icons are separated by 1 pixel
#define ENABLE_MODIFIED_TOOLBAR_TEXTURES (1 && ENABLE_2_2_0_BETA1)
// Enable configurable paths export (fullpath or not) to 3mf and amf
#define ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF (1 && ENABLE_2_2_0_BETA1)
// Enable 6 degrees of freedom camera
#define ENABLE_6DOF_CAMERA (1 && ENABLE_2_2_0_BETA1)
#endif // _technologies_h_

View file

@ -158,6 +158,53 @@ inline std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
// Variant of std::lower_bound() with compare predicate, but without the key.
// This variant is very useful in case that the T type is large or it does not even have a public constructor.
template<class ForwardIt, class LowerThanKeyPredicate>
ForwardIt lower_bound_by_predicate(ForwardIt first, ForwardIt last, LowerThanKeyPredicate lower_thank_key)
{
ForwardIt it;
typename std::iterator_traits<ForwardIt>::difference_type count, step;
count = std::distance(first, last);
while (count > 0) {
it = first;
step = count / 2;
std::advance(it, step);
if (lower_thank_key(*it)) {
first = ++it;
count -= step + 1;
}
else
count = step;
}
return first;
}
// from https://en.cppreference.com/w/cpp/algorithm/lower_bound
template<class ForwardIt, class T, class Compare=std::less<>>
ForwardIt binary_find(ForwardIt first, ForwardIt last, const T& value, Compare comp={})
{
// Note: BOTH type T and the type after ForwardIt is dereferenced
// must be implicitly convertible to BOTH Type1 and Type2, used in Compare.
// This is stricter than lower_bound requirement (see above)
first = std::lower_bound(first, last, value, comp);
return first != last && !comp(value, *first) ? first : last;
}
// from https://en.cppreference.com/w/cpp/algorithm/lower_bound
template<class ForwardIt, class LowerThanKeyPredicate, class EqualToKeyPredicate>
ForwardIt binary_find_by_predicate(ForwardIt first, ForwardIt last, LowerThanKeyPredicate lower_thank_key, EqualToKeyPredicate equal_to_key)
{
// Note: BOTH type T and the type after ForwardIt is dereferenced
// must be implicitly convertible to BOTH Type1 and Type2, used in Compare.
// This is stricter than lower_bound requirement (see above)
first = lower_bound_by_predicate(first, last, lower_thank_key);
return first != last && equal_to_key(*first) ? first : last;
}
template<typename T>
static inline T sqr(T x)
{

View file

@ -26,8 +26,20 @@ bool open_zip(mz_zip_archive *zip, const char *fname, bool isread)
return false;
}
return isread ? mz_zip_reader_init_cfile(zip, f, 0, 0)
: mz_zip_writer_init_cfile(zip, f, 0);
bool res = false;
if (isread)
{
res = mz_zip_reader_init_cfile(zip, f, 0, 0);
if (!res)
// if we get here it means we tried to open a non-zip file
// we need to close the file here because the call to mz_zip_get_cfile() made into close_zip() returns a null pointer
// see: https://github.com/prusa3d/PrusaSlicer/issues/3536
fclose(f);
}
else
res = mz_zip_writer_init_cfile(zip, f, 0);
return res;
}
bool close_zip(mz_zip_archive *zip, bool isread)

View file

@ -63,6 +63,7 @@
#include <boost/bind.hpp>
#include <boost/config.hpp>
#include <boost/config/warning_disable.hpp>
#include <boost/container/small_vector.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem.hpp>