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:
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 © : object->_shifted_copies) {
|
||||
for (const Point © : 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 © : object._shifted_copies) {
|
||||
for (const Point © : object.copies()) {
|
||||
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
|
||||
if (object_id != initial_print_object_id || © != object._shifted_copies.data()) {
|
||||
if (object_id != initial_print_object_id || © != 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 <p : 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()), © - object._shifted_copies.data());
|
||||
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), © - 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 ®ion = *print.regions[region_id];
|
||||
const PrintRegion ®ion = *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 ®ion : by_region) {
|
||||
m_config.apply(print.regions[®ion - &by_region.front()]->config);
|
||||
m_config.apply(print.regions()[®ion - &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 ®ion : by_region) {
|
||||
m_config.apply(print.regions[®ion - &by_region.front()]->config);
|
||||
m_config.apply(print.regions()[®ion - &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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 ®ion = *object.print()->regions[region_id];
|
||||
const PrintRegion ®ion = *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(), [<](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(), [<](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(), [<](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(), [<](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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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) :
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) :
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+333
-162
@@ -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 ®ion);
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+566
-338
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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_
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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_
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user