1
0
mirror of https://github.com/FULU-Foundation/OrcaSlicer-bambulab.git synced 2026-08-01 11:25:50 +00:00

Merge branch 'dev2' into lm_sla_supports_ui

This commit is contained in:
Lukas Matena
2018-09-21 11:43:30 +02:00
135 changed files with 6108 additions and 9593 deletions
+2 -2
View File
@@ -21,7 +21,7 @@ public:
BoundingBoxBase(const std::vector<PointClass>& points) : min(PointClass::Zero()), max(PointClass::Zero())
{
if (points.empty())
CONFESS("Empty point set supplied to BoundingBoxBase constructor");
throw std::invalid_argument("Empty point set supplied to BoundingBoxBase constructor");
typename std::vector<PointClass>::const_iterator it = points.begin();
this->min = *it;
@@ -65,7 +65,7 @@ public:
BoundingBox3Base(const std::vector<PointClass>& points)
{
if (points.empty())
CONFESS("Empty point set supplied to BoundingBox3Base constructor");
throw std::invalid_argument("Empty point set supplied to BoundingBox3Base constructor");
typename std::vector<PointClass>::const_iterator it = points.begin();
this->min = *it;
this->max = *it;
+2 -1
View File
@@ -437,7 +437,8 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
// perform triangulation
std::list<TPPLPoly> output;
int res = TPPLPartition().Triangulate_MONO(&input, &output);
if (res != 1) CONFESS("Triangulation failed");
if (res != 1)
throw std::runtime_error("Triangulation failed");
// convert output polygons
for (std::list<TPPLPoly>::iterator poly = output.begin(); poly != output.end(); ++poly) {
+2 -2
View File
@@ -120,8 +120,8 @@ public:
ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), m_role(rhs.m_role) {}
// ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(0) {};
ExtrusionPath& operator=(const ExtrusionPath &rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = rhs.polyline; return *this; }
ExtrusionPath& operator=(ExtrusionPath &&rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = std::move(rhs.polyline); return *this; }
ExtrusionPath& operator=(const ExtrusionPath &rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = rhs.polyline; return *this; }
ExtrusionPath& operator=(ExtrusionPath &&rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = std::move(rhs.polyline); return *this; }
ExtrusionPath* clone() const { return new ExtrusionPath (*this); }
void reverse() { this->polyline.reverse(); }
@@ -88,7 +88,7 @@ public:
// Following methods shall never be called on an ExtrusionEntityCollection.
Polyline as_polyline() const {
CONFESS("Calling as_polyline() on a ExtrusionEntityCollection");
throw std::runtime_error("Calling as_polyline() on a ExtrusionEntityCollection");
return Polyline();
};
@@ -98,7 +98,7 @@ public:
}
double length() const override {
CONFESS("Calling length() on a ExtrusionEntityCollection");
throw std::runtime_error("Calling length() on a ExtrusionEntityCollection");
return 0.;
}
};
+7 -7
View File
@@ -34,7 +34,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
{
// Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id;
double fill_density = layerm.region()->config.fill_density;
double fill_density = layerm.region()->config().fill_density;
Flow infill_flow = layerm.flow(frInfill);
Flow solid_infill_flow = layerm.flow(frSolidInfill);
Flow top_solid_infill_flow = layerm.flow(frTopSolidInfill);
@@ -69,7 +69,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) {
group_attrib[i].is_solid = true;
group_attrib[i].flow_width = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width;
group_attrib[i].pattern = surface.is_external() ? layerm.region()->config.external_fill_pattern.value : ipRectilinear;
group_attrib[i].pattern = surface.is_external() ? layerm.region()->config().external_fill_pattern.value : ipRectilinear;
}
}
// Loop through solid groups, find compatible groups and append them to this one.
@@ -152,7 +152,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
for (const Surface &surface : surfaces) {
if (surface.surface_type == stInternalVoid)
continue;
InfillPattern fill_pattern = layerm.region()->config.fill_pattern.value;
InfillPattern fill_pattern = layerm.region()->config().fill_pattern.value;
double density = fill_density;
FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill :
(surface.is_solid() ? frSolidInfill : frInfill);
@@ -161,7 +161,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
if (surface.is_solid()) {
density = 100.;
fill_pattern = (surface.is_external() && ! is_bridge) ?
layerm.region()->config.external_fill_pattern.value :
layerm.region()->config().external_fill_pattern.value :
ipRectilinear;
} else if (density <= 0)
continue;
@@ -190,7 +190,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// layer height
Flow internal_flow = layerm.region()->flow(
frInfill,
layerm.layer()->object()->config.layer_height.value, // TODO: handle infill_every_layers?
layerm.layer()->object()->config().layer_height.value, // TODO: handle infill_every_layers?
false, // no bridge
false, // no first layer
-1, // auto width
@@ -205,7 +205,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
double link_max_length = 0.;
if (! is_bridge) {
#if 0
link_max_length = layerm.region()->config.get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing());
link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing());
// printf("flow spacing: %f, is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length);
#else
if (density > 80.) // 80%
@@ -215,7 +215,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
f->layer_id = layerm.layer()->id();
f->z = layerm.layer()->print_z;
f->angle = float(Geometry::deg2rad(layerm.region()->config.fill_angle.value));
f->angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
// Maximum length of the perimeter segment linking two infill lines.
f->link_max_length = scale_(link_max_length);
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
+1 -1
View File
@@ -34,7 +34,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipArchimedeanChords: return new FillArchimedeanChords();
case ipHilbertCurve: return new FillHilbertCurve();
case ipOctagramSpiral: return new FillOctagramSpiral();
default: CONFESS("unknown type"); return nullptr;
default: throw std::invalid_argument("unknown type");;
}
}
+14 -14
View File
@@ -28,7 +28,7 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent
{
// we need layer height unless it's a bridge
if (height <= 0 && bridge_flow_ratio == 0)
CONFESS("Invalid flow height supplied to new_from_config_width()");
throw std::invalid_argument("Invalid flow height supplied to new_from_config_width()");
float w;
if (bridge_flow_ratio > 0) {
@@ -53,7 +53,7 @@ Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height,
{
// we need layer height unless it's a bridge
if (height <= 0 && !bridge)
CONFESS("Invalid flow height supplied to new_from_spacing()");
throw std::invalid_argument("Invalid flow height supplied to new_from_spacing()");
// Calculate width from spacing.
// For normal extrusons, extrusion width is wider than the spacing due to the rounding and squishing of the extrusions.
// For bridge extrusions, the extrusions are placed with a tiny BRIDGE_EXTRA_SPACING gaps between the threads.
@@ -111,23 +111,23 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
return Flow::new_from_config_width(
frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(object->config.support_material_extrusion_width.value > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width,
// if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
(object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width,
// if object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value),
// bridge_flow_ratio
0.f);
}
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
{
const auto &width = (object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width;
const auto &width = (object->print()->config().first_layer_extrusion_width.value > 0) ? object->print()->config().first_layer_extrusion_width : object->config().support_material_extrusion_width;
return Flow::new_from_config_width(
frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(width.value > 0) ? width : object->config.extrusion_width,
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.first_layer_height.get_abs_value(object->config.layer_height.value)),
(width.value > 0) ? width : object->config().extrusion_width,
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value)),
// bridge_flow_ratio
0.f);
}
@@ -137,10 +137,10 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig
return Flow::new_from_config_width(
frSupportMaterialInterface,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(object->config.support_material_extrusion_width > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width,
// if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
(object->config().support_material_extrusion_width > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width,
// if object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_interface_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value),
// bridge_flow_ratio
0.f);
}
+10 -1
View File
@@ -1278,11 +1278,15 @@ namespace Slic3r {
double inv_sy = 1.0 / sy;
double inv_sz = 1.0 / sz;
Eigen::Matrix3d m3x3;
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> m3x3;
m3x3 << transform(0, 0) * inv_sx, transform(0, 1) * inv_sy, transform(0, 2) * inv_sz,
transform(1, 0) * inv_sx, transform(1, 1) * inv_sy, transform(1, 2) * inv_sz,
transform(2, 0) * inv_sx, transform(2, 1) * inv_sy, transform(2, 2) * inv_sz;
#if ENABLE_MODELINSTANCE_3D_ROTATION
Vec3d angles = m3x3.eulerAngles(2, 1, 0);
Vec3d rotation(angles(2), angles(1), angles(0));
#else
Eigen::AngleAxisd rotation;
rotation.fromRotationMatrix(m3x3);
@@ -1291,6 +1295,7 @@ namespace Slic3r {
return;
double angle_z = (rotation.axis() == Vec3d::UnitZ()) ? rotation.angle() : -rotation.angle();
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#if ENABLE_MODELINSTANCE_3D_OFFSET
instance.set_offset(offset);
@@ -1299,7 +1304,11 @@ namespace Slic3r {
instance.offset(1) = offset_y;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
instance.scaling_factor = sx;
#if ENABLE_MODELINSTANCE_3D_ROTATION
instance.set_rotation(rotation);
#else
instance.rotation = angle_z;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
}
bool _3MF_Importer::_handle_start_config(const char** attributes, unsigned int num_attributes)
+63 -3
View File
@@ -30,7 +30,12 @@
// 0 : .amf, .amf.xml and .zip.amf files saved by older slic3r. No version definition in them.
// 1 : Introduction of amf versioning. No other change in data saved into amf files.
#if ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
// 2 : Added z component of offset
// Added x and y components of rotation
#else
// 2 : Added z component of offset.
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
const unsigned int VERSION_AMF = 2;
#else
const unsigned int VERSION_AMF = 1;
@@ -127,6 +132,10 @@ struct AMFParserContext
#if ENABLE_MODELINSTANCE_3D_OFFSET
NODE_TYPE_DELTAZ, // amf/constellation/instance/deltaz
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
NODE_TYPE_RX, // amf/constellation/instance/rx
NODE_TYPE_RY, // amf/constellation/instance/ry
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
NODE_TYPE_RZ, // amf/constellation/instance/rz
NODE_TYPE_SCALE, // amf/constellation/instance/scale
NODE_TYPE_METADATA, // anywhere under amf/*/metadata
@@ -134,7 +143,11 @@ struct AMFParserContext
struct Instance {
#if ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
Instance() : deltax_set(false), deltay_set(false), deltaz_set(false), rx_set(false), ry_set(false), rz_set(false), scale_set(false) {}
#else
Instance() : deltax_set(false), deltay_set(false), deltaz_set(false), rz_set(false), scale_set(false) {}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#else
Instance() : deltax_set(false), deltay_set(false), rz_set(false), scale_set(false) {}
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
@@ -149,6 +162,14 @@ struct AMFParserContext
float deltaz;
bool deltaz_set;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
// Rotation around the X axis.
float rx;
bool rx_set;
// Rotation around the Y axis.
float ry;
bool ry_set;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
// Rotation around the Z axis.
float rz;
bool rz_set;
@@ -275,6 +296,12 @@ void AMFParserContext::startElement(const char *name, const char **atts)
else if (strcmp(name, "deltaz") == 0)
node_type_new = NODE_TYPE_DELTAZ;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
else if (strcmp(name, "rx") == 0)
node_type_new = NODE_TYPE_RX;
else if (strcmp(name, "ry") == 0)
node_type_new = NODE_TYPE_RY;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
else if (strcmp(name, "rz") == 0)
node_type_new = NODE_TYPE_RZ;
else if (strcmp(name, "scale") == 0)
@@ -339,7 +366,11 @@ void AMFParserContext::characters(const XML_Char *s, int len)
if (m_path.back() == NODE_TYPE_DELTAX ||
m_path.back() == NODE_TYPE_DELTAY ||
m_path.back() == NODE_TYPE_DELTAZ ||
m_path.back() == NODE_TYPE_RZ ||
#if ENABLE_MODELINSTANCE_3D_ROTATION
m_path.back() == NODE_TYPE_RX ||
m_path.back() == NODE_TYPE_RY ||
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
m_path.back() == NODE_TYPE_RZ ||
m_path.back() == NODE_TYPE_SCALE)
#else
if (m_path.back() == NODE_TYPE_DELTAX || m_path.back() == NODE_TYPE_DELTAY || m_path.back() == NODE_TYPE_RZ || m_path.back() == NODE_TYPE_SCALE)
@@ -391,6 +422,20 @@ void AMFParserContext::endElement(const char * /* name */)
m_value[0].clear();
break;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
case NODE_TYPE_RX:
assert(m_instance);
m_instance->rx = float(atof(m_value[0].c_str()));
m_instance->rx_set = true;
m_value[0].clear();
break;
case NODE_TYPE_RY:
assert(m_instance);
m_instance->ry = float(atof(m_value[0].c_str()));
m_instance->ry_set = true;
m_value[0].clear();
break;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
case NODE_TYPE_RZ:
assert(m_instance);
m_instance->rz = float(atof(m_value[0].c_str()));
@@ -541,12 +586,16 @@ void AMFParserContext::endDocument()
if (instance.deltax_set && instance.deltay_set) {
ModelInstance *mi = m_model.objects[object.second.idx]->add_instance();
#if ENABLE_MODELINSTANCE_3D_OFFSET
mi->set_offset(Vec3d((double)instance.deltax, (double)instance.deltay, (double)instance.deltaz));
mi->set_offset(Vec3d(instance.deltax_set ? (double)instance.deltax : 0.0, instance.deltay_set ? (double)instance.deltay : 0.0, instance.deltaz_set ? (double)instance.deltaz : 0.0));
#else
mi->offset(0) = instance.deltax;
mi->offset(1) = instance.deltay;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
mi->set_rotation(Vec3d(instance.rx_set ? (double)instance.rx : 0.0, instance.ry_set ? (double)instance.ry : 0.0, instance.rz_set ? (double)instance.rz : 0.0));
#else
mi->rotation = instance.rz_set ? instance.rz : 0.f;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
mi->scaling_factor = instance.scale_set ? instance.scale : 1.f;
}
}
@@ -800,7 +849,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
for (ModelVolume *volume : object->volumes) {
vertices_offsets.push_back(num_vertices);
if (! volume->mesh.repaired)
CONFESS("store_amf() requires repair()");
throw std::runtime_error("store_amf() requires repair()");
auto &stl = volume->mesh.stl;
if (stl.v_shared == nullptr)
stl_generate_shared_vertices(&stl);
@@ -850,6 +899,10 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
#if ENABLE_MODELINSTANCE_3D_OFFSET
" <deltaz>%lf</deltaz>\n"
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
" <rx>%lf</rx>\n"
" <ry>%lf</ry>\n"
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
" <rz>%lf</rz>\n"
" <scale>%lf</scale>\n"
" </instance>\n",
@@ -862,8 +915,15 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
instance->offset(0),
instance->offset(1),
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
instance->get_rotation(X),
instance->get_rotation(Y),
instance->get_rotation(Z),
#else
instance->rotation,
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
instance->scaling_factor);
//FIXME missing instance->scaling_factor
instances.append(buf);
}
+14 -2
View File
@@ -164,7 +164,11 @@ bool load_prus(const char *path, Model *model)
const char *zero_tag = "<zero>";
const char *zero_xml = strstr(scene_xml_data.data(), zero_tag);
float trafo[3][4] = { 0 };
#if ENABLE_MODELINSTANCE_3D_ROTATION
Vec3d instance_rotation = Vec3d::Zero();
#else
double instance_rotation = 0.;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
double instance_scaling_factor = 1.f;
#if ENABLE_MODELINSTANCE_3D_OFFSET
Vec3d instance_offset = Vec3d::Zero();
@@ -197,10 +201,14 @@ bool load_prus(const char *path, Model *model)
instance_scaling_factor = scale[0];
scale[0] = scale[1] = scale[2] = 1.;
}
#if ENABLE_MODELINSTANCE_3D_ROTATION
instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]);
#else
if (rotation[0] == 0. && rotation[1] == 0.) {
instance_rotation = - rotation[2];
rotation[2] = 0.;
}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
Eigen::Matrix3f mat_rot, mat_scale, mat_trafo;
mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) *
Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) *
@@ -366,8 +374,12 @@ bool load_prus(const char *path, Model *model)
model_object = model->add_object(name_utf8.data(), path, std::move(mesh));
volume = model_object->volumes.front();
ModelInstance *instance = model_object->add_instance();
instance->rotation = instance_rotation;
instance->scaling_factor = instance_scaling_factor;
#if ENABLE_MODELINSTANCE_3D_ROTATION
instance->set_rotation(instance_rotation);
#else
instance->rotation = instance_rotation;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
instance->scaling_factor = instance_scaling_factor;
#if ENABLE_MODELINSTANCE_3D_OFFSET
instance->set_offset(instance_offset);
#else
+243 -217
View File
@@ -13,6 +13,7 @@
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/find.hpp>
#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
#include <boost/nowide/iostream.hpp>
@@ -337,15 +338,15 @@ std::string WipeTowerIntegration::finalize(GCode &gcodegen)
std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObject &object)
{
std::vector<GCode::LayerToPrint> layers_to_print;
layers_to_print.reserve(object.layers.size() + object.support_layers.size());
layers_to_print.reserve(object.layers().size() + object.support_layers().size());
// Pair the object layers with the support layers by z.
size_t idx_object_layer = 0;
size_t idx_support_layer = 0;
while (idx_object_layer < object.layers.size() || idx_support_layer < object.support_layers.size()) {
while (idx_object_layer < object.layers().size() || idx_support_layer < object.support_layers().size()) {
LayerToPrint layer_to_print;
layer_to_print.object_layer = (idx_object_layer < object.layers.size()) ? object.layers[idx_object_layer ++] : nullptr;
layer_to_print.support_layer = (idx_support_layer < object.support_layers.size()) ? object.support_layers[idx_support_layer ++] : nullptr;
layer_to_print.object_layer = (idx_object_layer < object.layers().size()) ? object.layers()[idx_object_layer ++] : nullptr;
layer_to_print.support_layer = (idx_support_layer < object.support_layers().size()) ? object.support_layers()[idx_support_layer ++] : nullptr;
if (layer_to_print.object_layer && layer_to_print.support_layer) {
if (layer_to_print.object_layer->print_z < layer_to_print.support_layer->print_z - EPSILON) {
layer_to_print.support_layer = nullptr;
@@ -417,6 +418,12 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
{
PROFILE_CLEAR();
// Does the file exist? If so, we hope that it is still valid.
if (print->is_step_done(psGCodeExport) && boost::filesystem::exists(boost::filesystem::path(path)))
return;
print->set_started(psGCodeExport);
BOOST_LOG_TRIVIAL(info) << "Exporting G-code...";
// Remove the old g-code if it exists.
@@ -429,27 +436,34 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
if (file == nullptr)
throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
this->m_placeholder_parser_failed_templates.clear();
this->_do_export(*print, file, preview_data);
fflush(file);
if (ferror(file)) {
try {
m_placeholder_parser_failed_templates.clear();
this->_do_export(*print, file, preview_data);
fflush(file);
if (ferror(file)) {
fclose(file);
boost::nowide::remove(path_tmp.c_str());
throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
}
} catch (std::exception &ex) {
// Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
// Close and remove the file.
fclose(file);
boost::nowide::remove(path_tmp.c_str());
throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
throw;
}
fclose(file);
if (print->config.remaining_times.value)
{
if (print->config().remaining_times.value) {
m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
if (m_silent_time_estimator_enabled)
m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
}
if (! this->m_placeholder_parser_failed_templates.empty()) {
if (! m_placeholder_parser_failed_templates.empty()) {
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n";
for (const std::string &name : this->m_placeholder_parser_failed_templates)
for (const std::string &name : m_placeholder_parser_failed_templates)
msg += std::string("\t") + name + "\n";
msg += "\nPlease inspect the file ";
msg += path_tmp + " for error messages enclosed between\n";
@@ -459,12 +473,13 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
throw std::runtime_error(msg);
}
if (boost::nowide::rename(path_tmp.c_str(), path) != 0)
if (rename_file(path_tmp, path) != 0)
throw std::runtime_error(
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' +
"Is " + path_tmp + " locked?" + '\n');
BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished";
print->set_done(psGCodeExport);
// Write the profiler measurements to file
PROFILE_UPDATE();
@@ -477,66 +492,66 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// resets time estimators
m_normal_time_estimator.reset();
m_normal_time_estimator.set_dialect(print.config.gcode_flavor);
m_silent_time_estimator_enabled = (print.config.gcode_flavor == gcfMarlin) && print.config.silent_mode;
m_normal_time_estimator.set_dialect(print.config().gcode_flavor);
m_silent_time_estimator_enabled = (print.config().gcode_flavor == gcfMarlin) && print.config().silent_mode;
// Until we have a UI support for the other firmwares than the Marlin, use the hardcoded default values
// and let the user to enter the G-code limits into the start G-code.
// If the following block is enabled for other firmwares than the Marlin, then the function
// this->print_machine_envelope(file, print);
// shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor.
if (print.config.gcode_flavor.value == gcfMarlin) {
m_normal_time_estimator.set_max_acceleration(print.config.machine_max_acceleration_extruding.values[0]);
m_normal_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[0]);
m_normal_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[0]);
m_normal_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[0]);
if (print.config().gcode_flavor.value == gcfMarlin) {
m_normal_time_estimator.set_max_acceleration(print.config().machine_max_acceleration_extruding.values[0]);
m_normal_time_estimator.set_retract_acceleration(print.config().machine_max_acceleration_retracting.values[0]);
m_normal_time_estimator.set_minimum_feedrate(print.config().machine_min_extruding_rate.values[0]);
m_normal_time_estimator.set_minimum_travel_feedrate(print.config().machine_min_travel_rate.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config().machine_max_acceleration_x.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config().machine_max_acceleration_y.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config().machine_max_acceleration_z.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config().machine_max_acceleration_e.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config().machine_max_feedrate_x.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config().machine_max_feedrate_y.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config().machine_max_feedrate_z.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config().machine_max_feedrate_e.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config().machine_max_jerk_x.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config().machine_max_jerk_y.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config().machine_max_jerk_z.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config().machine_max_jerk_e.values[0]);
if (m_silent_time_estimator_enabled)
{
m_silent_time_estimator.reset();
m_silent_time_estimator.set_dialect(print.config.gcode_flavor);
m_silent_time_estimator.set_max_acceleration(print.config.machine_max_acceleration_extruding.values[1]);
m_silent_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[1]);
m_silent_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[1]);
m_silent_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[1]);
if (print.config.single_extruder_multi_material) {
m_silent_time_estimator.set_dialect(print.config().gcode_flavor);
m_silent_time_estimator.set_max_acceleration(print.config().machine_max_acceleration_extruding.values[1]);
m_silent_time_estimator.set_retract_acceleration(print.config().machine_max_acceleration_retracting.values[1]);
m_silent_time_estimator.set_minimum_feedrate(print.config().machine_min_extruding_rate.values[1]);
m_silent_time_estimator.set_minimum_travel_feedrate(print.config().machine_min_travel_rate.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config().machine_max_acceleration_x.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config().machine_max_acceleration_y.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config().machine_max_acceleration_z.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config().machine_max_acceleration_e.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config().machine_max_feedrate_x.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config().machine_max_feedrate_y.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config().machine_max_feedrate_z.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config().machine_max_feedrate_e.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config().machine_max_jerk_x.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config().machine_max_jerk_y.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config().machine_max_jerk_z.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config().machine_max_jerk_e.values[1]);
if (print.config().single_extruder_multi_material) {
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
// are considered to be active for the single extruder multi-material printers only.
m_silent_time_estimator.set_filament_load_times(print.config.filament_load_time.values);
m_silent_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values);
m_silent_time_estimator.set_filament_load_times(print.config().filament_load_time.values);
m_silent_time_estimator.set_filament_unload_times(print.config().filament_unload_time.values);
}
}
}
// Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful.
if (print.config.single_extruder_multi_material) {
if (print.config().single_extruder_multi_material) {
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
// are considered to be active for the single extruder multi-material printers only.
m_normal_time_estimator.set_filament_load_times(print.config.filament_load_time.values);
m_normal_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values);
m_normal_time_estimator.set_filament_load_times(print.config().filament_load_time.values);
m_normal_time_estimator.set_filament_unload_times(print.config().filament_unload_time.values);
}
// resets analyzer
@@ -552,14 +567,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// change_layer() in turn increments the progress bar status.
m_layer_count = 0;
PrintObjectPtrs printable_objects = print.get_printable_objects();
if (print.config.complete_objects.value) {
if (print.config().complete_objects.value) {
// Add each of the object's layers separately.
for (auto object : printable_objects) {
std::vector<coordf_t> zs;
zs.reserve(object->layers.size() + object->support_layers.size());
for (auto layer : object->layers)
zs.reserve(object->layers().size() + object->support_layers().size());
for (auto layer : object->layers())
zs.push_back(layer->print_z);
for (auto layer : object->support_layers)
for (auto layer : object->support_layers())
zs.push_back(layer->print_z);
std::sort(zs.begin(), zs.end());
m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
@@ -568,18 +583,19 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Print all objects with the same print_z together.
std::vector<coordf_t> zs;
for (auto object : printable_objects) {
zs.reserve(zs.size() + object->layers.size() + object->support_layers.size());
for (auto layer : object->layers)
zs.reserve(zs.size() + object->layers().size() + object->support_layers().size());
for (auto layer : object->layers())
zs.push_back(layer->print_z);
for (auto layer : object->support_layers)
for (auto layer : object->support_layers())
zs.push_back(layer->print_z);
}
std::sort(zs.begin(), zs.end());
m_layer_count = (unsigned int)(std::unique(zs.begin(), zs.end()) - zs.begin());
}
print.throw_if_canceled();
m_enable_cooling_markers = true;
this->apply_print_config(print.config);
this->apply_print_config(print.config());
this->set_extruders(print.extruders());
// Initialize autospeed.
@@ -587,27 +603,28 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// get the minimum cross-section used in the print
std::vector<double> mm3_per_mm;
for (auto object : printable_objects) {
for (size_t region_id = 0; region_id < print.regions.size(); ++region_id) {
auto region = print.regions[region_id];
for (auto layer : object->layers) {
auto layerm = layer->regions[region_id];
if (region->config.get_abs_value("perimeter_speed" ) == 0 ||
region->config.get_abs_value("small_perimeter_speed" ) == 0 ||
region->config.get_abs_value("external_perimeter_speed" ) == 0 ||
region->config.get_abs_value("bridge_speed" ) == 0)
for (size_t region_id = 0; region_id < print.regions().size(); ++region_id) {
auto region = print.regions()[region_id];
for (auto layer : object->layers()) {
auto layerm = layer->regions()[region_id];
if (region->config().get_abs_value("perimeter_speed" ) == 0 ||
region->config().get_abs_value("small_perimeter_speed" ) == 0 ||
region->config().get_abs_value("external_perimeter_speed" ) == 0 ||
region->config().get_abs_value("bridge_speed" ) == 0)
mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm());
if (region->config.get_abs_value("infill_speed" ) == 0 ||
region->config.get_abs_value("solid_infill_speed" ) == 0 ||
region->config.get_abs_value("top_solid_infill_speed" ) == 0 ||
region->config.get_abs_value("bridge_speed" ) == 0)
if (region->config().get_abs_value("infill_speed" ) == 0 ||
region->config().get_abs_value("solid_infill_speed" ) == 0 ||
region->config().get_abs_value("top_solid_infill_speed" ) == 0 ||
region->config().get_abs_value("bridge_speed" ) == 0)
mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm());
}
}
if (object->config.get_abs_value("support_material_speed" ) == 0 ||
object->config.get_abs_value("support_material_interface_speed" ) == 0)
for (auto layer : object->support_layers)
if (object->config().get_abs_value("support_material_speed" ) == 0 ||
object->config().get_abs_value("support_material_interface_speed" ) == 0)
for (auto layer : object->support_layers())
mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm());
}
print.throw_if_canceled();
// filter out 0-width segments
mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end());
if (! mm3_per_mm.empty()) {
@@ -616,19 +633,20 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// volumetric speed as the volumetric speed produced by printing the
// smallest cross-section at the maximum speed: any larger cross-section
// will need slower feedrates.
m_volumetric_speed = *std::min_element(mm3_per_mm.begin(), mm3_per_mm.end()) * print.config.max_print_speed.value;
m_volumetric_speed = *std::min_element(mm3_per_mm.begin(), mm3_per_mm.end()) * print.config().max_print_speed.value;
// limit such volumetric speed with max_volumetric_speed if set
if (print.config.max_volumetric_speed.value > 0)
m_volumetric_speed = std::min(m_volumetric_speed, print.config.max_volumetric_speed.value);
if (print.config().max_volumetric_speed.value > 0)
m_volumetric_speed = std::min(m_volumetric_speed, print.config().max_volumetric_speed.value);
}
}
print.throw_if_canceled();
m_cooling_buffer = make_unique<CoolingBuffer>(*this);
if (print.config.spiral_vase.value)
m_spiral_vase = make_unique<SpiralVase>(print.config);
if (print.config.max_volumetric_extrusion_rate_slope_positive.value > 0 ||
print.config.max_volumetric_extrusion_rate_slope_negative.value > 0)
m_pressure_equalizer = make_unique<PressureEqualizer>(&print.config);
if (print.config().spiral_vase.value)
m_spiral_vase = make_unique<SpiralVase>(print.config());
if (print.config().max_volumetric_extrusion_rate_slope_positive.value > 0 ||
print.config().max_volumetric_extrusion_rate_slope_negative.value > 0)
m_pressure_equalizer = make_unique<PressureEqualizer>(&print.config());
m_enable_extrusion_role_markers = (bool)m_pressure_equalizer;
// Write information on the generator.
@@ -636,7 +654,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Write notes (content of the Print Settings tab -> Notes)
{
std::list<std::string> lines;
boost::split(lines, print.config.notes.value, boost::is_any_of("\n"), boost::token_compress_off);
boost::split(lines, print.config().notes.value, boost::is_any_of("\n"), boost::token_compress_off);
for (auto line : lines) {
// Remove the trailing '\r' from the '\r\n' sequence.
if (! line.empty() && line.back() == '\r')
@@ -646,12 +664,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
if (! lines.empty())
_write(file, "\n");
}
print.throw_if_canceled();
// Write some terse information on the slicing parameters.
const PrintObject *first_object = printable_objects.front();
const double layer_height = first_object->config.layer_height.value;
const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height);
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
auto region = print.regions[region_id];
const double layer_height = first_object->config().layer_height.value;
const double first_layer_height = first_object->config().first_layer_height.get_abs_value(layer_height);
for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) {
auto region = print.regions()[region_id];
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width);
_write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width);
@@ -659,13 +679,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width);
if (print.has_support_material())
_write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
if (print.config.first_layer_extrusion_width.value > 0)
if (print.config().first_layer_extrusion_width.value > 0)
_write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width);
_write_format(file, "\n");
}
print.throw_if_canceled();
// adds tags for time estimators
if (print.config.remaining_times.value)
if (print.config().remaining_times.value)
{
_writeln(file, GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag);
if (m_silent_time_estimator_enabled)
@@ -673,7 +694,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
}
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
m_placeholder_parser = print.placeholder_parser;
m_placeholder_parser = print.placeholder_parser();
m_placeholder_parser.update_timestamp();
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
@@ -683,7 +704,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
unsigned int final_extruder_id = (unsigned int)-1;
size_t initial_print_object_id = 0;
bool has_wipe_tower = false;
if (print.config.complete_objects.value) {
if (print.config().complete_objects.value) {
// Find the 1st printing object, find its tool ordering and the initial extruder ID.
for (; initial_print_object_id < printable_objects.size(); ++initial_print_object_id) {
tool_ordering = ToolOrdering(*printable_objects[initial_print_object_id], initial_extruder_id);
@@ -693,11 +714,11 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
} else {
// Find tool ordering for all the objects at once, and the initial extruder ID.
// If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
tool_ordering = print.m_tool_ordering.empty() ?
tool_ordering = print.wipe_tower_data().tool_ordering.empty() ?
ToolOrdering(print, initial_extruder_id) :
print.m_tool_ordering;
print.wipe_tower_data().tool_ordering;
has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
initial_extruder_id = (has_wipe_tower && ! print.config.single_extruder_multi_material_priming) ?
initial_extruder_id = (has_wipe_tower && ! print.config().single_extruder_multi_material_priming) ?
// The priming towers will be skipped.
tool_ordering.all_extruders().back() :
// Don't skip the priming towers.
@@ -711,6 +732,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
final_extruder_id = tool_ordering.last_extruder();
assert(final_extruder_id != (unsigned int)-1);
}
print.throw_if_canceled();
m_cooling_buffer->set_current_extruder(initial_extruder_id);
@@ -718,7 +740,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
this->print_machine_envelope(file, print);
// Disable fan.
if (! print.config.cooling.get_at(initial_extruder_id) || print.config.disable_fan_first_layers.get_at(initial_extruder_id))
if (! print.config().cooling.get_at(initial_extruder_id) || print.config().disable_fan_first_layers.get_at(initial_extruder_id))
_write(file, m_writer.set_fan(0, true));
// Let the start-up script prime the 1st printing tool.
@@ -729,8 +751,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
m_placeholder_parser.set("current_object_idx", 0);
// For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided.
m_placeholder_parser.set("has_wipe_tower", has_wipe_tower);
m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config.single_extruder_multi_material_priming);
std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config.start_gcode.value, initial_extruder_id);
m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming);
std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id);
// Set bed temperature if the start G-code does not contain any bed temp control G-codes.
this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true);
// Set extruder(s) temperature before and after start G-code.
@@ -747,49 +769,51 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Write the custom start G-code
_writeln(file, start_gcode);
// Process filament-specific gcode in extruder order.
if (print.config.single_extruder_multi_material) {
if (print.config().single_extruder_multi_material) {
if (has_wipe_tower) {
// Wipe tower will control the extruder switching, it will call the start_filament_gcode.
} else {
// Only initialize the initial extruder.
_writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id));
_writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id));
}
} else {
for (const std::string &start_gcode : print.config.start_filament_gcode.values)
_writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front())));
for (const std::string &start_gcode : print.config().start_filament_gcode.values)
_writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config().start_filament_gcode.values.front())));
}
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
print.throw_if_canceled();
// Set other general things.
_write(file, this->preamble());
// Initialize a motion planner for object-to-object travel moves.
if (print.config.avoid_crossing_perimeters.value) {
if (print.config().avoid_crossing_perimeters.value) {
// Collect outer contours of all objects over all layers.
// Discard objects only containing thin walls (offset would fail on an empty polygon).
Polygons islands;
for (const PrintObject *object : printable_objects)
for (const Layer *layer : object->layers)
for (const Layer *layer : object->layers())
for (const ExPolygon &expoly : layer->slices.expolygons)
for (const Point &copy : object->_shifted_copies) {
for (const Point &copy : object->copies()) {
islands.emplace_back(expoly.contour);
islands.back().translate(- copy);
}
//FIXME Mege the islands in parallel.
m_avoid_crossing_perimeters.init_external_mp(union_ex(islands));
print.throw_if_canceled();
}
// Calculate wiping points if needed
if (print.config.ooze_prevention.value && ! print.config.single_extruder_multi_material) {
if (print.config().ooze_prevention.value && ! print.config().single_extruder_multi_material) {
Points skirt_points;
for (const ExtrusionEntity *ee : print.skirt.entities)
for (const ExtrusionEntity *ee : print.skirt().entities)
for (const ExtrusionPath &path : dynamic_cast<const ExtrusionLoop*>(ee)->paths)
append(skirt_points, path.polyline.points);
if (! skirt_points.empty()) {
Polygon outer_skirt = Slic3r::Geometry::convex_hull(skirt_points);
Polygons skirts;
for (unsigned int extruder_id : print.extruders()) {
const Vec2d &extruder_offset = print.config.extruder_offset.get_at(extruder_id);
const Vec2d &extruder_offset = print.config().extruder_offset.get_at(extruder_id);
Polygon s(outer_skirt);
s.translate(Point::new_scale(- extruder_offset(0), - extruder_offset(1)));
skirts.emplace_back(std::move(s));
@@ -807,16 +831,17 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
);
#endif
}
print.throw_if_canceled();
}
if (! (has_wipe_tower && print.config.single_extruder_multi_material_priming)) {
if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming)) {
// Set initial extruder only after custom start G-code.
// Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
_write(file, this->set_extruder(initial_extruder_id));
}
// Do all objects for each layer.
if (print.config.complete_objects.value) {
if (print.config().complete_objects.value) {
// Print objects from the smallest to the tallest to avoid collisions
// when moving onto next object starting point.
std::vector<PrintObject*> objects(printable_objects);
@@ -824,9 +849,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
size_t finished_objects = 0;
for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) {
const PrintObject &object = *objects[object_id];
for (const Point &copy : object._shifted_copies) {
for (const Point &copy : object.copies()) {
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
if (object_id != initial_print_object_id || &copy != object._shifted_copies.data()) {
if (object_id != initial_print_object_id || &copy != object.copies().data()) {
// Don't initialize for the first object and first copy.
tool_ordering = ToolOrdering(object, final_extruder_id);
unsigned int new_extruder_id = tool_ordering.first_extruder();
@@ -837,6 +862,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
final_extruder_id = tool_ordering.last_extruder();
assert(final_extruder_id != (unsigned int)-1);
}
print.throw_if_canceled();
this->set_origin(unscale(copy));
if (finished_objects > 0) {
// Move to the origin position for the copy we're going to print.
@@ -852,7 +878,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// another one, set first layer temperatures. This happens before the Z move
// is triggered, so machine has more time to reach such temperatures.
m_placeholder_parser.set("current_object_idx", int(finished_objects));
std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config.between_objects_gcode.value, initial_extruder_id);
std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id);
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
@@ -866,7 +892,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
for (const LayerToPrint &ltp : layers_to_print) {
std::vector<LayerToPrint> lrs;
lrs.emplace_back(std::move(ltp));
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), &copy - object._shifted_copies.data());
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), &copy - object.copies().data());
print.throw_if_canceled();
}
if (m_pressure_equalizer)
_write(file, m_pressure_equalizer->process("", true));
@@ -882,16 +909,16 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
Points object_reference_points;
PrintObjectPtrs printable_objects = print.get_printable_objects();
for (PrintObject *object : printable_objects)
object_reference_points.push_back(object->_shifted_copies.front());
object_reference_points.push_back(object->copies().front());
Slic3r::Geometry::chained_path(object_reference_points, object_indices);
// Sort layers by Z.
// All extrusion moves with the same top layer height are extruded uninterrupted.
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
// Prusa Multi-Material wipe tower.
if (has_wipe_tower && ! layers_to_print.empty()) {
m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get()));
m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()));
_write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
if (print.config.single_extruder_multi_material_priming) {
if (print.config().single_extruder_multi_material_priming) {
_write(file, m_wipe_tower->prime(*this));
// Verify, whether the print overaps the priming extrusions.
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
@@ -915,6 +942,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write(file, "M1 S10\n");
}
}
print.throw_if_canceled();
}
// Extrude the layers.
for (auto &layer : layers_to_print) {
@@ -922,6 +950,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
if (m_wipe_tower && layer_tools.has_wipe_tower)
m_wipe_tower->next_layer();
this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
print.throw_if_canceled();
}
if (m_pressure_equalizer)
_write(file, m_pressure_equalizer->process("", true));
@@ -947,17 +976,18 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value));
if (print.config.single_extruder_multi_material) {
if (print.config().single_extruder_multi_material) {
// Process the end_filament_gcode for the active filament only.
_writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id(), &config));
_writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id(), &config));
} else {
for (const std::string &end_gcode : print.config.end_filament_gcode.values)
_writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front()), &config));
for (const std::string &end_gcode : print.config().end_filament_gcode.values)
_writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config().end_filament_gcode.values.front()), &config));
}
_writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id(), &config));
_writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config));
}
_write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
_write(file, m_writer.postamble());
print.throw_if_canceled();
// calculates estimated printing time
m_normal_time_estimator.calculate_time(false);
@@ -965,37 +995,30 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
m_silent_time_estimator.calculate_time(false);
// Get filament stats.
print.filament_stats.clear();
print.total_used_filament = 0.;
print.total_extruded_volume = 0.;
print.total_weight = 0.;
print.total_cost = 0.;
print.total_wipe_tower_cost = 0.;
print.total_wipe_tower_filament = 0.;
print.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
print.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
print.m_print_statistics.clear();
print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
for (const Extruder &extruder : m_writer.extruders()) {
double used_filament = extruder.used_filament() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] : 0.f);
double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
double used_filament = extruder.used_filament() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] : 0.f);
double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
print.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
print.m_print_statistics.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
_write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001);
if (filament_weight > 0.) {
print.total_weight = print.total_weight + filament_weight;
print.m_print_statistics.total_weight = print.m_print_statistics.total_weight + filament_weight;
_write_format(file, "; filament used = %.1lf\n", filament_weight);
if (filament_cost > 0.) {
print.total_cost = print.total_cost + filament_cost;
print.m_print_statistics.total_cost = print.m_print_statistics.total_cost + filament_cost;
_write_format(file, "; filament cost = %.1lf\n", filament_cost);
}
}
print.total_used_filament += used_filament;
print.total_extruded_volume += extruded_volume;
print.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
print.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
print.m_print_statistics.total_used_filament += used_filament;
print.m_print_statistics.total_extruded_volume += extruded_volume;
print.m_print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
print.m_print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
}
_write_format(file, "; total filament cost = %.1lf\n", print.total_cost);
_write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost);
_write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str());
if (m_silent_time_estimator_enabled)
_write_format(file, "; estimated printing time (silent mode) = %s\n", m_silent_time_estimator.get_time_dhms().c_str());
@@ -1008,6 +1031,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
if (!full_config.empty())
_write(file, full_config);
}
print.throw_if_canceled();
// starts analizer calculations
if (preview_data != nullptr)
@@ -1020,7 +1044,7 @@ std::string GCode::placeholder_parser_process(const std::string &name, const std
return m_placeholder_parser.process(templ, current_extruder_id, config_override);
} catch (std::runtime_error &err) {
// Collect the names of failed template substitutions for error reporting.
this->m_placeholder_parser_failed_templates.insert(name);
m_placeholder_parser_failed_templates.insert(name);
// Insert the macro error message into the G-code.
return
std::string("\n!!!!! Failed to process the custom G-code template ") + name + "\n" +
@@ -1087,29 +1111,29 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
// Do not process this piece of G-code by the time estimator, it already knows the values through another sources.
void GCode::print_machine_envelope(FILE *file, Print &print)
{
if (print.config.gcode_flavor.value == gcfMarlin) {
if (print.config().gcode_flavor.value == gcfMarlin) {
fprintf(file, "M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n",
int(print.config.machine_max_acceleration_x.values.front() + 0.5),
int(print.config.machine_max_acceleration_y.values.front() + 0.5),
int(print.config.machine_max_acceleration_z.values.front() + 0.5),
int(print.config.machine_max_acceleration_e.values.front() + 0.5));
int(print.config().machine_max_acceleration_x.values.front() + 0.5),
int(print.config().machine_max_acceleration_y.values.front() + 0.5),
int(print.config().machine_max_acceleration_z.values.front() + 0.5),
int(print.config().machine_max_acceleration_e.values.front() + 0.5));
fprintf(file, "M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n",
int(print.config.machine_max_feedrate_x.values.front() + 0.5),
int(print.config.machine_max_feedrate_y.values.front() + 0.5),
int(print.config.machine_max_feedrate_z.values.front() + 0.5),
int(print.config.machine_max_feedrate_e.values.front() + 0.5));
int(print.config().machine_max_feedrate_x.values.front() + 0.5),
int(print.config().machine_max_feedrate_y.values.front() + 0.5),
int(print.config().machine_max_feedrate_z.values.front() + 0.5),
int(print.config().machine_max_feedrate_e.values.front() + 0.5));
fprintf(file, "M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
int(print.config.machine_max_acceleration_extruding.values.front() + 0.5),
int(print.config.machine_max_acceleration_retracting.values.front() + 0.5),
int(print.config.machine_max_acceleration_extruding.values.front() + 0.5));
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),
int(print.config().machine_max_acceleration_retracting.values.front() + 0.5),
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5));
fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
print.config.machine_max_jerk_x.values.front(),
print.config.machine_max_jerk_y.values.front(),
print.config.machine_max_jerk_z.values.front(),
print.config.machine_max_jerk_e.values.front());
print.config().machine_max_jerk_x.values.front(),
print.config().machine_max_jerk_y.values.front(),
print.config().machine_max_jerk_z.values.front(),
print.config().machine_max_jerk_e.values.front());
fprintf(file, "M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n",
int(print.config.machine_min_extruding_rate.values.front() + 0.5),
int(print.config.machine_min_travel_rate.values.front() + 0.5));
int(print.config().machine_min_extruding_rate.values.front() + 0.5),
int(print.config().machine_min_travel_rate.values.front() + 0.5));
}
}
@@ -1120,7 +1144,7 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
{
// Initial bed temperature based on the first extruder.
int temp = print.config.first_layer_bed_temperature.get_at(first_printing_extruder_id);
int temp = print.config().first_layer_bed_temperature.get_at(first_printing_extruder_id);
// Is the bed temperature set by the provided custom G-code?
int temp_by_gcode = -1;
bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, temp_by_gcode);
@@ -1143,23 +1167,23 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
int temp_by_gcode = -1;
if (custom_gcode_sets_temperature(gcode, 104, 109, temp_by_gcode)) {
// Set the extruder temperature at m_writer, but throw away the generated G-code as it will be written with the custom G-code.
int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id);
int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id);
if (temp_by_gcode >= 0 && temp_by_gcode < 1000)
temp = temp_by_gcode;
m_writer.set_temperature(temp_by_gcode, wait, first_printing_extruder_id);
} else {
// Custom G-code does not set the extruder temperature. Do it now.
if (print.config.single_extruder_multi_material.value) {
if (print.config().single_extruder_multi_material.value) {
// Set temperature of the first printing extruder only.
int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id);
int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id);
if (temp > 0)
_write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id));
} else {
// Set temperatures of all the printing extruders.
for (unsigned int tool_id : print.extruders()) {
int temp = print.config.first_layer_temperature.get_at(tool_id);
if (print.config.ooze_prevention.value)
temp += print.config.standby_temperature_delta.value;
int temp = print.config().first_layer_temperature.get_at(tool_id);
if (print.config().ooze_prevention.value)
temp += print.config().standby_temperature_delta.value;
if (temp > 0)
_write(file, m_writer.set_temperature(temp, wait, tool_id));
}
@@ -1232,15 +1256,15 @@ void GCode::process_layer(
unsigned int first_extruder_id = layer_tools.extruders.front();
// Initialize config with the 1st object to be printed at this layer.
m_config.apply(layer.object()->config, true);
m_config.apply(layer.object()->config(), true);
// Check whether it is possible to apply the spiral vase logic for this layer.
// Just a reminder: A spiral vase mode is allowed for a single object, single material print only.
if (m_spiral_vase && layers.size() == 1 && support_layer == nullptr) {
bool enable = (layer.id() > 0 || print.config.brim_width.value == 0.) && (layer.id() >= print.config.skirt_height.value && ! print.has_infinite_skirt());
bool enable = (layer.id() > 0 || print.config().brim_width.value == 0.) && (layer.id() >= print.config().skirt_height.value && ! print.has_infinite_skirt());
if (enable) {
for (const LayerRegion *layer_region : layer.regions)
if (layer_region->region()->config.bottom_solid_layers.value > layer.id() ||
for (const LayerRegion *layer_region : layer.regions())
if (layer_region->region()->config().bottom_solid_layers.value > layer.id() ||
layer_region->perimeters.items_count() > 1 ||
layer_region->fills.items_count() > 0) {
enable = false;
@@ -1255,22 +1279,22 @@ void GCode::process_layer(
std::string gcode;
// Set new layer - this will change Z and force a retraction if retract_layer_change is enabled.
if (! print.config.before_layer_gcode.value.empty()) {
if (! print.config().before_layer_gcode.value.empty()) {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
gcode += this->placeholder_parser_process("before_layer_gcode",
print.config.before_layer_gcode.value, m_writer.extruder()->id(), &config)
print.config().before_layer_gcode.value, m_writer.extruder()->id(), &config)
+ "\n";
}
gcode += this->change_layer(print_z); // this will increase m_layer_index
m_layer = &layer;
if (! print.config.layer_gcode.value.empty()) {
if (! print.config().layer_gcode.value.empty()) {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
gcode += this->placeholder_parser_process("layer_gcode",
print.config.layer_gcode.value, m_writer.extruder()->id(), &config)
print.config().layer_gcode.value, m_writer.extruder()->id(), &config)
+ "\n";
}
@@ -1278,14 +1302,14 @@ void GCode::process_layer(
// Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent
// first_layer_temperature vs. temperature settings.
for (const Extruder &extruder : m_writer.extruders()) {
if (print.config.single_extruder_multi_material.value && extruder.id() != m_writer.extruder()->id())
if (print.config().single_extruder_multi_material.value && extruder.id() != m_writer.extruder()->id())
// In single extruder multi material mode, set the temperature for the current extruder only.
continue;
int temperature = print.config.temperature.get_at(extruder.id());
if (temperature > 0 && temperature != print.config.first_layer_temperature.get_at(extruder.id()))
int temperature = print.config().temperature.get_at(extruder.id());
if (temperature > 0 && temperature != print.config().first_layer_temperature.get_at(extruder.id()))
gcode += m_writer.set_temperature(temperature, false, extruder.id());
}
gcode += m_writer.set_bed_temperature(print.config.bed_temperature.get_at(first_extruder_id));
gcode += m_writer.set_bed_temperature(print.config().bed_temperature.get_at(first_extruder_id));
// Mark the temperature transition from 1st to 2nd layer to be finished.
m_second_layer_things_done = true;
}
@@ -1293,9 +1317,9 @@ void GCode::process_layer(
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
bool extrude_skirt =
! print.skirt.entities.empty() &&
! print.skirt().entities.empty() &&
// Not enough skirt layers printed yet.
(m_skirt_done.size() < print.config.skirt_height.value || print.has_infinite_skirt()) &&
(m_skirt_done.size() < print.config().skirt_height.value || print.has_infinite_skirt()) &&
// This print_z has not been extruded yet
(m_skirt_done.empty() ? 0. : m_skirt_done.back()) < print_z - EPSILON &&
// and this layer is the 1st layer, or it is an object layer, or it is a raft layer.
@@ -1317,7 +1341,7 @@ void GCode::process_layer(
extruder_ids.front() = first_extruder_id;
break;
}
size_t n_loops = print.skirt.entities.size();
size_t n_loops = print.skirt().entities.size();
if (n_loops <= extruder_ids.size()) {
for (size_t i = 0; i < n_loops; ++i)
skirt_loops_per_extruder[extruder_ids[i]] = std::pair<size_t, size_t>(i, i + 1);
@@ -1336,7 +1360,7 @@ void GCode::process_layer(
}
} else
// Extrude all skirts with the current extruder.
skirt_loops_per_extruder[first_extruder_id] = std::pair<size_t, size_t>(0, print.config.skirts.value);
skirt_loops_per_extruder[first_extruder_id] = std::pair<size_t, size_t>(0, print.config().skirts.value);
}
// Group extrusions by an extruder, then by an object, an island and a region.
@@ -1350,22 +1374,22 @@ void GCode::process_layer(
bool has_support = role == erMixed || role == erSupportMaterial;
bool has_interface = role == erMixed || role == erSupportMaterialInterface;
// Extruder ID of the support base. -1 if "don't care".
unsigned int support_extruder = object.config.support_material_extruder.value - 1;
unsigned int support_extruder = object.config().support_material_extruder.value - 1;
// Shall the support be printed with the active extruder, preferably with non-soluble, to avoid tool changes?
bool support_dontcare = object.config.support_material_extruder.value == 0;
bool support_dontcare = object.config().support_material_extruder.value == 0;
// Extruder ID of the support interface. -1 if "don't care".
unsigned int interface_extruder = object.config.support_material_interface_extruder.value - 1;
unsigned int interface_extruder = object.config().support_material_interface_extruder.value - 1;
// Shall the support interface be printed with the active extruder, preferably with non-soluble, to avoid tool changes?
bool interface_dontcare = object.config.support_material_interface_extruder.value == 0;
bool interface_dontcare = object.config().support_material_interface_extruder.value == 0;
if (support_dontcare || interface_dontcare) {
// Some support will be printed with "don't care" material, preferably non-soluble.
// Is the current extruder assigned a soluble filament?
unsigned int dontcare_extruder = first_extruder_id;
if (print.config.filament_soluble.get_at(dontcare_extruder)) {
if (print.config().filament_soluble.get_at(dontcare_extruder)) {
// The last extruder printed on the previous layer extrudes soluble filament.
// Try to find a non-soluble extruder on the same layer.
for (unsigned int extruder_id : layer_tools.extruders)
if (! print.config.filament_soluble.get_at(extruder_id)) {
if (! print.config().filament_soluble.get_at(extruder_id)) {
dontcare_extruder = extruder_id;
break;
}
@@ -1412,11 +1436,11 @@ void GCode::process_layer(
layer.slices.expolygons[i].contour.contains(point);
};
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
const LayerRegion *layerm = layer.regions[region_id];
for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) {
const LayerRegion *layerm = layer.regions()[region_id];
if (layerm == nullptr)
continue;
const PrintRegion &region = *print.regions[region_id];
const PrintRegion &region = *print.regions()[region_id];
// Now we must process perimeters and infills and create islands of extrusions in by_region std::map.
@@ -1433,11 +1457,14 @@ void GCode::process_layer(
continue;
// This extrusion is part of certain Region, which tells us which extruder should be used for it:
int correct_extruder_id = Print::get_extruder(*fill, region); entity_type=="infills" ? 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);
int correct_extruder_id = Print::get_extruder(*fill, region);
//FIXME what is this?
entity_type=="infills" ?
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);
// Let's recover vector of extruder overrides:
const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size());
const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->copies().size());
// Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
for (unsigned int extruder : layer_tools.extruders)
@@ -1459,8 +1486,8 @@ void GCode::process_layer(
// fill->first_point fits inside ith slice
point_inside_surface(i, fill->first_point())) {
if (islands[i].by_region.empty())
islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->_shifted_copies.size());
islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region());
islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size());
break;
}
}
@@ -1494,7 +1521,7 @@ void GCode::process_layer(
Flow skirt_flow = print.skirt_flow();
for (size_t i = loops.first; i < loops.second; ++ i) {
// Adjust flow according to this layer's layer height.
ExtrusionLoop loop = *dynamic_cast<const ExtrusionLoop*>(print.skirt.entities[i]);
ExtrusionLoop loop = *dynamic_cast<const ExtrusionLoop*>(print.skirt().entities[i]);
Flow layer_skirt_flow(skirt_flow);
layer_skirt_flow.height = (float)skirt_height;
double mm3_per_mm = layer_skirt_flow.mm3_per_mm();
@@ -1515,7 +1542,7 @@ void GCode::process_layer(
if (! m_brim_done) {
this->set_origin(0., 0.);
m_avoid_crossing_perimeters.use_external_mp = true;
for (const ExtrusionEntity *ee : print.brim.entities)
for (const ExtrusionEntity *ee : print.brim().entities)
gcode += this->extrude_loop(*dynamic_cast<const ExtrusionLoop*>(ee), "brim", m_config.support_material_speed.value);
m_brim_done = true;
m_avoid_crossing_perimeters.use_external_mp = false;
@@ -1527,7 +1554,6 @@ void GCode::process_layer(
auto objects_by_extruder_it = by_extruder.find(extruder_id);
if (objects_by_extruder_it == by_extruder.end())
continue;
// We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
for (int print_wipe_extrusions=const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
if (print_wipe_extrusions == 0)
@@ -1540,15 +1566,15 @@ void GCode::process_layer(
// This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
continue;
m_config.apply(print_object->config, true);
m_config.apply(print_object->config(), true);
m_layer = layers[layer_id].layer();
if (m_config.avoid_crossing_perimeters)
m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
Points copies;
if (single_object_idx == size_t(-1))
copies = print_object->_shifted_copies;
copies = print_object->copies();
else
copies.push_back(print_object->_shifted_copies[single_object_idx]);
copies.push_back(print_object->copies()[single_object_idx]);
// Sort the copies by the closest point starting with the current print position.
unsigned int copy_id = 0;
@@ -1569,7 +1595,7 @@ void GCode::process_layer(
for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
const auto& by_region_specific = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
if (print.config.infill_first) {
if (print.config().infill_first) {
gcode += this->extrude_infill(print, by_region_specific);
gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
} else {
@@ -1612,14 +1638,14 @@ void GCode::apply_print_config(const PrintConfig &print_config)
void GCode::append_full_config(const Print& print, std::string& str)
{
const StaticPrintConfig *configs[] = { static_cast<const GCodeConfig*>(&print.config), &print.default_object_config, &print.default_region_config };
const StaticPrintConfig *configs[] = { static_cast<const GCodeConfig*>(&print.config()), &print.default_object_config(), &print.default_region_config() };
for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) {
const StaticPrintConfig *cfg = configs[i];
for (const std::string &key : cfg->keys())
if (key != "compatible_printers")
str += "; " + key + " = " + cfg->serialize(key) + "\n";
}
const DynamicConfig &full_config = print.placeholder_parser.config();
const DynamicConfig &full_config = print.placeholder_parser().config();
for (const char *key : {
"print_settings_id", "filament_settings_id", "printer_settings_id",
"printer_model", "printer_variant", "default_print_profile", "default_filament_profile",
@@ -2178,7 +2204,7 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string des
else if (const ExtrusionLoop* loop = dynamic_cast<const ExtrusionLoop*>(&entity))
return this->extrude_loop(*loop, description, speed, lower_layer_edge_grid);
else {
CONFESS("Invalid argument supplied to extrude()");
throw std::invalid_argument("Invalid argument supplied to extrude()");
return "";
}
}
@@ -2202,7 +2228,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
{
std::string gcode;
for (const ObjectByExtruder::Island::Region &region : by_region) {
m_config.apply(print.regions[&region - &by_region.front()]->config);
m_config.apply(print.regions()[&region - &by_region.front()]->config());
for (ExtrusionEntity *ee : region.perimeters.entities)
gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid);
}
@@ -2214,7 +2240,7 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy
{
std::string gcode;
for (const ObjectByExtruder::Island::Region &region : by_region) {
m_config.apply(print.regions[&region - &by_region.front()]->config);
m_config.apply(print.regions()[&region - &by_region.front()]->config());
ExtrusionEntityCollection chained = region.infills.chained_path_from(m_last_pos, false);
for (ExtrusionEntity *fill : chained.entities) {
auto *eec = dynamic_cast<ExtrusionEntityCollection*>(fill);
@@ -2363,7 +2389,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
} else if (path.role() == erGapFill) {
speed = m_config.get_abs_value("gap_fill_speed");
} else {
CONFESS("Invalid speed");
throw std::invalid_argument("Invalid speed");
}
}
if (this->on_first_layer())
@@ -2689,7 +2715,7 @@ void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, co
}
else
if (type != "infills") {
CONFESS("Unknown parameter!");
throw std::invalid_argument("Unknown parameter!");
return;
}
+3 -1
View File
@@ -150,7 +150,8 @@ public:
{}
~GCode() {}
// throws std::runtime_exception
// throws std::runtime_exception on error,
// throws CanceledException through print->throw_if_canceled().
void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr);
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
@@ -164,6 +165,7 @@ public:
const Layer* layer() const { return m_layer; }
GCodeWriter& writer() { return m_writer; }
PlaceholderParser& placeholder_parser() { return m_placeholder_parser; }
const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; }
// Process a template through the placeholder parser, collect error messages to be reported
// inside the generated string and after the G-code export finishes.
std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr);
+60
View File
@@ -0,0 +1,60 @@
#include "PostProcessor.hpp"
#if 1
//#ifdef WIN32
namespace Slic3r {
//FIXME Ignore until we include boost::process
void run_post_process_scripts(const std::string &path, const PrintConfig &config)
{
}
} // namespace Slic3r
#else
#include <boost/process/system.hpp>
namespace Slic3r {
void run_post_process_scripts(const std::string &path, const PrintConfig &config)
{
if (config.post_process.values.empty())
return;
config.setenv_();
for (std::string script: config.post_process.values) {
// Ignore empty post processing script lines.
boost::trim(script);
if (script.empty())
continue;
BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path;
if (! boost::filesystem::exists(boost::filesystem::path(path)))
throw std::runtime_exception(std::string("The configured post-processing script does not exist: ") + path);
#ifndef WIN32
file_status fs = boost::filesystem::status(path);
//FIXME test if executible by the effective UID / GID.
// throw std::runtime_exception(std::string("The configured post-processing script is not executable: check permissions. ") + path));
#endif
int result = 0;
#ifdef WIN32
if (boost::iends_with(file, ".gcode")) {
// The current process may be slic3r.exe or slic3r-console.exe.
// Find the path of the process:
wchar_t wpath_exe[_MAX_PATH + 1];
::GetModuleFileNameW(nullptr, wpath_exe, _MAX_PATH);
boost::filesystem::path path_exe(wpath_exe);
// Replace it with the current perl interpreter.
result = boost::process::system((path_exe.parent_path() / "perl5.24.0.exe").string(), script, output_file);
} else
#else
result = boost::process::system(script, output_file);
#endif
if (result < 0)
BOOST_LOG_TRIVIAL(error) << "Script " << script << " on file " << path << " failed. Negative error code returned.";
}
}
} // namespace Slic3r
#endif
+15
View File
@@ -0,0 +1,15 @@
#ifndef slic3r_GCode_PostProcessor_hpp_
#define slic3r_GCode_PostProcessor_hpp_
#include <string>
#include "../libslic3r.h"
#include "../PrintConfig.hpp"
namespace Slic3r {
extern void run_post_process_scripts(const std::string &path, const PrintConfig &config);
} // namespace Slic3r
#endif /* slic3r_GCode_PostProcessor_hpp_ */
+5 -5
View File
@@ -49,10 +49,10 @@ void PressureEqualizer::reset()
// Volumetric rate of a 0.45mm x 0.2mm extrusion at 60mm/s XY movement: 0.45*0.2*60*60=5.4*60 = 324 mm^3/min
// Volumetric rate of a 0.45mm x 0.2mm extrusion at 20mm/s XY movement: 0.45*0.2*20*60=1.8*60 = 108 mm^3/min
// Slope of the volumetric rate, changing from 20mm/s to 60mm/s over 2 seconds: (5.4-1.8)*60*60/2=60*60*1.8 = 6480 mm^3/min^2 = 1.8 mm^3/s^2
m_max_volumetric_extrusion_rate_slope_positive = (this->m_config == NULL) ? 6480.f :
this->m_config->max_volumetric_extrusion_rate_slope_positive.value * 60.f * 60.f;
m_max_volumetric_extrusion_rate_slope_negative = (this->m_config == NULL) ? 6480.f :
this->m_config->max_volumetric_extrusion_rate_slope_negative.value * 60.f * 60.f;
m_max_volumetric_extrusion_rate_slope_positive = (m_config == NULL) ? 6480.f :
m_config->max_volumetric_extrusion_rate_slope_positive.value * 60.f * 60.f;
m_max_volumetric_extrusion_rate_slope_negative = (m_config == NULL) ? 6480.f :
m_config->max_volumetric_extrusion_rate_slope_negative.value * 60.f * 60.f;
for (size_t i = 0; i < numExtrusionRoles; ++ i) {
m_max_volumetric_extrusion_rate_slopes[i].negative = m_max_volumetric_extrusion_rate_slope_negative;
@@ -171,7 +171,7 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
if (strncmp(line, EXTRUSION_ROLE_TAG, strlen(EXTRUSION_ROLE_TAG)) == 0) {
line += strlen(EXTRUSION_ROLE_TAG);
int role = atoi(line);
this->m_current_extrusion_role = ExtrusionRole(role);
m_current_extrusion_role = ExtrusionRole(role);
++ line_idx;
return false;
}
+11 -11
View File
@@ -93,25 +93,25 @@ static BoundingBoxf extrusionentity_extents(const ExtrusionEntity *extrusion_ent
auto *extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(extrusion_entity);
if (extrusion_entity_collection != nullptr)
return extrusionentity_extents(*extrusion_entity_collection);
CONFESS("Unexpected extrusion_entity type in extrusionentity_extents()");
throw std::runtime_error("Unexpected extrusion_entity type in extrusionentity_extents()");
return BoundingBoxf();
}
BoundingBoxf get_print_extrusions_extents(const Print &print)
{
BoundingBoxf bbox(extrusionentity_extents(print.brim));
bbox.merge(extrusionentity_extents(print.skirt));
BoundingBoxf bbox(extrusionentity_extents(print.brim()));
bbox.merge(extrusionentity_extents(print.skirt()));
return bbox;
}
BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object, const coordf_t max_print_z)
{
BoundingBoxf bbox;
for (const Layer *layer : print_object.layers) {
for (const Layer *layer : print_object.layers()) {
if (layer->print_z > max_print_z)
break;
BoundingBoxf bbox_this;
for (const LayerRegion *layerm : layer->regions) {
for (const LayerRegion *layerm : layer->regions()) {
bbox_this.merge(extrusionentity_extents(layerm->perimeters));
for (const ExtrusionEntity *ee : layerm->fills.entities)
// fill represents infill extrusions of a single island.
@@ -121,7 +121,7 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
if (support_layer)
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
bbox_this.merge(extrusionentity_extents(extrusion_entity));
for (const Point &offset : print_object._shifted_copies) {
for (const Point &offset : print_object.copies()) {
BoundingBoxf bbox_translated(bbox_this);
bbox_translated.translate(unscale(offset));
bbox.merge(bbox_translated);
@@ -137,11 +137,11 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
// Wipe tower extrusions are saved as if the tower was at the origin with no rotation
// We need to get position and angle of the wipe tower to transform them to actual position.
Transform2d trafo =
Eigen::Translation2d(print.config.wipe_tower_x.value, print.config.wipe_tower_y.value) *
Eigen::Rotation2Dd(print.config.wipe_tower_rotation_angle.value);
Eigen::Translation2d(print.config().wipe_tower_x.value, print.config().wipe_tower_y.value) *
Eigen::Rotation2Dd(print.config().wipe_tower_rotation_angle.value);
BoundingBoxf bbox;
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.m_wipe_tower_tool_changes) {
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.wipe_tower_data().tool_changes) {
if (! tool_changes.empty() && tool_changes.front().print_z > max_print_z)
break;
for (const WipeTower::ToolChangeResult &tcr : tool_changes) {
@@ -164,8 +164,8 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print)
{
BoundingBoxf bbox;
if (print.m_wipe_tower_priming) {
const WipeTower::ToolChangeResult &tcr = *print.m_wipe_tower_priming.get();
if (print.wipe_tower_data().priming != nullptr) {
const WipeTower::ToolChangeResult &tcr = *print.wipe_tower_data().priming;
for (size_t i = 1; i < tcr.extrusions.size(); ++ i) {
const WipeTower::Extrusion &e = tcr.extrusions[i];
if (e.width > 0) {
+61 -61
View File
@@ -34,19 +34,19 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
// For the use case when each object is printed separately
// (print.config.complete_objects is true).
// (print.config().complete_objects is true).
ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material)
{
if (object.layers.empty())
if (object.layers().empty())
return;
// Initialize the print layers for just a single object.
{
std::vector<coordf_t> zs;
zs.reserve(zs.size() + object.layers.size() + object.support_layers.size());
for (auto layer : object.layers)
zs.reserve(zs.size() + object.layers().size() + object.support_layers().size());
for (auto layer : object.layers())
zs.emplace_back(layer->print_z);
for (auto layer : object.support_layers)
for (auto layer : object.support_layers())
zs.emplace_back(layer->print_z);
this->initialize_layers(zs);
}
@@ -57,16 +57,16 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
// Reorder the extruders to minimize tool switches.
this->reorder_extruders(first_extruder);
this->fill_wipe_tower_partitions(object.print()->config, object.layers.front()->print_z - object.layers.front()->height);
this->fill_wipe_tower_partitions(object.print()->config(), object.layers().front()->print_z - object.layers().front()->height);
this->collect_extruder_statistics(prime_multi_material);
}
// For the use case when all objects are printed at once.
// (print.config.complete_objects is false).
// (print.config().complete_objects is false).
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material)
{
m_print_config_ptr = &print.config;
m_print_config_ptr = &print.config();
PrintObjectPtrs objects = print.get_printable_objects();
// Initialize the print layers for all objects and all layers.
@@ -74,13 +74,13 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
{
std::vector<coordf_t> zs;
for (auto object : objects) {
zs.reserve(zs.size() + object->layers.size() + object->support_layers.size());
for (auto layer : object->layers)
zs.reserve(zs.size() + object->layers().size() + object->support_layers().size());
for (auto layer : object->layers())
zs.emplace_back(layer->print_z);
for (auto layer : object->support_layers)
for (auto layer : object->support_layers())
zs.emplace_back(layer->print_z);
if (! object->layers.empty())
object_bottom_z = object->layers.front()->print_z - object->layers.front()->height;
if (! object->layers().empty())
object_bottom_z = object->layers().front()->print_z - object->layers().front()->height;
}
this->initialize_layers(zs);
}
@@ -92,7 +92,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
// Reorder the extruders to minimize tool switches.
this->reorder_extruders(first_extruder);
this->fill_wipe_tower_partitions(print.config, object_bottom_z);
this->fill_wipe_tower_partitions(print.config(), object_bottom_z);
this->collect_extruder_statistics(prime_multi_material);
}
@@ -133,13 +133,13 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
void ToolOrdering::collect_extruders(const PrintObject &object)
{
// Collect the support extruders.
for (auto support_layer : object.support_layers) {
for (auto support_layer : object.support_layers()) {
LayerTools &layer_tools = this->tools_for_layer(support_layer->print_z);
ExtrusionRole role = support_layer->support_fills.role();
bool has_support = role == erMixed || role == erSupportMaterial;
bool has_interface = role == erMixed || role == erSupportMaterialInterface;
unsigned int extruder_support = object.config.support_material_extruder.value;
unsigned int extruder_interface = object.config.support_material_interface_extruder.value;
unsigned int extruder_support = object.config().support_material_extruder.value;
unsigned int extruder_interface = object.config().support_material_interface_extruder.value;
if (has_support)
layer_tools.extruders.push_back(extruder_support);
if (has_interface)
@@ -148,14 +148,14 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
layer_tools.has_support = true;
}
// Collect the object extruders.
for (auto layer : object.layers) {
for (auto layer : object.layers()) {
LayerTools &layer_tools = this->tools_for_layer(layer->print_z);
// What extruders are required to print this object layer?
for (size_t region_id = 0; region_id < object.print()->regions.size(); ++ region_id) {
const LayerRegion *layerm = (region_id < layer->regions.size()) ? layer->regions[region_id] : nullptr;
for (size_t region_id = 0; region_id < object.print()->regions().size(); ++ region_id) {
const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr;
if (layerm == nullptr)
continue;
const PrintRegion &region = *object.print()->regions[region_id];
const PrintRegion &region = *object.print()->regions()[region_id];
if (! layerm->perimeters.entities.empty()) {
bool something_nonoverriddable = true;
@@ -170,7 +170,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
}
if (something_nonoverriddable)
layer_tools.extruders.push_back(region.config.perimeter_extruder.value);
layer_tools.extruders.push_back(region.config().perimeter_extruder.value);
layer_tools.has_object = true;
}
@@ -197,9 +197,9 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
if (something_nonoverriddable || !m_print_config_ptr)
{
if (has_solid_infill)
layer_tools.extruders.push_back(region.config.solid_infill_extruder);
layer_tools.extruders.push_back(region.config().solid_infill_extruder);
if (has_infill)
layer_tools.extruders.push_back(region.config.infill_extruder);
layer_tools.extruders.push_back(region.config().infill_extruder);
}
if (has_solid_infill || has_infill)
layer_tools.has_object = true;
@@ -430,10 +430,10 @@ bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, con
if (print_config.filament_soluble.get_at(Print::get_extruder(eec, region)))
return false;
if (object.config.wipe_into_objects)
if (object.config().wipe_into_objects)
return true;
if (!region.config.wipe_into_infill || eec.role() != erInternalInfill)
if (!region.config().wipe_into_infill || eec.role() != erInternalInfill)
return false;
return true;
@@ -447,12 +447,12 @@ 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))
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
// we will sort objects so that dedicated for wiping are at the beginning:
PrintObjectPtrs object_list = print.get_printable_objects();
std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; });
std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config().wipe_into_objects; });
// We will now iterate through
// - first the dedicated objects to mark perimeters or infills (depending on infill_first)
@@ -462,7 +462,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
bool perimeters_done = false;
for (int i=0 ; i<(int)object_list.size() + (perimeters_done ? 0 : 1); ++i) {
if (!perimeters_done && (i==(int)object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list
if (!perimeters_done && (i==(int)object_list.size() || !object_list[i]->config().wipe_into_objects)) { // we passed the last dedicated object in list
perimeters_done = true;
i=-1; // let's go from the start again
continue;
@@ -471,26 +471,26 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
const auto& 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())
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;
unsigned int num_of_copies = object->_shifted_copies.size();
unsigned int 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
for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
const auto& region = *object->print()->regions[region_id];
for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) {
const auto& region = *object->print()->regions()[region_id];
if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
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)) {
for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections
if ((!print.config().infill_first ? perimeters_done : !perimeters_done) || (!object->config().wipe_into_objects && region.config().wipe_into_infill)) {
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))
if (!is_overriddable(*fill, print.config(), *object, region))
continue;
// What extruder would this normally be printed with?
@@ -499,10 +499,10 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
if (volume_to_wipe<=0)
continue;
if (!object->config.wipe_into_objects && !print.config.infill_first && region.config.wipe_into_infill)
if (!object->config().wipe_into_objects && !print.config().infill_first && region.config().wipe_into_infill)
// 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(region.config().perimeter_extruder - 1, new_extruder))
continue;
if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder
@@ -513,11 +513,11 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
}
// 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 : !perimeters_done))
{
for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) {
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))
if (!is_overriddable(*fill, print.config(), *object, region))
continue;
if (volume_to_wipe<=0)
@@ -544,29 +544,29 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
{
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);
unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config());
unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config());
PrintObjectPtrs printable_objects = print.get_printable_objects();
for (const PrintObject* object : printable_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())
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;
unsigned int num_of_copies = object->_shifted_copies.size();
unsigned int 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
for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
const auto& region = *object->print()->regions[region_id];
for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) {
const auto& region = *object->print()->regions()[region_id];
if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
continue;
for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections
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)
if (!is_overriddable(*fill, print.config(), *object, region)
|| is_entity_overridden(fill, copy) )
continue;
@@ -574,12 +574,12 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
// printed before its perimeter, or not be printed at all (in case its original extruder has
// not been added to LayerTools
// 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)
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)
)
set_extruder_override(fill, copy, (print.config.infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
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.
// Force overriding would mean it gets printed before its perimeter.
@@ -587,13 +587,13 @@ 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
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)
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);
set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
}
}
}
@@ -1166,7 +1166,6 @@ void WipeTowerPrusaMM::save_on_last_wipe()
}
}
// Processes vector m_plan and calls respective functions to generate G-code for the wipe tower
// Resulting ToolChangeResults are appended into vector "result"
void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
@@ -1256,6 +1255,4 @@ void WipeTowerPrusaMM::make_wipe_tower_square()
lay.extra_spacing = lay.depth / lay.toolchanges_depth();
}
}; // namespace Slic3r
+1 -1
View File
@@ -101,7 +101,7 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pair<const char*, co
(cmd_len == 3 && command.first[1] == '9' && command.first[2] == '2')) {
for (size_t i = 0; i < NUM_AXES; ++ i)
if (gline.has(Axis(i)))
this->m_position[i] = gline.value(Axis(i));
m_position[i] = gline.value(Axis(i));
}
}
}
+2 -2
View File
@@ -1,4 +1,5 @@
#include "GCodeTimeEstimator.hpp"
#include "Utils.hpp"
#include <boost/bind.hpp>
#include <cmath>
@@ -367,8 +368,7 @@ namespace Slic3r {
fclose(out);
in.close();
boost::nowide::remove(filename.c_str());
if (boost::nowide::rename(path_tmp.c_str(), filename.c_str()) != 0)
if (rename_file(path_tmp, filename) != 0)
throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' +
"Is " + path_tmp + " locked?" + '\n');
+1 -1
View File
@@ -432,7 +432,7 @@ Pointfs arrange(size_t num_parts, const Vec2d &part_size, coordf_t gap, const Bo
size_t cellw = size_t(floor((bed_bbox.size()(0) + gap) / cell_size(0)));
size_t cellh = size_t(floor((bed_bbox.size()(1) + gap) / cell_size(1)));
if (num_parts > cellw * cellh)
CONFESS(PRINTF_ZU " parts won't fit in your print area!\n", num_parts);
throw std::invalid_argument(PRINTF_ZU " parts won't fit in your print area!\n", num_parts);
// Get a bounding box of cellw x cellh cells, centered at the center of the bed.
Vec2d cells_size(cellw * cell_size(0) - gap, cellh * cell_size(1) - gap);
+29 -30
View File
@@ -12,24 +12,24 @@ namespace Slic3r {
Layer::~Layer()
{
this->lower_layer = this->upper_layer = nullptr;
for (LayerRegion *region : this->regions)
for (LayerRegion *region : m_regions)
delete region;
this->regions.clear();
m_regions.clear();
}
LayerRegion* Layer::add_region(PrintRegion* print_region)
{
this->regions.emplace_back(new LayerRegion(this, print_region));
return this->regions.back();
m_regions.emplace_back(new LayerRegion(this, print_region));
return m_regions.back();
}
// merge all regions' slices to get islands
void Layer::make_slices()
{
ExPolygons slices;
if (this->regions.size() == 1) {
if (m_regions.size() == 1) {
// optimization: if we only have one region, take its slices
slices = this->regions.front()->slices;
slices = m_regions.front()->slices;
} else {
Polygons slices_p;
FOREACH_LAYERREGION(this, layerm) {
@@ -58,10 +58,10 @@ void Layer::make_slices()
void Layer::merge_slices()
{
if (this->regions.size() == 1) {
if (m_regions.size() == 1) {
// Optimization, also more robust. Don't merge classified pieces of layerm->slices,
// but use the non-split islands of a layer. For a single region print, these shall be equal.
this->regions.front()->slices.set(this->slices.expolygons, stInternal);
m_regions.front()->slices.set(this->slices.expolygons, stInternal);
} else {
FOREACH_LAYERREGION(this, layerm) {
// without safety offset, artifacts are generated (GH #2494)
@@ -81,18 +81,18 @@ void Layer::make_perimeters()
std::set<size_t> done;
FOREACH_LAYERREGION(this, layerm) {
size_t region_id = layerm - this->regions.begin();
size_t region_id = layerm - m_regions.begin();
if (done.find(region_id) != done.end()) continue;
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id;
done.insert(region_id);
const PrintRegionConfig &config = (*layerm)->region()->config;
const PrintRegionConfig &config = (*layerm)->region()->config();
// find compatible regions
LayerRegionPtrs layerms;
layerms.push_back(*layerm);
for (LayerRegionPtrs::const_iterator it = layerm + 1; it != this->regions.end(); ++it) {
for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it) {
LayerRegion* other_layerm = *it;
const PrintRegionConfig &other_config = other_layerm->region()->config;
const PrintRegionConfig &other_config = other_layerm->region()->config();
if (config.perimeter_extruder == other_config.perimeter_extruder
&& config.perimeters == other_config.perimeters
@@ -104,7 +104,7 @@ void Layer::make_perimeters()
&& config.thin_walls == other_config.thin_walls
&& config.external_perimeters_first == other_config.external_perimeters_first) {
layerms.push_back(other_layerm);
done.insert(it - this->regions.begin());
done.insert(it - m_regions.begin());
}
}
@@ -150,13 +150,12 @@ void Layer::make_fills()
#ifdef SLIC3R_DEBUG
printf("Making fills for layer " PRINTF_ZU "\n", this->id());
#endif
for (LayerRegionPtrs::iterator it_layerm = regions.begin(); it_layerm != regions.end(); ++ it_layerm) {
LayerRegion &layerm = *(*it_layerm);
layerm.fills.clear();
make_fill(layerm, layerm.fills);
for (LayerRegion *layerm : m_regions) {
layerm->fills.clear();
make_fill(*layerm, layerm->fills);
#ifndef NDEBUG
for (size_t i = 0; i < layerm.fills.entities.size(); ++ i)
assert(dynamic_cast<ExtrusionEntityCollection*>(layerm.fills.entities[i]) != NULL);
assert(dynamic_cast<ExtrusionEntityCollection*>(layerm->fills.entities[i]) != NULL);
#endif
}
}
@@ -164,18 +163,18 @@ void Layer::make_fills()
void Layer::export_region_slices_to_svg(const char *path) const
{
BoundingBox bbox;
for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region)
for (Surfaces::const_iterator surface = (*region)->slices.surfaces.begin(); surface != (*region)->slices.surfaces.end(); ++surface)
bbox.merge(get_extents(surface->expolygon));
for (const auto *region : m_regions)
for (const auto &surface : region->slices.surfaces)
bbox.merge(get_extents(surface.expolygon));
Point legend_size = export_surface_type_legend_to_svg_box_size();
Point legend_pos(bbox.min(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region)
for (Surfaces::const_iterator surface = (*region)->slices.surfaces.begin(); surface != (*region)->slices.surfaces.end(); ++surface)
svg.draw(surface->expolygon, surface_type_to_color_name(surface->surface_type), transparency);
for (const auto *region : m_regions)
for (const auto &surface : region->slices.surfaces)
svg.draw(surface.expolygon, surface_type_to_color_name(surface.surface_type), transparency);
export_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
}
@@ -190,18 +189,18 @@ void Layer::export_region_slices_to_svg_debug(const char *name) const
void Layer::export_region_fill_surfaces_to_svg(const char *path) const
{
BoundingBox bbox;
for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region)
for (Surfaces::const_iterator surface = (*region)->fill_surfaces.surfaces.begin(); surface != (*region)->fill_surfaces.surfaces.end(); ++surface)
bbox.merge(get_extents(surface->expolygon));
for (const auto *region : m_regions)
for (const auto &surface : region->slices.surfaces)
bbox.merge(get_extents(surface.expolygon));
Point legend_size = export_surface_type_legend_to_svg_box_size();
Point legend_pos(bbox.min(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region)
for (Surfaces::const_iterator surface = (*region)->fill_surfaces.surfaces.begin(); surface != (*region)->fill_surfaces.surfaces.end(); ++surface)
svg.draw(surface->expolygon, surface_type_to_color_name(surface->surface_type), transparency);
for (const auto *region : m_regions)
for (const auto &surface : region->slices.surfaces)
svg.draw(surface.expolygon, surface_type_to_color_name(surface.surface_type), transparency);
export_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
}
+85 -72
View File
@@ -15,87 +15,93 @@ class Layer;
class PrintRegion;
class PrintObject;
// TODO: make stuff private
class LayerRegion
{
friend class Layer;
public:
Layer* layer() { return this->_layer; }
const Layer* layer() const { return this->_layer; }
PrintRegion* region() { return this->_region; }
const PrintRegion* region() const { return this->_region; }
Layer* layer() { return m_layer; }
const Layer* layer() const { return m_layer; }
PrintRegion* region() { return m_region; }
const PrintRegion* region() const { return m_region; }
// Collection of surfaces generated by slicing the original geometry, divided by type top/bottom/internal.
// collection of surfaces generated by slicing the original geometry
// divided by type top/bottom/internal
SurfaceCollection slices;
// collection of extrusion paths/loops filling gaps
// These fills are generated by the perimeter generator.
// They are not printed on their own, but they are copied to this->fills during infill generation.
ExtrusionEntityCollection thin_fills;
// Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature")
// and for re-starting of infills.
ExPolygons fill_expolygons;
// collection of surfaces for infill generation
SurfaceCollection fill_surfaces;
// Collection of extrusion paths/loops filling gaps.
// These fills are generated by the perimeter generator.
// They are not printed on their own, but they are copied to this->fills during infill generation.
ExtrusionEntityCollection thin_fills;
// Collection of expolygons representing the bridged areas (thus not needing support material).
//FIXME Not used as of now.
// Collection of perimeter surfaces. This is a cached result of diff(slices, fill_surfaces).
// While not necessary, the memory consumption is meager and it speeds up calculation.
// The perimeter_surfaces keep the IDs of the slices (top/bottom/)
SurfaceCollection perimeter_surfaces;
// collection of expolygons representing the bridged areas (thus not
// needing support material)
Polygons bridged;
// collection of polylines representing the unsupported bridge edges
PolylineCollection unsupported_bridge_edges;
// Ordered collection of extrusion paths/loops to build all perimeters.
// This collection contains only ExtrusionEntityCollection objects.
// ordered collection of extrusion paths/loops to build all perimeters
// (this collection contains only ExtrusionEntityCollection objects)
ExtrusionEntityCollection perimeters;
// Ordered collection of extrusion paths to fill surfaces.
// This collection contains only ExtrusionEntityCollection objects.
// ordered collection of extrusion paths to fill surfaces
// (this collection contains only ExtrusionEntityCollection objects)
ExtrusionEntityCollection fills;
Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
void slices_to_fill_surfaces_clipped();
void prepare_fill_surfaces();
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
void process_external_surfaces(const Layer* lower_layer);
Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
void slices_to_fill_surfaces_clipped();
void prepare_fill_surfaces();
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
void process_external_surfaces(const Layer* lower_layer);
double infill_area_threshold() const;
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;
// Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export.
void export_region_slices_to_svg_debug(const char *name) const;
void export_region_fill_surfaces_to_svg_debug(const char *name) const;
void export_region_slices_to_svg_debug(const char *name) const;
void export_region_fill_surfaces_to_svg_debug(const char *name) const;
// Is there any valid extrusion assigned to this LayerRegion?
bool has_extrusions() const { return ! this->perimeters.entities.empty() || ! this->fills.entities.empty(); }
bool has_extrusions() const { return ! this->perimeters.entities.empty() || ! this->fills.entities.empty(); }
protected:
friend class Layer;
LayerRegion(Layer *layer, PrintRegion *region) : m_layer(layer), m_region(region) {}
~LayerRegion() {}
private:
Layer *_layer;
PrintRegion *_region;
LayerRegion(Layer *layer, PrintRegion *region) : _layer(layer), _region(region) {}
~LayerRegion() {}
Layer *m_layer;
PrintRegion *m_region;
};
typedef std::vector<LayerRegion*> LayerRegionPtrs;
class Layer {
friend class PrintObject;
class Layer
{
public:
size_t id() const { return this->_id; }
void set_id(size_t id) { this->_id = id; }
PrintObject* object() { return this->_object; }
const PrintObject* object() const { return this->_object; }
size_t id() const { return m_id; }
void set_id(size_t id) { m_id = id; }
PrintObject* object() { return m_object; }
const PrintObject* object() const { return m_object; }
Layer *upper_layer;
Layer *lower_layer;
LayerRegionPtrs regions;
bool slicing_errors;
coordf_t slice_z; // Z used for slicing in unscaled coordinates
coordf_t print_z; // Z used for printing in unscaled coordinates
coordf_t height; // layer height in unscaled coordinates
Layer *upper_layer;
Layer *lower_layer;
bool slicing_errors;
coordf_t slice_z; // Z used for slicing in unscaled coordinates
coordf_t print_z; // Z used for printing in unscaled coordinates
coordf_t height; // layer height in unscaled coordinates
// collection of expolygons generated by slicing the original geometry;
// also known as 'islands' (all regions and surface types are merged here)
@@ -103,57 +109,64 @@ public:
// order will be recovered by the G-code generator.
ExPolygonCollection slices;
size_t region_count() const { return this->regions.size(); }
const LayerRegion* get_region(int idx) const { return this->regions.at(idx); }
LayerRegion* get_region(int idx) { return this->regions.at(idx); }
LayerRegion* add_region(PrintRegion* print_region);
size_t region_count() const { return m_regions.size(); }
const LayerRegion* get_region(int idx) const { return m_regions.at(idx); }
LayerRegion* get_region(int idx) { return m_regions[idx]; }
LayerRegion* add_region(PrintRegion* print_region);
const LayerRegionPtrs& regions() const { return m_regions; }
void make_slices();
void merge_slices();
void make_slices();
void merge_slices();
template <class T> bool any_internal_region_slice_contains(const T &item) const {
for (const LayerRegion *layerm : this->regions) if (layerm->slices.any_internal_contains(item)) return true;
for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_internal_contains(item)) return true;
return false;
}
template <class T> bool any_bottom_region_slice_contains(const T &item) const {
for (const LayerRegion *layerm : this->regions) if (layerm->slices.any_bottom_contains(item)) return true;
for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_bottom_contains(item)) return true;
return false;
}
void make_perimeters();
void make_fills();
void make_perimeters();
void make_fills();
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;
// Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export.
void export_region_slices_to_svg_debug(const char *name) const;
void export_region_fill_surfaces_to_svg_debug(const char *name) const;
void export_region_slices_to_svg_debug(const char *name) const;
void export_region_fill_surfaces_to_svg_debug(const char *name) const;
// Is there any valid extrusion assigned to this LayerRegion?
virtual bool has_extrusions() const { for (auto layerm : this->regions) if (layerm->has_extrusions()) return true; return false; }
virtual bool has_extrusions() const { for (auto layerm : m_regions) if (layerm->has_extrusions()) return true; return false; }
protected:
size_t _id; // sequential number of layer, 0-based
PrintObject *_object;
friend class PrintObject;
Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) :
upper_layer(nullptr), lower_layer(nullptr), slicing_errors(false),
slice_z(slice_z), print_z(print_z), height(height),
_id(id), _object(object) {}
m_id(id), m_object(object) {}
virtual ~Layer();
private:
// sequential number of layer, 0-based
size_t m_id;
PrintObject *m_object;
LayerRegionPtrs m_regions;
};
class SupportLayer : public Layer {
friend class PrintObject;
class SupportLayer : public Layer
{
public:
// Polygons covered by the supports: base, interface and contact areas.
ExPolygonCollection support_islands;
ExPolygonCollection support_islands;
// Extrusion paths for the support base and for the support interface and contacts.
ExtrusionEntityCollection support_fills;
ExtrusionEntityCollection support_fills;
// Is there any valid extrusion assigned to this LayerRegion?
virtual bool has_extrusions() const { return ! support_fills.empty(); }
virtual bool has_extrusions() const { return ! support_fills.empty(); }
protected:
friend class PrintObject;
//protected:
// The constructor has been made public to be able to insert additional support layers for the skirt or a wipe tower
// between the raft and the object first layer.
SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) :
+15 -15
View File
@@ -17,13 +17,13 @@ namespace Slic3r {
Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const
{
return this->_region->flow(
return m_region->flow(
role,
this->_layer->height,
m_layer->height,
bridge,
this->_layer->id() == 0,
m_layer->id() == 0,
width,
*this->_layer->object()
*m_layer->object()
);
}
@@ -60,9 +60,9 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
&slices,
this->layer()->height,
this->flow(frPerimeter),
&this->region()->config,
&this->layer()->object()->config,
&this->layer()->object()->print()->config,
&this->region()->config(),
&this->layer()->object()->config(),
&this->layer()->object()->print()->config(),
// output:
&this->perimeters,
@@ -115,7 +115,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
{
// bottom_polygons are used to trim inflated top surfaces.
fill_boundaries.reserve(number_polygons(surfaces));
bool has_infill = this->region()->config.fill_density.value > 0.;
bool has_infill = this->region()->config().fill_density.value > 0.;
for (const Surface &surface : this->fill_surfaces.surfaces) {
if (surface.surface_type == stTop) {
// Collect the top surfaces, inflate them and trim them by the bottom surfaces.
@@ -258,9 +258,9 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
#ifdef SLIC3R_DEBUG
printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id());
#endif
if (bd.detect_angle(Geometry::deg2rad(this->region()->config.bridge_angle.value))) {
if (bd.detect_angle(Geometry::deg2rad(this->region()->config().bridge_angle.value))) {
bridges[idx_last].bridge_angle = bd.angle;
if (this->layer()->object()->config.support_material) {
if (this->layer()->object()->config().support_material) {
polygons_append(this->bridged, bd.coverage());
this->unsupported_bridge_edges.append(bd.unsupported_edges());
}
@@ -350,13 +350,13 @@ void LayerRegion::prepare_fill_surfaces()
the only meaningful information returned by psPerimeters. */
// if no solid layers are requested, turn top/bottom surfaces to internal
if (this->region()->config.top_solid_layers == 0) {
if (this->region()->config().top_solid_layers == 0) {
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface)
if (surface->surface_type == stTop)
surface->surface_type = (this->layer()->object()->config.infill_only_where_needed) ?
surface->surface_type = (this->layer()->object()->config().infill_only_where_needed) ?
stInternalVoid : stInternal;
}
if (this->region()->config.bottom_solid_layers == 0) {
if (this->region()->config().bottom_solid_layers == 0) {
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
if (surface->surface_type == stBottom || surface->surface_type == stBottomBridge)
surface->surface_type = stInternal;
@@ -364,9 +364,9 @@ void LayerRegion::prepare_fill_surfaces()
}
// turn too small internal regions into solid regions according to the user setting
if (this->region()->config.fill_density.value > 0) {
if (this->region()->config().fill_density.value > 0) {
// scaling an area requires two calls!
double min_area = scale_(scale_(this->region()->config.solid_infill_below_area.value));
double min_area = scale_(scale_(this->region()->config().solid_infill_below_area.value));
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
if (surface->surface_type == stInternal && surface->area() <= min_area)
surface->surface_type = stInternalSolid;
+43 -4
View File
@@ -340,7 +340,7 @@ void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
Pointfs model_sizes(copies_num-1, to_2d(this->bounding_box().size()));
Pointfs positions;
if (! _arrange(model_sizes, dist, bb, positions))
CONFESS("Cannot duplicate part as the resulting objects would not fit on the print bed.\n");
throw std::invalid_argument("Cannot duplicate part as the resulting objects would not fit on the print bed.\n");
// note that this will leave the object count unaltered
@@ -671,7 +671,8 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const
BoundingBoxf3 bb;
for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) {
if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances");
if (this->instances.empty())
throw std::invalid_argument("Can't call raw_bounding_box() with no instances");
bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true));
}
return bb;
@@ -885,6 +886,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
return;
}
// Called by Print::validate() from the UI thread.
void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
{
for (const ModelVolume* vol : this->volumes)
@@ -989,8 +991,6 @@ const TriangleMesh& ModelVolume::get_convex_hull() const
ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
{
// Legacy support
if (s == "0")
return MODEL_PART;
if (s == "1")
return PARAMETER_MODIFIER;
// New type (supporting the support enforcers & blockers)
@@ -1002,6 +1002,9 @@ ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
return SUPPORT_ENFORCER;
if (s == "SupportBlocker")
return SUPPORT_BLOCKER;
assert(s == "0");
// Default value if invalud type string received.
return MODEL_PART;
}
std::string ModelVolume::type_to_string(const Type t)
@@ -1051,6 +1054,29 @@ size_t ModelVolume::split(unsigned int max_extruders)
return idx;
}
#if ENABLE_MODELINSTANCE_3D_ROTATION
void ModelInstance::set_rotation(const Vec3d& rotation)
{
set_rotation(X, rotation(0));
set_rotation(Y, rotation(1));
set_rotation(Z, rotation(2));
}
void ModelInstance::set_rotation(Axis axis, double rotation)
{
static const double TWO_PI = 2.0 * (double)PI;
while (rotation < 0.0)
{
rotation += TWO_PI;
}
while (TWO_PI < rotation)
{
rotation -= TWO_PI;
}
m_rotation(axis) = rotation;
}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
{
mesh->transform(world_matrix(dont_translate).cast<float>());
@@ -1095,7 +1121,12 @@ Vec3d ModelInstance::transform_vector(const Vec3d& v, bool dont_translate) const
void ModelInstance::transform_polygon(Polygon* polygon) const
{
#if ENABLE_MODELINSTANCE_3D_ROTATION
// CHECK_ME -> Is the following correct or it should take in account all three rotations ?
polygon->rotate(this->m_rotation(2)); // rotate around polygon origin
#else
polygon->rotate(this->rotation); // rotate around polygon origin
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
polygon->scale(this->scaling_factor); // scale around polygon origin
}
@@ -1111,7 +1142,15 @@ Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, b
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
if (!dont_rotate)
#if ENABLE_MODELINSTANCE_3D_ROTATION
{
m.rotate(Eigen::AngleAxisd(m_rotation(2), Vec3d::UnitZ()));
m.rotate(Eigen::AngleAxisd(m_rotation(1), Vec3d::UnitY()));
m.rotate(Eigen::AngleAxisd(m_rotation(0), Vec3d::UnitX()));
}
#else
m.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ()));
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
if (!dont_scale)
m.scale(scaling_factor);
+20
View File
@@ -133,6 +133,7 @@ public:
void cut(coordf_t z, Model* model) const;
void split(ModelObjectPtrs* new_objects);
// Called by Print::validate() from the UI thread.
void check_instances_print_volume_state(const BoundingBoxf3& print_volume);
// Print object statistics to console.
@@ -249,11 +250,16 @@ public:
#if ENABLE_MODELINSTANCE_3D_OFFSET
private:
Vec3d m_offset; // in unscaled coordinates
#if ENABLE_MODELINSTANCE_3D_ROTATION
Vec3d m_rotation; // Rotation around the three axes, in radians around mesh center point
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
public:
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if !ENABLE_MODELINSTANCE_3D_ROTATION
double rotation; // Rotation around the Z axis, in radians around mesh center point
#endif // !ENABLE_MODELINSTANCE_3D_ROTATION
double scaling_factor;
#if !ENABLE_MODELINSTANCE_3D_OFFSET
Vec2d offset; // in unscaled coordinates
@@ -272,6 +278,14 @@ public:
void set_offset(Axis axis, double offset) { m_offset(axis) = offset; }
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
const Vec3d& get_rotation() const { return m_rotation; }
double get_rotation(Axis axis) const { return m_rotation(axis); }
void set_rotation(const Vec3d& rotation);
void set_rotation(Axis axis, double rotation);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
// To be called on an external mesh
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
// Calculate a bounding box of a transformed mesh. To be called on an external mesh.
@@ -292,9 +306,15 @@ private:
ModelObject* object;
#if ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
ModelInstance(ModelObject *object) : m_rotation(Vec3d::Zero()), scaling_factor(1), m_offset(Vec3d::Zero()), object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
m_rotation(other.m_rotation), scaling_factor(other.scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {}
#else
ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), m_offset(Vec3d::Zero()), object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
rotation(other.rotation), scaling_factor(other.scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#else
ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), offset(Vec2d::Zero()), object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
+92 -79
View File
@@ -292,59 +292,59 @@ protected:
using Distance = TCoord<PointImpl>;
using Pile = sl::Shapes<PolygonImpl>;
Packer pck_;
PConfig pconf_; // Placement configuration
double bin_area_;
SpatIndex rtree_;
SpatIndex smallsrtree_;
double norm_;
Pile merged_pile_;
Box pilebb_;
ItemGroup remaining_;
ItemGroup items_;
Packer m_pck;
PConfig m_pconf; // Placement configuration
double m_bin_area;
SpatIndex m_rtree;
SpatIndex m_smallsrtree;
double m_norm;
Pile m_merged_pile;
Box m_pilebb;
ItemGroup m_remaining;
ItemGroup m_items;
public:
_ArrBase(const TBin& bin, Distance dist,
std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcond):
pck_(bin, dist), bin_area_(sl::area(bin)),
norm_(std::sqrt(sl::area(bin)))
m_pck(bin, dist), m_bin_area(sl::area(bin)),
m_norm(std::sqrt(sl::area(bin)))
{
fillConfig(pconf_);
fillConfig(m_pconf);
pconf_.before_packing =
m_pconf.before_packing =
[this](const Pile& merged_pile, // merged pile
const ItemGroup& items, // packed items
const ItemGroup& remaining) // future items to be packed
{
items_ = items;
merged_pile_ = merged_pile;
remaining_ = remaining;
m_items = items;
m_merged_pile = merged_pile;
m_remaining = remaining;
pilebb_ = sl::boundingBox(merged_pile);
m_pilebb = sl::boundingBox(merged_pile);
rtree_.clear();
smallsrtree_.clear();
m_rtree.clear();
m_smallsrtree.clear();
// We will treat big items (compared to the print bed) differently
auto isBig = [this](double a) {
return a/bin_area_ > BIG_ITEM_TRESHOLD ;
return a/m_bin_area > BIG_ITEM_TRESHOLD ;
};
for(unsigned idx = 0; idx < items.size(); ++idx) {
Item& itm = items[idx];
if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx});
smallsrtree_.insert({itm.boundingBox(), idx});
if(isBig(itm.area())) m_rtree.insert({itm.boundingBox(), idx});
m_smallsrtree.insert({itm.boundingBox(), idx});
}
};
pck_.progressIndicator(progressind);
pck_.stopCondition(stopcond);
m_pck.progressIndicator(progressind);
m_pck.stopCondition(stopcond);
}
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
rtree_.clear();
return pck_.executeIndexed(std::forward<Args>(args)...);
m_rtree.clear();
return m_pck.executeIndexed(std::forward<Args>(args)...);
}
};
@@ -358,18 +358,18 @@ public:
_ArrBase<Box>(bin, dist, progressind, stopcond)
{
pconf_.object_function = [this, bin] (const Item &item) {
m_pconf.object_function = [this, bin] (const Item &item) {
auto result = objfunc(bin.center(),
merged_pile_,
pilebb_,
items_,
m_merged_pile,
m_pilebb,
m_items,
item,
bin_area_,
norm_,
rtree_,
smallsrtree_,
remaining_);
m_bin_area,
m_norm,
m_rtree,
m_smallsrtree,
m_remaining);
double score = std::get<0>(result);
auto& fullbb = std::get<1>(result);
@@ -381,7 +381,7 @@ public:
return score;
};
pck_.configure(pconf_);
m_pck.configure(m_pconf);
}
};
@@ -396,27 +396,27 @@ public:
std::function<bool(void)> stopcond):
_ArrBase<lnCircle>(bin, dist, progressind, stopcond) {
pconf_.object_function = [this, &bin] (const Item &item) {
m_pconf.object_function = [this, &bin] (const Item &item) {
auto result = objfunc(bin.center(),
merged_pile_,
pilebb_,
items_,
m_merged_pile,
m_pilebb,
m_items,
item,
bin_area_,
norm_,
rtree_,
smallsrtree_,
remaining_);
m_bin_area,
m_norm,
m_rtree,
m_smallsrtree,
m_remaining);
double score = std::get<0>(result);
auto isBig = [this](const Item& itm) {
return itm.area()/bin_area_ > BIG_ITEM_TRESHOLD ;
return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ;
};
if(isBig(item)) {
auto mp = merged_pile_;
auto mp = m_merged_pile;
mp.push_back(item.transformedShape());
auto chull = sl::convexHull(mp);
double miss = Placer::overfit(chull, bin);
@@ -427,7 +427,7 @@ public:
return score;
};
pck_.configure(pconf_);
m_pck.configure(m_pconf);
}
};
@@ -439,25 +439,25 @@ public:
std::function<bool(void)> stopcond):
_ArrBase<PolygonImpl>(bin, dist, progressind, stopcond)
{
pconf_.object_function = [this, &bin] (const Item &item) {
m_pconf.object_function = [this, &bin] (const Item &item) {
auto binbb = sl::boundingBox(bin);
auto result = objfunc(binbb.center(),
merged_pile_,
pilebb_,
items_,
m_merged_pile,
m_pilebb,
m_items,
item,
bin_area_,
norm_,
rtree_,
smallsrtree_,
remaining_);
m_bin_area,
m_norm,
m_rtree,
m_smallsrtree,
m_remaining);
double score = std::get<0>(result);
return score;
};
pck_.configure(pconf_);
m_pck.configure(m_pconf);
}
};
@@ -469,22 +469,22 @@ public:
std::function<bool(void)> stopcond):
_ArrBase<Box>(Box(0, 0), dist, progressind, stopcond)
{
this->pconf_.object_function = [this] (const Item &item) {
this->m_pconf.object_function = [this] (const Item &item) {
auto result = objfunc({0, 0},
merged_pile_,
pilebb_,
items_,
m_merged_pile,
m_pilebb,
m_items,
item,
0,
norm_,
rtree_,
smallsrtree_,
remaining_);
m_norm,
m_rtree,
m_smallsrtree,
m_remaining);
return std::get<0>(result);
};
this->pck_.configure(pconf_);
this->m_pck.configure(m_pconf);
}
};
@@ -527,14 +527,19 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
// Invalid geometries would throw exceptions when arranging
if(item.vertexCount() > 3) {
item.rotation(objinst->rotation);
item.translation( {
#if ENABLE_MODELINSTANCE_3D_OFFSET
ClipperLib::cInt(objinst->get_offset(X) / SCALING_FACTOR),
ClipperLib::cInt(objinst->get_offset(Y) / SCALING_FACTOR)
#if ENABLE_MODELINSTANCE_3D_ROTATION
// CHECK_ME -> is the following correct or it should take in account all three rotations ?
item.rotation(objinst->get_rotation(Z));
#else
ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR),
ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR)
item.rotation(objinst->rotation);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
item.translation({
#if ENABLE_MODELINSTANCE_3D_OFFSET
ClipperLib::cInt(objinst->get_offset(X)/SCALING_FACTOR),
ClipperLib::cInt(objinst->get_offset(Y)/SCALING_FACTOR)
#else
ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR),
ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR)
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
});
ret.emplace_back(objinst, item);
@@ -668,18 +673,25 @@ void applyResult(
// Get the model instance from the shapemap using the index
ModelInstance *inst_ptr = shapemap[idx].first;
// Get the tranformation data from the item object and scale it
// Get the transformation data from the item object and scale it
// appropriately
auto off = item.translation();
Radians rot = item.rotation();
#if ENABLE_MODELINSTANCE_3D_OFFSET
Vec3d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR, 0.0);
Vec3d foff(off.X*SCALING_FACTOR + batch_offset,
off.Y*SCALING_FACTOR,
0.0);
#else
Vec2d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR);
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
// write the tranformation data into the model instance
// write the transformation data into the model instance
#if ENABLE_MODELINSTANCE_3D_ROTATION
// CHECK_ME -> Is the following correct ?
inst_ptr->set_rotation(Vec3d(0.0, 0.0, rot));
#else
inst_ptr->rotation = rot;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#if ENABLE_MODELINSTANCE_3D_OFFSET
inst_ptr->set_offset(foff);
#else
@@ -695,7 +707,7 @@ void applyResult(
* The arrangement considers multiple bins (aka. print beds) for placing all
* the items provided in the model argument. If the items don't fit on one
* print bed, the remaining will be placed onto newly created print beds.
* The first_bin_only parameter, if set to true, disables this behaviour and
* The first_bin_only parameter, if set to true, disables this behavior and
* makes sure that only one print bed is filled and the remaining items will be
* untouched. When set to false, the items which could not fit onto the
* print bed will be placed next to the print bed so the user should see a
@@ -741,6 +753,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
IndexedPackGroup result;
// If there is no hint about the shape, we will try to guess
if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed);
BoundingBox bbb(bed);
+23 -25
View File
@@ -37,30 +37,30 @@ typedef std::vector<PerimeterGeneratorLoop> PerimeterGeneratorLoops;
class PerimeterGenerator {
public:
// Inputs:
const SurfaceCollection* slices;
const ExPolygonCollection* lower_slices;
double layer_height;
int layer_id;
Flow perimeter_flow;
Flow ext_perimeter_flow;
Flow overhang_flow;
Flow solid_infill_flow;
PrintRegionConfig* config;
PrintObjectConfig* object_config;
PrintConfig* print_config;
const SurfaceCollection *slices;
const ExPolygonCollection *lower_slices;
double layer_height;
int layer_id;
Flow perimeter_flow;
Flow ext_perimeter_flow;
Flow overhang_flow;
Flow solid_infill_flow;
const PrintRegionConfig *config;
const PrintObjectConfig *object_config;
const PrintConfig *print_config;
// Outputs:
ExtrusionEntityCollection* loops;
ExtrusionEntityCollection* gap_fill;
SurfaceCollection* fill_surfaces;
ExtrusionEntityCollection *loops;
ExtrusionEntityCollection *gap_fill;
SurfaceCollection *fill_surfaces;
PerimeterGenerator(
// Input:
const SurfaceCollection* slices,
double layer_height,
Flow flow,
PrintRegionConfig* config,
PrintObjectConfig* object_config,
PrintConfig* print_config,
const PrintRegionConfig* config,
const PrintObjectConfig* object_config,
const PrintConfig* print_config,
// Output:
// Loops with the external thin walls
ExtrusionEntityCollection* loops,
@@ -78,15 +78,13 @@ public:
void process();
private:
double _ext_mm3_per_mm;
double _mm3_per_mm;
double _mm3_per_mm_overhang;
Polygons _lower_slices_p;
double _ext_mm3_per_mm;
double _mm3_per_mm;
double _mm3_per_mm_overhang;
Polygons _lower_slices_p;
ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops,
ThickPolylines &thin_walls) const;
ExtrusionEntityCollection _variable_width
(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const;
ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const;
ExtrusionEntityCollection _variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const;
};
}
+8 -8
View File
@@ -69,7 +69,7 @@ PlaceholderParser::PlaceholderParser()
this->update_timestamp();
}
void PlaceholderParser::update_timestamp()
void PlaceholderParser::update_timestamp(DynamicConfig &config)
{
time_t rawtime;
time(&rawtime);
@@ -84,14 +84,14 @@ void PlaceholderParser::update_timestamp()
ss << std::setw(2) << std::setfill('0') << timeinfo->tm_hour;
ss << std::setw(2) << std::setfill('0') << timeinfo->tm_min;
ss << std::setw(2) << std::setfill('0') << timeinfo->tm_sec;
this->set("timestamp", ss.str());
config.set_key_value("timestamp", new ConfigOptionString(ss.str()));
}
this->set("year", 1900 + timeinfo->tm_year);
this->set("month", 1 + timeinfo->tm_mon);
this->set("day", timeinfo->tm_mday);
this->set("hour", timeinfo->tm_hour);
this->set("minute", timeinfo->tm_min);
this->set("second", timeinfo->tm_sec);
config.set_key_value("year", new ConfigOptionInt(1900 + timeinfo->tm_year));
config.set_key_value("month", new ConfigOptionInt(1 + timeinfo->tm_mon));
config.set_key_value("day", new ConfigOptionInt(timeinfo->tm_mday));
config.set_key_value("hour", new ConfigOptionInt(timeinfo->tm_hour));
config.set_key_value("minute", new ConfigOptionInt(timeinfo->tm_min));
config.set_key_value("second", new ConfigOptionInt(timeinfo->tm_sec));
}
// Scalar configuration values are stored into m_single,
+5 -1
View File
@@ -14,7 +14,6 @@ class PlaceholderParser
public:
PlaceholderParser();
void update_timestamp();
void apply_config(const DynamicPrintConfig &config);
void apply_env_variables();
@@ -37,6 +36,11 @@ public:
// Throws std::runtime_error on syntax or runtime error.
static bool evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override = nullptr);
// Update timestamp, year, month, day, hour, minute, second variables at the provided config.
static void update_timestamp(DynamicConfig &config);
// Update timestamp, year, month, day, hour, minute, second variables at m_config.
void update_timestamp() { update_timestamp(m_config); }
private:
DynamicConfig m_config;
};
+1 -1
View File
@@ -47,7 +47,7 @@ Polygon::split_at_vertex(const Point &point) const
for (const Point &pt : this->points)
if (pt == point)
return this->split_at_index(&pt - &this->points.front());
CONFESS("Point not found");
throw std::invalid_argument("Point not found");
return Polyline();
}
+2 -1
View File
@@ -18,7 +18,8 @@ Polyline::operator Polylines() const
Polyline::operator Line() const
{
if (this->points.size() > 2) CONFESS("Can't convert polyline with more than two points to a line");
if (this->points.size() > 2)
throw std::invalid_argument("Can't convert polyline with more than two points to a line");
return Line(this->points.front(), this->points.back());
}
+2 -1
View File
@@ -77,7 +77,8 @@ Polylines PolylineCollection::_chained_path_from(
Point PolylineCollection::leftmost_point(const Polylines &polylines)
{
if (polylines.empty()) CONFESS("leftmost_point() called on empty PolylineCollection");
if (polylines.empty())
throw std::invalid_argument("leftmost_point() called on empty PolylineCollection");
Polylines::const_iterator it = polylines.begin();
Point p = it->leftmost_point();
for (++ it; it != polylines.end(); ++it) {
+364 -263
View File
File diff suppressed because it is too large Load Diff
+333 -162
View File
@@ -2,9 +2,11 @@
#define slic3r_Print_hpp_
#include "libslic3r.h"
#include <atomic>
#include <set>
#include <vector>
#include <string>
#include <functional>
#include "BoundingBox.hpp"
#include "Flow.hpp"
#include "PrintConfig.hpp"
@@ -17,56 +19,110 @@
#include "GCode/WipeTower.hpp"
#include "tbb/atomic.h"
// tbb/mutex.h includes Windows, which in turn defines min/max macros. Convince Windows.h to not define these min/max macros.
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "tbb/mutex.h"
namespace Slic3r {
class Print;
class PrintObject;
class ModelObject;
class GCode;
class GCodePreviewData;
// Print step IDs for keeping track of the print state.
enum PrintStep {
psSkirt, psBrim, psWipeTower, psCount,
psSkirt, psBrim, psWipeTower, psGCodeExport, psCount,
};
enum PrintObjectStep {
posSlice, posPerimeters, posPrepareInfill,
posInfill, posSupportMaterial, posCount,
};
class CanceledException : public std::exception {
public:
const char* what() const throw() { return "Background processing has been canceled"; }
};
// To be instantiated over PrintStep or PrintObjectStep enums.
template <class StepType, size_t COUNT>
class PrintState
{
public:
PrintState() { memset(state, 0, sizeof(state)); }
PrintState() { for (size_t i = 0; i < COUNT; ++ i) m_state[i].store(INVALID, std::memory_order_relaxed); }
enum State {
INVALID,
STARTED,
DONE,
};
State state[COUNT];
bool is_started(StepType step) const { return this->state[step] == STARTED; }
bool is_done(StepType step) const { return this->state[step] == DONE; }
void set_started(StepType step) { this->state[step] = STARTED; }
void set_done(StepType step) { this->state[step] = DONE; }
bool invalidate(StepType step) {
bool invalidated = this->state[step] != INVALID;
this->state[step] = INVALID;
// With full memory barrier.
bool is_done(StepType step) const { return m_state[step] == DONE; }
// Set the step as started. Block on mutex while the Print / PrintObject / PrintRegion objects are being
// modified by the UI thread.
// This is necessary to block until the Print::apply_config() updates its state, which may
// influence the processing step being entered.
void set_started(StepType step, tbb::mutex &mtx) {
mtx.lock();
m_state[step].store(STARTED, std::memory_order_relaxed);
mtx.unlock();
}
// Set the step as done. Block on mutex while the Print / PrintObject / PrintRegion objects are being
// modified by the UI thread.
void set_done(StepType step, tbb::mutex &mtx) {
mtx.lock();
m_state[step].store(DONE, std::memory_order_relaxed);
mtx.unlock();
}
// Make the step invalid.
// The provided mutex should be locked at this point, guarding access to m_state.
// In case the step has already been entered or finished, cancel the background
// processing by calling the cancel callback.
template<typename CancelationCallback>
bool invalidate(StepType step, tbb::mutex &mtx, CancelationCallback &cancel) {
bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID;
if (invalidated) {
#if 0
if (mtx.state != mtx.HELD) {
printf("Not held!\n");
}
#endif
mtx.unlock();
cancel();
mtx.lock();
}
return invalidated;
}
bool invalidate_all() {
// Make all steps invalid.
// The provided mutex should be locked at this point, guarding access to m_state.
// In case any step has already been entered or finished, cancel the background
// processing by calling the cancel callback.
template<typename CancelationCallback>
bool invalidate_all(tbb::mutex &mtx, CancelationCallback &cancel) {
bool invalidated = false;
for (size_t i = 0; i < COUNT; ++ i)
if (this->state[i] != INVALID) {
invalidated = true;
break;
if (m_state[i].load(std::memory_order_relaxed) != INVALID) {
if (! invalidated) {
mtx.unlock();
cancel();
mtx.lock();
invalidated = true;
}
m_state[i].store(INVALID, std::memory_order_relaxed);
}
memset(state, 0, sizeof(state));
return invalidated;
}
private:
std::atomic<State> m_state[COUNT];
};
// A PrintRegion object represents a group of volumes to print
@@ -75,20 +131,27 @@ class PrintRegion
{
friend class Print;
// Methods NOT modifying the PrintRegion's state:
public:
PrintRegionConfig config;
Print* print() { return this->_print; }
Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
const Print* print() const { return m_print; }
const PrintRegionConfig& config() const { return m_config; }
Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
// Average diameter of nozzles participating on extruding this region.
coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const;
coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const;
// Average diameter of nozzles participating on extruding this region.
coordf_t bridging_height_avg(const PrintConfig &print_config) const;
// Methods modifying the PrintRegion's state:
public:
Print* print() { return m_print; }
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); }
private:
Print* _print;
Print *m_print;
PrintRegionConfig m_config;
PrintRegion(Print* print) : _print(print) {}
PrintRegion(Print* print) : m_print(print) {}
PrintRegion(Print* print, const PrintRegionConfig &config) : m_print(print), m_config(config) {}
~PrintRegion() {}
};
@@ -104,43 +167,35 @@ class PrintObject
public:
// vector of (vectors of volume ids), indexed by region_id
std::vector<std::vector<int>> region_volumes;
PrintObjectConfig config;
t_layer_height_ranges layer_height_ranges;
t_layer_height_ranges layer_height_ranges;
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
// The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
// layer_height_profile must not be set by the background thread.
std::vector<coordf_t> layer_height_profile;
std::vector<coordf_t> layer_height_profile;
// There is a layer_height_profile at both PrintObject and ModelObject. The layer_height_profile at the ModelObject
// is used for interactive editing and for loading / storing into a project file (AMF file as of today).
// This flag indicates that the layer_height_profile at the UI has been updated, therefore the backend needs to get it.
// This flag is necessary as we cannot safely clear the layer_height_profile if the background calculation is running.
bool layer_height_profile_valid;
bool layer_height_profile_valid;
// this is set to true when LayerRegion->slices is split in top/internal/bottom
// so that next call to make_perimeters() performs a union() before computing loops
bool typed_slices;
bool typed_slices;
Vec3crd size; // XYZ in scaled coordinates
Vec3crd size; // XYZ in scaled coordinates
// scaled coordinates to add to copies (to compensate for the alignment
// operated when creating the object but still preserving a coherent API
// for external callers)
Point _copies_shift;
Print* print() { return m_print; }
const Print* print() const { return m_print; }
ModelObject* model_object() { return m_model_object; }
const ModelObject* model_object() const { return m_model_object; }
const PrintObjectConfig& config() const { return m_config; }
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); }
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); }
const LayerPtrs& layers() const { return m_layers; }
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
// Slic3r::Point objects in scaled G-code coordinates in our coordinates
Points _shifted_copies;
LayerPtrs layers;
SupportLayerPtrs support_layers;
PrintState<PrintObjectStep, posCount> state;
Print* print() { return this->_print; }
const Print* print() const { return this->_print; }
ModelObject* model_object() { return this->_model_object; }
const ModelObject* model_object() const { return this->_model_object; }
const Points& copies() const { return this->_copies; }
const Points& copies() const { return m_copies; }
bool add_copy(const Vec2d &point);
bool delete_last_copy();
bool delete_all_copies() { return this->set_copies(Points()); }
@@ -159,24 +214,26 @@ public:
// this value is not supposed to be compared with Layer::id
// since they have different semantics.
size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); }
size_t layer_count() const { return this->layers.size(); }
size_t layer_count() const { return m_layers.size(); }
void clear_layers();
Layer* get_layer(int idx) { return this->layers.at(idx); }
const Layer* get_layer(int idx) const { return this->layers.at(idx); }
Layer* get_layer(int idx) { return m_layers[idx]; }
const Layer* get_layer(int idx) const { return m_layers[idx]; }
// 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);
size_t support_layer_count() const { return this->support_layers.size(); }
size_t support_layer_count() const { return m_support_layers.size(); }
void clear_support_layers();
SupportLayer* get_support_layer(int idx) { return this->support_layers.at(idx); }
SupportLayer* get_support_layer(int idx) { return m_support_layers[idx]; }
SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z);
SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
void delete_support_layer(int idx);
// methods for handling state
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
bool invalidate_step(PrintObjectStep step);
bool invalidate_all_steps() { return this->state.invalidate_all(); }
bool invalidate_all_steps();
bool is_step_done(PrintObjectStep step) const { return m_state.is_done(step); }
// To be used over the layer_height_profile of both the PrintObject and ModelObject
// to initialize the height profile with the height ranges.
@@ -196,168 +253,282 @@ public:
// (layer height, first layer height, raft settings, print nozzle diameter etc).
SlicingParameters slicing_parameters() const;
void _slice();
std::string _fix_slicing_errors();
void _simplify_slices(double distance);
void _prepare_infill();
bool has_support_material() const;
void detect_surfaces_type();
void process_external_surfaces();
void discover_vertical_shells();
void bridge_over_infill();
void _make_perimeters();
void _infill();
void clip_fill_surfaces();
void discover_horizontal_shells();
void combine_infill();
void _generate_support_material();
bool is_printable() const { return !this->_shifted_copies.empty(); }
// Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t
void slice();
// Helpers to slice support enforcer / blocker meshes by the support generator.
std::vector<ExPolygons> slice_support_enforcers() const;
std::vector<ExPolygons> slice_support_blockers() const;
private:
Print* _print;
ModelObject* _model_object;
Points _copies; // Slic3r::Point objects in scaled G-code coordinates
void make_perimeters();
void prepare_infill();
void infill();
void generate_support_material();
void _slice();
std::string _fix_slicing_errors();
void _simplify_slices(double distance);
void _make_perimeters();
bool has_support_material() const;
void detect_surfaces_type();
void process_external_surfaces();
void discover_vertical_shells();
void bridge_over_infill();
void clip_fill_surfaces();
void discover_horizontal_shells();
void combine_infill();
void _generate_support_material();
bool is_printable() const { return ! m_copies.empty(); }
Print *m_print;
ModelObject *m_model_object;
PrintObjectConfig m_config;
// Slic3r::Point objects in scaled G-code coordinates
Points m_copies;
// scaled coordinates to add to copies (to compensate for the alignment
// operated when creating the object but still preserving a coherent API
// for external callers)
Point m_copies_shift;
LayerPtrs m_layers;
SupportLayerPtrs m_support_layers;
PrintState<PrintObjectStep, posCount> m_state;
// TODO: call model_object->get_bounding_box() instead of accepting
// parameter
PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox);
~PrintObject() {}
void set_started(PrintObjectStep step);
void set_done(PrintObjectStep step);
std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
std::vector<ExPolygons> _slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
};
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;
// Cache of tool changes per print layer.
std::unique_ptr<WipeTower::ToolChangeResult> priming;
std::vector<std::vector<WipeTower::ToolChangeResult>> tool_changes;
std::unique_ptr<WipeTower::ToolChangeResult> final_purge;
std::vector<float> used_filament;
int number_of_toolchanges;
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
float depth;
void clear() {
tool_ordering.clear();
priming.reset(nullptr);
tool_changes.clear();
final_purge.reset(nullptr);
used_filament.clear();
number_of_toolchanges = -1;
depth = 0.f;
}
};
struct PrintStatistics
{
PrintStatistics() { clear(); }
std::string estimated_normal_print_time;
std::string estimated_silent_print_time;
double total_used_filament;
double total_extruded_volume;
double total_cost;
double total_weight;
double total_wipe_tower_cost;
double total_wipe_tower_filament;
std::map<size_t, float> filament_stats;
void clear() {
estimated_normal_print_time.clear();
estimated_silent_print_time.clear();
total_used_filament = 0.;
total_extruded_volume = 0.;
total_cost = 0.;
total_weight = 0.;
total_wipe_tower_cost = 0.;
total_wipe_tower_filament = 0.;
filament_stats.clear();
}
};
typedef std::vector<PrintObject*> PrintObjectPtrs;
typedef std::vector<PrintRegion*> PrintRegionPtrs;
class ProgressIndicator;
using ProgressIndicatorPtr = std::shared_ptr<ProgressIndicator>;
// The complete print tray with possibly multiple objects.
class Print
{
public:
PrintConfig config;
PrintObjectConfig default_object_config;
PrintRegionConfig default_region_config;
PrintObjectPtrs objects;
PrintRegionPtrs regions;
PlaceholderParser placeholder_parser;
// TODO: status_cb
ProgressIndicatorPtr progressindicator;
std::string estimated_normal_print_time;
std::string estimated_silent_print_time;
double total_used_filament, total_extruded_volume, total_cost, total_weight, total_wipe_tower_cost, total_wipe_tower_filament;
std::map<size_t, float> filament_stats;
PrintState<PrintStep, psCount> state;
// ordered collections of extrusion paths to build skirt loops and brim
ExtrusionEntityCollection skirt, brim;
Print() : total_used_filament(0), total_extruded_volume(0) { restart(); }
Print() { restart(); }
~Print() { clear_objects(); }
// methods for handling objects
void clear_objects();
PrintObject* get_object(size_t idx) { return objects.at(idx); }
const PrintObject* get_object(size_t idx) const { return objects.at(idx); }
void delete_object(size_t idx);
void reload_object(size_t idx);
bool reload_model_instances();
// Methods, which change the state of Print / PrintObject / PrintRegion.
// The following methods are synchronized with process() and export_gcode(),
// so that process() and export_gcode() may be called from a background thread.
// In case the following methods need to modify data processed by process() or export_gcode(),
// a cancellation callback is executed to stop the background processing before the operation.
void clear_objects();
void delete_object(size_t idx);
void reload_object(size_t idx);
bool reload_model_instances();
void add_model_object(ModelObject* model_object, int idx = -1);
bool apply_config(DynamicPrintConfig config);
void process();
void export_gcode(const std::string &path_template, GCodePreviewData *preview_data);
// SLA export, temporary.
void export_png(const std::string &dirpath);
PrintObjectPtrs get_printable_objects() const;
// methods for handling regions
PrintRegion* get_region(size_t idx) { return regions.at(idx); }
const PrintRegion* get_region(size_t idx) const { return regions.at(idx); }
PrintRegion* add_region();
// methods for handling state
bool invalidate_step(PrintStep step);
bool invalidate_all_steps() { return this->state.invalidate_all(); }
bool step_done(PrintObjectStep step) const;
void add_model_object(ModelObject* model_object, int idx = -1);
bool apply_config(DynamicPrintConfig config);
float get_wipe_tower_depth() const { return m_wipe_tower_depth; }
bool has_infinite_skirt() const;
bool has_skirt() const;
bool is_step_done(PrintStep step) const { return m_state.is_done(step); }
bool is_step_done(PrintObjectStep step) const;
bool has_infinite_skirt() const;
bool has_skirt() const;
PrintObjectPtrs get_printable_objects() const;
float get_wipe_tower_depth() const { return m_wipe_tower_data.depth; }
// Returns an empty string if valid, otherwise returns an error message.
std::string validate() const;
BoundingBox bounding_box() const;
BoundingBox total_bounding_box() const;
double skirt_first_layer_height() const;
Flow brim_flow() const;
Flow skirt_flow() const;
std::string validate() const;
BoundingBox bounding_box() const;
BoundingBox total_bounding_box() const;
double skirt_first_layer_height() const;
Flow brim_flow() const;
Flow skirt_flow() const;
std::vector<unsigned int> object_extruders() const;
std::vector<unsigned int> support_material_extruders() const;
std::vector<unsigned int> extruders() const;
void _simplify_slices(double distance);
double max_allowed_layer_height() const;
bool has_support_material() const;
void auto_assign_extruders(ModelObject* model_object) const;
double max_allowed_layer_height() const;
bool has_support_material() const;
// Make sure the background processing has no access to this model_object during this call!
void auto_assign_extruders(ModelObject* model_object) const;
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; }
const PrintObjectPtrs& objects() const { return m_objects; }
const PrintObject* get_object(int idx) const { return m_objects[idx]; }
const PrintRegionPtrs& regions() const { return m_regions; }
const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; }
// Returns extruder this eec should be printed with, according to PrintRegion config:
static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region);
void _make_skirt();
void _make_brim();
const ExtrusionEntityCollection& skirt() const { return m_skirt; }
const ExtrusionEntityCollection& brim() const { return m_brim; }
const PrintStatistics& print_statistics() const { return m_print_statistics; }
// Wipe tower support.
bool has_wipe_tower() const;
void _clear_wipe_tower();
void _make_wipe_tower();
// 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 m_tool_ordering;
// Cache of tool changes per print layer.
std::unique_ptr<WipeTower::ToolChangeResult> m_wipe_tower_priming;
std::vector<std::vector<WipeTower::ToolChangeResult>> m_wipe_tower_tool_changes;
std::unique_ptr<WipeTower::ToolChangeResult> m_wipe_tower_final_purge;
std::vector<float> m_wipe_tower_used_filament;
int m_wipe_tower_number_of_toolchanges = -1;
bool has_wipe_tower() const;
const WipeTowerData& wipe_tower_data() const { return m_wipe_tower_data; }
std::string output_filename();
std::string output_filepath(const std::string &path);
std::string output_filename() const;
std::string output_filepath(const std::string &path) const;
// Calls a registered callback to update the status.
void set_status(int percent, const std::string &message);
// Cancel the running computation. Stop execution of all the background threads.
void cancel() { m_canceled = true; }
// Cancel the running computation. Stop execution of all the background threads.
void restart() { m_canceled = false; }
typedef std::function<void(int, const std::string&)> status_callback_type;
// Default status console print out in the form of percent => message.
void set_status_default() { m_status_callback = nullptr; }
// No status output or callback whatsoever, useful mostly for automatic tests.
void set_status_silent() { m_status_callback = [](int, const std::string&){}; }
// Register a custom status callback.
void set_status_callback(status_callback_type cb) { m_status_callback = cb; }
// Calls a registered callback to update the status, or print out the default message.
void set_status(int percent, const std::string &message) {
if (m_status_callback) m_status_callback(percent, message);
else printf("%d => %s\n", percent, message.c_str());
}
typedef std::function<void()> cancel_callback_type;
// Various methods will call this callback to stop the background processing (the Print::process() call)
// in case a successive change of the Print / PrintObject / PrintRegion instances changed
// the state of the finished or running calculations.
void set_cancel_callback(cancel_callback_type cancel_callback) { m_cancel_callback = cancel_callback; }
// Has the calculation been canceled?
bool canceled() { return m_canceled; }
bool canceled() const { return m_canceled; }
// Cancel the running computation. Stop execution of all the background threads.
void cancel() { m_canceled = true; }
// Cancel the running computation. Stop execution of all the background threads.
void restart() { m_canceled = false; }
void print_to_png(std::string dirpath);
// Accessed by SupportMaterial
const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; }
protected:
void set_started(PrintStep step) { m_state.set_started(step, m_mutex); throw_if_canceled(); }
void set_done(PrintStep step) { m_state.set_done(step, m_mutex); throw_if_canceled(); }
bool invalidate_step(PrintStep step);
bool invalidate_all_steps() { return m_state.invalidate_all(m_mutex, m_cancel_callback); }
// methods for handling regions
PrintRegion* get_region(size_t idx) { return m_regions[idx]; }
PrintRegion* add_region();
PrintRegion* add_region(const PrintRegionConfig &config);
private:
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
// If the background processing stop was requested, throw CanceledException.
// To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.
void throw_if_canceled() const { if (m_canceled) throw CanceledException(); }
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
float m_wipe_tower_depth = 0.f;
void _make_skirt();
void _make_brim();
void _make_wipe_tower();
void _simplify_slices(double distance);
PrintState<PrintStep, psCount> m_state;
// Mutex used for synchronization of the worker thread with the UI thread:
// The mutex will be used to guard the worker thread against entering a stage
// while the data influencing the stage is modified.
mutable tbb::mutex m_mutex;
// Has the calculation been canceled?
tbb::atomic<bool> m_canceled;
tbb::atomic<bool> m_canceled;
// Callback to be evoked regularly to update state of the UI thread.
status_callback_type m_status_callback;
// Callback to be evoked to stop the background processing before a state is updated.
cancel_callback_type m_cancel_callback = [](){};
PrintConfig m_config;
PrintObjectConfig m_default_object_config;
PrintRegionConfig m_default_region_config;
PrintObjectPtrs m_objects;
PrintRegionPtrs m_regions;
PlaceholderParser m_placeholder_parser;
// Ordered collections of extrusion paths to build skirt loops and brim.
ExtrusionEntityCollection m_skirt;
ExtrusionEntityCollection m_brim;
// Following section will be consumed by the GCodeGenerator.
WipeTowerData m_wipe_tower_data;
// Estimated print time, filament consumed.
PrintStatistics m_print_statistics;
// To allow GCode to set the Print's GCodeExport step status.
friend class GCode;
// Allow PrintObject to access m_mutex and m_cancel_callback.
friend class PrintObject;
};
#define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator)
#define FOREACH_REGION(print, region) FOREACH_BASE(PrintRegionPtrs, (print)->regions, region)
#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->objects, object)
#define FOREACH_LAYER(object, layer) FOREACH_BASE(LayerPtrs, (object)->layers, layer)
#define FOREACH_LAYERREGION(layer, layerm) FOREACH_BASE(LayerRegionPtrs, (layer)->regions, layerm)
#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->m_objects, object)
#define FOREACH_LAYER(object, layer) FOREACH_BASE(LayerPtrs, (object)->m_layers, layer)
#define FOREACH_LAYERREGION(layer, layerm) FOREACH_BASE(LayerRegionPtrs, (layer)->m_regions, layerm)
}
+10 -11
View File
@@ -161,7 +161,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Speed for printing bridges.");
def->sidetext = L("mm/s");
def->cli = "bridge-speed=f";
def->aliases.push_back("bridge_feed_rate");
def->aliases = { "bridge_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloat(60);
@@ -274,7 +274,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Distance used for the auto-arrange feature of the plater.");
def->sidetext = L("mm");
def->cli = "duplicate-distance=f";
def->aliases.push_back("multiply_distance");
def->aliases = { "multiply_distance" };
def->min = 0;
def->default_value = new ConfigOptionFloat(6);
@@ -335,7 +335,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Archimedean Chords"));
def->enum_labels.push_back(L("Octagram Spiral"));
// solid_fill_pattern is an obsolete equivalent to external_fill_pattern.
def->aliases.push_back("solid_fill_pattern");
def->aliases = { "solid_fill_pattern" };
def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear);
def = this->add("external_perimeter_extrusion_width", coFloatOrPercent);
@@ -923,8 +923,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Speed for printing the internal fill. Set to zero for auto.");
def->sidetext = L("mm/s");
def->cli = "infill-speed=f";
def->aliases.push_back("print_feed_rate");
def->aliases.push_back("infill_feed_rate");
def->aliases = { "print_feed_rate", "infill_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloat(80);
@@ -1272,7 +1271,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Extruders");
def->tooltip = L("The extruder to use when printing perimeters and brim. First extruder is 1.");
def->cli = "perimeter-extruder=i";
def->aliases.push_back("perimeters_extruder");
def->aliases = { "perimeters_extruder" };
def->min = 1;
def->default_value = new ConfigOptionInt(1);
@@ -1285,7 +1284,7 @@ void PrintConfigDef::init_fff_params()
"If expressed as percentage (for example 200%) it will be computed over layer height.");
def->sidetext = L("mm or % (leave 0 for default)");
def->cli = "perimeter-extrusion-width=s";
def->aliases.push_back("perimeters_extrusion_width");
def->aliases = { "perimeters_extrusion_width" };
def->default_value = new ConfigOptionFloatOrPercent(0, false);
def = this->add("perimeter_speed", coFloat);
@@ -1294,7 +1293,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Speed for perimeters (contours, aka vertical shells). Set to zero for auto.");
def->sidetext = L("mm/s");
def->cli = "perimeter-speed=f";
def->aliases.push_back("perimeter_feed_rate");
def->aliases = { "perimeter_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloat(60);
@@ -1307,7 +1306,7 @@ void PrintConfigDef::init_fff_params()
"if the Extra Perimeters option is enabled.");
def->sidetext = L("(minimum)");
def->cli = "perimeters=i";
def->aliases.push_back("perimeter_offsets");
def->aliases = { "perimeter_offsets" };
def->min = 0;
def->default_value = new ConfigOptionInt(3);
@@ -1635,7 +1634,7 @@ void PrintConfigDef::init_fff_params()
def->sidetext = L("mm/s or %");
def->cli = "solid-infill-speed=s";
def->ratio_over = "infill_speed";
def->aliases.push_back("solid_infill_feed_rate");
def->aliases = { "solid_infill_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloatOrPercent(20, false);
@@ -1989,7 +1988,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Speed for travel moves (jumps between distant extrusion points).");
def->sidetext = L("mm/s");
def->cli = "travel-speed=f";
def->aliases.push_back("travel_feed_rate");
def->aliases = { "travel_feed_rate" };
def->min = 1;
def->default_value = new ConfigOptionFloat(130);
+115 -109
View File
@@ -7,10 +7,6 @@
#include <fstream>
#include <sstream>
#include <wx/stdstream.h>
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
#include <boost/log/trivial.hpp>
#include "Rasterizer/Rasterizer.hpp"
@@ -32,14 +28,14 @@ enum class FilePrinterFormat {
* different implementations of this class template for each supported format.
*
*/
template<FilePrinterFormat format>
template<FilePrinterFormat format, class LayerFormat = void>
class FilePrinter {
public:
void printConfig(const Print&);
void print_config(const Print&);
// Draw an ExPolygon which is a polygon inside a slice on the specified layer.
void drawPolygon(const ExPolygon& p, unsigned lyr);
void draw_polygon(const ExPolygon& p, unsigned lyr);
// Tell the printer how many layers should it consider.
void layers(unsigned layernum);
@@ -51,32 +47,57 @@ public:
* specified layer number than an appropriate number of layers will be
* allocated in the printer.
*/
void beginLayer(unsigned layer);
void begin_layer(unsigned layer);
// Allocate a new layer on top of the last and switch to it.
void beginLayer();
void begin_layer();
/*
* Finish the selected layer. It means that no drawing is allowed on that
* layer anymore. This fact can be used to prepare the file system output
* data like png comprimation and so on.
*/
void finishLayer(unsigned layer);
void finish_layer(unsigned layer);
// Finish the top layer.
void finishLayer();
void finish_layer();
// Save all the layers into the file (or dir) specified in the path argument
void save(const std::string& path);
// Save only the selected layer to the file specified in path argument.
void saveLayer(unsigned lyr, const std::string& path);
void save_layer(unsigned lyr, const std::string& path);
};
template<class T = void> struct VeryFalse { static const bool value = false; };
// This has to be explicitly implemented in the gui layer or a default zlib
// based implementation is needed.
template<class Backend> class LayerWriter {
public:
LayerWriter(const std::string& /*zipfile_path*/) {
static_assert(VeryFalse<Backend>::value,
"No layer writer implementation provided!");
}
void next_entry(const std::string& /*fname*/) {}
std::string get_name() { return ""; }
bool is_ok() { return false; }
template<class T> LayerWriter& operator<<(const T& /*arg*/) {
return *this;
}
void close() {}
};
// Implementation for PNG raster output
// Be aware that if a large number of layers are allocated, it can very well
// exhaust the available memory especially on 32 bit platform.
template<> class FilePrinter<FilePrinterFormat::PNG> {
template<class LyrFormat> class FilePrinter<FilePrinterFormat::PNG, LyrFormat> {
struct Layer {
Raster first;
@@ -91,22 +112,22 @@ template<> class FilePrinter<FilePrinterFormat::PNG> {
// We will save the compressed PNG data into stringstreams which can be done
// in parallel. Later we can write every layer to the disk sequentially.
std::vector<Layer> layers_rst_;
Raster::Resolution res_;
Raster::PixelDim pxdim_;
const Print *print_ = nullptr;
double exp_time_s_ = .0, exp_time_first_s_ = .0;
std::vector<Layer> m_layers_rst;
Raster::Resolution m_res;
Raster::PixelDim m_pxdim;
const Print *m_print = nullptr;
double m_exp_time_s = .0, m_exp_time_first_s = .0;
std::string createIniContent(const std::string& projectname) {
double layer_height = print_?
print_->default_object_config.layer_height.getFloat() :
double layer_height = m_print?
m_print->default_object_config().layer_height.getFloat() :
0.05;
using std::string;
using std::to_string;
auto expt_str = to_string(exp_time_s_);
auto expt_first_str = to_string(exp_time_first_s_);
auto expt_str = to_string(m_exp_time_s);
auto expt_first_str = to_string(m_exp_time_first_s);
auto stepnum_str = to_string(static_cast<unsigned>(800*layer_height));
auto layerh_str = to_string(layer_height);
@@ -134,92 +155,84 @@ public:
inline FilePrinter(double width_mm, double height_mm,
unsigned width_px, unsigned height_px,
double exp_time, double exp_time_first):
res_(width_px, height_px),
pxdim_(width_mm/width_px, height_mm/height_px),
exp_time_s_(exp_time),
exp_time_first_s_(exp_time_first)
m_res(width_px, height_px),
m_pxdim(width_mm/width_px, height_mm/height_px),
m_exp_time_s(exp_time),
m_exp_time_first_s(exp_time_first)
{
}
FilePrinter(const FilePrinter& ) = delete;
FilePrinter(FilePrinter&& m):
layers_rst_(std::move(m.layers_rst_)),
res_(m.res_),
pxdim_(m.pxdim_) {}
m_layers_rst(std::move(m.m_layers_rst)),
m_res(m.m_res),
m_pxdim(m.m_pxdim) {}
inline void layers(unsigned cnt) { if(cnt > 0) layers_rst_.resize(cnt); }
inline unsigned layers() const { return layers_rst_.size(); }
inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); }
inline unsigned layers() const { return unsigned(m_layers_rst.size()); }
void printConfig(const Print& printconf) { print_ = &printconf; }
void print_config(const Print& printconf) { m_print = &printconf; }
inline void drawPolygon(const ExPolygon& p, unsigned lyr) {
assert(lyr < layers_rst_.size());
layers_rst_[lyr].first.draw(p);
inline void draw_polygon(const ExPolygon& p, unsigned lyr) {
assert(lyr < m_layers_rst.size());
m_layers_rst[lyr].first.draw(p);
}
inline void beginLayer(unsigned lyr) {
if(layers_rst_.size() <= lyr) layers_rst_.resize(lyr+1);
layers_rst_[lyr].first.reset(res_, pxdim_, ORIGIN);
inline void begin_layer(unsigned lyr) {
if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1);
m_layers_rst[lyr].first.reset(m_res, m_pxdim, ORIGIN);
}
inline void beginLayer() {
layers_rst_.emplace_back();
layers_rst_.front().first.reset(res_, pxdim_, ORIGIN);
inline void begin_layer() {
m_layers_rst.emplace_back();
m_layers_rst.front().first.reset(m_res, m_pxdim, ORIGIN);
}
inline void finishLayer(unsigned lyr_id) {
assert(lyr_id < layers_rst_.size());
layers_rst_[lyr_id].first.save(layers_rst_[lyr_id].second,
inline void finish_layer(unsigned lyr_id) {
assert(lyr_id < m_layers_rst.size());
m_layers_rst[lyr_id].first.save(m_layers_rst[lyr_id].second,
Raster::Compression::PNG);
layers_rst_[lyr_id].first.reset();
m_layers_rst[lyr_id].first.reset();
}
inline void finishLayer() {
if(!layers_rst_.empty()) {
layers_rst_.back().first.save(layers_rst_.back().second,
inline void finish_layer() {
if(!m_layers_rst.empty()) {
m_layers_rst.back().first.save(m_layers_rst.back().second,
Raster::Compression::PNG);
layers_rst_.back().first.reset();
m_layers_rst.back().first.reset();
}
}
inline void save(const std::string& path) {
try {
LayerWriter<LyrFormat> writer(path);
wxFileName filepath(path);
std::string project = writer.get_name();
wxFFileOutputStream zipfile(path);
writer.next_entry("config.ini");
writer << createIniContent(project);
std::string project = filepath.GetName().ToStdString();
for(unsigned i = 0; i < m_layers_rst.size(); i++) {
if(m_layers_rst[i].second.rdbuf()->in_avail() > 0) {
char lyrnum[6];
std::sprintf(lyrnum, "%.5d", i);
auto zfilename = project + lyrnum + ".png";
writer.next_entry(zfilename);
writer << m_layers_rst[i].second.rdbuf();
m_layers_rst[i].second.str("");
}
}
if(!zipfile.IsOk()) {
BOOST_LOG_TRIVIAL(error) << "Can't create zip file for layers! "
<< path;
writer.close();
} catch(std::exception& e) {
BOOST_LOG_TRIVIAL(error) << e.what();
return;
}
wxZipOutputStream zipstream(zipfile);
wxStdOutputStream pngstream(zipstream);
zipstream.PutNextEntry("config.ini");
pngstream << createIniContent(project);
for(unsigned i = 0; i < layers_rst_.size(); i++) {
if(layers_rst_[i].second.rdbuf()->in_avail() > 0) {
char lyrnum[6];
std::sprintf(lyrnum, "%.5d", i);
auto zfilename = project + lyrnum + ".png";
zipstream.PutNextEntry(zfilename);
pngstream << layers_rst_[i].second.rdbuf();
layers_rst_[i].second.str("");
}
}
zipstream.Close();
zipfile.Close();
}
void saveLayer(unsigned lyr, const std::string& path) {
void save_layer(unsigned lyr, const std::string& path) {
unsigned i = lyr;
assert(i < layers_rst_.size());
assert(i < m_layers_rst.size());
char lyrnum[6];
std::sprintf(lyrnum, "%.5d", lyr);
@@ -227,23 +240,23 @@ public:
std::fstream out(loc, std::fstream::out | std::fstream::binary);
if(out.good()) {
layers_rst_[i].first.save(out, Raster::Compression::PNG);
m_layers_rst[i].first.save(out, Raster::Compression::PNG);
} else {
BOOST_LOG_TRIVIAL(error) << "Can't create file for layer";
}
out.close();
layers_rst_[i].first.reset();
m_layers_rst[i].first.reset();
}
};
// Let's shadow this eigen interface
inline coord_t px(const Point& p) { return p(0); }
inline coord_t py(const Point& p) { return p(1); }
inline coord_t px(const Point& p) { return p(0); }
inline coord_t py(const Point& p) { return p(1); }
inline coordf_t px(const Vec2d& p) { return p(0); }
inline coordf_t py(const Vec2d& p) { return p(1); }
template<FilePrinterFormat format, class...Args>
template<FilePrinterFormat format, class LayerFormat, class...Args>
void print_to(Print& print,
std::string dirpath,
double width_mm,
@@ -258,16 +271,18 @@ void print_to(Print& print,
// rasterized to the same image.
std::map<long long, LayerPtrs> layers;
auto& objects = print.objects;
auto& objects = print.objects();
// Merge the sliced layers with the support layers
std::for_each(objects.begin(), objects.end(), [&layers](PrintObject *o) {
for(auto l : o->layers) {
std::for_each(objects.cbegin(), objects.cend(),
[&layers](const PrintObject *o)
{
for(const auto l : o->layers()) {
auto& lyrs = layers[static_cast<long long>(scale_(l->print_z))];
lyrs.push_back(l);
}
for(auto l : o->support_layers) {
for(const auto l : o->support_layers()) {
auto& lyrs = layers[static_cast<long long>(scale_(l->print_z))];
lyrs.push_back(l);
}
@@ -288,10 +303,10 @@ void print_to(Print& print,
auto cy = scale_(height_mm)/2 - (py(print_bb.center()) - py(print_bb.min));
// Create the actual printer, forward any additional arguments to it.
FilePrinter<format> printer(width_mm, height_mm,
std::forward<Args>(args)...);
FilePrinter<format, LayerFormat> printer(width_mm, height_mm,
std::forward<Args>(args)...);
printer.printConfig(print);
printer.print_config(print);
printer.layers(layers.size()); // Allocate space for all the layers
@@ -303,18 +318,16 @@ void print_to(Print& print,
keys.reserve(layers.size());
for(auto& e : layers) keys.push_back(e.first);
int initstatus = print.progressindicator? print.progressindicator->state()
: 0;
print.set_status(initstatus, jobdesc);
print.set_status(0, jobdesc);
// Method that prints one layer
auto process_layer = [&layers, &keys, &printer, &st_prev, &m,
&jobdesc, print_bb, dir, cx, cy, &print, initstatus]
&jobdesc, print_bb, dir, cx, cy, &print]
(unsigned layer_id)
{
LayerPtrs lrange = layers[keys[layer_id]];
printer.beginLayer(layer_id); // Switch to the appropriate layer
printer.begin_layer(layer_id); // Switch to the appropriate layer
for(Layer *lp : lrange) {
Layer& l = *lp;
@@ -329,21 +342,14 @@ void print_to(Print& print,
});
// Draw all the polygons in the slice to the actual layer.
std::for_each(l.object()->_shifted_copies.begin(),
l.object()->_shifted_copies.end(),
[&] (Point d)
{
std::for_each(slices.expolygons.begin(),
slices.expolygons.end(),
[&] (ExPolygon slice)
{
for (const Point &d : l.object()->copies())
for (ExPolygon slice : slices.expolygons) {
slice.translate(px(d), py(d));
slice.translate(-px(print_bb.min) + cx,
-py(print_bb.min) + cy);
printer.drawPolygon(slice, layer_id);
});
});
printer.draw_polygon(slice, layer_id);
}
/*if(print.has_support_material() && layer_id > 0) {
BOOST_LOG_TRIVIAL(warning) << "support material for layer "
@@ -355,12 +361,12 @@ void print_to(Print& print,
}
printer.finishLayer(layer_id); // Finish the layer for later saving it.
printer.finish_layer(layer_id); // Finish the layer for later saving it.
auto st = static_cast<int>(layer_id*80.0/layers.size());
m.lock();
if( st - st_prev > 10) {
print.set_status(initstatus + st, jobdesc);
print.set_status(st, jobdesc);
st_prev = st;
}
m.unlock();
@@ -379,9 +385,9 @@ void print_to(Print& print,
// print.set_status(100, jobdesc);
// Save the print into the file system.
print.set_status(initstatus + 90, "Writing layers to disk");
print.set_status(90, "Writing layers to disk");
printer.save(dir);
print.set_status(initstatus + 100, "Writing layers completed");
print.set_status(100, "Writing layers completed");
}
}
File diff suppressed because it is too large Load Diff
+20 -21
View File
@@ -2,8 +2,7 @@
namespace Slic3r {
Flow
PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const
Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const
{
ConfigOptionFloatOrPercent config_width;
if (width != -1) {
@@ -13,53 +12,53 @@ PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_la
} else {
// otherwise, get extrusion width from configuration
// (might be an absolute value, or a percent value, or zero for auto)
if (first_layer && this->_print->config.first_layer_extrusion_width.value > 0) {
config_width = this->_print->config.first_layer_extrusion_width;
if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) {
config_width = m_print->config().first_layer_extrusion_width;
} else if (role == frExternalPerimeter) {
config_width = this->config.external_perimeter_extrusion_width;
config_width = m_config.external_perimeter_extrusion_width;
} else if (role == frPerimeter) {
config_width = this->config.perimeter_extrusion_width;
config_width = m_config.perimeter_extrusion_width;
} else if (role == frInfill) {
config_width = this->config.infill_extrusion_width;
config_width = m_config.infill_extrusion_width;
} else if (role == frSolidInfill) {
config_width = this->config.solid_infill_extrusion_width;
config_width = m_config.solid_infill_extrusion_width;
} else if (role == frTopSolidInfill) {
config_width = this->config.top_infill_extrusion_width;
config_width = m_config.top_infill_extrusion_width;
} else {
CONFESS("Unknown role");
throw std::invalid_argument("Unknown role");
}
}
if (config_width.value == 0) {
config_width = object.config.extrusion_width;
config_width = object.config().extrusion_width;
}
// get the configured nozzle_diameter for the extruder associated
// to the flow role requested
size_t extruder = 0; // 1-based
if (role == frPerimeter || role == frExternalPerimeter) {
extruder = this->config.perimeter_extruder;
extruder = m_config.perimeter_extruder;
} else if (role == frInfill) {
extruder = this->config.infill_extruder;
extruder = m_config.infill_extruder;
} else if (role == frSolidInfill || role == frTopSolidInfill) {
extruder = this->config.solid_infill_extruder;
extruder = m_config.solid_infill_extruder;
} else {
CONFESS("Unknown role $role");
throw std::invalid_argument("Unknown role");
}
double nozzle_diameter = this->_print->config.nozzle_diameter.get_at(extruder-1);
double nozzle_diameter = m_print->config().nozzle_diameter.get_at(extruder-1);
return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? (float)this->config.bridge_flow_ratio : 0.0);
return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? (float)m_config.bridge_flow_ratio : 0.0);
}
coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
{
return (print_config.nozzle_diameter.get_at(this->config.perimeter_extruder.value - 1) +
print_config.nozzle_diameter.get_at(this->config.infill_extruder.value - 1) +
print_config.nozzle_diameter.get_at(this->config.solid_infill_extruder.value - 1)) / 3.;
return (print_config.nozzle_diameter.get_at(m_config.perimeter_extruder.value - 1) +
print_config.nozzle_diameter.get_at(m_config.infill_extruder.value - 1) +
print_config.nozzle_diameter.get_at(m_config.solid_infill_extruder.value - 1)) / 3.;
}
coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const
{
return this->nozzle_dmr_avg(print_config) * sqrt(this->config.bridge_flow_ratio.value);
return this->nozzle_dmr_avg(print_config) * sqrt(m_config.bridge_flow_ratio.value);
}
}
+45 -45
View File
@@ -37,37 +37,37 @@ public:
using Origin = Raster::Origin;
private:
Raster::Resolution resolution_;
Raster::PixelDim pxdim_;
TBuffer buf_;
TRawBuffer rbuf_;
TPixelRenderer pixfmt_;
TRawRenderer raw_renderer_;
TRendererAA renderer_;
Origin o_;
std::function<void(agg::path_storage&)> flipy_ = [](agg::path_storage&) {};
Raster::Resolution m_resolution;
Raster::PixelDim m_pxdim;
TBuffer m_buf;
TRawBuffer m_rbuf;
TPixelRenderer m_pixfmt;
TRawRenderer m_raw_renderer;
TRendererAA m_renderer;
Origin m_o;
std::function<void(agg::path_storage&)> m_flipy = [](agg::path_storage&) {};
public:
inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd,
Origin o):
resolution_(res), pxdim_(pd),
buf_(res.pixels()),
rbuf_(reinterpret_cast<TPixelRenderer::value_type*>(buf_.data()),
m_resolution(res), m_pxdim(pd),
m_buf(res.pixels()),
m_rbuf(reinterpret_cast<TPixelRenderer::value_type*>(m_buf.data()),
res.width_px, res.height_px,
res.width_px*TPixelRenderer::num_components),
pixfmt_(rbuf_),
raw_renderer_(pixfmt_),
renderer_(raw_renderer_),
o_(o)
m_pixfmt(m_rbuf),
m_raw_renderer(m_pixfmt),
m_renderer(m_raw_renderer),
m_o(o)
{
renderer_.color(ColorWhite);
m_renderer.color(ColorWhite);
// If we would like to play around with gamma
// ras.gamma(agg::gamma_power(1.0));
clear();
if(o_ == Origin::TOP_LEFT) flipy_ = [this](agg::path_storage& path) {
path.flip_y(0, resolution_.height_px);
if(m_o == Origin::TOP_LEFT) m_flipy = [this](agg::path_storage& path) {
path.flip_y(0, m_resolution.height_px);
};
}
@@ -76,35 +76,35 @@ public:
agg::scanline_p8 scanlines;
auto&& path = to_path(poly.contour);
flipy_(path);
m_flipy(path);
ras.add_path(path);
for(auto h : poly.holes) {
auto&& holepath = to_path(h);
flipy_(holepath);
m_flipy(holepath);
ras.add_path(holepath);
}
agg::render_scanlines(ras, scanlines, renderer_);
agg::render_scanlines(ras, scanlines, m_renderer);
}
inline void clear() {
raw_renderer_.clear(ColorBlack);
m_raw_renderer.clear(ColorBlack);
}
inline TBuffer& buffer() { return buf_; }
inline TBuffer& buffer() { return m_buf; }
inline const Raster::Resolution resolution() { return resolution_; }
inline const Raster::Resolution resolution() { return m_resolution; }
inline Origin origin() const /*noexcept*/ { return o_; }
inline Origin origin() const /*noexcept*/ { return m_o; }
private:
double getPx(const Point& p) {
return p(0) * SCALING_FACTOR/pxdim_.w_mm;
return p(0) * SCALING_FACTOR/m_pxdim.w_mm;
}
double getPy(const Point& p) {
return p(1) * SCALING_FACTOR/pxdim_.h_mm;
return p(1) * SCALING_FACTOR/m_pxdim.h_mm;
}
agg::path_storage to_path(const Polygon& poly) {
@@ -124,57 +124,57 @@ const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255);
const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0);
Raster::Raster(const Resolution &r, const PixelDim &pd, Origin o):
impl_(new Impl(r, pd, o)) {}
m_impl(new Impl(r, pd, o)) {}
Raster::Raster() {}
Raster::~Raster() {}
Raster::Raster(Raster &&m):
impl_(std::move(m.impl_)) {}
m_impl(std::move(m.m_impl)) {}
void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd)
{
// Free up the unnecessary memory and make sure it stays clear after
// an exception
auto o = impl_? impl_->origin() : Origin::TOP_LEFT;
auto o = m_impl? m_impl->origin() : Origin::TOP_LEFT;
reset(r, pd, o);
}
void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd,
Raster::Origin o)
{
impl_.reset();
impl_.reset(new Impl(r, pd, o));
m_impl.reset();
m_impl.reset(new Impl(r, pd, o));
}
void Raster::reset()
{
impl_.reset();
m_impl.reset();
}
Raster::Resolution Raster::resolution() const
{
if(impl_) return impl_->resolution();
if(m_impl) return m_impl->resolution();
return Resolution(0, 0);
}
void Raster::clear()
{
assert(impl_);
impl_->clear();
assert(m_impl);
m_impl->clear();
}
void Raster::draw(const ExPolygon &poly)
{
assert(impl_);
impl_->draw(poly);
assert(m_impl);
m_impl->draw(poly);
}
void Raster::save(std::ostream& stream, Compression comp)
{
assert(impl_);
assert(m_impl);
switch(comp) {
case Compression::PNG: {
@@ -188,7 +188,7 @@ void Raster::save(std::ostream& stream, Compression comp)
wr.write_info();
auto& b = impl_->buffer();
auto& b = m_impl->buffer();
auto ptr = reinterpret_cast<png::byte*>( b.data() );
unsigned stride =
sizeof(Impl::TBuffer::value_type) * resolution().width_px;
@@ -201,12 +201,12 @@ void Raster::save(std::ostream& stream, Compression comp)
}
case Compression::RAW: {
stream << "P5 "
<< impl_->resolution().width_px << " "
<< impl_->resolution().height_px << " "
<< m_impl->resolution().width_px << " "
<< m_impl->resolution().height_px << " "
<< "255 ";
stream.write(reinterpret_cast<const char*>(impl_->buffer().data()),
impl_->buffer().size()*sizeof(Impl::TBuffer::value_type));
stream.write(reinterpret_cast<const char*>(m_impl->buffer().data()),
m_impl->buffer().size()*sizeof(Impl::TBuffer::value_type));
}
}
}
+2 -2
View File
@@ -18,7 +18,7 @@ class ExPolygon;
*/
class Raster {
class Impl;
std::unique_ptr<Impl> impl_;
std::unique_ptr<Impl> m_impl;
public:
/// Supported compression types
@@ -65,7 +65,7 @@ public:
/**
* Release the allocated resources. Drawing in this state ends in
* unspecified behaviour.
* unspecified behavior.
*/
void reset();
+1 -1
View File
@@ -446,7 +446,7 @@ void ground_layer(const TriangleMesh &mesh, ExPolygons &output, float h)
std::vector<ExPolygons> tmp;
slicer.slice({h}, &tmp);
slicer.slice({h}, &tmp, [](){});
output = tmp.front();
}
+67 -64
View File
@@ -142,8 +142,8 @@ void export_print_z_polygons_and_extrusions_to_svg(
PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) :
m_object (object),
m_print_config (&object->print()->config),
m_object_config (&object->config),
m_print_config (&object->print()->config()),
m_object_config (&object->config()),
m_slicing_params (slicing_params),
m_first_layer_flow (support_material_1st_layer_flow(object, float(slicing_params.first_print_layer_height))),
m_support_material_flow (support_material_flow(object, float(slicing_params.layer_height))),
@@ -164,7 +164,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
coordf_t external_perimeter_width = 0.;
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
if (! object->region_volumes[region_id].empty()) {
const PrintRegionConfig &config = object->print()->get_region(region_id)->config;
const PrintRegionConfig &config = object->print()->get_region(region_id)->config();
coordf_t width = config.external_perimeter_extrusion_width.get_abs_value(slicing_params.layer_height);
if (width <= 0.)
width = m_print_config->nozzle_diameter.get_at(config.perimeter_extruder-1);
@@ -226,7 +226,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
coordf_t max_object_layer_height = 0.;
for (size_t i = 0; i < object.layer_count(); ++ i)
max_object_layer_height = std::max(max_object_layer_height, object.layers[i]->height);
max_object_layer_height = std::max(max_object_layer_height, object.layers()[i]->height);
// Layer instances will be allocated by std::deque and they will be kept until the end of this function call.
// The layers will be referenced by various LayersPtr (of type std::vector<Layer*>)
@@ -266,9 +266,9 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
layer_support_areas);
#ifdef SLIC3R_DEBUG
for (size_t layer_id = 0; layer_id < object.layers.size(); ++ layer_id)
for (size_t layer_id = 0; layer_id < object.layers().size(); ++ layer_id)
Slic3r::SVG::export_expolygons(
debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers[layer_id]->print_z),
debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers()[layer_id]->print_z),
union_ex(layer_support_areas[layer_id], false));
#endif /* SLIC3R_DEBUG */
@@ -364,7 +364,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
// Sort the layers lexicographically by a raising print_z and a decreasing height.
std::sort(layers_sorted.begin(), layers_sorted.end(), MyLayersPtrCompare());
int layer_id = 0;
assert(object.support_layers.empty());
assert(object.support_layers().empty());
for (int i = 0; i < int(layers_sorted.size());) {
// Find the last layer with roughly the same print_z, find the minimum layer height of all.
// Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should.
@@ -430,14 +430,14 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t
{
// 1) Count the new polygons first.
size_t n_polygons_new = 0;
for (const LayerRegion *region : layer.regions)
for (const LayerRegion *region : layer.regions())
for (const Surface &surface : region->slices.surfaces)
if (surface.surface_type == surface_type)
n_polygons_new += surface.expolygon.holes.size() + 1;
// 2) Collect the new polygons.
Polygons out;
out.reserve(n_polygons_new);
for (const LayerRegion *region : layer.regions)
for (const LayerRegion *region : layer.regions())
for (const Surface &surface : region->slices.surfaces)
if (surface.surface_type == surface_type)
polygons_append(out, surface.expolygon);
@@ -679,7 +679,7 @@ namespace SupportMaterialInternal {
}
static bool has_bridging_extrusions(const Layer &layer)
{
for (const LayerRegion *region : layer.regions) {
for (const LayerRegion *region : layer.regions()) {
if (SupportMaterialInternal::has_bridging_perimeters(region->perimeters))
return true;
if (region->fill_surfaces.has(stBottomBridge) && has_bridging_fills(region->fills))
@@ -742,7 +742,7 @@ namespace SupportMaterialInternal {
// Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
Polygons lower_grown_slices = offset(lower_layer_polygons,
//FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1))),
0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder-1))),
SUPPORT_SURFACES_OFFSET_PARAMETERS);
// Collect perimeters of this layer.
//FIXME split_at_first_point() could split a bridge mid-way
@@ -825,9 +825,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
std::vector<Polygons> buildplate_covered;
if (buildplate_only) {
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() - collecting regions covering the print bed.";
buildplate_covered.assign(object.layers.size(), Polygons());
for (size_t layer_id = 1; layer_id < object.layers.size(); ++ layer_id) {
const Layer &lower_layer = *object.layers[layer_id-1];
buildplate_covered.assign(object.layers().size(), Polygons());
for (size_t layer_id = 1; layer_id < object.layers().size(); ++ layer_id) {
const Layer &lower_layer = *object.layers()[layer_id-1];
// Merge the new slices with the preceding slices.
// Apply the safety offset to the newly added polygons, so they will connect
// with the polygons collected before,
@@ -856,7 +856,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
(const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id)
{
const Layer &layer = *object.layers[layer_id];
const Layer &layer = *object.layers()[layer_id];
// Detect overhangs and contact areas needed to support them.
// Collect overhangs and contacts of all regions of this layer supported by the layer immediately below.
@@ -864,7 +864,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
Polygons contact_polygons;
Polygons slices_margin_cached;
float slices_margin_cached_offset = -1.;
Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers[layer_id-1]->slices.expolygons);
Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices.expolygons);
// Offset of the lower layer, to trim the support polygons with to calculate dense supports.
float no_interface_offset = 0.f;
if (layer_id == 0) {
@@ -876,14 +876,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
contact_polygons = offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN));
} else {
// Generate overhang / contact_polygons for non-raft layers.
const Layer &lower_layer = *object.layers[layer_id-1];
for (LayerRegion *layerm : layer.regions) {
const Layer &lower_layer = *object.layers()[layer_id-1];
for (LayerRegion *layerm : layer.regions()) {
// Extrusion width accounts for the roundings of the extrudates.
// It is the maximum widh of the extrudate.
float fw = float(layerm->flow(frExternalPerimeter).scaled_width());
no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw);
float lower_layer_offset =
(layer_id < this->m_object_config->support_material_enforce_layers.value) ?
(layer_id < m_object_config->support_material_enforce_layers.value) ?
// Enforce a full possible support, ignore the overhang angle.
0.f :
(threshold_rad > 0. ?
@@ -1041,8 +1041,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Align the contact surface height with a layer immediately below the supported layer.
// Interface layer will be synchronized with the object.
new_layer.print_z = layer.print_z - layer.height;
new_layer.height = object.layers[layer_id - 1]->height;
new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers[layer_id - 2]->print_z;
new_layer.height = object.layers()[layer_id - 1]->height;
new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers()[layer_id - 2]->print_z;
} else {
new_layer.print_z = layer.print_z - layer.height - m_object_config->support_material_contact_distance;
new_layer.bottom_z = new_layer.print_z;
@@ -1068,9 +1068,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// it will support layers printed with a bridging flow.
if (SupportMaterialInternal::has_bridging_extrusions(layer)) {
coordf_t bridging_height = 0.;
for (const LayerRegion *region : layer.regions)
for (const LayerRegion *region : layer.regions())
bridging_height += region->region()->bridging_height_avg(*m_print_config);
bridging_height /= coordf_t(layer.regions.size());
bridging_height /= coordf_t(layer.regions().size());
coordf_t bridging_print_z = layer.print_z - bridging_height - m_object_config->support_material_contact_distance;
if (bridging_print_z >= m_slicing_params.first_print_layer_height - EPSILON) {
// Not below the first layer height means this layer is printable.
@@ -1107,15 +1107,15 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3, true));
// 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
if (layer_id == 0) {
if (layer_id == 0 || m_slicing_params.soluble_interface) {
// if (no_interface_offset == 0.f) {
new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, true);
} else {
// Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
Polygons dense_interface_polygons = diff(overhang_polygons,
offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS));
// offset(lower_layer_polygons, no_interface_offset * 0.6f, SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (! dense_interface_polygons.empty()) {
//FIXME do it for non-soluble support interfaces only.
//FIXME do it for the bridges only?
SupportGridPattern support_grid_pattern(
// Support islands, to be stretched into a grid.
@@ -1297,13 +1297,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
// If the layer is extruded with no bridging flow, support just the normal extrusions.
layer_new.height = m_slicing_params.soluble_interface ?
// Align the interface layer with the object's layer height.
object.layers[layer_id + 1]->height :
object.layers()[layer_id + 1]->height :
// Place a bridge flow interface layer over the top surface.
//FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?)
// According to Jindrich the bottom surfaces work well.
//FIXME test the bridging flow instead?
m_support_material_interface_flow.nozzle_diameter;
layer_new.print_z = m_slicing_params.soluble_interface ? object.layers[layer_id + 1]->print_z :
layer_new.print_z = m_slicing_params.soluble_interface ? object.layers()[layer_id + 1]->print_z :
layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value;
layer_new.bottom_z = layer.print_z;
layer_new.idx_object_layer_below = layer_id;
@@ -1324,7 +1324,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
if (diff > 0.) {
// The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer.
assert(diff < layer_new.height + EPSILON);
assert(layer_new.height - diff >= this->m_support_layer_height_min - EPSILON);
assert(layer_new.height - diff >= m_support_layer_height_min - EPSILON);
layer_new.print_z = top_contacts[top_idx]->print_z;
layer_new.height -= diff;
} else {
@@ -1347,7 +1347,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
//FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage?
touching = offset(touching, float(SCALED_EPSILON));
for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) {
const Layer &layer_above = *object.layers[layer_id_above];
const Layer &layer_above = *object.layers()[layer_id_above];
if (layer_above.print_z > layer_new.print_z - EPSILON)
break;
if (! layer_support_areas[layer_id_above].empty()) {
@@ -1578,7 +1578,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int
// Verify that the extremes are separated by m_support_layer_height_min.
for (size_t i = 1; i < extremes.size(); ++ i) {
assert(extremes[i]->extreme_z() - extremes[i-1]->extreme_z() == 0. ||
extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > this->m_support_layer_height_min - EPSILON);
extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > m_support_layer_height_min - EPSILON);
assert(extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > 0. ||
extremes[i]->layer_type == extremes[i-1]->layer_type ||
(extremes[i]->layer_type == sltBottomContact && extremes[i - 1]->layer_type == sltTopContact));
@@ -1602,7 +1602,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int
// This is a bottom of a synchronized (or soluble) top contact layer, its height has been decided in this->top_contact_layers().
assert(extr2->layer_type == sltTopContact);
assert(extr2->bottom_z == m_slicing_params.first_print_layer_height);
assert(extr2->print_z >= m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON);
assert(extr2->print_z >= m_slicing_params.first_print_layer_height + m_support_layer_height_min - EPSILON);
if (intermediate_layers.empty() || intermediate_layers.back()->print_z < m_slicing_params.first_print_layer_height) {
MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate);
layer_new.bottom_z = 0.;
@@ -1642,7 +1642,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int
if (synchronize) {
// Emit support layers synchronized with the object layers.
// Find the first object layer, which has its print_z in this support Z range.
while (idx_layer_object < object.layers.size() && object.layers[idx_layer_object]->print_z < extr1z + EPSILON)
while (idx_layer_object < object.layers().size() && object.layers()[idx_layer_object]->print_z < extr1z + EPSILON)
++ idx_layer_object;
if (idx_layer_object == 0 && extr1z == m_slicing_params.raft_interface_top_z) {
// Insert one base support layer below the object.
@@ -1653,11 +1653,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int
intermediate_layers.push_back(&layer_new);
}
// Emit all intermediate support layers synchronized with object layers up to extr2z.
for (; idx_layer_object < object.layers.size() && object.layers[idx_layer_object]->print_z < extr2z + EPSILON; ++ idx_layer_object) {
for (; idx_layer_object < object.layers().size() && object.layers()[idx_layer_object]->print_z < extr2z + EPSILON; ++ idx_layer_object) {
MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate);
layer_new.print_z = object.layers[idx_layer_object]->print_z;
layer_new.height = object.layers[idx_layer_object]->height;
layer_new.bottom_z = (idx_layer_object > 0) ? object.layers[idx_layer_object - 1]->print_z : (layer_new.print_z - layer_new.height);
layer_new.print_z = object.layers()[idx_layer_object]->print_z;
layer_new.height = object.layers()[idx_layer_object]->height;
layer_new.bottom_z = (idx_layer_object > 0) ? object.layers()[idx_layer_object - 1]->print_z : (layer_new.print_z - layer_new.height);
assert(intermediate_layers.empty() || intermediate_layers.back()->print_z < layer_new.print_z + EPSILON);
intermediate_layers.push_back(&layer_new);
}
@@ -1667,10 +1667,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int
assert(n_layers_extra > 0);
coordf_t step = dist / coordf_t(n_layers_extra);
if (extr1 != nullptr && extr1->layer_type == sltTopContact &&
extr1->print_z + this->m_support_layer_height_min > extr1->bottom_z + step) {
extr1->print_z + m_support_layer_height_min > extr1->bottom_z + step) {
// The bottom extreme is a bottom of a top surface. Ensure that the gap
// between the 1st intermediate layer print_z and extr1->print_z is not too small.
assert(extr1->bottom_z + this->m_support_layer_height_min < extr1->print_z + EPSILON);
assert(extr1->bottom_z + m_support_layer_height_min < extr1->print_z + EPSILON);
// Generate the first intermediate layer.
MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate);
layer_new.bottom_z = extr1->bottom_z;
@@ -1764,7 +1764,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
Polygons polygons_new;
// Use the precomputed layer_support_areas.
idx_object_layer_above = std::max(0, idx_lower_or_equal(object.layers, idx_object_layer_above,
idx_object_layer_above = std::max(0, idx_lower_or_equal(object.layers(), idx_object_layer_above,
[&layer_intermediate](const Layer *layer){ return layer->print_z <= layer_intermediate.print_z + EPSILON; }));
polygons_new = layer_support_areas[idx_object_layer_above];
@@ -1900,23 +1900,23 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
// Find the overlapping object layers including the extra above / below gap.
coordf_t z_threshold = support_layer.print_z - support_layer.height - gap_extra_below + EPSILON;
idx_object_layer_overlapping = idx_higher_or_equal(
object.layers, idx_object_layer_overlapping,
object.layers(), idx_object_layer_overlapping,
[z_threshold](const Layer *layer){ return layer->print_z >= z_threshold; });
// Collect all the object layers intersecting with this layer.
Polygons polygons_trimming;
size_t i = idx_object_layer_overlapping;
for (; i < object.layers.size(); ++ i) {
const Layer &object_layer = *object.layers[i];
for (; i < object.layers().size(); ++ i) {
const Layer &object_layer = *object.layers()[i];
if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON)
break;
polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
}
if (! this->m_slicing_params.soluble_interface) {
if (! m_slicing_params.soluble_interface) {
// Collect all bottom surfaces, which will be extruded with a bridging flow.
for (; i < object.layers.size(); ++ i) {
const Layer &object_layer = *object.layers[i];
for (; i < object.layers().size(); ++ i) {
const Layer &object_layer = *object.layers()[i];
bool some_region_overlaps = false;
for (LayerRegion *region : object_layer.regions) {
for (LayerRegion *region : object_layer.regions()) {
coordf_t bridging_height = region->region()->bridging_height_avg(*this->m_print_config);
if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON)
break;
@@ -1924,7 +1924,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
polygons_append(polygons_trimming,
offset(to_expolygons(region->fill_surfaces.filter_by_type(stBottomBridge)),
gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (region->region()->config.overhangs.value)
if (region->region()->config().overhangs.value)
SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming);
}
if (! some_region_overlaps)
@@ -2022,7 +2022,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
// Expand the bases of the support columns in the 1st layer.
columns_base->polygons = diff(
offset(columns_base->polygons, inflate_factor_1st_layer),
offset(m_object->layers.front()->slices.expolygons, scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
offset(m_object->layers().front()->slices.expolygons, scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (contacts != nullptr)
columns_base->polygons = diff(columns_base->polygons, interface_polygons);
}
@@ -2168,8 +2168,8 @@ struct MyLayerExtruded
else
*m_polygons_to_extrude = std::move(polygons);
}
Polygons& polygons_to_extrude() { return (this->m_polygons_to_extrude == nullptr) ? layer->polygons : *this->m_polygons_to_extrude; }
const Polygons& polygons_to_extrude() const { return (this->m_polygons_to_extrude == nullptr) ? layer->polygons : *this->m_polygons_to_extrude; }
Polygons& polygons_to_extrude() { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; }
const Polygons& polygons_to_extrude() const { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; }
bool could_merge(const MyLayerExtruded &other) const {
return ! this->empty() && ! other.empty() &&
@@ -2182,21 +2182,21 @@ struct MyLayerExtruded
assert(this->could_merge(other));
// 1) Merge the rest polygons to extrude, if there are any.
if (other.m_polygons_to_extrude != nullptr) {
if (this->m_polygons_to_extrude == nullptr) {
if (m_polygons_to_extrude == nullptr) {
// This layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet).
assert(this->extrusions.empty());
this->m_polygons_to_extrude = new Polygons(this->layer->polygons);
m_polygons_to_extrude = new Polygons(this->layer->polygons);
}
Slic3r::polygons_append(*this->m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude));
*this->m_polygons_to_extrude = union_(*this->m_polygons_to_extrude, true);
Slic3r::polygons_append(*m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude));
*m_polygons_to_extrude = union_(*m_polygons_to_extrude, true);
delete other.m_polygons_to_extrude;
other.m_polygons_to_extrude = nullptr;
} else if (this->m_polygons_to_extrude != nullptr) {
} else if (m_polygons_to_extrude != nullptr) {
assert(other.m_polygons_to_extrude == nullptr);
// The other layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet).
assert(other.extrusions.empty());
Slic3r::polygons_append(*this->m_polygons_to_extrude, other.layer->polygons);
*this->m_polygons_to_extrude = union_(*this->m_polygons_to_extrude, true);
Slic3r::polygons_append(*m_polygons_to_extrude, other.layer->polygons);
*m_polygons_to_extrude = union_(*m_polygons_to_extrude, true);
}
// 2) Merge the extrusions.
this->extrusions.insert(this->extrusions.end(), other.extrusions.begin(), other.extrusions.end());
@@ -2618,7 +2618,10 @@ void modulate_extrusion_by_overlapping_layers(
(fragment_end.is_start ? &polyline.points.front() : &polyline.points.back());
}
private:
ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {}
ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {
return *this;
}
const std::vector<ExtrusionPathFragment> &m_path_fragments;
};
const coord_t search_radius = 7;
@@ -2794,7 +2797,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id)
{
assert(support_layer_id < raft_layers.size());
SupportLayer &support_layer = *object.support_layers[support_layer_id];
SupportLayer &support_layer = *object.support_layers()[support_layer_id];
assert(support_layer.support_fills.entities.empty());
MyLayer &raft_layer = *raft_layers[support_layer_id];
@@ -2890,9 +2893,9 @@ void PrintObjectSupportMaterial::generate_toolpaths(
MyLayerExtruded interface_layer;
std::vector<LayerCacheItem> overlaps;
};
std::vector<LayerCache> layer_caches(object.support_layers.size(), LayerCache());
std::vector<LayerCache> layer_caches(object.support_layers().size(), LayerCache());
tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, object.support_layers.size()),
tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, object.support_layers().size()),
[this, &object, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &layer_caches, &loop_interface_processor,
infill_pattern, &bbox_object, support_density, interface_density, interface_angle, &angles, link_max_length_factor, with_sheath]
(const tbb::blocked_range<size_t>& range) {
@@ -2907,7 +2910,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
filler_support->set_bounding_box(bbox_object);
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id)
{
SupportLayer &support_layer = *object.support_layers[support_layer_id];
SupportLayer &support_layer = *object.support_layers()[support_layer_id];
LayerCache &layer_cache = layer_caches[support_layer_id];
// Find polygons with the same print_z.
@@ -3105,11 +3108,11 @@ void PrintObjectSupportMaterial::generate_toolpaths(
});
// Now modulate the support layer height in parallel.
tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, object.support_layers.size()),
tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, object.support_layers().size()),
[this, &object, &layer_caches]
(const tbb::blocked_range<size_t>& range) {
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) {
SupportLayer &support_layer = *object.support_layers[support_layer_id];
SupportLayer &support_layer = *object.support_layers()[support_layer_id];
LayerCache &layer_cache = layer_caches[support_layer_id];
for (LayerCacheItem &layer_cache_item : layer_cache.overlaps) {
modulate_extrusion_by_overlapping_layers(layer_cache_item.layer_extruded->extrusions, *layer_cache_item.layer_extruded->layer, layer_cache_item.overlapping);
+1 -1
View File
@@ -224,7 +224,7 @@ private:
// Produce the actual G-code.
void generate_toolpaths(
const PrintObject &object,
const PrintObject &object,
const MyLayersPtr &raft_layers,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
+4
View File
@@ -6,6 +6,10 @@
// Add z coordinate to model instances' offset
#define ENABLE_MODELINSTANCE_3D_OFFSET (1 && ENABLE_1_42_0)
// Add double click on gizmo grabbers to reset transformation components to their default value
#define ENABLE_GIZMOS_RESET (1 && ENABLE_1_42_0)
// Add x and y rotation components to model instances' offset
#define ENABLE_MODELINSTANCE_3D_ROTATION (1 && ENABLE_MODELINSTANCE_3D_OFFSET)
#endif // _technologies_h_
+37 -23
View File
@@ -308,7 +308,8 @@ void TriangleMesh::rotate(double angle, Point* center)
bool TriangleMesh::has_multiple_patches() const
{
// we need neighbors
if (!this->repaired) CONFESS("split() requires repair()");
if (!this->repaired)
throw std::runtime_error("split() requires repair()");
if (this->stl.stats.number_of_facets == 0)
return false;
@@ -338,7 +339,8 @@ bool TriangleMesh::has_multiple_patches() const
size_t TriangleMesh::number_of_patches() const
{
// we need neighbors
if (!this->repaired) CONFESS("split() requires repair()");
if (!this->repaired)
throw std::runtime_error("split() requires repair()");
if (this->stl.stats.number_of_facets == 0)
return false;
@@ -382,7 +384,7 @@ TriangleMeshPtrs TriangleMesh::split() const
// we need neighbors
if (!this->repaired)
CONFESS("split() requires repair()");
throw std::runtime_error("split() requires repair()");
// loop while we have remaining facets
for (;;) {
@@ -643,10 +645,11 @@ void TriangleMesh::require_shared_vertices()
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
}
TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
mesh(_mesh)
void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type throw_on_cancel)
{
mesh = _mesh;
_mesh->require_shared_vertices();
throw_on_cancel();
facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1);
v_scaled_shared.assign(_mesh->stl.v_shared, _mesh->stl.v_shared + _mesh->stl.stats.shared_vertices);
// Scale the copied vertices.
@@ -683,6 +686,7 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
e2f.face_edge = - e2f.face_edge;
}
}
throw_on_cancel();
std::sort(edges_map.begin(), edges_map.end());
// Assign a unique common edge id to touching triangle edges.
@@ -722,10 +726,12 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
edge_j.face = -1;
}
++ num_edges;
if ((i & 0x0ffff) == 0)
throw_on_cancel();
}
}
void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
{
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice";
@@ -762,13 +768,17 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
boost::mutex lines_mutex;
tbb::parallel_for(
tbb::blocked_range<int>(0,this->mesh->stl.stats.number_of_facets),
[&lines, &lines_mutex, &z, this](const tbb::blocked_range<int>& range) {
for (int facet_idx = range.begin(); facet_idx < range.end(); ++ facet_idx)
[&lines, &lines_mutex, &z, throw_on_cancel, this](const tbb::blocked_range<int>& range) {
for (int facet_idx = range.begin(); facet_idx < range.end(); ++ facet_idx) {
if ((facet_idx & 0x0ffff) == 0)
throw_on_cancel();
this->_slice_do(facet_idx, &lines, &lines_mutex, z);
}
}
);
}
throw_on_cancel();
// v_scaled_shared could be freed here
// build loops
@@ -776,9 +786,12 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
layers->resize(z.size());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, z.size()),
[&lines, &layers, this](const tbb::blocked_range<size_t>& range) {
for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx)
[&lines, &layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) {
if ((line_idx & 0x0ffff) == 0)
throw_on_cancel();
this->make_loops(lines[line_idx], &(*layers)[line_idx]);
}
}
);
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice finished";
@@ -877,24 +890,25 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
}
}
void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
{
std::vector<Polygons> layers_p;
this->slice(z, &layers_p);
this->slice(z, &layers_p, throw_on_cancel);
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start";
layers->resize(z.size());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, z.size()),
[&layers_p, layers, this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start";
layers->resize(z.size());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, z.size()),
[&layers_p, layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]);
#endif
this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]);
}
});
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end";
throw_on_cancel();
this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]);
}
});
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end";
}
// Return true, if the facet has been sliced and line_out has been filled.
+8 -3
View File
@@ -3,6 +3,7 @@
#include "libslic3r.h"
#include <admesh/stl.h>
#include <functional>
#include <vector>
#include <boost/thread.hpp>
#include "BoundingBox.hpp"
@@ -156,9 +157,13 @@ typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
class TriangleMeshSlicer
{
public:
TriangleMeshSlicer(TriangleMesh* _mesh);
void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const;
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const;
typedef std::function<void()> throw_on_cancel_callback_type;
TriangleMeshSlicer() : mesh(nullptr) {}
// Not quite nice, but the constructor and init() methods require non-const mesh pointer to be able to call mesh->require_shared_vertices()
TriangleMeshSlicer(TriangleMesh* mesh) { this->init(mesh, [](){}); }
void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel);
void slice(const std::vector<float> &z, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
enum FacetSliceType {
NoSlice = 0,
Slicing = 1,
+22 -20
View File
@@ -44,6 +44,14 @@ extern local_encoded_string encode_path(const char *src);
extern std::string decode_path(const char *src);
extern std::string normalize_utf8_nfc(const char *src);
// Safely rename a file even if the target exists.
// On Windows, the file explorer (or anti-virus or whatever else) often locks the file
// for a short while, so the file may not be movable. Retry while we see recoverable errors.
extern int rename_file(const std::string &from, const std::string &to);
// Copy a file, adjust the access attributes, so that the target is writable.
extern int copy_file(const std::string &from, const std::string &to);
// File path / name / extension splitting utilities, working with UTF-8,
// to be published to Perl.
namespace PerlUtils {
@@ -66,6 +74,20 @@ inline std::string header_slic3r_generated() { return std::string("generated by
// getpid platform wrapper
extern unsigned get_current_pid();
template <typename Real>
Real round_nearest(Real value, unsigned int decimals)
{
Real res = (Real)0;
if (decimals == 0)
res = ::round(value);
else
{
Real power = ::pow((Real)10, (int)decimals);
res = ::round(value * power + (Real)0.5) / power;
}
return res;
}
// Compute the next highest power of 2 of 32-bit v
// http://graphics.stanford.edu/~seander/bithacks.html
template<typename T>
@@ -87,26 +109,6 @@ inline T next_highest_power_of_2(T v)
extern std::string xml_escape(std::string text);
class PerlCallback {
public:
PerlCallback(void *sv) : m_callback(nullptr) { this->register_callback(sv); }
PerlCallback() : m_callback(nullptr) {}
~PerlCallback() { this->deregister_callback(); }
void register_callback(void *sv);
void deregister_callback();
void call() const;
void call(int i) const;
void call(int i, int j) const;
void call(const std::vector<int>& ints) const;
void call(double a) const;
void call(double a, double b) const;
void call(double a, double b, double c) const;
void call(double a, double b, double c, double d) const;
void call(bool b) const;
private:
void *m_callback;
};
} // namespace Slic3r
#endif // slic3r_Utils_hpp_
-8
View File
@@ -48,14 +48,6 @@ typedef double coordf_t;
//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
#define scale_(val) ((val) / SCALING_FACTOR)
#define SCALED_EPSILON scale_(EPSILON)
/* Implementation of CONFESS("foo"): */
#ifdef _MSC_VER
#define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#else
#define CONFESS(...) confess_at(__FILE__, __LINE__, __func__, __VA_ARGS__)
#endif
void confess_at(const char *file, int line, const char *func, const char *pat, ...);
/* End implementation of CONFESS("foo"): */
// Which C++ version is supported?
// For example, could optimized functions with move semantics be used?
+73 -170
View File
@@ -23,6 +23,9 @@
#include <boost/nowide/fstream.hpp>
#include <boost/nowide/integration/filesystem.hpp>
#include <boost/nowide/convert.hpp>
#include <boost/nowide/cstdio.hpp>
#include <tbb/task_scheduler_init.h>
#include <tbb/task_scheduler_init.h>
@@ -149,188 +152,88 @@ const std::string& data_dir()
return g_data_dir;
}
} // namespace Slic3r
#include <xsinit.h>
void
confess_at(const char *file, int line, const char *func,
const char *pat, ...)
// borrowed from LVVM lib/Support/Windows/Path.inc
int rename_file(const std::string &from, const std::string &to)
{
#ifdef SLIC3RXS
va_list args;
SV *error_sv = newSVpvf("Error in function %s at %s:%d: ", func,
file, line);
int ec = 0;
va_start(args, pat);
sv_vcatpvf(error_sv, pat, &args);
va_end(args);
#ifdef _WIN32
sv_catpvn(error_sv, "\n\t", 2);
// Convert to utf-16.
std::wstring wide_from = boost::nowide::widen(from);
std::wstring wide_to = boost::nowide::widen(to);
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs( sv_2mortal(error_sv) );
PUTBACK;
call_pv("Carp::confess", G_DISCARD);
FREETMPS;
LEAVE;
#endif
}
// Retry while we see recoverable errors.
// System scanners (eg. indexer) might open the source file when it is written
// and closed.
bool TryReplace = true;
void PerlCallback::register_callback(void *sv)
{
if (! SvROK((SV*)sv) || SvTYPE(SvRV((SV*)sv)) != SVt_PVCV)
croak("Not a Callback %_ for PerlFunction", (SV*)sv);
if (m_callback)
SvSetSV((SV*)m_callback, (SV*)sv);
else
m_callback = newSVsv((SV*)sv);
}
void PerlCallback::deregister_callback()
{
if (m_callback) {
sv_2mortal((SV*)m_callback);
m_callback = nullptr;
}
}
void PerlCallback::call() const
{
if (! m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(int i) const
{
if (! m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(i)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(int i, int j) const
{
if (! m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(i)));
XPUSHs(sv_2mortal(newSViv(j)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(const std::vector<int>& ints) const
{
if (! m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
for (int i : ints)
{
XPUSHs(sv_2mortal(newSViv(i)));
// This loop may take more than 2000 x 1ms to finish.
for (int i = 0; i < 2000; ++ i) {
if (i > 0)
// Sleep 1ms
::Sleep(1);
if (TryReplace) {
// Try ReplaceFile first, as it is able to associate a new data stream
// with the destination even if the destination file is currently open.
if (::ReplaceFileW(wide_to.data(), wide_from.data(), NULL, 0, NULL, NULL))
return 0;
DWORD ReplaceError = ::GetLastError();
ec = -1; // ReplaceError
// If ReplaceFileW returned ERROR_UNABLE_TO_MOVE_REPLACEMENT or
// ERROR_UNABLE_TO_MOVE_REPLACEMENT_2, retry but only use MoveFileExW().
if (ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT ||
ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2) {
TryReplace = false;
continue;
}
// If ReplaceFileW returned ERROR_UNABLE_TO_REMOVE_REPLACED, retry
// using ReplaceFileW().
if (ReplaceError == ERROR_UNABLE_TO_REMOVE_REPLACED)
continue;
// We get ERROR_FILE_NOT_FOUND if the destination file is missing.
// MoveFileEx can handle this case.
if (ReplaceError != ERROR_ACCESS_DENIED && ReplaceError != ERROR_FILE_NOT_FOUND && ReplaceError != ERROR_SHARING_VIOLATION)
break;
}
if (::MoveFileExW(wide_from.c_str(), wide_to.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING))
return 0;
DWORD MoveError = ::GetLastError();
ec = -1; // MoveError
if (MoveError != ERROR_ACCESS_DENIED && MoveError != ERROR_SHARING_VIOLATION)
break;
}
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
#else
boost::nowide::remove(to.c_str());
ec = boost::nowide::rename(from.c_str(), to.c_str());
#endif
return ec;
}
void PerlCallback::call(double a) const
int copy_file(const std::string &from, const std::string &to)
{
if (!m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVnv(a)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
const boost::filesystem::path source(from);
const boost::filesystem::path target(to);
static const auto perms = boost::filesystem::owner_read | boost::filesystem::owner_write | boost::filesystem::group_read | boost::filesystem::others_read; // aka 644
// Make sure the file has correct permission both before and after we copy over it.
try {
if (boost::filesystem::exists(target))
boost::filesystem::permissions(target, perms);
boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists);
boost::filesystem::permissions(target, perms);
} catch (std::exception & /* ex */) {
return -1;
}
return 0;
}
void PerlCallback::call(double a, double b) const
{
if (!m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVnv(a)));
XPUSHs(sv_2mortal(newSVnv(b)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(double a, double b, double c) const
{
if (!m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVnv(a)));
XPUSHs(sv_2mortal(newSVnv(b)));
XPUSHs(sv_2mortal(newSVnv(c)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(double a, double b, double c, double d) const
{
if (!m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVnv(a)));
XPUSHs(sv_2mortal(newSVnv(b)));
XPUSHs(sv_2mortal(newSVnv(c)));
XPUSHs(sv_2mortal(newSVnv(d)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(bool b) const
{
call(b ? 1 : 0);
}
} // namespace Slic3r
#ifdef WIN32
#ifndef NOMINMAX