Merge branch 'dev2' into lm_sla_supports_ui

This commit is contained in:
Lukas Matena 2018-09-21 11:43:30 +02:00
commit 088fe6cec6
135 changed files with 6108 additions and 9593 deletions

155
xs/src/callback.cpp Normal file
View file

@ -0,0 +1,155 @@
#include "callback.hpp"
#include <xsinit.h>
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)));
}
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(double a) const
{
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;
}
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);
}

32
xs/src/callback.hpp Normal file
View file

@ -0,0 +1,32 @@
#ifndef slic3r_PerlCallback_hpp_
#define slic3r_PerlCallback_hpp_
#include <locale>
#include "libslic3r.h"
namespace Slic3r {
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_PerlCallback_hpp_ */

View file

@ -21,7 +21,7 @@ public:
BoundingBoxBase(const std::vector<PointClass>& points) : min(PointClass::Zero()), max(PointClass::Zero())
{
if (points.empty())
CONFESS("Empty point set supplied to BoundingBoxBase constructor");
throw std::invalid_argument("Empty point set supplied to BoundingBoxBase constructor");
typename std::vector<PointClass>::const_iterator it = points.begin();
this->min = *it;
@ -65,7 +65,7 @@ public:
BoundingBox3Base(const std::vector<PointClass>& points)
{
if (points.empty())
CONFESS("Empty point set supplied to BoundingBox3Base constructor");
throw std::invalid_argument("Empty point set supplied to BoundingBox3Base constructor");
typename std::vector<PointClass>::const_iterator it = points.begin();
this->min = *it;
this->max = *it;

View file

@ -437,7 +437,8 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
// perform triangulation
std::list<TPPLPoly> output;
int res = TPPLPartition().Triangulate_MONO(&input, &output);
if (res != 1) CONFESS("Triangulation failed");
if (res != 1)
throw std::runtime_error("Triangulation failed");
// convert output polygons
for (std::list<TPPLPoly>::iterator poly = output.begin(); poly != output.end(); ++poly) {

View file

@ -120,8 +120,8 @@ public:
ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), m_role(rhs.m_role) {}
// ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(0) {};
ExtrusionPath& operator=(const ExtrusionPath &rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = rhs.polyline; return *this; }
ExtrusionPath& operator=(ExtrusionPath &&rhs) { this->m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = std::move(rhs.polyline); return *this; }
ExtrusionPath& operator=(const ExtrusionPath &rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = rhs.polyline; return *this; }
ExtrusionPath& operator=(ExtrusionPath &&rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->polyline = std::move(rhs.polyline); return *this; }
ExtrusionPath* clone() const { return new ExtrusionPath (*this); }
void reverse() { this->polyline.reverse(); }

View file

@ -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.;
}
};

View file

@ -34,7 +34,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
{
// Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id;
double fill_density = layerm.region()->config.fill_density;
double fill_density = layerm.region()->config().fill_density;
Flow infill_flow = layerm.flow(frInfill);
Flow solid_infill_flow = layerm.flow(frSolidInfill);
Flow top_solid_infill_flow = layerm.flow(frTopSolidInfill);
@ -69,7 +69,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) {
group_attrib[i].is_solid = true;
group_attrib[i].flow_width = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width;
group_attrib[i].pattern = surface.is_external() ? layerm.region()->config.external_fill_pattern.value : ipRectilinear;
group_attrib[i].pattern = surface.is_external() ? layerm.region()->config().external_fill_pattern.value : ipRectilinear;
}
}
// Loop through solid groups, find compatible groups and append them to this one.
@ -152,7 +152,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
for (const Surface &surface : surfaces) {
if (surface.surface_type == stInternalVoid)
continue;
InfillPattern fill_pattern = layerm.region()->config.fill_pattern.value;
InfillPattern fill_pattern = layerm.region()->config().fill_pattern.value;
double density = fill_density;
FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill :
(surface.is_solid() ? frSolidInfill : frInfill);
@ -161,7 +161,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
if (surface.is_solid()) {
density = 100.;
fill_pattern = (surface.is_external() && ! is_bridge) ?
layerm.region()->config.external_fill_pattern.value :
layerm.region()->config().external_fill_pattern.value :
ipRectilinear;
} else if (density <= 0)
continue;
@ -190,7 +190,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// layer height
Flow internal_flow = layerm.region()->flow(
frInfill,
layerm.layer()->object()->config.layer_height.value, // TODO: handle infill_every_layers?
layerm.layer()->object()->config().layer_height.value, // TODO: handle infill_every_layers?
false, // no bridge
false, // no first layer
-1, // auto width
@ -205,7 +205,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
double link_max_length = 0.;
if (! is_bridge) {
#if 0
link_max_length = layerm.region()->config.get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing());
link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing());
// printf("flow spacing: %f, is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length);
#else
if (density > 80.) // 80%
@ -215,7 +215,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
f->layer_id = layerm.layer()->id();
f->z = layerm.layer()->print_z;
f->angle = float(Geometry::deg2rad(layerm.region()->config.fill_angle.value));
f->angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
// Maximum length of the perimeter segment linking two infill lines.
f->link_max_length = scale_(link_max_length);
// Used by the concentric infill pattern to clip the loops to create extrusion paths.

View file

@ -34,7 +34,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipArchimedeanChords: return new FillArchimedeanChords();
case ipHilbertCurve: return new FillHilbertCurve();
case ipOctagramSpiral: return new FillOctagramSpiral();
default: CONFESS("unknown type"); return nullptr;
default: throw std::invalid_argument("unknown type");;
}
}

View file

@ -28,7 +28,7 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent
{
// we need layer height unless it's a bridge
if (height <= 0 && bridge_flow_ratio == 0)
CONFESS("Invalid flow height supplied to new_from_config_width()");
throw std::invalid_argument("Invalid flow height supplied to new_from_config_width()");
float w;
if (bridge_flow_ratio > 0) {
@ -53,7 +53,7 @@ Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height,
{
// we need layer height unless it's a bridge
if (height <= 0 && !bridge)
CONFESS("Invalid flow height supplied to new_from_spacing()");
throw std::invalid_argument("Invalid flow height supplied to new_from_spacing()");
// Calculate width from spacing.
// For normal extrusons, extrusion width is wider than the spacing due to the rounding and squishing of the extrusions.
// For bridge extrusions, the extrusions are placed with a tiny BRIDGE_EXTRA_SPACING gaps between the threads.
@ -111,23 +111,23 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
return Flow::new_from_config_width(
frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(object->config.support_material_extrusion_width.value > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width,
// if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
(object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width,
// if object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value),
// bridge_flow_ratio
0.f);
}
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
{
const auto &width = (object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width;
const auto &width = (object->print()->config().first_layer_extrusion_width.value > 0) ? object->print()->config().first_layer_extrusion_width : object->config().support_material_extrusion_width;
return Flow::new_from_config_width(
frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(width.value > 0) ? width : object->config.extrusion_width,
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.first_layer_height.get_abs_value(object->config.layer_height.value)),
(width.value > 0) ? width : object->config().extrusion_width,
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value)),
// bridge_flow_ratio
0.f);
}
@ -137,10 +137,10 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig
return Flow::new_from_config_width(
frSupportMaterialInterface,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(object->config.support_material_extrusion_width > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width,
// if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
(object->config().support_material_extrusion_width > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width,
// if object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_interface_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value),
// bridge_flow_ratio
0.f);
}

View file

@ -1278,11 +1278,15 @@ namespace Slic3r {
double inv_sy = 1.0 / sy;
double inv_sz = 1.0 / sz;
Eigen::Matrix3d m3x3;
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> m3x3;
m3x3 << transform(0, 0) * inv_sx, transform(0, 1) * inv_sy, transform(0, 2) * inv_sz,
transform(1, 0) * inv_sx, transform(1, 1) * inv_sy, transform(1, 2) * inv_sz,
transform(2, 0) * inv_sx, transform(2, 1) * inv_sy, transform(2, 2) * inv_sz;
#if ENABLE_MODELINSTANCE_3D_ROTATION
Vec3d angles = m3x3.eulerAngles(2, 1, 0);
Vec3d rotation(angles(2), angles(1), angles(0));
#else
Eigen::AngleAxisd rotation;
rotation.fromRotationMatrix(m3x3);
@ -1291,6 +1295,7 @@ namespace Slic3r {
return;
double angle_z = (rotation.axis() == Vec3d::UnitZ()) ? rotation.angle() : -rotation.angle();
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#if ENABLE_MODELINSTANCE_3D_OFFSET
instance.set_offset(offset);
@ -1299,7 +1304,11 @@ namespace Slic3r {
instance.offset(1) = offset_y;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
instance.scaling_factor = sx;
#if ENABLE_MODELINSTANCE_3D_ROTATION
instance.set_rotation(rotation);
#else
instance.rotation = angle_z;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
}
bool _3MF_Importer::_handle_start_config(const char** attributes, unsigned int num_attributes)

View file

@ -30,7 +30,12 @@
// 0 : .amf, .amf.xml and .zip.amf files saved by older slic3r. No version definition in them.
// 1 : Introduction of amf versioning. No other change in data saved into amf files.
#if ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
// 2 : Added z component of offset
// Added x and y components of rotation
#else
// 2 : Added z component of offset.
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
const unsigned int VERSION_AMF = 2;
#else
const unsigned int VERSION_AMF = 1;
@ -127,6 +132,10 @@ struct AMFParserContext
#if ENABLE_MODELINSTANCE_3D_OFFSET
NODE_TYPE_DELTAZ, // amf/constellation/instance/deltaz
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
NODE_TYPE_RX, // amf/constellation/instance/rx
NODE_TYPE_RY, // amf/constellation/instance/ry
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
NODE_TYPE_RZ, // amf/constellation/instance/rz
NODE_TYPE_SCALE, // amf/constellation/instance/scale
NODE_TYPE_METADATA, // anywhere under amf/*/metadata
@ -134,7 +143,11 @@ struct AMFParserContext
struct Instance {
#if ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
Instance() : deltax_set(false), deltay_set(false), deltaz_set(false), rx_set(false), ry_set(false), rz_set(false), scale_set(false) {}
#else
Instance() : deltax_set(false), deltay_set(false), deltaz_set(false), rz_set(false), scale_set(false) {}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#else
Instance() : deltax_set(false), deltay_set(false), rz_set(false), scale_set(false) {}
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
@ -149,6 +162,14 @@ struct AMFParserContext
float deltaz;
bool deltaz_set;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
// Rotation around the X axis.
float rx;
bool rx_set;
// Rotation around the Y axis.
float ry;
bool ry_set;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
// Rotation around the Z axis.
float rz;
bool rz_set;
@ -275,6 +296,12 @@ void AMFParserContext::startElement(const char *name, const char **atts)
else if (strcmp(name, "deltaz") == 0)
node_type_new = NODE_TYPE_DELTAZ;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
else if (strcmp(name, "rx") == 0)
node_type_new = NODE_TYPE_RX;
else if (strcmp(name, "ry") == 0)
node_type_new = NODE_TYPE_RY;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
else if (strcmp(name, "rz") == 0)
node_type_new = NODE_TYPE_RZ;
else if (strcmp(name, "scale") == 0)
@ -339,7 +366,11 @@ void AMFParserContext::characters(const XML_Char *s, int len)
if (m_path.back() == NODE_TYPE_DELTAX ||
m_path.back() == NODE_TYPE_DELTAY ||
m_path.back() == NODE_TYPE_DELTAZ ||
m_path.back() == NODE_TYPE_RZ ||
#if ENABLE_MODELINSTANCE_3D_ROTATION
m_path.back() == NODE_TYPE_RX ||
m_path.back() == NODE_TYPE_RY ||
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
m_path.back() == NODE_TYPE_RZ ||
m_path.back() == NODE_TYPE_SCALE)
#else
if (m_path.back() == NODE_TYPE_DELTAX || m_path.back() == NODE_TYPE_DELTAY || m_path.back() == NODE_TYPE_RZ || m_path.back() == NODE_TYPE_SCALE)
@ -391,6 +422,20 @@ void AMFParserContext::endElement(const char * /* name */)
m_value[0].clear();
break;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
case NODE_TYPE_RX:
assert(m_instance);
m_instance->rx = float(atof(m_value[0].c_str()));
m_instance->rx_set = true;
m_value[0].clear();
break;
case NODE_TYPE_RY:
assert(m_instance);
m_instance->ry = float(atof(m_value[0].c_str()));
m_instance->ry_set = true;
m_value[0].clear();
break;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
case NODE_TYPE_RZ:
assert(m_instance);
m_instance->rz = float(atof(m_value[0].c_str()));
@ -541,12 +586,16 @@ void AMFParserContext::endDocument()
if (instance.deltax_set && instance.deltay_set) {
ModelInstance *mi = m_model.objects[object.second.idx]->add_instance();
#if ENABLE_MODELINSTANCE_3D_OFFSET
mi->set_offset(Vec3d((double)instance.deltax, (double)instance.deltay, (double)instance.deltaz));
mi->set_offset(Vec3d(instance.deltax_set ? (double)instance.deltax : 0.0, instance.deltay_set ? (double)instance.deltay : 0.0, instance.deltaz_set ? (double)instance.deltaz : 0.0));
#else
mi->offset(0) = instance.deltax;
mi->offset(1) = instance.deltay;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
mi->set_rotation(Vec3d(instance.rx_set ? (double)instance.rx : 0.0, instance.ry_set ? (double)instance.ry : 0.0, instance.rz_set ? (double)instance.rz : 0.0));
#else
mi->rotation = instance.rz_set ? instance.rz : 0.f;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
mi->scaling_factor = instance.scale_set ? instance.scale : 1.f;
}
}
@ -800,7 +849,7 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
for (ModelVolume *volume : object->volumes) {
vertices_offsets.push_back(num_vertices);
if (! volume->mesh.repaired)
CONFESS("store_amf() requires repair()");
throw std::runtime_error("store_amf() requires repair()");
auto &stl = volume->mesh.stl;
if (stl.v_shared == nullptr)
stl_generate_shared_vertices(&stl);
@ -850,6 +899,10 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
#if ENABLE_MODELINSTANCE_3D_OFFSET
" <deltaz>%lf</deltaz>\n"
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
" <rx>%lf</rx>\n"
" <ry>%lf</ry>\n"
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
" <rz>%lf</rz>\n"
" <scale>%lf</scale>\n"
" </instance>\n",
@ -862,8 +915,15 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
instance->offset(0),
instance->offset(1),
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
instance->get_rotation(X),
instance->get_rotation(Y),
instance->get_rotation(Z),
#else
instance->rotation,
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
instance->scaling_factor);
//FIXME missing instance->scaling_factor
instances.append(buf);
}

View file

@ -164,7 +164,11 @@ bool load_prus(const char *path, Model *model)
const char *zero_tag = "<zero>";
const char *zero_xml = strstr(scene_xml_data.data(), zero_tag);
float trafo[3][4] = { 0 };
#if ENABLE_MODELINSTANCE_3D_ROTATION
Vec3d instance_rotation = Vec3d::Zero();
#else
double instance_rotation = 0.;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
double instance_scaling_factor = 1.f;
#if ENABLE_MODELINSTANCE_3D_OFFSET
Vec3d instance_offset = Vec3d::Zero();
@ -197,10 +201,14 @@ bool load_prus(const char *path, Model *model)
instance_scaling_factor = scale[0];
scale[0] = scale[1] = scale[2] = 1.;
}
#if ENABLE_MODELINSTANCE_3D_ROTATION
instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]);
#else
if (rotation[0] == 0. && rotation[1] == 0.) {
instance_rotation = - rotation[2];
rotation[2] = 0.;
}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
Eigen::Matrix3f mat_rot, mat_scale, mat_trafo;
mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) *
Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) *
@ -366,8 +374,12 @@ bool load_prus(const char *path, Model *model)
model_object = model->add_object(name_utf8.data(), path, std::move(mesh));
volume = model_object->volumes.front();
ModelInstance *instance = model_object->add_instance();
instance->rotation = instance_rotation;
instance->scaling_factor = instance_scaling_factor;
#if ENABLE_MODELINSTANCE_3D_ROTATION
instance->set_rotation(instance_rotation);
#else
instance->rotation = instance_rotation;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
instance->scaling_factor = instance_scaling_factor;
#if ENABLE_MODELINSTANCE_3D_OFFSET
instance->set_offset(instance_offset);
#else

View file

@ -13,6 +13,7 @@
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/find.hpp>
#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
#include <boost/nowide/iostream.hpp>
@ -337,15 +338,15 @@ std::string WipeTowerIntegration::finalize(GCode &gcodegen)
std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObject &object)
{
std::vector<GCode::LayerToPrint> layers_to_print;
layers_to_print.reserve(object.layers.size() + object.support_layers.size());
layers_to_print.reserve(object.layers().size() + object.support_layers().size());
// Pair the object layers with the support layers by z.
size_t idx_object_layer = 0;
size_t idx_support_layer = 0;
while (idx_object_layer < object.layers.size() || idx_support_layer < object.support_layers.size()) {
while (idx_object_layer < object.layers().size() || idx_support_layer < object.support_layers().size()) {
LayerToPrint layer_to_print;
layer_to_print.object_layer = (idx_object_layer < object.layers.size()) ? object.layers[idx_object_layer ++] : nullptr;
layer_to_print.support_layer = (idx_support_layer < object.support_layers.size()) ? object.support_layers[idx_support_layer ++] : nullptr;
layer_to_print.object_layer = (idx_object_layer < object.layers().size()) ? object.layers()[idx_object_layer ++] : nullptr;
layer_to_print.support_layer = (idx_support_layer < object.support_layers().size()) ? object.support_layers()[idx_support_layer ++] : nullptr;
if (layer_to_print.object_layer && layer_to_print.support_layer) {
if (layer_to_print.object_layer->print_z < layer_to_print.support_layer->print_z - EPSILON) {
layer_to_print.support_layer = nullptr;
@ -417,6 +418,12 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
{
PROFILE_CLEAR();
// Does the file exist? If so, we hope that it is still valid.
if (print->is_step_done(psGCodeExport) && boost::filesystem::exists(boost::filesystem::path(path)))
return;
print->set_started(psGCodeExport);
BOOST_LOG_TRIVIAL(info) << "Exporting G-code...";
// Remove the old g-code if it exists.
@ -429,27 +436,34 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
if (file == nullptr)
throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
this->m_placeholder_parser_failed_templates.clear();
this->_do_export(*print, file, preview_data);
fflush(file);
if (ferror(file)) {
try {
m_placeholder_parser_failed_templates.clear();
this->_do_export(*print, file, preview_data);
fflush(file);
if (ferror(file)) {
fclose(file);
boost::nowide::remove(path_tmp.c_str());
throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
}
} catch (std::exception &ex) {
// Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
// Close and remove the file.
fclose(file);
boost::nowide::remove(path_tmp.c_str());
throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
throw;
}
fclose(file);
if (print->config.remaining_times.value)
{
if (print->config().remaining_times.value) {
m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
if (m_silent_time_estimator_enabled)
m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
}
if (! this->m_placeholder_parser_failed_templates.empty()) {
if (! m_placeholder_parser_failed_templates.empty()) {
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n";
for (const std::string &name : this->m_placeholder_parser_failed_templates)
for (const std::string &name : m_placeholder_parser_failed_templates)
msg += std::string("\t") + name + "\n";
msg += "\nPlease inspect the file ";
msg += path_tmp + " for error messages enclosed between\n";
@ -459,12 +473,13 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
throw std::runtime_error(msg);
}
if (boost::nowide::rename(path_tmp.c_str(), path) != 0)
if (rename_file(path_tmp, path) != 0)
throw std::runtime_error(
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' +
"Is " + path_tmp + " locked?" + '\n');
BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished";
print->set_done(psGCodeExport);
// Write the profiler measurements to file
PROFILE_UPDATE();
@ -477,66 +492,66 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// resets time estimators
m_normal_time_estimator.reset();
m_normal_time_estimator.set_dialect(print.config.gcode_flavor);
m_silent_time_estimator_enabled = (print.config.gcode_flavor == gcfMarlin) && print.config.silent_mode;
m_normal_time_estimator.set_dialect(print.config().gcode_flavor);
m_silent_time_estimator_enabled = (print.config().gcode_flavor == gcfMarlin) && print.config().silent_mode;
// Until we have a UI support for the other firmwares than the Marlin, use the hardcoded default values
// and let the user to enter the G-code limits into the start G-code.
// If the following block is enabled for other firmwares than the Marlin, then the function
// this->print_machine_envelope(file, print);
// shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor.
if (print.config.gcode_flavor.value == gcfMarlin) {
m_normal_time_estimator.set_max_acceleration(print.config.machine_max_acceleration_extruding.values[0]);
m_normal_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[0]);
m_normal_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[0]);
m_normal_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[0]);
if (print.config().gcode_flavor.value == gcfMarlin) {
m_normal_time_estimator.set_max_acceleration(print.config().machine_max_acceleration_extruding.values[0]);
m_normal_time_estimator.set_retract_acceleration(print.config().machine_max_acceleration_retracting.values[0]);
m_normal_time_estimator.set_minimum_feedrate(print.config().machine_min_extruding_rate.values[0]);
m_normal_time_estimator.set_minimum_travel_feedrate(print.config().machine_min_travel_rate.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config().machine_max_acceleration_x.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config().machine_max_acceleration_y.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config().machine_max_acceleration_z.values[0]);
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config().machine_max_acceleration_e.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config().machine_max_feedrate_x.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config().machine_max_feedrate_y.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config().machine_max_feedrate_z.values[0]);
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config().machine_max_feedrate_e.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config().machine_max_jerk_x.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config().machine_max_jerk_y.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config().machine_max_jerk_z.values[0]);
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config().machine_max_jerk_e.values[0]);
if (m_silent_time_estimator_enabled)
{
m_silent_time_estimator.reset();
m_silent_time_estimator.set_dialect(print.config.gcode_flavor);
m_silent_time_estimator.set_max_acceleration(print.config.machine_max_acceleration_extruding.values[1]);
m_silent_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[1]);
m_silent_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[1]);
m_silent_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[1]);
if (print.config.single_extruder_multi_material) {
m_silent_time_estimator.set_dialect(print.config().gcode_flavor);
m_silent_time_estimator.set_max_acceleration(print.config().machine_max_acceleration_extruding.values[1]);
m_silent_time_estimator.set_retract_acceleration(print.config().machine_max_acceleration_retracting.values[1]);
m_silent_time_estimator.set_minimum_feedrate(print.config().machine_min_extruding_rate.values[1]);
m_silent_time_estimator.set_minimum_travel_feedrate(print.config().machine_min_travel_rate.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config().machine_max_acceleration_x.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config().machine_max_acceleration_y.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config().machine_max_acceleration_z.values[1]);
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config().machine_max_acceleration_e.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config().machine_max_feedrate_x.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config().machine_max_feedrate_y.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config().machine_max_feedrate_z.values[1]);
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config().machine_max_feedrate_e.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config().machine_max_jerk_x.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config().machine_max_jerk_y.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config().machine_max_jerk_z.values[1]);
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config().machine_max_jerk_e.values[1]);
if (print.config().single_extruder_multi_material) {
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
// are considered to be active for the single extruder multi-material printers only.
m_silent_time_estimator.set_filament_load_times(print.config.filament_load_time.values);
m_silent_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values);
m_silent_time_estimator.set_filament_load_times(print.config().filament_load_time.values);
m_silent_time_estimator.set_filament_unload_times(print.config().filament_unload_time.values);
}
}
}
// Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful.
if (print.config.single_extruder_multi_material) {
if (print.config().single_extruder_multi_material) {
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
// are considered to be active for the single extruder multi-material printers only.
m_normal_time_estimator.set_filament_load_times(print.config.filament_load_time.values);
m_normal_time_estimator.set_filament_unload_times(print.config.filament_unload_time.values);
m_normal_time_estimator.set_filament_load_times(print.config().filament_load_time.values);
m_normal_time_estimator.set_filament_unload_times(print.config().filament_unload_time.values);
}
// resets analyzer
@ -552,14 +567,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// change_layer() in turn increments the progress bar status.
m_layer_count = 0;
PrintObjectPtrs printable_objects = print.get_printable_objects();
if (print.config.complete_objects.value) {
if (print.config().complete_objects.value) {
// Add each of the object's layers separately.
for (auto object : printable_objects) {
std::vector<coordf_t> zs;
zs.reserve(object->layers.size() + object->support_layers.size());
for (auto layer : object->layers)
zs.reserve(object->layers().size() + object->support_layers().size());
for (auto layer : object->layers())
zs.push_back(layer->print_z);
for (auto layer : object->support_layers)
for (auto layer : object->support_layers())
zs.push_back(layer->print_z);
std::sort(zs.begin(), zs.end());
m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
@ -568,18 +583,19 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Print all objects with the same print_z together.
std::vector<coordf_t> zs;
for (auto object : printable_objects) {
zs.reserve(zs.size() + object->layers.size() + object->support_layers.size());
for (auto layer : object->layers)
zs.reserve(zs.size() + object->layers().size() + object->support_layers().size());
for (auto layer : object->layers())
zs.push_back(layer->print_z);
for (auto layer : object->support_layers)
for (auto layer : object->support_layers())
zs.push_back(layer->print_z);
}
std::sort(zs.begin(), zs.end());
m_layer_count = (unsigned int)(std::unique(zs.begin(), zs.end()) - zs.begin());
}
print.throw_if_canceled();
m_enable_cooling_markers = true;
this->apply_print_config(print.config);
this->apply_print_config(print.config());
this->set_extruders(print.extruders());
// Initialize autospeed.
@ -587,27 +603,28 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// get the minimum cross-section used in the print
std::vector<double> mm3_per_mm;
for (auto object : printable_objects) {
for (size_t region_id = 0; region_id < print.regions.size(); ++region_id) {
auto region = print.regions[region_id];
for (auto layer : object->layers) {
auto layerm = layer->regions[region_id];
if (region->config.get_abs_value("perimeter_speed" ) == 0 ||
region->config.get_abs_value("small_perimeter_speed" ) == 0 ||
region->config.get_abs_value("external_perimeter_speed" ) == 0 ||
region->config.get_abs_value("bridge_speed" ) == 0)
for (size_t region_id = 0; region_id < print.regions().size(); ++region_id) {
auto region = print.regions()[region_id];
for (auto layer : object->layers()) {
auto layerm = layer->regions()[region_id];
if (region->config().get_abs_value("perimeter_speed" ) == 0 ||
region->config().get_abs_value("small_perimeter_speed" ) == 0 ||
region->config().get_abs_value("external_perimeter_speed" ) == 0 ||
region->config().get_abs_value("bridge_speed" ) == 0)
mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm());
if (region->config.get_abs_value("infill_speed" ) == 0 ||
region->config.get_abs_value("solid_infill_speed" ) == 0 ||
region->config.get_abs_value("top_solid_infill_speed" ) == 0 ||
region->config.get_abs_value("bridge_speed" ) == 0)
if (region->config().get_abs_value("infill_speed" ) == 0 ||
region->config().get_abs_value("solid_infill_speed" ) == 0 ||
region->config().get_abs_value("top_solid_infill_speed" ) == 0 ||
region->config().get_abs_value("bridge_speed" ) == 0)
mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm());
}
}
if (object->config.get_abs_value("support_material_speed" ) == 0 ||
object->config.get_abs_value("support_material_interface_speed" ) == 0)
for (auto layer : object->support_layers)
if (object->config().get_abs_value("support_material_speed" ) == 0 ||
object->config().get_abs_value("support_material_interface_speed" ) == 0)
for (auto layer : object->support_layers())
mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm());
}
print.throw_if_canceled();
// filter out 0-width segments
mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end());
if (! mm3_per_mm.empty()) {
@ -616,19 +633,20 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// volumetric speed as the volumetric speed produced by printing the
// smallest cross-section at the maximum speed: any larger cross-section
// will need slower feedrates.
m_volumetric_speed = *std::min_element(mm3_per_mm.begin(), mm3_per_mm.end()) * print.config.max_print_speed.value;
m_volumetric_speed = *std::min_element(mm3_per_mm.begin(), mm3_per_mm.end()) * print.config().max_print_speed.value;
// limit such volumetric speed with max_volumetric_speed if set
if (print.config.max_volumetric_speed.value > 0)
m_volumetric_speed = std::min(m_volumetric_speed, print.config.max_volumetric_speed.value);
if (print.config().max_volumetric_speed.value > 0)
m_volumetric_speed = std::min(m_volumetric_speed, print.config().max_volumetric_speed.value);
}
}
print.throw_if_canceled();
m_cooling_buffer = make_unique<CoolingBuffer>(*this);
if (print.config.spiral_vase.value)
m_spiral_vase = make_unique<SpiralVase>(print.config);
if (print.config.max_volumetric_extrusion_rate_slope_positive.value > 0 ||
print.config.max_volumetric_extrusion_rate_slope_negative.value > 0)
m_pressure_equalizer = make_unique<PressureEqualizer>(&print.config);
if (print.config().spiral_vase.value)
m_spiral_vase = make_unique<SpiralVase>(print.config());
if (print.config().max_volumetric_extrusion_rate_slope_positive.value > 0 ||
print.config().max_volumetric_extrusion_rate_slope_negative.value > 0)
m_pressure_equalizer = make_unique<PressureEqualizer>(&print.config());
m_enable_extrusion_role_markers = (bool)m_pressure_equalizer;
// Write information on the generator.
@ -636,7 +654,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Write notes (content of the Print Settings tab -> Notes)
{
std::list<std::string> lines;
boost::split(lines, print.config.notes.value, boost::is_any_of("\n"), boost::token_compress_off);
boost::split(lines, print.config().notes.value, boost::is_any_of("\n"), boost::token_compress_off);
for (auto line : lines) {
// Remove the trailing '\r' from the '\r\n' sequence.
if (! line.empty() && line.back() == '\r')
@ -646,12 +664,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
if (! lines.empty())
_write(file, "\n");
}
print.throw_if_canceled();
// Write some terse information on the slicing parameters.
const PrintObject *first_object = printable_objects.front();
const double layer_height = first_object->config.layer_height.value;
const double first_layer_height = first_object->config.first_layer_height.get_abs_value(layer_height);
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
auto region = print.regions[region_id];
const double layer_height = first_object->config().layer_height.value;
const double first_layer_height = first_object->config().first_layer_height.get_abs_value(layer_height);
for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) {
auto region = print.regions()[region_id];
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width);
_write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width);
@ -659,13 +679,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width);
if (print.has_support_material())
_write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
if (print.config.first_layer_extrusion_width.value > 0)
if (print.config().first_layer_extrusion_width.value > 0)
_write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width);
_write_format(file, "\n");
}
print.throw_if_canceled();
// adds tags for time estimators
if (print.config.remaining_times.value)
if (print.config().remaining_times.value)
{
_writeln(file, GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag);
if (m_silent_time_estimator_enabled)
@ -673,7 +694,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
}
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
m_placeholder_parser = print.placeholder_parser;
m_placeholder_parser = print.placeholder_parser();
m_placeholder_parser.update_timestamp();
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
@ -683,7 +704,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
unsigned int final_extruder_id = (unsigned int)-1;
size_t initial_print_object_id = 0;
bool has_wipe_tower = false;
if (print.config.complete_objects.value) {
if (print.config().complete_objects.value) {
// Find the 1st printing object, find its tool ordering and the initial extruder ID.
for (; initial_print_object_id < printable_objects.size(); ++initial_print_object_id) {
tool_ordering = ToolOrdering(*printable_objects[initial_print_object_id], initial_extruder_id);
@ -693,11 +714,11 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
} else {
// Find tool ordering for all the objects at once, and the initial extruder ID.
// If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
tool_ordering = print.m_tool_ordering.empty() ?
tool_ordering = print.wipe_tower_data().tool_ordering.empty() ?
ToolOrdering(print, initial_extruder_id) :
print.m_tool_ordering;
print.wipe_tower_data().tool_ordering;
has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
initial_extruder_id = (has_wipe_tower && ! print.config.single_extruder_multi_material_priming) ?
initial_extruder_id = (has_wipe_tower && ! print.config().single_extruder_multi_material_priming) ?
// The priming towers will be skipped.
tool_ordering.all_extruders().back() :
// Don't skip the priming towers.
@ -711,6 +732,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
final_extruder_id = tool_ordering.last_extruder();
assert(final_extruder_id != (unsigned int)-1);
}
print.throw_if_canceled();
m_cooling_buffer->set_current_extruder(initial_extruder_id);
@ -718,7 +740,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
this->print_machine_envelope(file, print);
// Disable fan.
if (! print.config.cooling.get_at(initial_extruder_id) || print.config.disable_fan_first_layers.get_at(initial_extruder_id))
if (! print.config().cooling.get_at(initial_extruder_id) || print.config().disable_fan_first_layers.get_at(initial_extruder_id))
_write(file, m_writer.set_fan(0, true));
// Let the start-up script prime the 1st printing tool.
@ -729,8 +751,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
m_placeholder_parser.set("current_object_idx", 0);
// For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided.
m_placeholder_parser.set("has_wipe_tower", has_wipe_tower);
m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config.single_extruder_multi_material_priming);
std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config.start_gcode.value, initial_extruder_id);
m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming);
std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id);
// Set bed temperature if the start G-code does not contain any bed temp control G-codes.
this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true);
// Set extruder(s) temperature before and after start G-code.
@ -747,49 +769,51 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Write the custom start G-code
_writeln(file, start_gcode);
// Process filament-specific gcode in extruder order.
if (print.config.single_extruder_multi_material) {
if (print.config().single_extruder_multi_material) {
if (has_wipe_tower) {
// Wipe tower will control the extruder switching, it will call the start_filament_gcode.
} else {
// Only initialize the initial extruder.
_writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config.start_filament_gcode.values[initial_extruder_id], initial_extruder_id));
_writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id));
}
} else {
for (const std::string &start_gcode : print.config.start_filament_gcode.values)
_writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config.start_filament_gcode.values.front())));
for (const std::string &start_gcode : print.config().start_filament_gcode.values)
_writeln(file, this->placeholder_parser_process("start_gcode", start_gcode, (unsigned int)(&start_gcode - &print.config().start_filament_gcode.values.front())));
}
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
print.throw_if_canceled();
// Set other general things.
_write(file, this->preamble());
// Initialize a motion planner for object-to-object travel moves.
if (print.config.avoid_crossing_perimeters.value) {
if (print.config().avoid_crossing_perimeters.value) {
// Collect outer contours of all objects over all layers.
// Discard objects only containing thin walls (offset would fail on an empty polygon).
Polygons islands;
for (const PrintObject *object : printable_objects)
for (const Layer *layer : object->layers)
for (const Layer *layer : object->layers())
for (const ExPolygon &expoly : layer->slices.expolygons)
for (const Point &copy : object->_shifted_copies) {
for (const Point &copy : object->copies()) {
islands.emplace_back(expoly.contour);
islands.back().translate(- copy);
}
//FIXME Mege the islands in parallel.
m_avoid_crossing_perimeters.init_external_mp(union_ex(islands));
print.throw_if_canceled();
}
// Calculate wiping points if needed
if (print.config.ooze_prevention.value && ! print.config.single_extruder_multi_material) {
if (print.config().ooze_prevention.value && ! print.config().single_extruder_multi_material) {
Points skirt_points;
for (const ExtrusionEntity *ee : print.skirt.entities)
for (const ExtrusionEntity *ee : print.skirt().entities)
for (const ExtrusionPath &path : dynamic_cast<const ExtrusionLoop*>(ee)->paths)
append(skirt_points, path.polyline.points);
if (! skirt_points.empty()) {
Polygon outer_skirt = Slic3r::Geometry::convex_hull(skirt_points);
Polygons skirts;
for (unsigned int extruder_id : print.extruders()) {
const Vec2d &extruder_offset = print.config.extruder_offset.get_at(extruder_id);
const Vec2d &extruder_offset = print.config().extruder_offset.get_at(extruder_id);
Polygon s(outer_skirt);
s.translate(Point::new_scale(- extruder_offset(0), - extruder_offset(1)));
skirts.emplace_back(std::move(s));
@ -807,16 +831,17 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
);
#endif
}
print.throw_if_canceled();
}
if (! (has_wipe_tower && print.config.single_extruder_multi_material_priming)) {
if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming)) {
// Set initial extruder only after custom start G-code.
// Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
_write(file, this->set_extruder(initial_extruder_id));
}
// Do all objects for each layer.
if (print.config.complete_objects.value) {
if (print.config().complete_objects.value) {
// Print objects from the smallest to the tallest to avoid collisions
// when moving onto next object starting point.
std::vector<PrintObject*> objects(printable_objects);
@ -824,9 +849,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
size_t finished_objects = 0;
for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) {
const PrintObject &object = *objects[object_id];
for (const Point &copy : object._shifted_copies) {
for (const Point &copy : object.copies()) {
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
if (object_id != initial_print_object_id || &copy != object._shifted_copies.data()) {
if (object_id != initial_print_object_id || &copy != object.copies().data()) {
// Don't initialize for the first object and first copy.
tool_ordering = ToolOrdering(object, final_extruder_id);
unsigned int new_extruder_id = tool_ordering.first_extruder();
@ -837,6 +862,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
final_extruder_id = tool_ordering.last_extruder();
assert(final_extruder_id != (unsigned int)-1);
}
print.throw_if_canceled();
this->set_origin(unscale(copy));
if (finished_objects > 0) {
// Move to the origin position for the copy we're going to print.
@ -852,7 +878,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// another one, set first layer temperatures. This happens before the Z move
// is triggered, so machine has more time to reach such temperatures.
m_placeholder_parser.set("current_object_idx", int(finished_objects));
std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config.between_objects_gcode.value, initial_extruder_id);
std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id);
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
@ -866,7 +892,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
for (const LayerToPrint &ltp : layers_to_print) {
std::vector<LayerToPrint> lrs;
lrs.emplace_back(std::move(ltp));
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), &copy - object._shifted_copies.data());
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), &copy - object.copies().data());
print.throw_if_canceled();
}
if (m_pressure_equalizer)
_write(file, m_pressure_equalizer->process("", true));
@ -882,16 +909,16 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
Points object_reference_points;
PrintObjectPtrs printable_objects = print.get_printable_objects();
for (PrintObject *object : printable_objects)
object_reference_points.push_back(object->_shifted_copies.front());
object_reference_points.push_back(object->copies().front());
Slic3r::Geometry::chained_path(object_reference_points, object_indices);
// Sort layers by Z.
// All extrusion moves with the same top layer height are extruded uninterrupted.
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
// Prusa Multi-Material wipe tower.
if (has_wipe_tower && ! layers_to_print.empty()) {
m_wipe_tower.reset(new WipeTowerIntegration(print.config, *print.m_wipe_tower_priming.get(), print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get()));
m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()));
_write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
if (print.config.single_extruder_multi_material_priming) {
if (print.config().single_extruder_multi_material_priming) {
_write(file, m_wipe_tower->prime(*this));
// Verify, whether the print overaps the priming extrusions.
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
@ -915,6 +942,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write(file, "M1 S10\n");
}
}
print.throw_if_canceled();
}
// Extrude the layers.
for (auto &layer : layers_to_print) {
@ -922,6 +950,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
if (m_wipe_tower && layer_tools.has_wipe_tower)
m_wipe_tower->next_layer();
this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
print.throw_if_canceled();
}
if (m_pressure_equalizer)
_write(file, m_pressure_equalizer->process("", true));
@ -947,17 +976,18 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value));
if (print.config.single_extruder_multi_material) {
if (print.config().single_extruder_multi_material) {
// Process the end_filament_gcode for the active filament only.
_writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config.end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id(), &config));
_writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id(), &config));
} else {
for (const std::string &end_gcode : print.config.end_filament_gcode.values)
_writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config.end_filament_gcode.values.front()), &config));
for (const std::string &end_gcode : print.config().end_filament_gcode.values)
_writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config().end_filament_gcode.values.front()), &config));
}
_writeln(file, this->placeholder_parser_process("end_gcode", print.config.end_gcode, m_writer.extruder()->id(), &config));
_writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config));
}
_write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
_write(file, m_writer.postamble());
print.throw_if_canceled();
// calculates estimated printing time
m_normal_time_estimator.calculate_time(false);
@ -965,37 +995,30 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
m_silent_time_estimator.calculate_time(false);
// Get filament stats.
print.filament_stats.clear();
print.total_used_filament = 0.;
print.total_extruded_volume = 0.;
print.total_weight = 0.;
print.total_cost = 0.;
print.total_wipe_tower_cost = 0.;
print.total_wipe_tower_filament = 0.;
print.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
print.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
print.m_print_statistics.clear();
print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
for (const Extruder &extruder : m_writer.extruders()) {
double used_filament = extruder.used_filament() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] : 0.f);
double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.m_wipe_tower_used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
double used_filament = extruder.used_filament() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] : 0.f);
double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
print.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
print.m_print_statistics.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
_write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001);
if (filament_weight > 0.) {
print.total_weight = print.total_weight + filament_weight;
print.m_print_statistics.total_weight = print.m_print_statistics.total_weight + filament_weight;
_write_format(file, "; filament used = %.1lf\n", filament_weight);
if (filament_cost > 0.) {
print.total_cost = print.total_cost + filament_cost;
print.m_print_statistics.total_cost = print.m_print_statistics.total_cost + filament_cost;
_write_format(file, "; filament cost = %.1lf\n", filament_cost);
}
}
print.total_used_filament += used_filament;
print.total_extruded_volume += extruded_volume;
print.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
print.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
print.m_print_statistics.total_used_filament += used_filament;
print.m_print_statistics.total_extruded_volume += extruded_volume;
print.m_print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
print.m_print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
}
_write_format(file, "; total filament cost = %.1lf\n", print.total_cost);
_write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost);
_write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str());
if (m_silent_time_estimator_enabled)
_write_format(file, "; estimated printing time (silent mode) = %s\n", m_silent_time_estimator.get_time_dhms().c_str());
@ -1008,6 +1031,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
if (!full_config.empty())
_write(file, full_config);
}
print.throw_if_canceled();
// starts analizer calculations
if (preview_data != nullptr)
@ -1020,7 +1044,7 @@ std::string GCode::placeholder_parser_process(const std::string &name, const std
return m_placeholder_parser.process(templ, current_extruder_id, config_override);
} catch (std::runtime_error &err) {
// Collect the names of failed template substitutions for error reporting.
this->m_placeholder_parser_failed_templates.insert(name);
m_placeholder_parser_failed_templates.insert(name);
// Insert the macro error message into the G-code.
return
std::string("\n!!!!! Failed to process the custom G-code template ") + name + "\n" +
@ -1087,29 +1111,29 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
// Do not process this piece of G-code by the time estimator, it already knows the values through another sources.
void GCode::print_machine_envelope(FILE *file, Print &print)
{
if (print.config.gcode_flavor.value == gcfMarlin) {
if (print.config().gcode_flavor.value == gcfMarlin) {
fprintf(file, "M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n",
int(print.config.machine_max_acceleration_x.values.front() + 0.5),
int(print.config.machine_max_acceleration_y.values.front() + 0.5),
int(print.config.machine_max_acceleration_z.values.front() + 0.5),
int(print.config.machine_max_acceleration_e.values.front() + 0.5));
int(print.config().machine_max_acceleration_x.values.front() + 0.5),
int(print.config().machine_max_acceleration_y.values.front() + 0.5),
int(print.config().machine_max_acceleration_z.values.front() + 0.5),
int(print.config().machine_max_acceleration_e.values.front() + 0.5));
fprintf(file, "M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n",
int(print.config.machine_max_feedrate_x.values.front() + 0.5),
int(print.config.machine_max_feedrate_y.values.front() + 0.5),
int(print.config.machine_max_feedrate_z.values.front() + 0.5),
int(print.config.machine_max_feedrate_e.values.front() + 0.5));
int(print.config().machine_max_feedrate_x.values.front() + 0.5),
int(print.config().machine_max_feedrate_y.values.front() + 0.5),
int(print.config().machine_max_feedrate_z.values.front() + 0.5),
int(print.config().machine_max_feedrate_e.values.front() + 0.5));
fprintf(file, "M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
int(print.config.machine_max_acceleration_extruding.values.front() + 0.5),
int(print.config.machine_max_acceleration_retracting.values.front() + 0.5),
int(print.config.machine_max_acceleration_extruding.values.front() + 0.5));
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),
int(print.config().machine_max_acceleration_retracting.values.front() + 0.5),
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5));
fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
print.config.machine_max_jerk_x.values.front(),
print.config.machine_max_jerk_y.values.front(),
print.config.machine_max_jerk_z.values.front(),
print.config.machine_max_jerk_e.values.front());
print.config().machine_max_jerk_x.values.front(),
print.config().machine_max_jerk_y.values.front(),
print.config().machine_max_jerk_z.values.front(),
print.config().machine_max_jerk_e.values.front());
fprintf(file, "M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n",
int(print.config.machine_min_extruding_rate.values.front() + 0.5),
int(print.config.machine_min_travel_rate.values.front() + 0.5));
int(print.config().machine_min_extruding_rate.values.front() + 0.5),
int(print.config().machine_min_travel_rate.values.front() + 0.5));
}
}
@ -1120,7 +1144,7 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
{
// Initial bed temperature based on the first extruder.
int temp = print.config.first_layer_bed_temperature.get_at(first_printing_extruder_id);
int temp = print.config().first_layer_bed_temperature.get_at(first_printing_extruder_id);
// Is the bed temperature set by the provided custom G-code?
int temp_by_gcode = -1;
bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, temp_by_gcode);
@ -1143,23 +1167,23 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
int temp_by_gcode = -1;
if (custom_gcode_sets_temperature(gcode, 104, 109, temp_by_gcode)) {
// Set the extruder temperature at m_writer, but throw away the generated G-code as it will be written with the custom G-code.
int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id);
int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id);
if (temp_by_gcode >= 0 && temp_by_gcode < 1000)
temp = temp_by_gcode;
m_writer.set_temperature(temp_by_gcode, wait, first_printing_extruder_id);
} else {
// Custom G-code does not set the extruder temperature. Do it now.
if (print.config.single_extruder_multi_material.value) {
if (print.config().single_extruder_multi_material.value) {
// Set temperature of the first printing extruder only.
int temp = print.config.first_layer_temperature.get_at(first_printing_extruder_id);
int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id);
if (temp > 0)
_write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id));
} else {
// Set temperatures of all the printing extruders.
for (unsigned int tool_id : print.extruders()) {
int temp = print.config.first_layer_temperature.get_at(tool_id);
if (print.config.ooze_prevention.value)
temp += print.config.standby_temperature_delta.value;
int temp = print.config().first_layer_temperature.get_at(tool_id);
if (print.config().ooze_prevention.value)
temp += print.config().standby_temperature_delta.value;
if (temp > 0)
_write(file, m_writer.set_temperature(temp, wait, tool_id));
}
@ -1232,15 +1256,15 @@ void GCode::process_layer(
unsigned int first_extruder_id = layer_tools.extruders.front();
// Initialize config with the 1st object to be printed at this layer.
m_config.apply(layer.object()->config, true);
m_config.apply(layer.object()->config(), true);
// Check whether it is possible to apply the spiral vase logic for this layer.
// Just a reminder: A spiral vase mode is allowed for a single object, single material print only.
if (m_spiral_vase && layers.size() == 1 && support_layer == nullptr) {
bool enable = (layer.id() > 0 || print.config.brim_width.value == 0.) && (layer.id() >= print.config.skirt_height.value && ! print.has_infinite_skirt());
bool enable = (layer.id() > 0 || print.config().brim_width.value == 0.) && (layer.id() >= print.config().skirt_height.value && ! print.has_infinite_skirt());
if (enable) {
for (const LayerRegion *layer_region : layer.regions)
if (layer_region->region()->config.bottom_solid_layers.value > layer.id() ||
for (const LayerRegion *layer_region : layer.regions())
if (layer_region->region()->config().bottom_solid_layers.value > layer.id() ||
layer_region->perimeters.items_count() > 1 ||
layer_region->fills.items_count() > 0) {
enable = false;
@ -1255,22 +1279,22 @@ void GCode::process_layer(
std::string gcode;
// Set new layer - this will change Z and force a retraction if retract_layer_change is enabled.
if (! print.config.before_layer_gcode.value.empty()) {
if (! print.config().before_layer_gcode.value.empty()) {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
gcode += this->placeholder_parser_process("before_layer_gcode",
print.config.before_layer_gcode.value, m_writer.extruder()->id(), &config)
print.config().before_layer_gcode.value, m_writer.extruder()->id(), &config)
+ "\n";
}
gcode += this->change_layer(print_z); // this will increase m_layer_index
m_layer = &layer;
if (! print.config.layer_gcode.value.empty()) {
if (! print.config().layer_gcode.value.empty()) {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
gcode += this->placeholder_parser_process("layer_gcode",
print.config.layer_gcode.value, m_writer.extruder()->id(), &config)
print.config().layer_gcode.value, m_writer.extruder()->id(), &config)
+ "\n";
}
@ -1278,14 +1302,14 @@ void GCode::process_layer(
// Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent
// first_layer_temperature vs. temperature settings.
for (const Extruder &extruder : m_writer.extruders()) {
if (print.config.single_extruder_multi_material.value && extruder.id() != m_writer.extruder()->id())
if (print.config().single_extruder_multi_material.value && extruder.id() != m_writer.extruder()->id())
// In single extruder multi material mode, set the temperature for the current extruder only.
continue;
int temperature = print.config.temperature.get_at(extruder.id());
if (temperature > 0 && temperature != print.config.first_layer_temperature.get_at(extruder.id()))
int temperature = print.config().temperature.get_at(extruder.id());
if (temperature > 0 && temperature != print.config().first_layer_temperature.get_at(extruder.id()))
gcode += m_writer.set_temperature(temperature, false, extruder.id());
}
gcode += m_writer.set_bed_temperature(print.config.bed_temperature.get_at(first_extruder_id));
gcode += m_writer.set_bed_temperature(print.config().bed_temperature.get_at(first_extruder_id));
// Mark the temperature transition from 1st to 2nd layer to be finished.
m_second_layer_things_done = true;
}
@ -1293,9 +1317,9 @@ void GCode::process_layer(
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
bool extrude_skirt =
! print.skirt.entities.empty() &&
! print.skirt().entities.empty() &&
// Not enough skirt layers printed yet.
(m_skirt_done.size() < print.config.skirt_height.value || print.has_infinite_skirt()) &&
(m_skirt_done.size() < print.config().skirt_height.value || print.has_infinite_skirt()) &&
// This print_z has not been extruded yet
(m_skirt_done.empty() ? 0. : m_skirt_done.back()) < print_z - EPSILON &&
// and this layer is the 1st layer, or it is an object layer, or it is a raft layer.
@ -1317,7 +1341,7 @@ void GCode::process_layer(
extruder_ids.front() = first_extruder_id;
break;
}
size_t n_loops = print.skirt.entities.size();
size_t n_loops = print.skirt().entities.size();
if (n_loops <= extruder_ids.size()) {
for (size_t i = 0; i < n_loops; ++i)
skirt_loops_per_extruder[extruder_ids[i]] = std::pair<size_t, size_t>(i, i + 1);
@ -1336,7 +1360,7 @@ void GCode::process_layer(
}
} else
// Extrude all skirts with the current extruder.
skirt_loops_per_extruder[first_extruder_id] = std::pair<size_t, size_t>(0, print.config.skirts.value);
skirt_loops_per_extruder[first_extruder_id] = std::pair<size_t, size_t>(0, print.config().skirts.value);
}
// Group extrusions by an extruder, then by an object, an island and a region.
@ -1350,22 +1374,22 @@ void GCode::process_layer(
bool has_support = role == erMixed || role == erSupportMaterial;
bool has_interface = role == erMixed || role == erSupportMaterialInterface;
// Extruder ID of the support base. -1 if "don't care".
unsigned int support_extruder = object.config.support_material_extruder.value - 1;
unsigned int support_extruder = object.config().support_material_extruder.value - 1;
// Shall the support be printed with the active extruder, preferably with non-soluble, to avoid tool changes?
bool support_dontcare = object.config.support_material_extruder.value == 0;
bool support_dontcare = object.config().support_material_extruder.value == 0;
// Extruder ID of the support interface. -1 if "don't care".
unsigned int interface_extruder = object.config.support_material_interface_extruder.value - 1;
unsigned int interface_extruder = object.config().support_material_interface_extruder.value - 1;
// Shall the support interface be printed with the active extruder, preferably with non-soluble, to avoid tool changes?
bool interface_dontcare = object.config.support_material_interface_extruder.value == 0;
bool interface_dontcare = object.config().support_material_interface_extruder.value == 0;
if (support_dontcare || interface_dontcare) {
// Some support will be printed with "don't care" material, preferably non-soluble.
// Is the current extruder assigned a soluble filament?
unsigned int dontcare_extruder = first_extruder_id;
if (print.config.filament_soluble.get_at(dontcare_extruder)) {
if (print.config().filament_soluble.get_at(dontcare_extruder)) {
// The last extruder printed on the previous layer extrudes soluble filament.
// Try to find a non-soluble extruder on the same layer.
for (unsigned int extruder_id : layer_tools.extruders)
if (! print.config.filament_soluble.get_at(extruder_id)) {
if (! print.config().filament_soluble.get_at(extruder_id)) {
dontcare_extruder = extruder_id;
break;
}
@ -1412,11 +1436,11 @@ void GCode::process_layer(
layer.slices.expolygons[i].contour.contains(point);
};
for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) {
const LayerRegion *layerm = layer.regions[region_id];
for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) {
const LayerRegion *layerm = layer.regions()[region_id];
if (layerm == nullptr)
continue;
const PrintRegion &region = *print.regions[region_id];
const PrintRegion &region = *print.regions()[region_id];
// Now we must process perimeters and infills and create islands of extrusions in by_region std::map.
@ -1433,11 +1457,14 @@ void GCode::process_layer(
continue;
// This extrusion is part of certain Region, which tells us which extruder should be used for it:
int correct_extruder_id = Print::get_extruder(*fill, region); entity_type=="infills" ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
std::max<int>(region.config.perimeter_extruder.value - 1, 0);
int correct_extruder_id = Print::get_extruder(*fill, region);
//FIXME what is this?
entity_type=="infills" ?
std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1) :
std::max<int>(region.config().perimeter_extruder.value - 1, 0);
// Let's recover vector of extruder overrides:
const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size());
const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->copies().size());
// Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
for (unsigned int extruder : layer_tools.extruders)
@ -1459,8 +1486,8 @@ void GCode::process_layer(
// fill->first_point fits inside ith slice
point_inside_surface(i, fill->first_point())) {
if (islands[i].by_region.empty())
islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->_shifted_copies.size());
islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region());
islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size());
break;
}
}
@ -1494,7 +1521,7 @@ void GCode::process_layer(
Flow skirt_flow = print.skirt_flow();
for (size_t i = loops.first; i < loops.second; ++ i) {
// Adjust flow according to this layer's layer height.
ExtrusionLoop loop = *dynamic_cast<const ExtrusionLoop*>(print.skirt.entities[i]);
ExtrusionLoop loop = *dynamic_cast<const ExtrusionLoop*>(print.skirt().entities[i]);
Flow layer_skirt_flow(skirt_flow);
layer_skirt_flow.height = (float)skirt_height;
double mm3_per_mm = layer_skirt_flow.mm3_per_mm();
@ -1515,7 +1542,7 @@ void GCode::process_layer(
if (! m_brim_done) {
this->set_origin(0., 0.);
m_avoid_crossing_perimeters.use_external_mp = true;
for (const ExtrusionEntity *ee : print.brim.entities)
for (const ExtrusionEntity *ee : print.brim().entities)
gcode += this->extrude_loop(*dynamic_cast<const ExtrusionLoop*>(ee), "brim", m_config.support_material_speed.value);
m_brim_done = true;
m_avoid_crossing_perimeters.use_external_mp = false;
@ -1527,7 +1554,6 @@ void GCode::process_layer(
auto objects_by_extruder_it = by_extruder.find(extruder_id);
if (objects_by_extruder_it == by_extruder.end())
continue;
// We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
for (int print_wipe_extrusions=const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
if (print_wipe_extrusions == 0)
@ -1540,15 +1566,15 @@ void GCode::process_layer(
// This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
continue;
m_config.apply(print_object->config, true);
m_config.apply(print_object->config(), true);
m_layer = layers[layer_id].layer();
if (m_config.avoid_crossing_perimeters)
m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
Points copies;
if (single_object_idx == size_t(-1))
copies = print_object->_shifted_copies;
copies = print_object->copies();
else
copies.push_back(print_object->_shifted_copies[single_object_idx]);
copies.push_back(print_object->copies()[single_object_idx]);
// Sort the copies by the closest point starting with the current print position.
unsigned int copy_id = 0;
@ -1569,7 +1595,7 @@ void GCode::process_layer(
for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
const auto& by_region_specific = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
if (print.config.infill_first) {
if (print.config().infill_first) {
gcode += this->extrude_infill(print, by_region_specific);
gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
} else {
@ -1612,14 +1638,14 @@ void GCode::apply_print_config(const PrintConfig &print_config)
void GCode::append_full_config(const Print& print, std::string& str)
{
const StaticPrintConfig *configs[] = { static_cast<const GCodeConfig*>(&print.config), &print.default_object_config, &print.default_region_config };
const StaticPrintConfig *configs[] = { static_cast<const GCodeConfig*>(&print.config()), &print.default_object_config(), &print.default_region_config() };
for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) {
const StaticPrintConfig *cfg = configs[i];
for (const std::string &key : cfg->keys())
if (key != "compatible_printers")
str += "; " + key + " = " + cfg->serialize(key) + "\n";
}
const DynamicConfig &full_config = print.placeholder_parser.config();
const DynamicConfig &full_config = print.placeholder_parser().config();
for (const char *key : {
"print_settings_id", "filament_settings_id", "printer_settings_id",
"printer_model", "printer_variant", "default_print_profile", "default_filament_profile",
@ -2178,7 +2204,7 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string des
else if (const ExtrusionLoop* loop = dynamic_cast<const ExtrusionLoop*>(&entity))
return this->extrude_loop(*loop, description, speed, lower_layer_edge_grid);
else {
CONFESS("Invalid argument supplied to extrude()");
throw std::invalid_argument("Invalid argument supplied to extrude()");
return "";
}
}
@ -2202,7 +2228,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
{
std::string gcode;
for (const ObjectByExtruder::Island::Region &region : by_region) {
m_config.apply(print.regions[&region - &by_region.front()]->config);
m_config.apply(print.regions()[&region - &by_region.front()]->config());
for (ExtrusionEntity *ee : region.perimeters.entities)
gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid);
}
@ -2214,7 +2240,7 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy
{
std::string gcode;
for (const ObjectByExtruder::Island::Region &region : by_region) {
m_config.apply(print.regions[&region - &by_region.front()]->config);
m_config.apply(print.regions()[&region - &by_region.front()]->config());
ExtrusionEntityCollection chained = region.infills.chained_path_from(m_last_pos, false);
for (ExtrusionEntity *fill : chained.entities) {
auto *eec = dynamic_cast<ExtrusionEntityCollection*>(fill);
@ -2363,7 +2389,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
} else if (path.role() == erGapFill) {
speed = m_config.get_abs_value("gap_fill_speed");
} else {
CONFESS("Invalid speed");
throw std::invalid_argument("Invalid speed");
}
}
if (this->on_first_layer())
@ -2689,7 +2715,7 @@ void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, co
}
else
if (type != "infills") {
CONFESS("Unknown parameter!");
throw std::invalid_argument("Unknown parameter!");
return;
}

View file

@ -150,7 +150,8 @@ public:
{}
~GCode() {}
// throws std::runtime_exception
// throws std::runtime_exception on error,
// throws CanceledException through print->throw_if_canceled().
void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr);
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
@ -164,6 +165,7 @@ public:
const Layer* layer() const { return m_layer; }
GCodeWriter& writer() { return m_writer; }
PlaceholderParser& placeholder_parser() { return m_placeholder_parser; }
const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; }
// Process a template through the placeholder parser, collect error messages to be reported
// inside the generated string and after the G-code export finishes.
std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr);

View file

@ -0,0 +1,60 @@
#include "PostProcessor.hpp"
#if 1
//#ifdef WIN32
namespace Slic3r {
//FIXME Ignore until we include boost::process
void run_post_process_scripts(const std::string &path, const PrintConfig &config)
{
}
} // namespace Slic3r
#else
#include <boost/process/system.hpp>
namespace Slic3r {
void run_post_process_scripts(const std::string &path, const PrintConfig &config)
{
if (config.post_process.values.empty())
return;
config.setenv_();
for (std::string script: config.post_process.values) {
// Ignore empty post processing script lines.
boost::trim(script);
if (script.empty())
continue;
BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path;
if (! boost::filesystem::exists(boost::filesystem::path(path)))
throw std::runtime_exception(std::string("The configured post-processing script does not exist: ") + path);
#ifndef WIN32
file_status fs = boost::filesystem::status(path);
//FIXME test if executible by the effective UID / GID.
// throw std::runtime_exception(std::string("The configured post-processing script is not executable: check permissions. ") + path));
#endif
int result = 0;
#ifdef WIN32
if (boost::iends_with(file, ".gcode")) {
// The current process may be slic3r.exe or slic3r-console.exe.
// Find the path of the process:
wchar_t wpath_exe[_MAX_PATH + 1];
::GetModuleFileNameW(nullptr, wpath_exe, _MAX_PATH);
boost::filesystem::path path_exe(wpath_exe);
// Replace it with the current perl interpreter.
result = boost::process::system((path_exe.parent_path() / "perl5.24.0.exe").string(), script, output_file);
} else
#else
result = boost::process::system(script, output_file);
#endif
if (result < 0)
BOOST_LOG_TRIVIAL(error) << "Script " << script << " on file " << path << " failed. Negative error code returned.";
}
}
} // namespace Slic3r
#endif

View file

@ -0,0 +1,15 @@
#ifndef slic3r_GCode_PostProcessor_hpp_
#define slic3r_GCode_PostProcessor_hpp_
#include <string>
#include "../libslic3r.h"
#include "../PrintConfig.hpp"
namespace Slic3r {
extern void run_post_process_scripts(const std::string &path, const PrintConfig &config);
} // namespace Slic3r
#endif /* slic3r_GCode_PostProcessor_hpp_ */

View file

@ -49,10 +49,10 @@ void PressureEqualizer::reset()
// Volumetric rate of a 0.45mm x 0.2mm extrusion at 60mm/s XY movement: 0.45*0.2*60*60=5.4*60 = 324 mm^3/min
// Volumetric rate of a 0.45mm x 0.2mm extrusion at 20mm/s XY movement: 0.45*0.2*20*60=1.8*60 = 108 mm^3/min
// Slope of the volumetric rate, changing from 20mm/s to 60mm/s over 2 seconds: (5.4-1.8)*60*60/2=60*60*1.8 = 6480 mm^3/min^2 = 1.8 mm^3/s^2
m_max_volumetric_extrusion_rate_slope_positive = (this->m_config == NULL) ? 6480.f :
this->m_config->max_volumetric_extrusion_rate_slope_positive.value * 60.f * 60.f;
m_max_volumetric_extrusion_rate_slope_negative = (this->m_config == NULL) ? 6480.f :
this->m_config->max_volumetric_extrusion_rate_slope_negative.value * 60.f * 60.f;
m_max_volumetric_extrusion_rate_slope_positive = (m_config == NULL) ? 6480.f :
m_config->max_volumetric_extrusion_rate_slope_positive.value * 60.f * 60.f;
m_max_volumetric_extrusion_rate_slope_negative = (m_config == NULL) ? 6480.f :
m_config->max_volumetric_extrusion_rate_slope_negative.value * 60.f * 60.f;
for (size_t i = 0; i < numExtrusionRoles; ++ i) {
m_max_volumetric_extrusion_rate_slopes[i].negative = m_max_volumetric_extrusion_rate_slope_negative;
@ -171,7 +171,7 @@ bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLi
if (strncmp(line, EXTRUSION_ROLE_TAG, strlen(EXTRUSION_ROLE_TAG)) == 0) {
line += strlen(EXTRUSION_ROLE_TAG);
int role = atoi(line);
this->m_current_extrusion_role = ExtrusionRole(role);
m_current_extrusion_role = ExtrusionRole(role);
++ line_idx;
return false;
}

View file

@ -93,25 +93,25 @@ static BoundingBoxf extrusionentity_extents(const ExtrusionEntity *extrusion_ent
auto *extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(extrusion_entity);
if (extrusion_entity_collection != nullptr)
return extrusionentity_extents(*extrusion_entity_collection);
CONFESS("Unexpected extrusion_entity type in extrusionentity_extents()");
throw std::runtime_error("Unexpected extrusion_entity type in extrusionentity_extents()");
return BoundingBoxf();
}
BoundingBoxf get_print_extrusions_extents(const Print &print)
{
BoundingBoxf bbox(extrusionentity_extents(print.brim));
bbox.merge(extrusionentity_extents(print.skirt));
BoundingBoxf bbox(extrusionentity_extents(print.brim()));
bbox.merge(extrusionentity_extents(print.skirt()));
return bbox;
}
BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object, const coordf_t max_print_z)
{
BoundingBoxf bbox;
for (const Layer *layer : print_object.layers) {
for (const Layer *layer : print_object.layers()) {
if (layer->print_z > max_print_z)
break;
BoundingBoxf bbox_this;
for (const LayerRegion *layerm : layer->regions) {
for (const LayerRegion *layerm : layer->regions()) {
bbox_this.merge(extrusionentity_extents(layerm->perimeters));
for (const ExtrusionEntity *ee : layerm->fills.entities)
// fill represents infill extrusions of a single island.
@ -121,7 +121,7 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
if (support_layer)
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
bbox_this.merge(extrusionentity_extents(extrusion_entity));
for (const Point &offset : print_object._shifted_copies) {
for (const Point &offset : print_object.copies()) {
BoundingBoxf bbox_translated(bbox_this);
bbox_translated.translate(unscale(offset));
bbox.merge(bbox_translated);
@ -137,11 +137,11 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
// Wipe tower extrusions are saved as if the tower was at the origin with no rotation
// We need to get position and angle of the wipe tower to transform them to actual position.
Transform2d trafo =
Eigen::Translation2d(print.config.wipe_tower_x.value, print.config.wipe_tower_y.value) *
Eigen::Rotation2Dd(print.config.wipe_tower_rotation_angle.value);
Eigen::Translation2d(print.config().wipe_tower_x.value, print.config().wipe_tower_y.value) *
Eigen::Rotation2Dd(print.config().wipe_tower_rotation_angle.value);
BoundingBoxf bbox;
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.m_wipe_tower_tool_changes) {
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.wipe_tower_data().tool_changes) {
if (! tool_changes.empty() && tool_changes.front().print_z > max_print_z)
break;
for (const WipeTower::ToolChangeResult &tcr : tool_changes) {
@ -164,8 +164,8 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print)
{
BoundingBoxf bbox;
if (print.m_wipe_tower_priming) {
const WipeTower::ToolChangeResult &tcr = *print.m_wipe_tower_priming.get();
if (print.wipe_tower_data().priming != nullptr) {
const WipeTower::ToolChangeResult &tcr = *print.wipe_tower_data().priming;
for (size_t i = 1; i < tcr.extrusions.size(); ++ i) {
const WipeTower::Extrusion &e = tcr.extrusions[i];
if (e.width > 0) {

View file

@ -34,19 +34,19 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
// For the use case when each object is printed separately
// (print.config.complete_objects is true).
// (print.config().complete_objects is true).
ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material)
{
if (object.layers.empty())
if (object.layers().empty())
return;
// Initialize the print layers for just a single object.
{
std::vector<coordf_t> zs;
zs.reserve(zs.size() + object.layers.size() + object.support_layers.size());
for (auto layer : object.layers)
zs.reserve(zs.size() + object.layers().size() + object.support_layers().size());
for (auto layer : object.layers())
zs.emplace_back(layer->print_z);
for (auto layer : object.support_layers)
for (auto layer : object.support_layers())
zs.emplace_back(layer->print_z);
this->initialize_layers(zs);
}
@ -57,16 +57,16 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
// Reorder the extruders to minimize tool switches.
this->reorder_extruders(first_extruder);
this->fill_wipe_tower_partitions(object.print()->config, object.layers.front()->print_z - object.layers.front()->height);
this->fill_wipe_tower_partitions(object.print()->config(), object.layers().front()->print_z - object.layers().front()->height);
this->collect_extruder_statistics(prime_multi_material);
}
// For the use case when all objects are printed at once.
// (print.config.complete_objects is false).
// (print.config().complete_objects is false).
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material)
{
m_print_config_ptr = &print.config;
m_print_config_ptr = &print.config();
PrintObjectPtrs objects = print.get_printable_objects();
// Initialize the print layers for all objects and all layers.
@ -74,13 +74,13 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
{
std::vector<coordf_t> zs;
for (auto object : objects) {
zs.reserve(zs.size() + object->layers.size() + object->support_layers.size());
for (auto layer : object->layers)
zs.reserve(zs.size() + object->layers().size() + object->support_layers().size());
for (auto layer : object->layers())
zs.emplace_back(layer->print_z);
for (auto layer : object->support_layers)
for (auto layer : object->support_layers())
zs.emplace_back(layer->print_z);
if (! object->layers.empty())
object_bottom_z = object->layers.front()->print_z - object->layers.front()->height;
if (! object->layers().empty())
object_bottom_z = object->layers().front()->print_z - object->layers().front()->height;
}
this->initialize_layers(zs);
}
@ -92,7 +92,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
// Reorder the extruders to minimize tool switches.
this->reorder_extruders(first_extruder);
this->fill_wipe_tower_partitions(print.config, object_bottom_z);
this->fill_wipe_tower_partitions(print.config(), object_bottom_z);
this->collect_extruder_statistics(prime_multi_material);
}
@ -133,13 +133,13 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
void ToolOrdering::collect_extruders(const PrintObject &object)
{
// Collect the support extruders.
for (auto support_layer : object.support_layers) {
for (auto support_layer : object.support_layers()) {
LayerTools &layer_tools = this->tools_for_layer(support_layer->print_z);
ExtrusionRole role = support_layer->support_fills.role();
bool has_support = role == erMixed || role == erSupportMaterial;
bool has_interface = role == erMixed || role == erSupportMaterialInterface;
unsigned int extruder_support = object.config.support_material_extruder.value;
unsigned int extruder_interface = object.config.support_material_interface_extruder.value;
unsigned int extruder_support = object.config().support_material_extruder.value;
unsigned int extruder_interface = object.config().support_material_interface_extruder.value;
if (has_support)
layer_tools.extruders.push_back(extruder_support);
if (has_interface)
@ -148,14 +148,14 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
layer_tools.has_support = true;
}
// Collect the object extruders.
for (auto layer : object.layers) {
for (auto layer : object.layers()) {
LayerTools &layer_tools = this->tools_for_layer(layer->print_z);
// What extruders are required to print this object layer?
for (size_t region_id = 0; region_id < object.print()->regions.size(); ++ region_id) {
const LayerRegion *layerm = (region_id < layer->regions.size()) ? layer->regions[region_id] : nullptr;
for (size_t region_id = 0; region_id < object.print()->regions().size(); ++ region_id) {
const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr;
if (layerm == nullptr)
continue;
const PrintRegion &region = *object.print()->regions[region_id];
const PrintRegion &region = *object.print()->regions()[region_id];
if (! layerm->perimeters.entities.empty()) {
bool something_nonoverriddable = true;
@ -170,7 +170,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
}
if (something_nonoverriddable)
layer_tools.extruders.push_back(region.config.perimeter_extruder.value);
layer_tools.extruders.push_back(region.config().perimeter_extruder.value);
layer_tools.has_object = true;
}
@ -197,9 +197,9 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
if (something_nonoverriddable || !m_print_config_ptr)
{
if (has_solid_infill)
layer_tools.extruders.push_back(region.config.solid_infill_extruder);
layer_tools.extruders.push_back(region.config().solid_infill_extruder);
if (has_infill)
layer_tools.extruders.push_back(region.config.infill_extruder);
layer_tools.extruders.push_back(region.config().infill_extruder);
}
if (has_solid_infill || has_infill)
layer_tools.has_object = true;
@ -430,10 +430,10 @@ bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, con
if (print_config.filament_soluble.get_at(Print::get_extruder(eec, region)))
return false;
if (object.config.wipe_into_objects)
if (object.config().wipe_into_objects)
return true;
if (!region.config.wipe_into_infill || eec.role() != erInternalInfill)
if (!region.config().wipe_into_infill || eec.role() != erInternalInfill)
return false;
return true;
@ -447,12 +447,12 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
const LayerTools& lt = *m_layer_tools;
const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
if (print.config.filament_soluble.get_at(old_extruder) || print.config.filament_soluble.get_at(new_extruder))
if (print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder))
return volume_to_wipe; // Soluble filament cannot be wiped in a random infill, neither the filament after it
// we will sort objects so that dedicated for wiping are at the beginning:
PrintObjectPtrs object_list = print.get_printable_objects();
std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; });
std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config().wipe_into_objects; });
// We will now iterate through
// - first the dedicated objects to mark perimeters or infills (depending on infill_first)
@ -462,7 +462,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
bool perimeters_done = false;
for (int i=0 ; i<(int)object_list.size() + (perimeters_done ? 0 : 1); ++i) {
if (!perimeters_done && (i==(int)object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list
if (!perimeters_done && (i==(int)object_list.size() || !object_list[i]->config().wipe_into_objects)) { // we passed the last dedicated object in list
perimeters_done = true;
i=-1; // let's go from the start again
continue;
@ -471,26 +471,26 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
const auto& object = object_list[i];
// Finds this layer:
auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
if (this_layer_it == object->layers.end())
auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
if (this_layer_it == object->layers().end())
continue;
const Layer* this_layer = *this_layer_it;
unsigned int num_of_copies = object->_shifted_copies.size();
unsigned int num_of_copies = object->copies().size();
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
const auto& region = *object->print()->regions[region_id];
for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) {
const auto& region = *object->print()->regions()[region_id];
if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
continue;
if ((!print.config.infill_first ? perimeters_done : !perimeters_done) || (!object->config.wipe_into_objects && region.config.wipe_into_infill)) {
for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections
if ((!print.config().infill_first ? perimeters_done : !perimeters_done) || (!object->config().wipe_into_objects && region.config().wipe_into_infill)) {
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config, *object, region))
if (!is_overriddable(*fill, print.config(), *object, region))
continue;
// What extruder would this normally be printed with?
@ -499,10 +499,10 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
if (volume_to_wipe<=0)
continue;
if (!object->config.wipe_into_objects && !print.config.infill_first && region.config.wipe_into_infill)
if (!object->config().wipe_into_objects && !print.config().infill_first && region.config().wipe_into_infill)
// In this case we must check that the original extruder is used on this layer before the one we are overridding
// (and the perimeters will be finished before the infill is printed):
if (!lt.is_extruder_order(region.config.perimeter_extruder - 1, new_extruder))
if (!lt.is_extruder_order(region.config().perimeter_extruder - 1, new_extruder))
continue;
if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder
@ -513,11 +513,11 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
}
// Now the same for perimeters - see comments above for explanation:
if (object->config.wipe_into_objects && (print.config.infill_first ? perimeters_done : !perimeters_done))
if (object->config().wipe_into_objects && (print.config().infill_first ? perimeters_done : !perimeters_done))
{
for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) {
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) {
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config, *object, region))
if (!is_overriddable(*fill, print.config(), *object, region))
continue;
if (volume_to_wipe<=0)
@ -544,29 +544,29 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
{
const LayerTools& lt = *m_layer_tools;
unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config);
unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config);
unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config());
unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config());
PrintObjectPtrs printable_objects = print.get_printable_objects();
for (const PrintObject* object : printable_objects) {
// Finds this layer:
auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
if (this_layer_it == object->layers.end())
auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
if (this_layer_it == object->layers().end())
continue;
const Layer* this_layer = *this_layer_it;
unsigned int num_of_copies = object->_shifted_copies.size();
unsigned int num_of_copies = object->copies().size();
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
const auto& region = *object->print()->regions[region_id];
for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) {
const auto& region = *object->print()->regions()[region_id];
if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
continue;
for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config, *object, region)
if (!is_overriddable(*fill, print.config(), *object, region)
|| is_entity_overridden(fill, copy) )
continue;
@ -574,12 +574,12 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
// printed before its perimeter, or not be printed at all (in case its original extruder has
// not been added to LayerTools
// Either way, we will now force-override it with something suitable:
if (print.config.infill_first
|| object->config.wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely
|| lt.is_extruder_order(region.config.perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
|| std::find(lt.extruders.begin(), lt.extruders.end(), region.config.infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME)
if (print.config().infill_first
|| object->config().wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely
|| lt.is_extruder_order(region.config().perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
|| std::find(lt.extruders.begin(), lt.extruders.end(), region.config().infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME)
)
set_extruder_override(fill, copy, (print.config.infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
set_extruder_override(fill, copy, (print.config().infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
else {
// In this case we can (and should) leave it to be printed normally.
// Force overriding would mean it gets printed before its perimeter.
@ -587,13 +587,13 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
}
// Now the same for perimeters - see comments above for explanation:
for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) { // iterate through all perimeter Collections
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { // iterate through all perimeter Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config, *object, region)
if (!is_overriddable(*fill, print.config(), *object, region)
|| is_entity_overridden(fill, copy) )
continue;
set_extruder_override(fill, copy, (print.config.infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
}
}
}

View file

@ -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

View file

@ -101,7 +101,7 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pair<const char*, co
(cmd_len == 3 && command.first[1] == '9' && command.first[2] == '2')) {
for (size_t i = 0; i < NUM_AXES; ++ i)
if (gline.has(Axis(i)))
this->m_position[i] = gline.value(Axis(i));
m_position[i] = gline.value(Axis(i));
}
}
}

View file

@ -1,4 +1,5 @@
#include "GCodeTimeEstimator.hpp"
#include "Utils.hpp"
#include <boost/bind.hpp>
#include <cmath>
@ -367,8 +368,7 @@ namespace Slic3r {
fclose(out);
in.close();
boost::nowide::remove(filename.c_str());
if (boost::nowide::rename(path_tmp.c_str(), filename.c_str()) != 0)
if (rename_file(path_tmp, filename) != 0)
throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' +
"Is " + path_tmp + " locked?" + '\n');

View file

@ -432,7 +432,7 @@ Pointfs arrange(size_t num_parts, const Vec2d &part_size, coordf_t gap, const Bo
size_t cellw = size_t(floor((bed_bbox.size()(0) + gap) / cell_size(0)));
size_t cellh = size_t(floor((bed_bbox.size()(1) + gap) / cell_size(1)));
if (num_parts > cellw * cellh)
CONFESS(PRINTF_ZU " parts won't fit in your print area!\n", num_parts);
throw std::invalid_argument(PRINTF_ZU " parts won't fit in your print area!\n", num_parts);
// Get a bounding box of cellw x cellh cells, centered at the center of the bed.
Vec2d cells_size(cellw * cell_size(0) - gap, cellh * cell_size(1) - gap);

View file

@ -12,24 +12,24 @@ namespace Slic3r {
Layer::~Layer()
{
this->lower_layer = this->upper_layer = nullptr;
for (LayerRegion *region : this->regions)
for (LayerRegion *region : m_regions)
delete region;
this->regions.clear();
m_regions.clear();
}
LayerRegion* Layer::add_region(PrintRegion* print_region)
{
this->regions.emplace_back(new LayerRegion(this, print_region));
return this->regions.back();
m_regions.emplace_back(new LayerRegion(this, print_region));
return m_regions.back();
}
// merge all regions' slices to get islands
void Layer::make_slices()
{
ExPolygons slices;
if (this->regions.size() == 1) {
if (m_regions.size() == 1) {
// optimization: if we only have one region, take its slices
slices = this->regions.front()->slices;
slices = m_regions.front()->slices;
} else {
Polygons slices_p;
FOREACH_LAYERREGION(this, layerm) {
@ -58,10 +58,10 @@ void Layer::make_slices()
void Layer::merge_slices()
{
if (this->regions.size() == 1) {
if (m_regions.size() == 1) {
// Optimization, also more robust. Don't merge classified pieces of layerm->slices,
// but use the non-split islands of a layer. For a single region print, these shall be equal.
this->regions.front()->slices.set(this->slices.expolygons, stInternal);
m_regions.front()->slices.set(this->slices.expolygons, stInternal);
} else {
FOREACH_LAYERREGION(this, layerm) {
// without safety offset, artifacts are generated (GH #2494)
@ -81,18 +81,18 @@ void Layer::make_perimeters()
std::set<size_t> done;
FOREACH_LAYERREGION(this, layerm) {
size_t region_id = layerm - this->regions.begin();
size_t region_id = layerm - m_regions.begin();
if (done.find(region_id) != done.end()) continue;
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id;
done.insert(region_id);
const PrintRegionConfig &config = (*layerm)->region()->config;
const PrintRegionConfig &config = (*layerm)->region()->config();
// find compatible regions
LayerRegionPtrs layerms;
layerms.push_back(*layerm);
for (LayerRegionPtrs::const_iterator it = layerm + 1; it != this->regions.end(); ++it) {
for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it) {
LayerRegion* other_layerm = *it;
const PrintRegionConfig &other_config = other_layerm->region()->config;
const PrintRegionConfig &other_config = other_layerm->region()->config();
if (config.perimeter_extruder == other_config.perimeter_extruder
&& config.perimeters == other_config.perimeters
@ -104,7 +104,7 @@ void Layer::make_perimeters()
&& config.thin_walls == other_config.thin_walls
&& config.external_perimeters_first == other_config.external_perimeters_first) {
layerms.push_back(other_layerm);
done.insert(it - this->regions.begin());
done.insert(it - m_regions.begin());
}
}
@ -150,13 +150,12 @@ void Layer::make_fills()
#ifdef SLIC3R_DEBUG
printf("Making fills for layer " PRINTF_ZU "\n", this->id());
#endif
for (LayerRegionPtrs::iterator it_layerm = regions.begin(); it_layerm != regions.end(); ++ it_layerm) {
LayerRegion &layerm = *(*it_layerm);
layerm.fills.clear();
make_fill(layerm, layerm.fills);
for (LayerRegion *layerm : m_regions) {
layerm->fills.clear();
make_fill(*layerm, layerm->fills);
#ifndef NDEBUG
for (size_t i = 0; i < layerm.fills.entities.size(); ++ i)
assert(dynamic_cast<ExtrusionEntityCollection*>(layerm.fills.entities[i]) != NULL);
assert(dynamic_cast<ExtrusionEntityCollection*>(layerm->fills.entities[i]) != NULL);
#endif
}
}
@ -164,18 +163,18 @@ void Layer::make_fills()
void Layer::export_region_slices_to_svg(const char *path) const
{
BoundingBox bbox;
for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region)
for (Surfaces::const_iterator surface = (*region)->slices.surfaces.begin(); surface != (*region)->slices.surfaces.end(); ++surface)
bbox.merge(get_extents(surface->expolygon));
for (const auto *region : m_regions)
for (const auto &surface : region->slices.surfaces)
bbox.merge(get_extents(surface.expolygon));
Point legend_size = export_surface_type_legend_to_svg_box_size();
Point legend_pos(bbox.min(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region)
for (Surfaces::const_iterator surface = (*region)->slices.surfaces.begin(); surface != (*region)->slices.surfaces.end(); ++surface)
svg.draw(surface->expolygon, surface_type_to_color_name(surface->surface_type), transparency);
for (const auto *region : m_regions)
for (const auto &surface : region->slices.surfaces)
svg.draw(surface.expolygon, surface_type_to_color_name(surface.surface_type), transparency);
export_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
}
@ -190,18 +189,18 @@ void Layer::export_region_slices_to_svg_debug(const char *name) const
void Layer::export_region_fill_surfaces_to_svg(const char *path) const
{
BoundingBox bbox;
for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region)
for (Surfaces::const_iterator surface = (*region)->fill_surfaces.surfaces.begin(); surface != (*region)->fill_surfaces.surfaces.end(); ++surface)
bbox.merge(get_extents(surface->expolygon));
for (const auto *region : m_regions)
for (const auto &surface : region->slices.surfaces)
bbox.merge(get_extents(surface.expolygon));
Point legend_size = export_surface_type_legend_to_svg_box_size();
Point legend_pos(bbox.min(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (LayerRegionPtrs::const_iterator region = this->regions.begin(); region != this->regions.end(); ++region)
for (Surfaces::const_iterator surface = (*region)->fill_surfaces.surfaces.begin(); surface != (*region)->fill_surfaces.surfaces.end(); ++surface)
svg.draw(surface->expolygon, surface_type_to_color_name(surface->surface_type), transparency);
for (const auto *region : m_regions)
for (const auto &surface : region->slices.surfaces)
svg.draw(surface.expolygon, surface_type_to_color_name(surface.surface_type), transparency);
export_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
}

View file

@ -15,87 +15,93 @@ class Layer;
class PrintRegion;
class PrintObject;
// TODO: make stuff private
class LayerRegion
{
friend class Layer;
public:
Layer* layer() { return this->_layer; }
const Layer* layer() const { return this->_layer; }
PrintRegion* region() { return this->_region; }
const PrintRegion* region() const { return this->_region; }
Layer* layer() { return m_layer; }
const Layer* layer() const { return m_layer; }
PrintRegion* region() { return m_region; }
const PrintRegion* region() const { return m_region; }
// Collection of surfaces generated by slicing the original geometry, divided by type top/bottom/internal.
// collection of surfaces generated by slicing the original geometry
// divided by type top/bottom/internal
SurfaceCollection slices;
// collection of extrusion paths/loops filling gaps
// These fills are generated by the perimeter generator.
// They are not printed on their own, but they are copied to this->fills during infill generation.
ExtrusionEntityCollection thin_fills;
// Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature")
// and for re-starting of infills.
ExPolygons fill_expolygons;
// collection of surfaces for infill generation
SurfaceCollection fill_surfaces;
// Collection of extrusion paths/loops filling gaps.
// These fills are generated by the perimeter generator.
// They are not printed on their own, but they are copied to this->fills during infill generation.
ExtrusionEntityCollection thin_fills;
// Collection of expolygons representing the bridged areas (thus not needing support material).
//FIXME Not used as of now.
// Collection of perimeter surfaces. This is a cached result of diff(slices, fill_surfaces).
// While not necessary, the memory consumption is meager and it speeds up calculation.
// The perimeter_surfaces keep the IDs of the slices (top/bottom/)
SurfaceCollection perimeter_surfaces;
// collection of expolygons representing the bridged areas (thus not
// needing support material)
Polygons bridged;
// collection of polylines representing the unsupported bridge edges
PolylineCollection unsupported_bridge_edges;
// Ordered collection of extrusion paths/loops to build all perimeters.
// This collection contains only ExtrusionEntityCollection objects.
// ordered collection of extrusion paths/loops to build all perimeters
// (this collection contains only ExtrusionEntityCollection objects)
ExtrusionEntityCollection perimeters;
// Ordered collection of extrusion paths to fill surfaces.
// This collection contains only ExtrusionEntityCollection objects.
// ordered collection of extrusion paths to fill surfaces
// (this collection contains only ExtrusionEntityCollection objects)
ExtrusionEntityCollection fills;
Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
void slices_to_fill_surfaces_clipped();
void prepare_fill_surfaces();
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
void process_external_surfaces(const Layer* lower_layer);
Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
void slices_to_fill_surfaces_clipped();
void prepare_fill_surfaces();
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
void process_external_surfaces(const Layer* lower_layer);
double infill_area_threshold() const;
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;
// Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export.
void export_region_slices_to_svg_debug(const char *name) const;
void export_region_fill_surfaces_to_svg_debug(const char *name) const;
void export_region_slices_to_svg_debug(const char *name) const;
void export_region_fill_surfaces_to_svg_debug(const char *name) const;
// Is there any valid extrusion assigned to this LayerRegion?
bool has_extrusions() const { return ! this->perimeters.entities.empty() || ! this->fills.entities.empty(); }
bool has_extrusions() const { return ! this->perimeters.entities.empty() || ! this->fills.entities.empty(); }
protected:
friend class Layer;
LayerRegion(Layer *layer, PrintRegion *region) : m_layer(layer), m_region(region) {}
~LayerRegion() {}
private:
Layer *_layer;
PrintRegion *_region;
LayerRegion(Layer *layer, PrintRegion *region) : _layer(layer), _region(region) {}
~LayerRegion() {}
Layer *m_layer;
PrintRegion *m_region;
};
typedef std::vector<LayerRegion*> LayerRegionPtrs;
class Layer {
friend class PrintObject;
class Layer
{
public:
size_t id() const { return this->_id; }
void set_id(size_t id) { this->_id = id; }
PrintObject* object() { return this->_object; }
const PrintObject* object() const { return this->_object; }
size_t id() const { return m_id; }
void set_id(size_t id) { m_id = id; }
PrintObject* object() { return m_object; }
const PrintObject* object() const { return m_object; }
Layer *upper_layer;
Layer *lower_layer;
LayerRegionPtrs regions;
bool slicing_errors;
coordf_t slice_z; // Z used for slicing in unscaled coordinates
coordf_t print_z; // Z used for printing in unscaled coordinates
coordf_t height; // layer height in unscaled coordinates
Layer *upper_layer;
Layer *lower_layer;
bool slicing_errors;
coordf_t slice_z; // Z used for slicing in unscaled coordinates
coordf_t print_z; // Z used for printing in unscaled coordinates
coordf_t height; // layer height in unscaled coordinates
// collection of expolygons generated by slicing the original geometry;
// also known as 'islands' (all regions and surface types are merged here)
@ -103,57 +109,64 @@ public:
// order will be recovered by the G-code generator.
ExPolygonCollection slices;
size_t region_count() const { return this->regions.size(); }
const LayerRegion* get_region(int idx) const { return this->regions.at(idx); }
LayerRegion* get_region(int idx) { return this->regions.at(idx); }
LayerRegion* add_region(PrintRegion* print_region);
size_t region_count() const { return m_regions.size(); }
const LayerRegion* get_region(int idx) const { return m_regions.at(idx); }
LayerRegion* get_region(int idx) { return m_regions[idx]; }
LayerRegion* add_region(PrintRegion* print_region);
const LayerRegionPtrs& regions() const { return m_regions; }
void make_slices();
void merge_slices();
void make_slices();
void merge_slices();
template <class T> bool any_internal_region_slice_contains(const T &item) const {
for (const LayerRegion *layerm : this->regions) if (layerm->slices.any_internal_contains(item)) return true;
for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_internal_contains(item)) return true;
return false;
}
template <class T> bool any_bottom_region_slice_contains(const T &item) const {
for (const LayerRegion *layerm : this->regions) if (layerm->slices.any_bottom_contains(item)) return true;
for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_bottom_contains(item)) return true;
return false;
}
void make_perimeters();
void make_fills();
void make_perimeters();
void make_fills();
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;
// Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export.
void export_region_slices_to_svg_debug(const char *name) const;
void export_region_fill_surfaces_to_svg_debug(const char *name) const;
void export_region_slices_to_svg_debug(const char *name) const;
void export_region_fill_surfaces_to_svg_debug(const char *name) const;
// Is there any valid extrusion assigned to this LayerRegion?
virtual bool has_extrusions() const { for (auto layerm : this->regions) if (layerm->has_extrusions()) return true; return false; }
virtual bool has_extrusions() const { for (auto layerm : m_regions) if (layerm->has_extrusions()) return true; return false; }
protected:
size_t _id; // sequential number of layer, 0-based
PrintObject *_object;
friend class PrintObject;
Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) :
upper_layer(nullptr), lower_layer(nullptr), slicing_errors(false),
slice_z(slice_z), print_z(print_z), height(height),
_id(id), _object(object) {}
m_id(id), m_object(object) {}
virtual ~Layer();
private:
// sequential number of layer, 0-based
size_t m_id;
PrintObject *m_object;
LayerRegionPtrs m_regions;
};
class SupportLayer : public Layer {
friend class PrintObject;
class SupportLayer : public Layer
{
public:
// Polygons covered by the supports: base, interface and contact areas.
ExPolygonCollection support_islands;
ExPolygonCollection support_islands;
// Extrusion paths for the support base and for the support interface and contacts.
ExtrusionEntityCollection support_fills;
ExtrusionEntityCollection support_fills;
// Is there any valid extrusion assigned to this LayerRegion?
virtual bool has_extrusions() const { return ! support_fills.empty(); }
virtual bool has_extrusions() const { return ! support_fills.empty(); }
protected:
friend class PrintObject;
//protected:
// The constructor has been made public to be able to insert additional support layers for the skirt or a wipe tower
// between the raft and the object first layer.
SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) :

View file

@ -17,13 +17,13 @@ namespace Slic3r {
Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const
{
return this->_region->flow(
return m_region->flow(
role,
this->_layer->height,
m_layer->height,
bridge,
this->_layer->id() == 0,
m_layer->id() == 0,
width,
*this->_layer->object()
*m_layer->object()
);
}
@ -60,9 +60,9 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
&slices,
this->layer()->height,
this->flow(frPerimeter),
&this->region()->config,
&this->layer()->object()->config,
&this->layer()->object()->print()->config,
&this->region()->config(),
&this->layer()->object()->config(),
&this->layer()->object()->print()->config(),
// output:
&this->perimeters,
@ -115,7 +115,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
{
// bottom_polygons are used to trim inflated top surfaces.
fill_boundaries.reserve(number_polygons(surfaces));
bool has_infill = this->region()->config.fill_density.value > 0.;
bool has_infill = this->region()->config().fill_density.value > 0.;
for (const Surface &surface : this->fill_surfaces.surfaces) {
if (surface.surface_type == stTop) {
// Collect the top surfaces, inflate them and trim them by the bottom surfaces.
@ -258,9 +258,9 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
#ifdef SLIC3R_DEBUG
printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id());
#endif
if (bd.detect_angle(Geometry::deg2rad(this->region()->config.bridge_angle.value))) {
if (bd.detect_angle(Geometry::deg2rad(this->region()->config().bridge_angle.value))) {
bridges[idx_last].bridge_angle = bd.angle;
if (this->layer()->object()->config.support_material) {
if (this->layer()->object()->config().support_material) {
polygons_append(this->bridged, bd.coverage());
this->unsupported_bridge_edges.append(bd.unsupported_edges());
}
@ -350,13 +350,13 @@ void LayerRegion::prepare_fill_surfaces()
the only meaningful information returned by psPerimeters. */
// if no solid layers are requested, turn top/bottom surfaces to internal
if (this->region()->config.top_solid_layers == 0) {
if (this->region()->config().top_solid_layers == 0) {
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface)
if (surface->surface_type == stTop)
surface->surface_type = (this->layer()->object()->config.infill_only_where_needed) ?
surface->surface_type = (this->layer()->object()->config().infill_only_where_needed) ?
stInternalVoid : stInternal;
}
if (this->region()->config.bottom_solid_layers == 0) {
if (this->region()->config().bottom_solid_layers == 0) {
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
if (surface->surface_type == stBottom || surface->surface_type == stBottomBridge)
surface->surface_type = stInternal;
@ -364,9 +364,9 @@ void LayerRegion::prepare_fill_surfaces()
}
// turn too small internal regions into solid regions according to the user setting
if (this->region()->config.fill_density.value > 0) {
if (this->region()->config().fill_density.value > 0) {
// scaling an area requires two calls!
double min_area = scale_(scale_(this->region()->config.solid_infill_below_area.value));
double min_area = scale_(scale_(this->region()->config().solid_infill_below_area.value));
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
if (surface->surface_type == stInternal && surface->area() <= min_area)
surface->surface_type = stInternalSolid;

View file

@ -340,7 +340,7 @@ void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
Pointfs model_sizes(copies_num-1, to_2d(this->bounding_box().size()));
Pointfs positions;
if (! _arrange(model_sizes, dist, bb, positions))
CONFESS("Cannot duplicate part as the resulting objects would not fit on the print bed.\n");
throw std::invalid_argument("Cannot duplicate part as the resulting objects would not fit on the print bed.\n");
// note that this will leave the object count unaltered
@ -671,7 +671,8 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const
BoundingBoxf3 bb;
for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) {
if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances");
if (this->instances.empty())
throw std::invalid_argument("Can't call raw_bounding_box() with no instances");
bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true));
}
return bb;
@ -885,6 +886,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
return;
}
// Called by Print::validate() from the UI thread.
void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
{
for (const ModelVolume* vol : this->volumes)
@ -989,8 +991,6 @@ const TriangleMesh& ModelVolume::get_convex_hull() const
ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
{
// Legacy support
if (s == "0")
return MODEL_PART;
if (s == "1")
return PARAMETER_MODIFIER;
// New type (supporting the support enforcers & blockers)
@ -1002,6 +1002,9 @@ ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
return SUPPORT_ENFORCER;
if (s == "SupportBlocker")
return SUPPORT_BLOCKER;
assert(s == "0");
// Default value if invalud type string received.
return MODEL_PART;
}
std::string ModelVolume::type_to_string(const Type t)
@ -1051,6 +1054,29 @@ size_t ModelVolume::split(unsigned int max_extruders)
return idx;
}
#if ENABLE_MODELINSTANCE_3D_ROTATION
void ModelInstance::set_rotation(const Vec3d& rotation)
{
set_rotation(X, rotation(0));
set_rotation(Y, rotation(1));
set_rotation(Z, rotation(2));
}
void ModelInstance::set_rotation(Axis axis, double rotation)
{
static const double TWO_PI = 2.0 * (double)PI;
while (rotation < 0.0)
{
rotation += TWO_PI;
}
while (TWO_PI < rotation)
{
rotation -= TWO_PI;
}
m_rotation(axis) = rotation;
}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
{
mesh->transform(world_matrix(dont_translate).cast<float>());
@ -1095,7 +1121,12 @@ Vec3d ModelInstance::transform_vector(const Vec3d& v, bool dont_translate) const
void ModelInstance::transform_polygon(Polygon* polygon) const
{
#if ENABLE_MODELINSTANCE_3D_ROTATION
// CHECK_ME -> Is the following correct or it should take in account all three rotations ?
polygon->rotate(this->m_rotation(2)); // rotate around polygon origin
#else
polygon->rotate(this->rotation); // rotate around polygon origin
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
polygon->scale(this->scaling_factor); // scale around polygon origin
}
@ -1111,7 +1142,15 @@ Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, b
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
if (!dont_rotate)
#if ENABLE_MODELINSTANCE_3D_ROTATION
{
m.rotate(Eigen::AngleAxisd(m_rotation(2), Vec3d::UnitZ()));
m.rotate(Eigen::AngleAxisd(m_rotation(1), Vec3d::UnitY()));
m.rotate(Eigen::AngleAxisd(m_rotation(0), Vec3d::UnitX()));
}
#else
m.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ()));
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
if (!dont_scale)
m.scale(scaling_factor);

View file

@ -133,6 +133,7 @@ public:
void cut(coordf_t z, Model* model) const;
void split(ModelObjectPtrs* new_objects);
// Called by Print::validate() from the UI thread.
void check_instances_print_volume_state(const BoundingBoxf3& print_volume);
// Print object statistics to console.
@ -249,11 +250,16 @@ public:
#if ENABLE_MODELINSTANCE_3D_OFFSET
private:
Vec3d m_offset; // in unscaled coordinates
#if ENABLE_MODELINSTANCE_3D_ROTATION
Vec3d m_rotation; // Rotation around the three axes, in radians around mesh center point
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
public:
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if !ENABLE_MODELINSTANCE_3D_ROTATION
double rotation; // Rotation around the Z axis, in radians around mesh center point
#endif // !ENABLE_MODELINSTANCE_3D_ROTATION
double scaling_factor;
#if !ENABLE_MODELINSTANCE_3D_OFFSET
Vec2d offset; // in unscaled coordinates
@ -272,6 +278,14 @@ public:
void set_offset(Axis axis, double offset) { m_offset(axis) = offset; }
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
const Vec3d& get_rotation() const { return m_rotation; }
double get_rotation(Axis axis) const { return m_rotation(axis); }
void set_rotation(const Vec3d& rotation);
void set_rotation(Axis axis, double rotation);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
// To be called on an external mesh
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
// Calculate a bounding box of a transformed mesh. To be called on an external mesh.
@ -292,9 +306,15 @@ private:
ModelObject* object;
#if ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
ModelInstance(ModelObject *object) : m_rotation(Vec3d::Zero()), scaling_factor(1), m_offset(Vec3d::Zero()), object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
m_rotation(other.m_rotation), scaling_factor(other.scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {}
#else
ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), m_offset(Vec3d::Zero()), object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
rotation(other.rotation), scaling_factor(other.scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#else
ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), offset(Vec2d::Zero()), object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :

View file

@ -292,59 +292,59 @@ protected:
using Distance = TCoord<PointImpl>;
using Pile = sl::Shapes<PolygonImpl>;
Packer pck_;
PConfig pconf_; // Placement configuration
double bin_area_;
SpatIndex rtree_;
SpatIndex smallsrtree_;
double norm_;
Pile merged_pile_;
Box pilebb_;
ItemGroup remaining_;
ItemGroup items_;
Packer m_pck;
PConfig m_pconf; // Placement configuration
double m_bin_area;
SpatIndex m_rtree;
SpatIndex m_smallsrtree;
double m_norm;
Pile m_merged_pile;
Box m_pilebb;
ItemGroup m_remaining;
ItemGroup m_items;
public:
_ArrBase(const TBin& bin, Distance dist,
std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcond):
pck_(bin, dist), bin_area_(sl::area(bin)),
norm_(std::sqrt(sl::area(bin)))
m_pck(bin, dist), m_bin_area(sl::area(bin)),
m_norm(std::sqrt(sl::area(bin)))
{
fillConfig(pconf_);
fillConfig(m_pconf);
pconf_.before_packing =
m_pconf.before_packing =
[this](const Pile& merged_pile, // merged pile
const ItemGroup& items, // packed items
const ItemGroup& remaining) // future items to be packed
{
items_ = items;
merged_pile_ = merged_pile;
remaining_ = remaining;
m_items = items;
m_merged_pile = merged_pile;
m_remaining = remaining;
pilebb_ = sl::boundingBox(merged_pile);
m_pilebb = sl::boundingBox(merged_pile);
rtree_.clear();
smallsrtree_.clear();
m_rtree.clear();
m_smallsrtree.clear();
// We will treat big items (compared to the print bed) differently
auto isBig = [this](double a) {
return a/bin_area_ > BIG_ITEM_TRESHOLD ;
return a/m_bin_area > BIG_ITEM_TRESHOLD ;
};
for(unsigned idx = 0; idx < items.size(); ++idx) {
Item& itm = items[idx];
if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx});
smallsrtree_.insert({itm.boundingBox(), idx});
if(isBig(itm.area())) m_rtree.insert({itm.boundingBox(), idx});
m_smallsrtree.insert({itm.boundingBox(), idx});
}
};
pck_.progressIndicator(progressind);
pck_.stopCondition(stopcond);
m_pck.progressIndicator(progressind);
m_pck.stopCondition(stopcond);
}
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
rtree_.clear();
return pck_.executeIndexed(std::forward<Args>(args)...);
m_rtree.clear();
return m_pck.executeIndexed(std::forward<Args>(args)...);
}
};
@ -358,18 +358,18 @@ public:
_ArrBase<Box>(bin, dist, progressind, stopcond)
{
pconf_.object_function = [this, bin] (const Item &item) {
m_pconf.object_function = [this, bin] (const Item &item) {
auto result = objfunc(bin.center(),
merged_pile_,
pilebb_,
items_,
m_merged_pile,
m_pilebb,
m_items,
item,
bin_area_,
norm_,
rtree_,
smallsrtree_,
remaining_);
m_bin_area,
m_norm,
m_rtree,
m_smallsrtree,
m_remaining);
double score = std::get<0>(result);
auto& fullbb = std::get<1>(result);
@ -381,7 +381,7 @@ public:
return score;
};
pck_.configure(pconf_);
m_pck.configure(m_pconf);
}
};
@ -396,27 +396,27 @@ public:
std::function<bool(void)> stopcond):
_ArrBase<lnCircle>(bin, dist, progressind, stopcond) {
pconf_.object_function = [this, &bin] (const Item &item) {
m_pconf.object_function = [this, &bin] (const Item &item) {
auto result = objfunc(bin.center(),
merged_pile_,
pilebb_,
items_,
m_merged_pile,
m_pilebb,
m_items,
item,
bin_area_,
norm_,
rtree_,
smallsrtree_,
remaining_);
m_bin_area,
m_norm,
m_rtree,
m_smallsrtree,
m_remaining);
double score = std::get<0>(result);
auto isBig = [this](const Item& itm) {
return itm.area()/bin_area_ > BIG_ITEM_TRESHOLD ;
return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ;
};
if(isBig(item)) {
auto mp = merged_pile_;
auto mp = m_merged_pile;
mp.push_back(item.transformedShape());
auto chull = sl::convexHull(mp);
double miss = Placer::overfit(chull, bin);
@ -427,7 +427,7 @@ public:
return score;
};
pck_.configure(pconf_);
m_pck.configure(m_pconf);
}
};
@ -439,25 +439,25 @@ public:
std::function<bool(void)> stopcond):
_ArrBase<PolygonImpl>(bin, dist, progressind, stopcond)
{
pconf_.object_function = [this, &bin] (const Item &item) {
m_pconf.object_function = [this, &bin] (const Item &item) {
auto binbb = sl::boundingBox(bin);
auto result = objfunc(binbb.center(),
merged_pile_,
pilebb_,
items_,
m_merged_pile,
m_pilebb,
m_items,
item,
bin_area_,
norm_,
rtree_,
smallsrtree_,
remaining_);
m_bin_area,
m_norm,
m_rtree,
m_smallsrtree,
m_remaining);
double score = std::get<0>(result);
return score;
};
pck_.configure(pconf_);
m_pck.configure(m_pconf);
}
};
@ -469,22 +469,22 @@ public:
std::function<bool(void)> stopcond):
_ArrBase<Box>(Box(0, 0), dist, progressind, stopcond)
{
this->pconf_.object_function = [this] (const Item &item) {
this->m_pconf.object_function = [this] (const Item &item) {
auto result = objfunc({0, 0},
merged_pile_,
pilebb_,
items_,
m_merged_pile,
m_pilebb,
m_items,
item,
0,
norm_,
rtree_,
smallsrtree_,
remaining_);
m_norm,
m_rtree,
m_smallsrtree,
m_remaining);
return std::get<0>(result);
};
this->pck_.configure(pconf_);
this->m_pck.configure(m_pconf);
}
};
@ -527,14 +527,19 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
// Invalid geometries would throw exceptions when arranging
if(item.vertexCount() > 3) {
item.rotation(objinst->rotation);
item.translation( {
#if ENABLE_MODELINSTANCE_3D_OFFSET
ClipperLib::cInt(objinst->get_offset(X) / SCALING_FACTOR),
ClipperLib::cInt(objinst->get_offset(Y) / SCALING_FACTOR)
#if ENABLE_MODELINSTANCE_3D_ROTATION
// CHECK_ME -> is the following correct or it should take in account all three rotations ?
item.rotation(objinst->get_rotation(Z));
#else
ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR),
ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR)
item.rotation(objinst->rotation);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
item.translation({
#if ENABLE_MODELINSTANCE_3D_OFFSET
ClipperLib::cInt(objinst->get_offset(X)/SCALING_FACTOR),
ClipperLib::cInt(objinst->get_offset(Y)/SCALING_FACTOR)
#else
ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR),
ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR)
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
});
ret.emplace_back(objinst, item);
@ -668,18 +673,25 @@ void applyResult(
// Get the model instance from the shapemap using the index
ModelInstance *inst_ptr = shapemap[idx].first;
// Get the tranformation data from the item object and scale it
// Get the transformation data from the item object and scale it
// appropriately
auto off = item.translation();
Radians rot = item.rotation();
#if ENABLE_MODELINSTANCE_3D_OFFSET
Vec3d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR, 0.0);
Vec3d foff(off.X*SCALING_FACTOR + batch_offset,
off.Y*SCALING_FACTOR,
0.0);
#else
Vec2d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR);
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
// write the tranformation data into the model instance
// write the transformation data into the model instance
#if ENABLE_MODELINSTANCE_3D_ROTATION
// CHECK_ME -> Is the following correct ?
inst_ptr->set_rotation(Vec3d(0.0, 0.0, rot));
#else
inst_ptr->rotation = rot;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#if ENABLE_MODELINSTANCE_3D_OFFSET
inst_ptr->set_offset(foff);
#else
@ -695,7 +707,7 @@ void applyResult(
* The arrangement considers multiple bins (aka. print beds) for placing all
* the items provided in the model argument. If the items don't fit on one
* print bed, the remaining will be placed onto newly created print beds.
* The first_bin_only parameter, if set to true, disables this behaviour and
* The first_bin_only parameter, if set to true, disables this behavior and
* makes sure that only one print bed is filled and the remaining items will be
* untouched. When set to false, the items which could not fit onto the
* print bed will be placed next to the print bed so the user should see a
@ -741,6 +753,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
IndexedPackGroup result;
// If there is no hint about the shape, we will try to guess
if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed);
BoundingBox bbb(bed);

View file

@ -37,30 +37,30 @@ typedef std::vector<PerimeterGeneratorLoop> PerimeterGeneratorLoops;
class PerimeterGenerator {
public:
// Inputs:
const SurfaceCollection* slices;
const ExPolygonCollection* lower_slices;
double layer_height;
int layer_id;
Flow perimeter_flow;
Flow ext_perimeter_flow;
Flow overhang_flow;
Flow solid_infill_flow;
PrintRegionConfig* config;
PrintObjectConfig* object_config;
PrintConfig* print_config;
const SurfaceCollection *slices;
const ExPolygonCollection *lower_slices;
double layer_height;
int layer_id;
Flow perimeter_flow;
Flow ext_perimeter_flow;
Flow overhang_flow;
Flow solid_infill_flow;
const PrintRegionConfig *config;
const PrintObjectConfig *object_config;
const PrintConfig *print_config;
// Outputs:
ExtrusionEntityCollection* loops;
ExtrusionEntityCollection* gap_fill;
SurfaceCollection* fill_surfaces;
ExtrusionEntityCollection *loops;
ExtrusionEntityCollection *gap_fill;
SurfaceCollection *fill_surfaces;
PerimeterGenerator(
// Input:
const SurfaceCollection* slices,
double layer_height,
Flow flow,
PrintRegionConfig* config,
PrintObjectConfig* object_config,
PrintConfig* print_config,
const PrintRegionConfig* config,
const PrintObjectConfig* object_config,
const PrintConfig* print_config,
// Output:
// Loops with the external thin walls
ExtrusionEntityCollection* loops,
@ -78,15 +78,13 @@ public:
void process();
private:
double _ext_mm3_per_mm;
double _mm3_per_mm;
double _mm3_per_mm_overhang;
Polygons _lower_slices_p;
double _ext_mm3_per_mm;
double _mm3_per_mm;
double _mm3_per_mm_overhang;
Polygons _lower_slices_p;
ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops,
ThickPolylines &thin_walls) const;
ExtrusionEntityCollection _variable_width
(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const;
ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const;
ExtrusionEntityCollection _variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const;
};
}

View file

@ -69,7 +69,7 @@ PlaceholderParser::PlaceholderParser()
this->update_timestamp();
}
void PlaceholderParser::update_timestamp()
void PlaceholderParser::update_timestamp(DynamicConfig &config)
{
time_t rawtime;
time(&rawtime);
@ -84,14 +84,14 @@ void PlaceholderParser::update_timestamp()
ss << std::setw(2) << std::setfill('0') << timeinfo->tm_hour;
ss << std::setw(2) << std::setfill('0') << timeinfo->tm_min;
ss << std::setw(2) << std::setfill('0') << timeinfo->tm_sec;
this->set("timestamp", ss.str());
config.set_key_value("timestamp", new ConfigOptionString(ss.str()));
}
this->set("year", 1900 + timeinfo->tm_year);
this->set("month", 1 + timeinfo->tm_mon);
this->set("day", timeinfo->tm_mday);
this->set("hour", timeinfo->tm_hour);
this->set("minute", timeinfo->tm_min);
this->set("second", timeinfo->tm_sec);
config.set_key_value("year", new ConfigOptionInt(1900 + timeinfo->tm_year));
config.set_key_value("month", new ConfigOptionInt(1 + timeinfo->tm_mon));
config.set_key_value("day", new ConfigOptionInt(timeinfo->tm_mday));
config.set_key_value("hour", new ConfigOptionInt(timeinfo->tm_hour));
config.set_key_value("minute", new ConfigOptionInt(timeinfo->tm_min));
config.set_key_value("second", new ConfigOptionInt(timeinfo->tm_sec));
}
// Scalar configuration values are stored into m_single,

View file

@ -14,7 +14,6 @@ class PlaceholderParser
public:
PlaceholderParser();
void update_timestamp();
void apply_config(const DynamicPrintConfig &config);
void apply_env_variables();
@ -37,6 +36,11 @@ public:
// Throws std::runtime_error on syntax or runtime error.
static bool evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override = nullptr);
// Update timestamp, year, month, day, hour, minute, second variables at the provided config.
static void update_timestamp(DynamicConfig &config);
// Update timestamp, year, month, day, hour, minute, second variables at m_config.
void update_timestamp() { update_timestamp(m_config); }
private:
DynamicConfig m_config;
};

View file

@ -47,7 +47,7 @@ Polygon::split_at_vertex(const Point &point) const
for (const Point &pt : this->points)
if (pt == point)
return this->split_at_index(&pt - &this->points.front());
CONFESS("Point not found");
throw std::invalid_argument("Point not found");
return Polyline();
}

View file

@ -18,7 +18,8 @@ Polyline::operator Polylines() const
Polyline::operator Line() const
{
if (this->points.size() > 2) CONFESS("Can't convert polyline with more than two points to a line");
if (this->points.size() > 2)
throw std::invalid_argument("Can't convert polyline with more than two points to a line");
return Line(this->points.front(), this->points.back());
}

View file

@ -77,7 +77,8 @@ Polylines PolylineCollection::_chained_path_from(
Point PolylineCollection::leftmost_point(const Polylines &polylines)
{
if (polylines.empty()) CONFESS("leftmost_point() called on empty PolylineCollection");
if (polylines.empty())
throw std::invalid_argument("leftmost_point() called on empty PolylineCollection");
Polylines::const_iterator it = polylines.begin();
Point p = it->leftmost_point();
for (++ it; it != polylines.end(); ++it) {

File diff suppressed because it is too large Load diff

View file

@ -2,9 +2,11 @@
#define slic3r_Print_hpp_
#include "libslic3r.h"
#include <atomic>
#include <set>
#include <vector>
#include <string>
#include <functional>
#include "BoundingBox.hpp"
#include "Flow.hpp"
#include "PrintConfig.hpp"
@ -17,56 +19,110 @@
#include "GCode/WipeTower.hpp"
#include "tbb/atomic.h"
// tbb/mutex.h includes Windows, which in turn defines min/max macros. Convince Windows.h to not define these min/max macros.
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "tbb/mutex.h"
namespace Slic3r {
class Print;
class PrintObject;
class ModelObject;
class GCode;
class GCodePreviewData;
// Print step IDs for keeping track of the print state.
enum PrintStep {
psSkirt, psBrim, psWipeTower, psCount,
psSkirt, psBrim, psWipeTower, psGCodeExport, psCount,
};
enum PrintObjectStep {
posSlice, posPerimeters, posPrepareInfill,
posInfill, posSupportMaterial, posCount,
};
class CanceledException : public std::exception {
public:
const char* what() const throw() { return "Background processing has been canceled"; }
};
// To be instantiated over PrintStep or PrintObjectStep enums.
template <class StepType, size_t COUNT>
class PrintState
{
public:
PrintState() { memset(state, 0, sizeof(state)); }
PrintState() { for (size_t i = 0; i < COUNT; ++ i) m_state[i].store(INVALID, std::memory_order_relaxed); }
enum State {
INVALID,
STARTED,
DONE,
};
State state[COUNT];
bool is_started(StepType step) const { return this->state[step] == STARTED; }
bool is_done(StepType step) const { return this->state[step] == DONE; }
void set_started(StepType step) { this->state[step] = STARTED; }
void set_done(StepType step) { this->state[step] = DONE; }
bool invalidate(StepType step) {
bool invalidated = this->state[step] != INVALID;
this->state[step] = INVALID;
// With full memory barrier.
bool is_done(StepType step) const { return m_state[step] == DONE; }
// Set the step as started. Block on mutex while the Print / PrintObject / PrintRegion objects are being
// modified by the UI thread.
// This is necessary to block until the Print::apply_config() updates its state, which may
// influence the processing step being entered.
void set_started(StepType step, tbb::mutex &mtx) {
mtx.lock();
m_state[step].store(STARTED, std::memory_order_relaxed);
mtx.unlock();
}
// Set the step as done. Block on mutex while the Print / PrintObject / PrintRegion objects are being
// modified by the UI thread.
void set_done(StepType step, tbb::mutex &mtx) {
mtx.lock();
m_state[step].store(DONE, std::memory_order_relaxed);
mtx.unlock();
}
// Make the step invalid.
// The provided mutex should be locked at this point, guarding access to m_state.
// In case the step has already been entered or finished, cancel the background
// processing by calling the cancel callback.
template<typename CancelationCallback>
bool invalidate(StepType step, tbb::mutex &mtx, CancelationCallback &cancel) {
bool invalidated = m_state[step].load(std::memory_order_relaxed) != INVALID;
if (invalidated) {
#if 0
if (mtx.state != mtx.HELD) {
printf("Not held!\n");
}
#endif
mtx.unlock();
cancel();
mtx.lock();
}
return invalidated;
}
bool invalidate_all() {
// Make all steps invalid.
// The provided mutex should be locked at this point, guarding access to m_state.
// In case any step has already been entered or finished, cancel the background
// processing by calling the cancel callback.
template<typename CancelationCallback>
bool invalidate_all(tbb::mutex &mtx, CancelationCallback &cancel) {
bool invalidated = false;
for (size_t i = 0; i < COUNT; ++ i)
if (this->state[i] != INVALID) {
invalidated = true;
break;
if (m_state[i].load(std::memory_order_relaxed) != INVALID) {
if (! invalidated) {
mtx.unlock();
cancel();
mtx.lock();
invalidated = true;
}
m_state[i].store(INVALID, std::memory_order_relaxed);
}
memset(state, 0, sizeof(state));
return invalidated;
}
private:
std::atomic<State> m_state[COUNT];
};
// A PrintRegion object represents a group of volumes to print
@ -75,20 +131,27 @@ class PrintRegion
{
friend class Print;
// Methods NOT modifying the PrintRegion's state:
public:
PrintRegionConfig config;
Print* print() { return this->_print; }
Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
const Print* print() const { return m_print; }
const PrintRegionConfig& config() const { return m_config; }
Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
// Average diameter of nozzles participating on extruding this region.
coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const;
coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const;
// Average diameter of nozzles participating on extruding this region.
coordf_t bridging_height_avg(const PrintConfig &print_config) const;
// Methods modifying the PrintRegion's state:
public:
Print* print() { return m_print; }
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); }
private:
Print* _print;
Print *m_print;
PrintRegionConfig m_config;
PrintRegion(Print* print) : _print(print) {}
PrintRegion(Print* print) : m_print(print) {}
PrintRegion(Print* print, const PrintRegionConfig &config) : m_print(print), m_config(config) {}
~PrintRegion() {}
};
@ -104,43 +167,35 @@ class PrintObject
public:
// vector of (vectors of volume ids), indexed by region_id
std::vector<std::vector<int>> region_volumes;
PrintObjectConfig config;
t_layer_height_ranges layer_height_ranges;
t_layer_height_ranges layer_height_ranges;
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
// The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
// layer_height_profile must not be set by the background thread.
std::vector<coordf_t> layer_height_profile;
std::vector<coordf_t> layer_height_profile;
// There is a layer_height_profile at both PrintObject and ModelObject. The layer_height_profile at the ModelObject
// is used for interactive editing and for loading / storing into a project file (AMF file as of today).
// This flag indicates that the layer_height_profile at the UI has been updated, therefore the backend needs to get it.
// This flag is necessary as we cannot safely clear the layer_height_profile if the background calculation is running.
bool layer_height_profile_valid;
bool layer_height_profile_valid;
// this is set to true when LayerRegion->slices is split in top/internal/bottom
// so that next call to make_perimeters() performs a union() before computing loops
bool typed_slices;
bool typed_slices;
Vec3crd size; // XYZ in scaled coordinates
Vec3crd size; // XYZ in scaled coordinates
// scaled coordinates to add to copies (to compensate for the alignment
// operated when creating the object but still preserving a coherent API
// for external callers)
Point _copies_shift;
Print* print() { return m_print; }
const Print* print() const { return m_print; }
ModelObject* model_object() { return m_model_object; }
const ModelObject* model_object() const { return m_model_object; }
const PrintObjectConfig& config() const { return m_config; }
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); }
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); }
const LayerPtrs& layers() const { return m_layers; }
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
// Slic3r::Point objects in scaled G-code coordinates in our coordinates
Points _shifted_copies;
LayerPtrs layers;
SupportLayerPtrs support_layers;
PrintState<PrintObjectStep, posCount> state;
Print* print() { return this->_print; }
const Print* print() const { return this->_print; }
ModelObject* model_object() { return this->_model_object; }
const ModelObject* model_object() const { return this->_model_object; }
const Points& copies() const { return this->_copies; }
const Points& copies() const { return m_copies; }
bool add_copy(const Vec2d &point);
bool delete_last_copy();
bool delete_all_copies() { return this->set_copies(Points()); }
@ -159,24 +214,26 @@ public:
// this value is not supposed to be compared with Layer::id
// since they have different semantics.
size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); }
size_t layer_count() const { return this->layers.size(); }
size_t layer_count() const { return m_layers.size(); }
void clear_layers();
Layer* get_layer(int idx) { return this->layers.at(idx); }
const Layer* get_layer(int idx) const { return this->layers.at(idx); }
Layer* get_layer(int idx) { return m_layers[idx]; }
const Layer* get_layer(int idx) const { return m_layers[idx]; }
// print_z: top of the layer; slice_z: center of the layer.
Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
size_t support_layer_count() const { return this->support_layers.size(); }
size_t support_layer_count() const { return m_support_layers.size(); }
void clear_support_layers();
SupportLayer* get_support_layer(int idx) { return this->support_layers.at(idx); }
SupportLayer* get_support_layer(int idx) { return m_support_layers[idx]; }
SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z);
SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
void delete_support_layer(int idx);
// methods for handling state
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
bool invalidate_step(PrintObjectStep step);
bool invalidate_all_steps() { return this->state.invalidate_all(); }
bool invalidate_all_steps();
bool is_step_done(PrintObjectStep step) const { return m_state.is_done(step); }
// To be used over the layer_height_profile of both the PrintObject and ModelObject
// to initialize the height profile with the height ranges.
@ -196,168 +253,282 @@ public:
// (layer height, first layer height, raft settings, print nozzle diameter etc).
SlicingParameters slicing_parameters() const;
void _slice();
std::string _fix_slicing_errors();
void _simplify_slices(double distance);
void _prepare_infill();
bool has_support_material() const;
void detect_surfaces_type();
void process_external_surfaces();
void discover_vertical_shells();
void bridge_over_infill();
void _make_perimeters();
void _infill();
void clip_fill_surfaces();
void discover_horizontal_shells();
void combine_infill();
void _generate_support_material();
bool is_printable() const { return !this->_shifted_copies.empty(); }
// Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t
void slice();
// Helpers to slice support enforcer / blocker meshes by the support generator.
std::vector<ExPolygons> slice_support_enforcers() const;
std::vector<ExPolygons> slice_support_blockers() const;
private:
Print* _print;
ModelObject* _model_object;
Points _copies; // Slic3r::Point objects in scaled G-code coordinates
void make_perimeters();
void prepare_infill();
void infill();
void generate_support_material();
void _slice();
std::string _fix_slicing_errors();
void _simplify_slices(double distance);
void _make_perimeters();
bool has_support_material() const;
void detect_surfaces_type();
void process_external_surfaces();
void discover_vertical_shells();
void bridge_over_infill();
void clip_fill_surfaces();
void discover_horizontal_shells();
void combine_infill();
void _generate_support_material();
bool is_printable() const { return ! m_copies.empty(); }
Print *m_print;
ModelObject *m_model_object;
PrintObjectConfig m_config;
// Slic3r::Point objects in scaled G-code coordinates
Points m_copies;
// scaled coordinates to add to copies (to compensate for the alignment
// operated when creating the object but still preserving a coherent API
// for external callers)
Point m_copies_shift;
LayerPtrs m_layers;
SupportLayerPtrs m_support_layers;
PrintState<PrintObjectStep, posCount> m_state;
// TODO: call model_object->get_bounding_box() instead of accepting
// parameter
PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox);
~PrintObject() {}
void set_started(PrintObjectStep step);
void set_done(PrintObjectStep step);
std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
std::vector<ExPolygons> _slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
};
struct WipeTowerData
{
// Following section will be consumed by the GCodeGenerator.
// Tool ordering of a non-sequential print has to be known to calculate the wipe tower.
// Cache it here, so it does not need to be recalculated during the G-code generation.
ToolOrdering tool_ordering;
// Cache of tool changes per print layer.
std::unique_ptr<WipeTower::ToolChangeResult> priming;
std::vector<std::vector<WipeTower::ToolChangeResult>> tool_changes;
std::unique_ptr<WipeTower::ToolChangeResult> final_purge;
std::vector<float> used_filament;
int number_of_toolchanges;
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
float depth;
void clear() {
tool_ordering.clear();
priming.reset(nullptr);
tool_changes.clear();
final_purge.reset(nullptr);
used_filament.clear();
number_of_toolchanges = -1;
depth = 0.f;
}
};
struct PrintStatistics
{
PrintStatistics() { clear(); }
std::string estimated_normal_print_time;
std::string estimated_silent_print_time;
double total_used_filament;
double total_extruded_volume;
double total_cost;
double total_weight;
double total_wipe_tower_cost;
double total_wipe_tower_filament;
std::map<size_t, float> filament_stats;
void clear() {
estimated_normal_print_time.clear();
estimated_silent_print_time.clear();
total_used_filament = 0.;
total_extruded_volume = 0.;
total_cost = 0.;
total_weight = 0.;
total_wipe_tower_cost = 0.;
total_wipe_tower_filament = 0.;
filament_stats.clear();
}
};
typedef std::vector<PrintObject*> PrintObjectPtrs;
typedef std::vector<PrintRegion*> PrintRegionPtrs;
class ProgressIndicator;
using ProgressIndicatorPtr = std::shared_ptr<ProgressIndicator>;
// The complete print tray with possibly multiple objects.
class Print
{
public:
PrintConfig config;
PrintObjectConfig default_object_config;
PrintRegionConfig default_region_config;
PrintObjectPtrs objects;
PrintRegionPtrs regions;
PlaceholderParser placeholder_parser;
// TODO: status_cb
ProgressIndicatorPtr progressindicator;
std::string estimated_normal_print_time;
std::string estimated_silent_print_time;
double total_used_filament, total_extruded_volume, total_cost, total_weight, total_wipe_tower_cost, total_wipe_tower_filament;
std::map<size_t, float> filament_stats;
PrintState<PrintStep, psCount> state;
// ordered collections of extrusion paths to build skirt loops and brim
ExtrusionEntityCollection skirt, brim;
Print() : total_used_filament(0), total_extruded_volume(0) { restart(); }
Print() { restart(); }
~Print() { clear_objects(); }
// methods for handling objects
void clear_objects();
PrintObject* get_object(size_t idx) { return objects.at(idx); }
const PrintObject* get_object(size_t idx) const { return objects.at(idx); }
void delete_object(size_t idx);
void reload_object(size_t idx);
bool reload_model_instances();
// Methods, which change the state of Print / PrintObject / PrintRegion.
// The following methods are synchronized with process() and export_gcode(),
// so that process() and export_gcode() may be called from a background thread.
// In case the following methods need to modify data processed by process() or export_gcode(),
// a cancellation callback is executed to stop the background processing before the operation.
void clear_objects();
void delete_object(size_t idx);
void reload_object(size_t idx);
bool reload_model_instances();
void add_model_object(ModelObject* model_object, int idx = -1);
bool apply_config(DynamicPrintConfig config);
void process();
void export_gcode(const std::string &path_template, GCodePreviewData *preview_data);
// SLA export, temporary.
void export_png(const std::string &dirpath);
PrintObjectPtrs get_printable_objects() const;
// methods for handling regions
PrintRegion* get_region(size_t idx) { return regions.at(idx); }
const PrintRegion* get_region(size_t idx) const { return regions.at(idx); }
PrintRegion* add_region();
// methods for handling state
bool invalidate_step(PrintStep step);
bool invalidate_all_steps() { return this->state.invalidate_all(); }
bool step_done(PrintObjectStep step) const;
void add_model_object(ModelObject* model_object, int idx = -1);
bool apply_config(DynamicPrintConfig config);
float get_wipe_tower_depth() const { return m_wipe_tower_depth; }
bool has_infinite_skirt() const;
bool has_skirt() const;
bool is_step_done(PrintStep step) const { return m_state.is_done(step); }
bool is_step_done(PrintObjectStep step) const;
bool has_infinite_skirt() const;
bool has_skirt() const;
PrintObjectPtrs get_printable_objects() const;
float get_wipe_tower_depth() const { return m_wipe_tower_data.depth; }
// Returns an empty string if valid, otherwise returns an error message.
std::string validate() const;
BoundingBox bounding_box() const;
BoundingBox total_bounding_box() const;
double skirt_first_layer_height() const;
Flow brim_flow() const;
Flow skirt_flow() const;
std::string validate() const;
BoundingBox bounding_box() const;
BoundingBox total_bounding_box() const;
double skirt_first_layer_height() const;
Flow brim_flow() const;
Flow skirt_flow() const;
std::vector<unsigned int> object_extruders() const;
std::vector<unsigned int> support_material_extruders() const;
std::vector<unsigned int> extruders() const;
void _simplify_slices(double distance);
double max_allowed_layer_height() const;
bool has_support_material() const;
void auto_assign_extruders(ModelObject* model_object) const;
double max_allowed_layer_height() const;
bool has_support_material() const;
// Make sure the background processing has no access to this model_object during this call!
void auto_assign_extruders(ModelObject* model_object) const;
const PrintConfig& config() const { return m_config; }
const PrintObjectConfig& default_object_config() const { return m_default_object_config; }
const PrintRegionConfig& default_region_config() const { return m_default_region_config; }
const PrintObjectPtrs& objects() const { return m_objects; }
const PrintObject* get_object(int idx) const { return m_objects[idx]; }
const PrintRegionPtrs& regions() const { return m_regions; }
const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; }
// Returns extruder this eec should be printed with, according to PrintRegion config:
static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region);
void _make_skirt();
void _make_brim();
const ExtrusionEntityCollection& skirt() const { return m_skirt; }
const ExtrusionEntityCollection& brim() const { return m_brim; }
const PrintStatistics& print_statistics() const { return m_print_statistics; }
// Wipe tower support.
bool has_wipe_tower() const;
void _clear_wipe_tower();
void _make_wipe_tower();
// Tool ordering of a non-sequential print has to be known to calculate the wipe tower.
// Cache it here, so it does not need to be recalculated during the G-code generation.
ToolOrdering m_tool_ordering;
// Cache of tool changes per print layer.
std::unique_ptr<WipeTower::ToolChangeResult> m_wipe_tower_priming;
std::vector<std::vector<WipeTower::ToolChangeResult>> m_wipe_tower_tool_changes;
std::unique_ptr<WipeTower::ToolChangeResult> m_wipe_tower_final_purge;
std::vector<float> m_wipe_tower_used_filament;
int m_wipe_tower_number_of_toolchanges = -1;
bool has_wipe_tower() const;
const WipeTowerData& wipe_tower_data() const { return m_wipe_tower_data; }
std::string output_filename();
std::string output_filepath(const std::string &path);
std::string output_filename() const;
std::string output_filepath(const std::string &path) const;
// Calls a registered callback to update the status.
void set_status(int percent, const std::string &message);
// Cancel the running computation. Stop execution of all the background threads.
void cancel() { m_canceled = true; }
// Cancel the running computation. Stop execution of all the background threads.
void restart() { m_canceled = false; }
typedef std::function<void(int, const std::string&)> status_callback_type;
// Default status console print out in the form of percent => message.
void set_status_default() { m_status_callback = nullptr; }
// No status output or callback whatsoever, useful mostly for automatic tests.
void set_status_silent() { m_status_callback = [](int, const std::string&){}; }
// Register a custom status callback.
void set_status_callback(status_callback_type cb) { m_status_callback = cb; }
// Calls a registered callback to update the status, or print out the default message.
void set_status(int percent, const std::string &message) {
if (m_status_callback) m_status_callback(percent, message);
else printf("%d => %s\n", percent, message.c_str());
}
typedef std::function<void()> cancel_callback_type;
// Various methods will call this callback to stop the background processing (the Print::process() call)
// in case a successive change of the Print / PrintObject / PrintRegion instances changed
// the state of the finished or running calculations.
void set_cancel_callback(cancel_callback_type cancel_callback) { m_cancel_callback = cancel_callback; }
// Has the calculation been canceled?
bool canceled() { return m_canceled; }
bool canceled() const { return m_canceled; }
// Cancel the running computation. Stop execution of all the background threads.
void cancel() { m_canceled = true; }
// Cancel the running computation. Stop execution of all the background threads.
void restart() { m_canceled = false; }
void print_to_png(std::string dirpath);
// Accessed by SupportMaterial
const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; }
protected:
void set_started(PrintStep step) { m_state.set_started(step, m_mutex); throw_if_canceled(); }
void set_done(PrintStep step) { m_state.set_done(step, m_mutex); throw_if_canceled(); }
bool invalidate_step(PrintStep step);
bool invalidate_all_steps() { return m_state.invalidate_all(m_mutex, m_cancel_callback); }
// methods for handling regions
PrintRegion* get_region(size_t idx) { return m_regions[idx]; }
PrintRegion* add_region();
PrintRegion* add_region(const PrintRegionConfig &config);
private:
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
// If the background processing stop was requested, throw CanceledException.
// To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.
void throw_if_canceled() const { if (m_canceled) throw CanceledException(); }
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
float m_wipe_tower_depth = 0.f;
void _make_skirt();
void _make_brim();
void _make_wipe_tower();
void _simplify_slices(double distance);
PrintState<PrintStep, psCount> m_state;
// Mutex used for synchronization of the worker thread with the UI thread:
// The mutex will be used to guard the worker thread against entering a stage
// while the data influencing the stage is modified.
mutable tbb::mutex m_mutex;
// Has the calculation been canceled?
tbb::atomic<bool> m_canceled;
tbb::atomic<bool> m_canceled;
// Callback to be evoked regularly to update state of the UI thread.
status_callback_type m_status_callback;
// Callback to be evoked to stop the background processing before a state is updated.
cancel_callback_type m_cancel_callback = [](){};
PrintConfig m_config;
PrintObjectConfig m_default_object_config;
PrintRegionConfig m_default_region_config;
PrintObjectPtrs m_objects;
PrintRegionPtrs m_regions;
PlaceholderParser m_placeholder_parser;
// Ordered collections of extrusion paths to build skirt loops and brim.
ExtrusionEntityCollection m_skirt;
ExtrusionEntityCollection m_brim;
// Following section will be consumed by the GCodeGenerator.
WipeTowerData m_wipe_tower_data;
// Estimated print time, filament consumed.
PrintStatistics m_print_statistics;
// To allow GCode to set the Print's GCodeExport step status.
friend class GCode;
// Allow PrintObject to access m_mutex and m_cancel_callback.
friend class PrintObject;
};
#define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator)
#define FOREACH_REGION(print, region) FOREACH_BASE(PrintRegionPtrs, (print)->regions, region)
#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->objects, object)
#define FOREACH_LAYER(object, layer) FOREACH_BASE(LayerPtrs, (object)->layers, layer)
#define FOREACH_LAYERREGION(layer, layerm) FOREACH_BASE(LayerRegionPtrs, (layer)->regions, layerm)
#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->m_objects, object)
#define FOREACH_LAYER(object, layer) FOREACH_BASE(LayerPtrs, (object)->m_layers, layer)
#define FOREACH_LAYERREGION(layer, layerm) FOREACH_BASE(LayerRegionPtrs, (layer)->m_regions, layerm)
}

View file

@ -161,7 +161,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Speed for printing bridges.");
def->sidetext = L("mm/s");
def->cli = "bridge-speed=f";
def->aliases.push_back("bridge_feed_rate");
def->aliases = { "bridge_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloat(60);
@ -274,7 +274,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Distance used for the auto-arrange feature of the plater.");
def->sidetext = L("mm");
def->cli = "duplicate-distance=f";
def->aliases.push_back("multiply_distance");
def->aliases = { "multiply_distance" };
def->min = 0;
def->default_value = new ConfigOptionFloat(6);
@ -335,7 +335,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Archimedean Chords"));
def->enum_labels.push_back(L("Octagram Spiral"));
// solid_fill_pattern is an obsolete equivalent to external_fill_pattern.
def->aliases.push_back("solid_fill_pattern");
def->aliases = { "solid_fill_pattern" };
def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear);
def = this->add("external_perimeter_extrusion_width", coFloatOrPercent);
@ -923,8 +923,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Speed for printing the internal fill. Set to zero for auto.");
def->sidetext = L("mm/s");
def->cli = "infill-speed=f";
def->aliases.push_back("print_feed_rate");
def->aliases.push_back("infill_feed_rate");
def->aliases = { "print_feed_rate", "infill_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloat(80);
@ -1272,7 +1271,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Extruders");
def->tooltip = L("The extruder to use when printing perimeters and brim. First extruder is 1.");
def->cli = "perimeter-extruder=i";
def->aliases.push_back("perimeters_extruder");
def->aliases = { "perimeters_extruder" };
def->min = 1;
def->default_value = new ConfigOptionInt(1);
@ -1285,7 +1284,7 @@ void PrintConfigDef::init_fff_params()
"If expressed as percentage (for example 200%) it will be computed over layer height.");
def->sidetext = L("mm or % (leave 0 for default)");
def->cli = "perimeter-extrusion-width=s";
def->aliases.push_back("perimeters_extrusion_width");
def->aliases = { "perimeters_extrusion_width" };
def->default_value = new ConfigOptionFloatOrPercent(0, false);
def = this->add("perimeter_speed", coFloat);
@ -1294,7 +1293,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Speed for perimeters (contours, aka vertical shells). Set to zero for auto.");
def->sidetext = L("mm/s");
def->cli = "perimeter-speed=f";
def->aliases.push_back("perimeter_feed_rate");
def->aliases = { "perimeter_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloat(60);
@ -1307,7 +1306,7 @@ void PrintConfigDef::init_fff_params()
"if the Extra Perimeters option is enabled.");
def->sidetext = L("(minimum)");
def->cli = "perimeters=i";
def->aliases.push_back("perimeter_offsets");
def->aliases = { "perimeter_offsets" };
def->min = 0;
def->default_value = new ConfigOptionInt(3);
@ -1635,7 +1634,7 @@ void PrintConfigDef::init_fff_params()
def->sidetext = L("mm/s or %");
def->cli = "solid-infill-speed=s";
def->ratio_over = "infill_speed";
def->aliases.push_back("solid_infill_feed_rate");
def->aliases = { "solid_infill_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloatOrPercent(20, false);
@ -1989,7 +1988,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Speed for travel moves (jumps between distant extrusion points).");
def->sidetext = L("mm/s");
def->cli = "travel-speed=f";
def->aliases.push_back("travel_feed_rate");
def->aliases = { "travel_feed_rate" };
def->min = 1;
def->default_value = new ConfigOptionFloat(130);

View file

@ -7,10 +7,6 @@
#include <fstream>
#include <sstream>
#include <wx/stdstream.h>
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
#include <boost/log/trivial.hpp>
#include "Rasterizer/Rasterizer.hpp"
@ -32,14 +28,14 @@ enum class FilePrinterFormat {
* different implementations of this class template for each supported format.
*
*/
template<FilePrinterFormat format>
template<FilePrinterFormat format, class LayerFormat = void>
class FilePrinter {
public:
void printConfig(const Print&);
void print_config(const Print&);
// Draw an ExPolygon which is a polygon inside a slice on the specified layer.
void drawPolygon(const ExPolygon& p, unsigned lyr);
void draw_polygon(const ExPolygon& p, unsigned lyr);
// Tell the printer how many layers should it consider.
void layers(unsigned layernum);
@ -51,32 +47,57 @@ public:
* specified layer number than an appropriate number of layers will be
* allocated in the printer.
*/
void beginLayer(unsigned layer);
void begin_layer(unsigned layer);
// Allocate a new layer on top of the last and switch to it.
void beginLayer();
void begin_layer();
/*
* Finish the selected layer. It means that no drawing is allowed on that
* layer anymore. This fact can be used to prepare the file system output
* data like png comprimation and so on.
*/
void finishLayer(unsigned layer);
void finish_layer(unsigned layer);
// Finish the top layer.
void finishLayer();
void finish_layer();
// Save all the layers into the file (or dir) specified in the path argument
void save(const std::string& path);
// Save only the selected layer to the file specified in path argument.
void saveLayer(unsigned lyr, const std::string& path);
void save_layer(unsigned lyr, const std::string& path);
};
template<class T = void> struct VeryFalse { static const bool value = false; };
// This has to be explicitly implemented in the gui layer or a default zlib
// based implementation is needed.
template<class Backend> class LayerWriter {
public:
LayerWriter(const std::string& /*zipfile_path*/) {
static_assert(VeryFalse<Backend>::value,
"No layer writer implementation provided!");
}
void next_entry(const std::string& /*fname*/) {}
std::string get_name() { return ""; }
bool is_ok() { return false; }
template<class T> LayerWriter& operator<<(const T& /*arg*/) {
return *this;
}
void close() {}
};
// Implementation for PNG raster output
// Be aware that if a large number of layers are allocated, it can very well
// exhaust the available memory especially on 32 bit platform.
template<> class FilePrinter<FilePrinterFormat::PNG> {
template<class LyrFormat> class FilePrinter<FilePrinterFormat::PNG, LyrFormat> {
struct Layer {
Raster first;
@ -91,22 +112,22 @@ template<> class FilePrinter<FilePrinterFormat::PNG> {
// We will save the compressed PNG data into stringstreams which can be done
// in parallel. Later we can write every layer to the disk sequentially.
std::vector<Layer> layers_rst_;
Raster::Resolution res_;
Raster::PixelDim pxdim_;
const Print *print_ = nullptr;
double exp_time_s_ = .0, exp_time_first_s_ = .0;
std::vector<Layer> m_layers_rst;
Raster::Resolution m_res;
Raster::PixelDim m_pxdim;
const Print *m_print = nullptr;
double m_exp_time_s = .0, m_exp_time_first_s = .0;
std::string createIniContent(const std::string& projectname) {
double layer_height = print_?
print_->default_object_config.layer_height.getFloat() :
double layer_height = m_print?
m_print->default_object_config().layer_height.getFloat() :
0.05;
using std::string;
using std::to_string;
auto expt_str = to_string(exp_time_s_);
auto expt_first_str = to_string(exp_time_first_s_);
auto expt_str = to_string(m_exp_time_s);
auto expt_first_str = to_string(m_exp_time_first_s);
auto stepnum_str = to_string(static_cast<unsigned>(800*layer_height));
auto layerh_str = to_string(layer_height);
@ -134,92 +155,84 @@ public:
inline FilePrinter(double width_mm, double height_mm,
unsigned width_px, unsigned height_px,
double exp_time, double exp_time_first):
res_(width_px, height_px),
pxdim_(width_mm/width_px, height_mm/height_px),
exp_time_s_(exp_time),
exp_time_first_s_(exp_time_first)
m_res(width_px, height_px),
m_pxdim(width_mm/width_px, height_mm/height_px),
m_exp_time_s(exp_time),
m_exp_time_first_s(exp_time_first)
{
}
FilePrinter(const FilePrinter& ) = delete;
FilePrinter(FilePrinter&& m):
layers_rst_(std::move(m.layers_rst_)),
res_(m.res_),
pxdim_(m.pxdim_) {}
m_layers_rst(std::move(m.m_layers_rst)),
m_res(m.m_res),
m_pxdim(m.m_pxdim) {}
inline void layers(unsigned cnt) { if(cnt > 0) layers_rst_.resize(cnt); }
inline unsigned layers() const { return layers_rst_.size(); }
inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); }
inline unsigned layers() const { return unsigned(m_layers_rst.size()); }
void printConfig(const Print& printconf) { print_ = &printconf; }
void print_config(const Print& printconf) { m_print = &printconf; }
inline void drawPolygon(const ExPolygon& p, unsigned lyr) {
assert(lyr < layers_rst_.size());
layers_rst_[lyr].first.draw(p);
inline void draw_polygon(const ExPolygon& p, unsigned lyr) {
assert(lyr < m_layers_rst.size());
m_layers_rst[lyr].first.draw(p);
}
inline void beginLayer(unsigned lyr) {
if(layers_rst_.size() <= lyr) layers_rst_.resize(lyr+1);
layers_rst_[lyr].first.reset(res_, pxdim_, ORIGIN);
inline void begin_layer(unsigned lyr) {
if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1);
m_layers_rst[lyr].first.reset(m_res, m_pxdim, ORIGIN);
}
inline void beginLayer() {
layers_rst_.emplace_back();
layers_rst_.front().first.reset(res_, pxdim_, ORIGIN);
inline void begin_layer() {
m_layers_rst.emplace_back();
m_layers_rst.front().first.reset(m_res, m_pxdim, ORIGIN);
}
inline void finishLayer(unsigned lyr_id) {
assert(lyr_id < layers_rst_.size());
layers_rst_[lyr_id].first.save(layers_rst_[lyr_id].second,
inline void finish_layer(unsigned lyr_id) {
assert(lyr_id < m_layers_rst.size());
m_layers_rst[lyr_id].first.save(m_layers_rst[lyr_id].second,
Raster::Compression::PNG);
layers_rst_[lyr_id].first.reset();
m_layers_rst[lyr_id].first.reset();
}
inline void finishLayer() {
if(!layers_rst_.empty()) {
layers_rst_.back().first.save(layers_rst_.back().second,
inline void finish_layer() {
if(!m_layers_rst.empty()) {
m_layers_rst.back().first.save(m_layers_rst.back().second,
Raster::Compression::PNG);
layers_rst_.back().first.reset();
m_layers_rst.back().first.reset();
}
}
inline void save(const std::string& path) {
try {
LayerWriter<LyrFormat> writer(path);
wxFileName filepath(path);
std::string project = writer.get_name();
wxFFileOutputStream zipfile(path);
writer.next_entry("config.ini");
writer << createIniContent(project);
std::string project = filepath.GetName().ToStdString();
for(unsigned i = 0; i < m_layers_rst.size(); i++) {
if(m_layers_rst[i].second.rdbuf()->in_avail() > 0) {
char lyrnum[6];
std::sprintf(lyrnum, "%.5d", i);
auto zfilename = project + lyrnum + ".png";
writer.next_entry(zfilename);
writer << m_layers_rst[i].second.rdbuf();
m_layers_rst[i].second.str("");
}
}
if(!zipfile.IsOk()) {
BOOST_LOG_TRIVIAL(error) << "Can't create zip file for layers! "
<< path;
writer.close();
} catch(std::exception& e) {
BOOST_LOG_TRIVIAL(error) << e.what();
return;
}
wxZipOutputStream zipstream(zipfile);
wxStdOutputStream pngstream(zipstream);
zipstream.PutNextEntry("config.ini");
pngstream << createIniContent(project);
for(unsigned i = 0; i < layers_rst_.size(); i++) {
if(layers_rst_[i].second.rdbuf()->in_avail() > 0) {
char lyrnum[6];
std::sprintf(lyrnum, "%.5d", i);
auto zfilename = project + lyrnum + ".png";
zipstream.PutNextEntry(zfilename);
pngstream << layers_rst_[i].second.rdbuf();
layers_rst_[i].second.str("");
}
}
zipstream.Close();
zipfile.Close();
}
void saveLayer(unsigned lyr, const std::string& path) {
void save_layer(unsigned lyr, const std::string& path) {
unsigned i = lyr;
assert(i < layers_rst_.size());
assert(i < m_layers_rst.size());
char lyrnum[6];
std::sprintf(lyrnum, "%.5d", lyr);
@ -227,23 +240,23 @@ public:
std::fstream out(loc, std::fstream::out | std::fstream::binary);
if(out.good()) {
layers_rst_[i].first.save(out, Raster::Compression::PNG);
m_layers_rst[i].first.save(out, Raster::Compression::PNG);
} else {
BOOST_LOG_TRIVIAL(error) << "Can't create file for layer";
}
out.close();
layers_rst_[i].first.reset();
m_layers_rst[i].first.reset();
}
};
// Let's shadow this eigen interface
inline coord_t px(const Point& p) { return p(0); }
inline coord_t py(const Point& p) { return p(1); }
inline coord_t px(const Point& p) { return p(0); }
inline coord_t py(const Point& p) { return p(1); }
inline coordf_t px(const Vec2d& p) { return p(0); }
inline coordf_t py(const Vec2d& p) { return p(1); }
template<FilePrinterFormat format, class...Args>
template<FilePrinterFormat format, class LayerFormat, class...Args>
void print_to(Print& print,
std::string dirpath,
double width_mm,
@ -258,16 +271,18 @@ void print_to(Print& print,
// rasterized to the same image.
std::map<long long, LayerPtrs> layers;
auto& objects = print.objects;
auto& objects = print.objects();
// Merge the sliced layers with the support layers
std::for_each(objects.begin(), objects.end(), [&layers](PrintObject *o) {
for(auto l : o->layers) {
std::for_each(objects.cbegin(), objects.cend(),
[&layers](const PrintObject *o)
{
for(const auto l : o->layers()) {
auto& lyrs = layers[static_cast<long long>(scale_(l->print_z))];
lyrs.push_back(l);
}
for(auto l : o->support_layers) {
for(const auto l : o->support_layers()) {
auto& lyrs = layers[static_cast<long long>(scale_(l->print_z))];
lyrs.push_back(l);
}
@ -288,10 +303,10 @@ void print_to(Print& print,
auto cy = scale_(height_mm)/2 - (py(print_bb.center()) - py(print_bb.min));
// Create the actual printer, forward any additional arguments to it.
FilePrinter<format> printer(width_mm, height_mm,
std::forward<Args>(args)...);
FilePrinter<format, LayerFormat> printer(width_mm, height_mm,
std::forward<Args>(args)...);
printer.printConfig(print);
printer.print_config(print);
printer.layers(layers.size()); // Allocate space for all the layers
@ -303,18 +318,16 @@ void print_to(Print& print,
keys.reserve(layers.size());
for(auto& e : layers) keys.push_back(e.first);
int initstatus = print.progressindicator? print.progressindicator->state()
: 0;
print.set_status(initstatus, jobdesc);
print.set_status(0, jobdesc);
// Method that prints one layer
auto process_layer = [&layers, &keys, &printer, &st_prev, &m,
&jobdesc, print_bb, dir, cx, cy, &print, initstatus]
&jobdesc, print_bb, dir, cx, cy, &print]
(unsigned layer_id)
{
LayerPtrs lrange = layers[keys[layer_id]];
printer.beginLayer(layer_id); // Switch to the appropriate layer
printer.begin_layer(layer_id); // Switch to the appropriate layer
for(Layer *lp : lrange) {
Layer& l = *lp;
@ -329,21 +342,14 @@ void print_to(Print& print,
});
// Draw all the polygons in the slice to the actual layer.
std::for_each(l.object()->_shifted_copies.begin(),
l.object()->_shifted_copies.end(),
[&] (Point d)
{
std::for_each(slices.expolygons.begin(),
slices.expolygons.end(),
[&] (ExPolygon slice)
{
for (const Point &d : l.object()->copies())
for (ExPolygon slice : slices.expolygons) {
slice.translate(px(d), py(d));
slice.translate(-px(print_bb.min) + cx,
-py(print_bb.min) + cy);
printer.drawPolygon(slice, layer_id);
});
});
printer.draw_polygon(slice, layer_id);
}
/*if(print.has_support_material() && layer_id > 0) {
BOOST_LOG_TRIVIAL(warning) << "support material for layer "
@ -355,12 +361,12 @@ void print_to(Print& print,
}
printer.finishLayer(layer_id); // Finish the layer for later saving it.
printer.finish_layer(layer_id); // Finish the layer for later saving it.
auto st = static_cast<int>(layer_id*80.0/layers.size());
m.lock();
if( st - st_prev > 10) {
print.set_status(initstatus + st, jobdesc);
print.set_status(st, jobdesc);
st_prev = st;
}
m.unlock();
@ -379,9 +385,9 @@ void print_to(Print& print,
// print.set_status(100, jobdesc);
// Save the print into the file system.
print.set_status(initstatus + 90, "Writing layers to disk");
print.set_status(90, "Writing layers to disk");
printer.save(dir);
print.set_status(initstatus + 100, "Writing layers completed");
print.set_status(100, "Writing layers completed");
}
}

File diff suppressed because it is too large Load diff

View file

@ -2,8 +2,7 @@
namespace Slic3r {
Flow
PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const
Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const
{
ConfigOptionFloatOrPercent config_width;
if (width != -1) {
@ -13,53 +12,53 @@ PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_la
} else {
// otherwise, get extrusion width from configuration
// (might be an absolute value, or a percent value, or zero for auto)
if (first_layer && this->_print->config.first_layer_extrusion_width.value > 0) {
config_width = this->_print->config.first_layer_extrusion_width;
if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) {
config_width = m_print->config().first_layer_extrusion_width;
} else if (role == frExternalPerimeter) {
config_width = this->config.external_perimeter_extrusion_width;
config_width = m_config.external_perimeter_extrusion_width;
} else if (role == frPerimeter) {
config_width = this->config.perimeter_extrusion_width;
config_width = m_config.perimeter_extrusion_width;
} else if (role == frInfill) {
config_width = this->config.infill_extrusion_width;
config_width = m_config.infill_extrusion_width;
} else if (role == frSolidInfill) {
config_width = this->config.solid_infill_extrusion_width;
config_width = m_config.solid_infill_extrusion_width;
} else if (role == frTopSolidInfill) {
config_width = this->config.top_infill_extrusion_width;
config_width = m_config.top_infill_extrusion_width;
} else {
CONFESS("Unknown role");
throw std::invalid_argument("Unknown role");
}
}
if (config_width.value == 0) {
config_width = object.config.extrusion_width;
config_width = object.config().extrusion_width;
}
// get the configured nozzle_diameter for the extruder associated
// to the flow role requested
size_t extruder = 0; // 1-based
if (role == frPerimeter || role == frExternalPerimeter) {
extruder = this->config.perimeter_extruder;
extruder = m_config.perimeter_extruder;
} else if (role == frInfill) {
extruder = this->config.infill_extruder;
extruder = m_config.infill_extruder;
} else if (role == frSolidInfill || role == frTopSolidInfill) {
extruder = this->config.solid_infill_extruder;
extruder = m_config.solid_infill_extruder;
} else {
CONFESS("Unknown role $role");
throw std::invalid_argument("Unknown role");
}
double nozzle_diameter = this->_print->config.nozzle_diameter.get_at(extruder-1);
double nozzle_diameter = m_print->config().nozzle_diameter.get_at(extruder-1);
return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? (float)this->config.bridge_flow_ratio : 0.0);
return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? (float)m_config.bridge_flow_ratio : 0.0);
}
coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
{
return (print_config.nozzle_diameter.get_at(this->config.perimeter_extruder.value - 1) +
print_config.nozzle_diameter.get_at(this->config.infill_extruder.value - 1) +
print_config.nozzle_diameter.get_at(this->config.solid_infill_extruder.value - 1)) / 3.;
return (print_config.nozzle_diameter.get_at(m_config.perimeter_extruder.value - 1) +
print_config.nozzle_diameter.get_at(m_config.infill_extruder.value - 1) +
print_config.nozzle_diameter.get_at(m_config.solid_infill_extruder.value - 1)) / 3.;
}
coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const
{
return this->nozzle_dmr_avg(print_config) * sqrt(this->config.bridge_flow_ratio.value);
return this->nozzle_dmr_avg(print_config) * sqrt(m_config.bridge_flow_ratio.value);
}
}

View file

@ -37,37 +37,37 @@ public:
using Origin = Raster::Origin;
private:
Raster::Resolution resolution_;
Raster::PixelDim pxdim_;
TBuffer buf_;
TRawBuffer rbuf_;
TPixelRenderer pixfmt_;
TRawRenderer raw_renderer_;
TRendererAA renderer_;
Origin o_;
std::function<void(agg::path_storage&)> flipy_ = [](agg::path_storage&) {};
Raster::Resolution m_resolution;
Raster::PixelDim m_pxdim;
TBuffer m_buf;
TRawBuffer m_rbuf;
TPixelRenderer m_pixfmt;
TRawRenderer m_raw_renderer;
TRendererAA m_renderer;
Origin m_o;
std::function<void(agg::path_storage&)> m_flipy = [](agg::path_storage&) {};
public:
inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd,
Origin o):
resolution_(res), pxdim_(pd),
buf_(res.pixels()),
rbuf_(reinterpret_cast<TPixelRenderer::value_type*>(buf_.data()),
m_resolution(res), m_pxdim(pd),
m_buf(res.pixels()),
m_rbuf(reinterpret_cast<TPixelRenderer::value_type*>(m_buf.data()),
res.width_px, res.height_px,
res.width_px*TPixelRenderer::num_components),
pixfmt_(rbuf_),
raw_renderer_(pixfmt_),
renderer_(raw_renderer_),
o_(o)
m_pixfmt(m_rbuf),
m_raw_renderer(m_pixfmt),
m_renderer(m_raw_renderer),
m_o(o)
{
renderer_.color(ColorWhite);
m_renderer.color(ColorWhite);
// If we would like to play around with gamma
// ras.gamma(agg::gamma_power(1.0));
clear();
if(o_ == Origin::TOP_LEFT) flipy_ = [this](agg::path_storage& path) {
path.flip_y(0, resolution_.height_px);
if(m_o == Origin::TOP_LEFT) m_flipy = [this](agg::path_storage& path) {
path.flip_y(0, m_resolution.height_px);
};
}
@ -76,35 +76,35 @@ public:
agg::scanline_p8 scanlines;
auto&& path = to_path(poly.contour);
flipy_(path);
m_flipy(path);
ras.add_path(path);
for(auto h : poly.holes) {
auto&& holepath = to_path(h);
flipy_(holepath);
m_flipy(holepath);
ras.add_path(holepath);
}
agg::render_scanlines(ras, scanlines, renderer_);
agg::render_scanlines(ras, scanlines, m_renderer);
}
inline void clear() {
raw_renderer_.clear(ColorBlack);
m_raw_renderer.clear(ColorBlack);
}
inline TBuffer& buffer() { return buf_; }
inline TBuffer& buffer() { return m_buf; }
inline const Raster::Resolution resolution() { return resolution_; }
inline const Raster::Resolution resolution() { return m_resolution; }
inline Origin origin() const /*noexcept*/ { return o_; }
inline Origin origin() const /*noexcept*/ { return m_o; }
private:
double getPx(const Point& p) {
return p(0) * SCALING_FACTOR/pxdim_.w_mm;
return p(0) * SCALING_FACTOR/m_pxdim.w_mm;
}
double getPy(const Point& p) {
return p(1) * SCALING_FACTOR/pxdim_.h_mm;
return p(1) * SCALING_FACTOR/m_pxdim.h_mm;
}
agg::path_storage to_path(const Polygon& poly) {
@ -124,57 +124,57 @@ const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255);
const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0);
Raster::Raster(const Resolution &r, const PixelDim &pd, Origin o):
impl_(new Impl(r, pd, o)) {}
m_impl(new Impl(r, pd, o)) {}
Raster::Raster() {}
Raster::~Raster() {}
Raster::Raster(Raster &&m):
impl_(std::move(m.impl_)) {}
m_impl(std::move(m.m_impl)) {}
void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd)
{
// Free up the unnecessary memory and make sure it stays clear after
// an exception
auto o = impl_? impl_->origin() : Origin::TOP_LEFT;
auto o = m_impl? m_impl->origin() : Origin::TOP_LEFT;
reset(r, pd, o);
}
void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd,
Raster::Origin o)
{
impl_.reset();
impl_.reset(new Impl(r, pd, o));
m_impl.reset();
m_impl.reset(new Impl(r, pd, o));
}
void Raster::reset()
{
impl_.reset();
m_impl.reset();
}
Raster::Resolution Raster::resolution() const
{
if(impl_) return impl_->resolution();
if(m_impl) return m_impl->resolution();
return Resolution(0, 0);
}
void Raster::clear()
{
assert(impl_);
impl_->clear();
assert(m_impl);
m_impl->clear();
}
void Raster::draw(const ExPolygon &poly)
{
assert(impl_);
impl_->draw(poly);
assert(m_impl);
m_impl->draw(poly);
}
void Raster::save(std::ostream& stream, Compression comp)
{
assert(impl_);
assert(m_impl);
switch(comp) {
case Compression::PNG: {
@ -188,7 +188,7 @@ void Raster::save(std::ostream& stream, Compression comp)
wr.write_info();
auto& b = impl_->buffer();
auto& b = m_impl->buffer();
auto ptr = reinterpret_cast<png::byte*>( b.data() );
unsigned stride =
sizeof(Impl::TBuffer::value_type) * resolution().width_px;
@ -201,12 +201,12 @@ void Raster::save(std::ostream& stream, Compression comp)
}
case Compression::RAW: {
stream << "P5 "
<< impl_->resolution().width_px << " "
<< impl_->resolution().height_px << " "
<< m_impl->resolution().width_px << " "
<< m_impl->resolution().height_px << " "
<< "255 ";
stream.write(reinterpret_cast<const char*>(impl_->buffer().data()),
impl_->buffer().size()*sizeof(Impl::TBuffer::value_type));
stream.write(reinterpret_cast<const char*>(m_impl->buffer().data()),
m_impl->buffer().size()*sizeof(Impl::TBuffer::value_type));
}
}
}

View file

@ -18,7 +18,7 @@ class ExPolygon;
*/
class Raster {
class Impl;
std::unique_ptr<Impl> impl_;
std::unique_ptr<Impl> m_impl;
public:
/// Supported compression types
@ -65,7 +65,7 @@ public:
/**
* Release the allocated resources. Drawing in this state ends in
* unspecified behaviour.
* unspecified behavior.
*/
void reset();

View file

@ -446,7 +446,7 @@ void ground_layer(const TriangleMesh &mesh, ExPolygons &output, float h)
std::vector<ExPolygons> tmp;
slicer.slice({h}, &tmp);
slicer.slice({h}, &tmp, [](){});
output = tmp.front();
}

View file

@ -142,8 +142,8 @@ void export_print_z_polygons_and_extrusions_to_svg(
PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) :
m_object (object),
m_print_config (&object->print()->config),
m_object_config (&object->config),
m_print_config (&object->print()->config()),
m_object_config (&object->config()),
m_slicing_params (slicing_params),
m_first_layer_flow (support_material_1st_layer_flow(object, float(slicing_params.first_print_layer_height))),
m_support_material_flow (support_material_flow(object, float(slicing_params.layer_height))),
@ -164,7 +164,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
coordf_t external_perimeter_width = 0.;
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
if (! object->region_volumes[region_id].empty()) {
const PrintRegionConfig &config = object->print()->get_region(region_id)->config;
const PrintRegionConfig &config = object->print()->get_region(region_id)->config();
coordf_t width = config.external_perimeter_extrusion_width.get_abs_value(slicing_params.layer_height);
if (width <= 0.)
width = m_print_config->nozzle_diameter.get_at(config.perimeter_extruder-1);
@ -226,7 +226,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
coordf_t max_object_layer_height = 0.;
for (size_t i = 0; i < object.layer_count(); ++ i)
max_object_layer_height = std::max(max_object_layer_height, object.layers[i]->height);
max_object_layer_height = std::max(max_object_layer_height, object.layers()[i]->height);
// Layer instances will be allocated by std::deque and they will be kept until the end of this function call.
// The layers will be referenced by various LayersPtr (of type std::vector<Layer*>)
@ -266,9 +266,9 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
layer_support_areas);
#ifdef SLIC3R_DEBUG
for (size_t layer_id = 0; layer_id < object.layers.size(); ++ layer_id)
for (size_t layer_id = 0; layer_id < object.layers().size(); ++ layer_id)
Slic3r::SVG::export_expolygons(
debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers[layer_id]->print_z),
debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers()[layer_id]->print_z),
union_ex(layer_support_areas[layer_id], false));
#endif /* SLIC3R_DEBUG */
@ -364,7 +364,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
// Sort the layers lexicographically by a raising print_z and a decreasing height.
std::sort(layers_sorted.begin(), layers_sorted.end(), MyLayersPtrCompare());
int layer_id = 0;
assert(object.support_layers.empty());
assert(object.support_layers().empty());
for (int i = 0; i < int(layers_sorted.size());) {
// Find the last layer with roughly the same print_z, find the minimum layer height of all.
// Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should.
@ -430,14 +430,14 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t
{
// 1) Count the new polygons first.
size_t n_polygons_new = 0;
for (const LayerRegion *region : layer.regions)
for (const LayerRegion *region : layer.regions())
for (const Surface &surface : region->slices.surfaces)
if (surface.surface_type == surface_type)
n_polygons_new += surface.expolygon.holes.size() + 1;
// 2) Collect the new polygons.
Polygons out;
out.reserve(n_polygons_new);
for (const LayerRegion *region : layer.regions)
for (const LayerRegion *region : layer.regions())
for (const Surface &surface : region->slices.surfaces)
if (surface.surface_type == surface_type)
polygons_append(out, surface.expolygon);
@ -679,7 +679,7 @@ namespace SupportMaterialInternal {
}
static bool has_bridging_extrusions(const Layer &layer)
{
for (const LayerRegion *region : layer.regions) {
for (const LayerRegion *region : layer.regions()) {
if (SupportMaterialInternal::has_bridging_perimeters(region->perimeters))
return true;
if (region->fill_surfaces.has(stBottomBridge) && has_bridging_fills(region->fills))
@ -742,7 +742,7 @@ namespace SupportMaterialInternal {
// Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
Polygons lower_grown_slices = offset(lower_layer_polygons,
//FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config.perimeter_extruder-1))),
0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder-1))),
SUPPORT_SURFACES_OFFSET_PARAMETERS);
// Collect perimeters of this layer.
//FIXME split_at_first_point() could split a bridge mid-way
@ -825,9 +825,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
std::vector<Polygons> buildplate_covered;
if (buildplate_only) {
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() - collecting regions covering the print bed.";
buildplate_covered.assign(object.layers.size(), Polygons());
for (size_t layer_id = 1; layer_id < object.layers.size(); ++ layer_id) {
const Layer &lower_layer = *object.layers[layer_id-1];
buildplate_covered.assign(object.layers().size(), Polygons());
for (size_t layer_id = 1; layer_id < object.layers().size(); ++ layer_id) {
const Layer &lower_layer = *object.layers()[layer_id-1];
// Merge the new slices with the preceding slices.
// Apply the safety offset to the newly added polygons, so they will connect
// with the polygons collected before,
@ -856,7 +856,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
(const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id)
{
const Layer &layer = *object.layers[layer_id];
const Layer &layer = *object.layers()[layer_id];
// Detect overhangs and contact areas needed to support them.
// Collect overhangs and contacts of all regions of this layer supported by the layer immediately below.
@ -864,7 +864,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
Polygons contact_polygons;
Polygons slices_margin_cached;
float slices_margin_cached_offset = -1.;
Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers[layer_id-1]->slices.expolygons);
Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices.expolygons);
// Offset of the lower layer, to trim the support polygons with to calculate dense supports.
float no_interface_offset = 0.f;
if (layer_id == 0) {
@ -876,14 +876,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
contact_polygons = offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN));
} else {
// Generate overhang / contact_polygons for non-raft layers.
const Layer &lower_layer = *object.layers[layer_id-1];
for (LayerRegion *layerm : layer.regions) {
const Layer &lower_layer = *object.layers()[layer_id-1];
for (LayerRegion *layerm : layer.regions()) {
// Extrusion width accounts for the roundings of the extrudates.
// It is the maximum widh of the extrudate.
float fw = float(layerm->flow(frExternalPerimeter).scaled_width());
no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw);
float lower_layer_offset =
(layer_id < this->m_object_config->support_material_enforce_layers.value) ?
(layer_id < m_object_config->support_material_enforce_layers.value) ?
// Enforce a full possible support, ignore the overhang angle.
0.f :
(threshold_rad > 0. ?
@ -1041,8 +1041,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Align the contact surface height with a layer immediately below the supported layer.
// Interface layer will be synchronized with the object.
new_layer.print_z = layer.print_z - layer.height;
new_layer.height = object.layers[layer_id - 1]->height;
new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers[layer_id - 2]->print_z;
new_layer.height = object.layers()[layer_id - 1]->height;
new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers()[layer_id - 2]->print_z;
} else {
new_layer.print_z = layer.print_z - layer.height - m_object_config->support_material_contact_distance;
new_layer.bottom_z = new_layer.print_z;
@ -1068,9 +1068,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// it will support layers printed with a bridging flow.
if (SupportMaterialInternal::has_bridging_extrusions(layer)) {
coordf_t bridging_height = 0.;
for (const LayerRegion *region : layer.regions)
for (const LayerRegion *region : layer.regions())
bridging_height += region->region()->bridging_height_avg(*m_print_config);
bridging_height /= coordf_t(layer.regions.size());
bridging_height /= coordf_t(layer.regions().size());
coordf_t bridging_print_z = layer.print_z - bridging_height - m_object_config->support_material_contact_distance;
if (bridging_print_z >= m_slicing_params.first_print_layer_height - EPSILON) {
// Not below the first layer height means this layer is printable.
@ -1107,15 +1107,15 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3, true));
// 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
if (layer_id == 0) {
if (layer_id == 0 || m_slicing_params.soluble_interface) {
// if (no_interface_offset == 0.f) {
new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, true);
} else {
// Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
Polygons dense_interface_polygons = diff(overhang_polygons,
offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS));
// offset(lower_layer_polygons, no_interface_offset * 0.6f, SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (! dense_interface_polygons.empty()) {
//FIXME do it for non-soluble support interfaces only.
//FIXME do it for the bridges only?
SupportGridPattern support_grid_pattern(
// Support islands, to be stretched into a grid.
@ -1297,13 +1297,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
// If the layer is extruded with no bridging flow, support just the normal extrusions.
layer_new.height = m_slicing_params.soluble_interface ?
// Align the interface layer with the object's layer height.
object.layers[layer_id + 1]->height :
object.layers()[layer_id + 1]->height :
// Place a bridge flow interface layer over the top surface.
//FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?)
// According to Jindrich the bottom surfaces work well.
//FIXME test the bridging flow instead?
m_support_material_interface_flow.nozzle_diameter;
layer_new.print_z = m_slicing_params.soluble_interface ? object.layers[layer_id + 1]->print_z :
layer_new.print_z = m_slicing_params.soluble_interface ? object.layers()[layer_id + 1]->print_z :
layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value;
layer_new.bottom_z = layer.print_z;
layer_new.idx_object_layer_below = layer_id;
@ -1324,7 +1324,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
if (diff > 0.) {
// The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer.
assert(diff < layer_new.height + EPSILON);
assert(layer_new.height - diff >= this->m_support_layer_height_min - EPSILON);
assert(layer_new.height - diff >= m_support_layer_height_min - EPSILON);
layer_new.print_z = top_contacts[top_idx]->print_z;
layer_new.height -= diff;
} else {
@ -1347,7 +1347,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
//FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage?
touching = offset(touching, float(SCALED_EPSILON));
for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) {
const Layer &layer_above = *object.layers[layer_id_above];
const Layer &layer_above = *object.layers()[layer_id_above];
if (layer_above.print_z > layer_new.print_z - EPSILON)
break;
if (! layer_support_areas[layer_id_above].empty()) {
@ -1578,7 +1578,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int
// Verify that the extremes are separated by m_support_layer_height_min.
for (size_t i = 1; i < extremes.size(); ++ i) {
assert(extremes[i]->extreme_z() - extremes[i-1]->extreme_z() == 0. ||
extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > this->m_support_layer_height_min - EPSILON);
extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > m_support_layer_height_min - EPSILON);
assert(extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > 0. ||
extremes[i]->layer_type == extremes[i-1]->layer_type ||
(extremes[i]->layer_type == sltBottomContact && extremes[i - 1]->layer_type == sltTopContact));
@ -1602,7 +1602,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int
// This is a bottom of a synchronized (or soluble) top contact layer, its height has been decided in this->top_contact_layers().
assert(extr2->layer_type == sltTopContact);
assert(extr2->bottom_z == m_slicing_params.first_print_layer_height);
assert(extr2->print_z >= m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON);
assert(extr2->print_z >= m_slicing_params.first_print_layer_height + m_support_layer_height_min - EPSILON);
if (intermediate_layers.empty() || intermediate_layers.back()->print_z < m_slicing_params.first_print_layer_height) {
MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate);
layer_new.bottom_z = 0.;
@ -1642,7 +1642,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int
if (synchronize) {
// Emit support layers synchronized with the object layers.
// Find the first object layer, which has its print_z in this support Z range.
while (idx_layer_object < object.layers.size() && object.layers[idx_layer_object]->print_z < extr1z + EPSILON)
while (idx_layer_object < object.layers().size() && object.layers()[idx_layer_object]->print_z < extr1z + EPSILON)
++ idx_layer_object;
if (idx_layer_object == 0 && extr1z == m_slicing_params.raft_interface_top_z) {
// Insert one base support layer below the object.
@ -1653,11 +1653,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int
intermediate_layers.push_back(&layer_new);
}
// Emit all intermediate support layers synchronized with object layers up to extr2z.
for (; idx_layer_object < object.layers.size() && object.layers[idx_layer_object]->print_z < extr2z + EPSILON; ++ idx_layer_object) {
for (; idx_layer_object < object.layers().size() && object.layers()[idx_layer_object]->print_z < extr2z + EPSILON; ++ idx_layer_object) {
MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate);
layer_new.print_z = object.layers[idx_layer_object]->print_z;
layer_new.height = object.layers[idx_layer_object]->height;
layer_new.bottom_z = (idx_layer_object > 0) ? object.layers[idx_layer_object - 1]->print_z : (layer_new.print_z - layer_new.height);
layer_new.print_z = object.layers()[idx_layer_object]->print_z;
layer_new.height = object.layers()[idx_layer_object]->height;
layer_new.bottom_z = (idx_layer_object > 0) ? object.layers()[idx_layer_object - 1]->print_z : (layer_new.print_z - layer_new.height);
assert(intermediate_layers.empty() || intermediate_layers.back()->print_z < layer_new.print_z + EPSILON);
intermediate_layers.push_back(&layer_new);
}
@ -1667,10 +1667,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int
assert(n_layers_extra > 0);
coordf_t step = dist / coordf_t(n_layers_extra);
if (extr1 != nullptr && extr1->layer_type == sltTopContact &&
extr1->print_z + this->m_support_layer_height_min > extr1->bottom_z + step) {
extr1->print_z + m_support_layer_height_min > extr1->bottom_z + step) {
// The bottom extreme is a bottom of a top surface. Ensure that the gap
// between the 1st intermediate layer print_z and extr1->print_z is not too small.
assert(extr1->bottom_z + this->m_support_layer_height_min < extr1->print_z + EPSILON);
assert(extr1->bottom_z + m_support_layer_height_min < extr1->print_z + EPSILON);
// Generate the first intermediate layer.
MyLayer &layer_new = layer_allocate(layer_storage, sltIntermediate);
layer_new.bottom_z = extr1->bottom_z;
@ -1764,7 +1764,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
Polygons polygons_new;
// Use the precomputed layer_support_areas.
idx_object_layer_above = std::max(0, idx_lower_or_equal(object.layers, idx_object_layer_above,
idx_object_layer_above = std::max(0, idx_lower_or_equal(object.layers(), idx_object_layer_above,
[&layer_intermediate](const Layer *layer){ return layer->print_z <= layer_intermediate.print_z + EPSILON; }));
polygons_new = layer_support_areas[idx_object_layer_above];
@ -1900,23 +1900,23 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
// Find the overlapping object layers including the extra above / below gap.
coordf_t z_threshold = support_layer.print_z - support_layer.height - gap_extra_below + EPSILON;
idx_object_layer_overlapping = idx_higher_or_equal(
object.layers, idx_object_layer_overlapping,
object.layers(), idx_object_layer_overlapping,
[z_threshold](const Layer *layer){ return layer->print_z >= z_threshold; });
// Collect all the object layers intersecting with this layer.
Polygons polygons_trimming;
size_t i = idx_object_layer_overlapping;
for (; i < object.layers.size(); ++ i) {
const Layer &object_layer = *object.layers[i];
for (; i < object.layers().size(); ++ i) {
const Layer &object_layer = *object.layers()[i];
if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON)
break;
polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
}
if (! this->m_slicing_params.soluble_interface) {
if (! m_slicing_params.soluble_interface) {
// Collect all bottom surfaces, which will be extruded with a bridging flow.
for (; i < object.layers.size(); ++ i) {
const Layer &object_layer = *object.layers[i];
for (; i < object.layers().size(); ++ i) {
const Layer &object_layer = *object.layers()[i];
bool some_region_overlaps = false;
for (LayerRegion *region : object_layer.regions) {
for (LayerRegion *region : object_layer.regions()) {
coordf_t bridging_height = region->region()->bridging_height_avg(*this->m_print_config);
if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON)
break;
@ -1924,7 +1924,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
polygons_append(polygons_trimming,
offset(to_expolygons(region->fill_surfaces.filter_by_type(stBottomBridge)),
gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (region->region()->config.overhangs.value)
if (region->region()->config().overhangs.value)
SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming);
}
if (! some_region_overlaps)
@ -2022,7 +2022,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
// Expand the bases of the support columns in the 1st layer.
columns_base->polygons = diff(
offset(columns_base->polygons, inflate_factor_1st_layer),
offset(m_object->layers.front()->slices.expolygons, scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
offset(m_object->layers().front()->slices.expolygons, scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (contacts != nullptr)
columns_base->polygons = diff(columns_base->polygons, interface_polygons);
}
@ -2168,8 +2168,8 @@ struct MyLayerExtruded
else
*m_polygons_to_extrude = std::move(polygons);
}
Polygons& polygons_to_extrude() { return (this->m_polygons_to_extrude == nullptr) ? layer->polygons : *this->m_polygons_to_extrude; }
const Polygons& polygons_to_extrude() const { return (this->m_polygons_to_extrude == nullptr) ? layer->polygons : *this->m_polygons_to_extrude; }
Polygons& polygons_to_extrude() { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; }
const Polygons& polygons_to_extrude() const { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; }
bool could_merge(const MyLayerExtruded &other) const {
return ! this->empty() && ! other.empty() &&
@ -2182,21 +2182,21 @@ struct MyLayerExtruded
assert(this->could_merge(other));
// 1) Merge the rest polygons to extrude, if there are any.
if (other.m_polygons_to_extrude != nullptr) {
if (this->m_polygons_to_extrude == nullptr) {
if (m_polygons_to_extrude == nullptr) {
// This layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet).
assert(this->extrusions.empty());
this->m_polygons_to_extrude = new Polygons(this->layer->polygons);
m_polygons_to_extrude = new Polygons(this->layer->polygons);
}
Slic3r::polygons_append(*this->m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude));
*this->m_polygons_to_extrude = union_(*this->m_polygons_to_extrude, true);
Slic3r::polygons_append(*m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude));
*m_polygons_to_extrude = union_(*m_polygons_to_extrude, true);
delete other.m_polygons_to_extrude;
other.m_polygons_to_extrude = nullptr;
} else if (this->m_polygons_to_extrude != nullptr) {
} else if (m_polygons_to_extrude != nullptr) {
assert(other.m_polygons_to_extrude == nullptr);
// The other layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet).
assert(other.extrusions.empty());
Slic3r::polygons_append(*this->m_polygons_to_extrude, other.layer->polygons);
*this->m_polygons_to_extrude = union_(*this->m_polygons_to_extrude, true);
Slic3r::polygons_append(*m_polygons_to_extrude, other.layer->polygons);
*m_polygons_to_extrude = union_(*m_polygons_to_extrude, true);
}
// 2) Merge the extrusions.
this->extrusions.insert(this->extrusions.end(), other.extrusions.begin(), other.extrusions.end());
@ -2618,7 +2618,10 @@ void modulate_extrusion_by_overlapping_layers(
(fragment_end.is_start ? &polyline.points.front() : &polyline.points.back());
}
private:
ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {}
ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {
return *this;
}
const std::vector<ExtrusionPathFragment> &m_path_fragments;
};
const coord_t search_radius = 7;
@ -2794,7 +2797,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id)
{
assert(support_layer_id < raft_layers.size());
SupportLayer &support_layer = *object.support_layers[support_layer_id];
SupportLayer &support_layer = *object.support_layers()[support_layer_id];
assert(support_layer.support_fills.entities.empty());
MyLayer &raft_layer = *raft_layers[support_layer_id];
@ -2890,9 +2893,9 @@ void PrintObjectSupportMaterial::generate_toolpaths(
MyLayerExtruded interface_layer;
std::vector<LayerCacheItem> overlaps;
};
std::vector<LayerCache> layer_caches(object.support_layers.size(), LayerCache());
std::vector<LayerCache> layer_caches(object.support_layers().size(), LayerCache());
tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, object.support_layers.size()),
tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, object.support_layers().size()),
[this, &object, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &layer_caches, &loop_interface_processor,
infill_pattern, &bbox_object, support_density, interface_density, interface_angle, &angles, link_max_length_factor, with_sheath]
(const tbb::blocked_range<size_t>& range) {
@ -2907,7 +2910,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
filler_support->set_bounding_box(bbox_object);
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id)
{
SupportLayer &support_layer = *object.support_layers[support_layer_id];
SupportLayer &support_layer = *object.support_layers()[support_layer_id];
LayerCache &layer_cache = layer_caches[support_layer_id];
// Find polygons with the same print_z.
@ -3105,11 +3108,11 @@ void PrintObjectSupportMaterial::generate_toolpaths(
});
// Now modulate the support layer height in parallel.
tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, object.support_layers.size()),
tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, object.support_layers().size()),
[this, &object, &layer_caches]
(const tbb::blocked_range<size_t>& range) {
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) {
SupportLayer &support_layer = *object.support_layers[support_layer_id];
SupportLayer &support_layer = *object.support_layers()[support_layer_id];
LayerCache &layer_cache = layer_caches[support_layer_id];
for (LayerCacheItem &layer_cache_item : layer_cache.overlaps) {
modulate_extrusion_by_overlapping_layers(layer_cache_item.layer_extruded->extrusions, *layer_cache_item.layer_extruded->layer, layer_cache_item.overlapping);

View file

@ -224,7 +224,7 @@ private:
// Produce the actual G-code.
void generate_toolpaths(
const PrintObject &object,
const PrintObject &object,
const MyLayersPtr &raft_layers,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,

View file

@ -6,6 +6,10 @@
// Add z coordinate to model instances' offset
#define ENABLE_MODELINSTANCE_3D_OFFSET (1 && ENABLE_1_42_0)
// Add double click on gizmo grabbers to reset transformation components to their default value
#define ENABLE_GIZMOS_RESET (1 && ENABLE_1_42_0)
// Add x and y rotation components to model instances' offset
#define ENABLE_MODELINSTANCE_3D_ROTATION (1 && ENABLE_MODELINSTANCE_3D_OFFSET)
#endif // _technologies_h_

View file

@ -308,7 +308,8 @@ void TriangleMesh::rotate(double angle, Point* center)
bool TriangleMesh::has_multiple_patches() const
{
// we need neighbors
if (!this->repaired) CONFESS("split() requires repair()");
if (!this->repaired)
throw std::runtime_error("split() requires repair()");
if (this->stl.stats.number_of_facets == 0)
return false;
@ -338,7 +339,8 @@ bool TriangleMesh::has_multiple_patches() const
size_t TriangleMesh::number_of_patches() const
{
// we need neighbors
if (!this->repaired) CONFESS("split() requires repair()");
if (!this->repaired)
throw std::runtime_error("split() requires repair()");
if (this->stl.stats.number_of_facets == 0)
return false;
@ -382,7 +384,7 @@ TriangleMeshPtrs TriangleMesh::split() const
// we need neighbors
if (!this->repaired)
CONFESS("split() requires repair()");
throw std::runtime_error("split() requires repair()");
// loop while we have remaining facets
for (;;) {
@ -643,10 +645,11 @@ void TriangleMesh::require_shared_vertices()
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
}
TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
mesh(_mesh)
void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type throw_on_cancel)
{
mesh = _mesh;
_mesh->require_shared_vertices();
throw_on_cancel();
facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1);
v_scaled_shared.assign(_mesh->stl.v_shared, _mesh->stl.v_shared + _mesh->stl.stats.shared_vertices);
// Scale the copied vertices.
@ -683,6 +686,7 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
e2f.face_edge = - e2f.face_edge;
}
}
throw_on_cancel();
std::sort(edges_map.begin(), edges_map.end());
// Assign a unique common edge id to touching triangle edges.
@ -722,10 +726,12 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
edge_j.face = -1;
}
++ num_edges;
if ((i & 0x0ffff) == 0)
throw_on_cancel();
}
}
void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
{
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice";
@ -762,13 +768,17 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
boost::mutex lines_mutex;
tbb::parallel_for(
tbb::blocked_range<int>(0,this->mesh->stl.stats.number_of_facets),
[&lines, &lines_mutex, &z, this](const tbb::blocked_range<int>& range) {
for (int facet_idx = range.begin(); facet_idx < range.end(); ++ facet_idx)
[&lines, &lines_mutex, &z, throw_on_cancel, this](const tbb::blocked_range<int>& range) {
for (int facet_idx = range.begin(); facet_idx < range.end(); ++ facet_idx) {
if ((facet_idx & 0x0ffff) == 0)
throw_on_cancel();
this->_slice_do(facet_idx, &lines, &lines_mutex, z);
}
}
);
}
throw_on_cancel();
// v_scaled_shared could be freed here
// build loops
@ -776,9 +786,12 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
layers->resize(z.size());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, z.size()),
[&lines, &layers, this](const tbb::blocked_range<size_t>& range) {
for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx)
[&lines, &layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) {
if ((line_idx & 0x0ffff) == 0)
throw_on_cancel();
this->make_loops(lines[line_idx], &(*layers)[line_idx]);
}
}
);
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice finished";
@ -877,24 +890,25 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
}
}
void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
{
std::vector<Polygons> layers_p;
this->slice(z, &layers_p);
this->slice(z, &layers_p, throw_on_cancel);
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start";
layers->resize(z.size());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, z.size()),
[&layers_p, layers, this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start";
layers->resize(z.size());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, z.size()),
[&layers_p, layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]);
#endif
this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]);
}
});
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end";
throw_on_cancel();
this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]);
}
});
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end";
}
// Return true, if the facet has been sliced and line_out has been filled.

View file

@ -3,6 +3,7 @@
#include "libslic3r.h"
#include <admesh/stl.h>
#include <functional>
#include <vector>
#include <boost/thread.hpp>
#include "BoundingBox.hpp"
@ -156,9 +157,13 @@ typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
class TriangleMeshSlicer
{
public:
TriangleMeshSlicer(TriangleMesh* _mesh);
void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const;
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const;
typedef std::function<void()> throw_on_cancel_callback_type;
TriangleMeshSlicer() : mesh(nullptr) {}
// Not quite nice, but the constructor and init() methods require non-const mesh pointer to be able to call mesh->require_shared_vertices()
TriangleMeshSlicer(TriangleMesh* mesh) { this->init(mesh, [](){}); }
void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel);
void slice(const std::vector<float> &z, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
enum FacetSliceType {
NoSlice = 0,
Slicing = 1,

View file

@ -44,6 +44,14 @@ extern local_encoded_string encode_path(const char *src);
extern std::string decode_path(const char *src);
extern std::string normalize_utf8_nfc(const char *src);
// Safely rename a file even if the target exists.
// On Windows, the file explorer (or anti-virus or whatever else) often locks the file
// for a short while, so the file may not be movable. Retry while we see recoverable errors.
extern int rename_file(const std::string &from, const std::string &to);
// Copy a file, adjust the access attributes, so that the target is writable.
extern int copy_file(const std::string &from, const std::string &to);
// File path / name / extension splitting utilities, working with UTF-8,
// to be published to Perl.
namespace PerlUtils {
@ -66,6 +74,20 @@ inline std::string header_slic3r_generated() { return std::string("generated by
// getpid platform wrapper
extern unsigned get_current_pid();
template <typename Real>
Real round_nearest(Real value, unsigned int decimals)
{
Real res = (Real)0;
if (decimals == 0)
res = ::round(value);
else
{
Real power = ::pow((Real)10, (int)decimals);
res = ::round(value * power + (Real)0.5) / power;
}
return res;
}
// Compute the next highest power of 2 of 32-bit v
// http://graphics.stanford.edu/~seander/bithacks.html
template<typename T>
@ -87,26 +109,6 @@ inline T next_highest_power_of_2(T v)
extern std::string xml_escape(std::string text);
class PerlCallback {
public:
PerlCallback(void *sv) : m_callback(nullptr) { this->register_callback(sv); }
PerlCallback() : m_callback(nullptr) {}
~PerlCallback() { this->deregister_callback(); }
void register_callback(void *sv);
void deregister_callback();
void call() const;
void call(int i) const;
void call(int i, int j) const;
void call(const std::vector<int>& ints) const;
void call(double a) const;
void call(double a, double b) const;
void call(double a, double b, double c) const;
void call(double a, double b, double c, double d) const;
void call(bool b) const;
private:
void *m_callback;
};
} // namespace Slic3r
#endif // slic3r_Utils_hpp_

View file

@ -48,14 +48,6 @@ typedef double coordf_t;
//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
#define scale_(val) ((val) / SCALING_FACTOR)
#define SCALED_EPSILON scale_(EPSILON)
/* Implementation of CONFESS("foo"): */
#ifdef _MSC_VER
#define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#else
#define CONFESS(...) confess_at(__FILE__, __LINE__, __func__, __VA_ARGS__)
#endif
void confess_at(const char *file, int line, const char *func, const char *pat, ...);
/* End implementation of CONFESS("foo"): */
// Which C++ version is supported?
// For example, could optimized functions with move semantics be used?

View file

@ -23,6 +23,9 @@
#include <boost/nowide/fstream.hpp>
#include <boost/nowide/integration/filesystem.hpp>
#include <boost/nowide/convert.hpp>
#include <boost/nowide/cstdio.hpp>
#include <tbb/task_scheduler_init.h>
#include <tbb/task_scheduler_init.h>
@ -149,188 +152,88 @@ const std::string& data_dir()
return g_data_dir;
}
} // namespace Slic3r
#include <xsinit.h>
void
confess_at(const char *file, int line, const char *func,
const char *pat, ...)
// borrowed from LVVM lib/Support/Windows/Path.inc
int rename_file(const std::string &from, const std::string &to)
{
#ifdef SLIC3RXS
va_list args;
SV *error_sv = newSVpvf("Error in function %s at %s:%d: ", func,
file, line);
int ec = 0;
va_start(args, pat);
sv_vcatpvf(error_sv, pat, &args);
va_end(args);
#ifdef _WIN32
sv_catpvn(error_sv, "\n\t", 2);
// Convert to utf-16.
std::wstring wide_from = boost::nowide::widen(from);
std::wstring wide_to = boost::nowide::widen(to);
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs( sv_2mortal(error_sv) );
PUTBACK;
call_pv("Carp::confess", G_DISCARD);
FREETMPS;
LEAVE;
#endif
}
// Retry while we see recoverable errors.
// System scanners (eg. indexer) might open the source file when it is written
// and closed.
bool TryReplace = true;
void PerlCallback::register_callback(void *sv)
{
if (! SvROK((SV*)sv) || SvTYPE(SvRV((SV*)sv)) != SVt_PVCV)
croak("Not a Callback %_ for PerlFunction", (SV*)sv);
if (m_callback)
SvSetSV((SV*)m_callback, (SV*)sv);
else
m_callback = newSVsv((SV*)sv);
}
void PerlCallback::deregister_callback()
{
if (m_callback) {
sv_2mortal((SV*)m_callback);
m_callback = nullptr;
}
}
void PerlCallback::call() const
{
if (! m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(int i) const
{
if (! m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(i)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(int i, int j) const
{
if (! m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(i)));
XPUSHs(sv_2mortal(newSViv(j)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(const std::vector<int>& ints) const
{
if (! m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
for (int i : ints)
{
XPUSHs(sv_2mortal(newSViv(i)));
// This loop may take more than 2000 x 1ms to finish.
for (int i = 0; i < 2000; ++ i) {
if (i > 0)
// Sleep 1ms
::Sleep(1);
if (TryReplace) {
// Try ReplaceFile first, as it is able to associate a new data stream
// with the destination even if the destination file is currently open.
if (::ReplaceFileW(wide_to.data(), wide_from.data(), NULL, 0, NULL, NULL))
return 0;
DWORD ReplaceError = ::GetLastError();
ec = -1; // ReplaceError
// If ReplaceFileW returned ERROR_UNABLE_TO_MOVE_REPLACEMENT or
// ERROR_UNABLE_TO_MOVE_REPLACEMENT_2, retry but only use MoveFileExW().
if (ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT ||
ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2) {
TryReplace = false;
continue;
}
// If ReplaceFileW returned ERROR_UNABLE_TO_REMOVE_REPLACED, retry
// using ReplaceFileW().
if (ReplaceError == ERROR_UNABLE_TO_REMOVE_REPLACED)
continue;
// We get ERROR_FILE_NOT_FOUND if the destination file is missing.
// MoveFileEx can handle this case.
if (ReplaceError != ERROR_ACCESS_DENIED && ReplaceError != ERROR_FILE_NOT_FOUND && ReplaceError != ERROR_SHARING_VIOLATION)
break;
}
if (::MoveFileExW(wide_from.c_str(), wide_to.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING))
return 0;
DWORD MoveError = ::GetLastError();
ec = -1; // MoveError
if (MoveError != ERROR_ACCESS_DENIED && MoveError != ERROR_SHARING_VIOLATION)
break;
}
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
#else
boost::nowide::remove(to.c_str());
ec = boost::nowide::rename(from.c_str(), to.c_str());
#endif
return ec;
}
void PerlCallback::call(double a) const
int copy_file(const std::string &from, const std::string &to)
{
if (!m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVnv(a)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
const boost::filesystem::path source(from);
const boost::filesystem::path target(to);
static const auto perms = boost::filesystem::owner_read | boost::filesystem::owner_write | boost::filesystem::group_read | boost::filesystem::others_read; // aka 644
// Make sure the file has correct permission both before and after we copy over it.
try {
if (boost::filesystem::exists(target))
boost::filesystem::permissions(target, perms);
boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists);
boost::filesystem::permissions(target, perms);
} catch (std::exception & /* ex */) {
return -1;
}
return 0;
}
void PerlCallback::call(double a, double b) const
{
if (!m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVnv(a)));
XPUSHs(sv_2mortal(newSVnv(b)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(double a, double b, double c) const
{
if (!m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVnv(a)));
XPUSHs(sv_2mortal(newSVnv(b)));
XPUSHs(sv_2mortal(newSVnv(c)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(double a, double b, double c, double d) const
{
if (!m_callback)
return;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVnv(a)));
XPUSHs(sv_2mortal(newSVnv(b)));
XPUSHs(sv_2mortal(newSVnv(c)));
XPUSHs(sv_2mortal(newSVnv(d)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
LEAVE;
}
void PerlCallback::call(bool b) const
{
call(b ? 1 : 0);
}
} // namespace Slic3r
#ifdef WIN32
#ifndef NOMINMAX

View file

@ -55,6 +55,7 @@ REGISTER_CLASS(SurfaceCollection, "Surface::Collection");
REGISTER_CLASS(PrintObjectSupportMaterial, "Print::SupportMaterial2");
REGISTER_CLASS(TriangleMesh, "TriangleMesh");
REGISTER_CLASS(AppConfig, "GUI::AppConfig");
REGISTER_CLASS(BackgroundSlicingProcess, "GUI::BackgroundSlicingProcess");
REGISTER_CLASS(GLShader, "GUI::_3DScene::GLShader");
REGISTER_CLASS(GLVolume, "GUI::_3DScene::GLVolume");
REGISTER_CLASS(GLVolumeCollection, "GUI::_3DScene::GLVolume::Collection");
@ -62,6 +63,7 @@ REGISTER_CLASS(Preset, "GUI::Preset");
REGISTER_CLASS(PresetCollection, "GUI::PresetCollection");
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
REGISTER_CLASS(TabIface, "GUI::Tab");
REGISTER_CLASS(PreviewIface, "GUI::Preview");
REGISTER_CLASS(ProgressStatusBar, "GUI::ProgressStatusBar");
REGISTER_CLASS(PresetUpdater, "PresetUpdater");
REGISTER_CLASS(AppController, "AppController");

View file

@ -11,15 +11,17 @@
#include <ModelArrange.hpp>
#include <slic3r/GUI/PresetBundle.hpp>
#include <Geometry.hpp>
#include <PrintConfig.hpp>
#include <Print.hpp>
#include <PrintExport.hpp>
#include <Geometry.hpp>
#include <Model.hpp>
#include <Utils.hpp>
namespace Slic3r {
class AppControllerBoilerplate::PriData {
class AppControllerGui::PriData {
public:
std::mutex m;
std::thread::id ui_thread;
@ -27,39 +29,234 @@ public:
inline explicit PriData(std::thread::id uit): ui_thread(uit) {}
};
AppControllerBoilerplate::AppControllerBoilerplate()
:pri_data_(new PriData(std::this_thread::get_id())) {}
AppControllerGui::AppControllerGui()
:m_pri_data(new PriData(std::this_thread::get_id())) {}
AppControllerBoilerplate::~AppControllerBoilerplate() {
pri_data_.reset();
AppControllerGui::~AppControllerGui() {
m_pri_data.reset();
}
bool AppControllerBoilerplate::is_main_thread() const
bool AppControllerGui::is_main_thread() const
{
return pri_data_->ui_thread == std::this_thread::get_id();
return m_pri_data->ui_thread == std::this_thread::get_id();
}
namespace GUI {
PresetBundle* get_preset_bundle();
}
AppControllerBoilerplate::ProgresIndicatorPtr
AppControllerBoilerplate::global_progress_indicator() {
static const PrintObjectStep STEP_SLICE = posSlice;
static const PrintObjectStep STEP_PERIMETERS = posPerimeters;
static const PrintObjectStep STEP_PREPARE_INFILL = posPrepareInfill;
static const PrintObjectStep STEP_INFILL = posInfill;
static const PrintObjectStep STEP_SUPPORTMATERIAL = posSupportMaterial;
static const PrintStep STEP_SKIRT = psSkirt;
static const PrintStep STEP_BRIM = psBrim;
static const PrintStep STEP_WIPE_TOWER = psWipeTower;
ProgresIndicatorPtr AppControllerGui::global_progress_indicator() {
ProgresIndicatorPtr ret;
pri_data_->m.lock();
ret = global_progressind_;
pri_data_->m.unlock();
m_pri_data->m.lock();
ret = m_global_progressind;
m_pri_data->m.unlock();
return ret;
}
void AppControllerBoilerplate::global_progress_indicator(
AppControllerBoilerplate::ProgresIndicatorPtr gpri)
void AppControllerGui::global_progress_indicator(ProgresIndicatorPtr gpri)
{
pri_data_->m.lock();
global_progressind_ = gpri;
pri_data_->m.unlock();
m_pri_data->m.lock();
m_global_progressind = gpri;
m_pri_data->m.unlock();
}
PrintController::PngExportData
PrintController::query_png_export_data(const DynamicPrintConfig& conf)
{
PngExportData ret;
auto c = GUI::get_appctl();
auto zippath = c->query_destination_path("Output zip file", "*.zip",
"export-png",
"out");
ret.zippath = zippath;
ret.width_mm = conf.opt_float("display_width");
ret.height_mm = conf.opt_float("display_height");
ret.width_px = conf.opt_int("display_pixels_x");
ret.height_px = conf.opt_int("display_pixels_y");
auto opt_corr = conf.opt<ConfigOptionFloats>("printer_correction");
if(opt_corr) {
ret.corr_x = opt_corr->values[0];
ret.corr_y = opt_corr->values[1];
ret.corr_z = opt_corr->values[2];
}
ret.exp_time_first_s = conf.opt_float("initial_exposure_time");
ret.exp_time_s = conf.opt_float("exposure_time");
return ret;
}
void PrintController::slice(ProgresIndicatorPtr pri)
{
m_print->set_status_callback([pri](int st, const std::string& msg){
pri->update(unsigned(st), msg);
});
m_print->process();
}
void PrintController::slice()
{
auto ctl = GUI::get_appctl();
auto pri = ctl->global_progress_indicator();
if(!pri) pri = ctl->create_progress_indicator(100, L("Slicing"));
slice(pri);
}
template<> class LayerWriter<Zipper> {
Zipper m_zip;
public:
inline LayerWriter(const std::string& zipfile_path): m_zip(zipfile_path) {}
inline void next_entry(const std::string& fname) { m_zip.next_entry(fname); }
inline std::string get_name() const { return m_zip.get_name(); }
template<class T> inline LayerWriter& operator<<(const T& arg) {
m_zip.stream() << arg; return *this;
}
inline void close() { m_zip.close(); }
};
void PrintController::slice_to_png()
{
using Pointf3 = Vec3d;
auto ctl = GUI::get_appctl();
auto presetbundle = GUI::get_preset_bundle();
assert(presetbundle);
// FIXME: this crashes in command line mode
auto pt = presetbundle->printers.get_selected_preset().printer_technology();
if(pt != ptSLA) {
ctl->report_issue(IssueType::ERR, L("Printer technology is not SLA!"),
L("Error"));
return;
}
auto conf = presetbundle->full_config();
conf.validate();
auto exd = query_png_export_data(conf);
if(exd.zippath.empty()) return;
Print *print = m_print;
try {
print->apply_config(conf);
print->validate();
} catch(std::exception& e) {
ctl->report_issue(IssueType::ERR, e.what(), "Error");
return;
}
// TODO: copy the model and work with the copy only
bool correction = false;
if(exd.corr_x != 1.0 || exd.corr_y != 1.0 || exd.corr_z != 1.0) {
correction = true;
// print->invalidate_all_steps();
// for(auto po : print->objects) {
// po->model_object()->scale(
// Pointf3(exd.corr_x, exd.corr_y, exd.corr_z)
// );
// po->model_object()->invalidate_bounding_box();
// po->reload_model_instances();
// po->invalidate_all_steps();
// }
}
// Turn back the correction scaling on the model.
auto scale_back = [this, print, correction, exd]() {
if(correction) { // scale the model back
// print->invalidate_all_steps();
// for(auto po : print->objects) {
// po->model_object()->scale(
// Pointf3(1.0/exd.corr_x, 1.0/exd.corr_y, 1.0/exd.corr_z)
// );
// po->model_object()->invalidate_bounding_box();
// po->reload_model_instances();
// po->invalidate_all_steps();
// }
}
};
auto print_bb = print->bounding_box();
Vec2d punsc = unscale(print_bb.size());
// If the print does not fit into the print area we should cry about it.
if(px(punsc) > exd.width_mm || py(punsc) > exd.height_mm) {
std::stringstream ss;
ss << L("Print will not fit and will be truncated!") << "\n"
<< L("Width needed: ") << px(punsc) << " mm\n"
<< L("Height needed: ") << py(punsc) << " mm\n";
if(!ctl->report_issue(IssueType::WARN_Q, ss.str(), L("Warning"))) {
scale_back();
return;
}
}
auto pri = ctl->create_progress_indicator(
200, L("Slicing to zipped png files..."));
pri->on_cancel([&print](){ print->cancel(); });
try {
pri->update(0, L("Slicing..."));
slice(pri);
} catch (std::exception& e) {
ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred"));
scale_back();
if(print->canceled()) print->restart();
return;
}
auto initstate = unsigned(pri->state());
print->set_status_callback([pri, initstate](int st, const std::string& msg)
{
pri->update(initstate + unsigned(st), msg);
});
try {
print_to<FilePrinterFormat::PNG, Zipper>( *print, exd.zippath,
exd.width_mm, exd.height_mm,
exd.width_px, exd.height_px,
exd.exp_time_s, exd.exp_time_first_s);
} catch (std::exception& e) {
ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred"));
}
scale_back();
if(print->canceled()) print->restart();
print->set_status_default();
}
const PrintConfig &PrintController::config() const
{
return m_print->config();
}
void ProgressIndicator::message_fmt(
@ -89,82 +286,81 @@ void ProgressIndicator::message_fmt(
message(ss.str());
}
const PrintConfig &PrintController::config() const
{
return print_->config;
}
void AppController::arrange_model()
{
auto ftr = std::async(
supports_asynch()? std::launch::async : std::launch::deferred,
[this]()
{
using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
unsigned count = 0;
for(auto obj : model_->objects) count += obj->instances.size();
auto ctl = GUI::get_appctl();
auto pind = global_progress_indicator();
if(m_arranging.load()) return;
float pmax = 1.0;
// to prevent UI reentrancies
m_arranging.store(true);
if(pind) {
pmax = pind->max();
unsigned count = 0;
for(auto obj : m_model->objects) count += obj->instances.size();
// Set the range of the progress to the object count
pind->max(count);
auto pind = ctl->global_progress_indicator();
}
float pmax = 1.0;
auto dist = print_ctl()->config().min_object_distance();
if(pind) {
pmax = pind->max();
// Create the arranger config
auto min_obj_distance = static_cast<Coord>(dist/SCALING_FACTOR);
// Set the range of the progress to the object count
pind->max(count);
auto& bedpoints = print_ctl()->config().bed_shape.values;
Polyline bed; bed.points.reserve(bedpoints.size());
for(auto& v : bedpoints)
bed.append(Point::new_scale(v(0), v(1)));
if(pind) pind->update(0, L("Arranging objects..."));
try {
arr::BedShapeHint hint;
// TODO: from Sasha from GUI
hint.type = arr::BedShapeType::WHO_KNOWS;
//FIXME merge error
/*
arr::arrange(*model_,
min_obj_distance,
bed,
hint,
false, // create many piles not just one pile
[pind, count](unsigned rem) {
if(pind)
pind->update(count - rem, L("Arranging objects..."));
});
*/
} catch(std::exception& e) {
std::cerr << e.what() << std::endl;
report_issue(IssueType::ERR,
L("Could not arrange model objects! "
"Some geometries may be invalid."),
L("Exception occurred"));
}
// Restore previous max value
if(pind) {
pind->max(pmax);
pind->update(0, L("Arranging done."));
}
});
while( ftr.wait_for(std::chrono::milliseconds(10))
!= std::future_status::ready) {
process_events();
pind->on_cancel([this](){
m_arranging.store(false);
});
}
auto dist = print_ctl()->config().min_object_distance();
// Create the arranger config
auto min_obj_distance = static_cast<Coord>(dist/SCALING_FACTOR);
auto& bedpoints = print_ctl()->config().bed_shape.values;
Polyline bed; bed.points.reserve(bedpoints.size());
for(auto& v : bedpoints)
bed.append(Point::new_scale(v(0), v(1)));
if(pind) pind->update(0, L("Arranging objects..."));
try {
arr::BedShapeHint hint;
// TODO: from Sasha from GUI
hint.type = arr::BedShapeType::WHO_KNOWS;
arr::arrange(*m_model,
min_obj_distance,
bed,
hint,
false, // create many piles not just one pile
[this, pind, &ctl, count](unsigned rem) {
if(pind)
pind->update(count - rem, L("Arranging objects..."));
ctl->process_events();
}, [this] () { return !m_arranging.load(); });
} catch(std::exception& e) {
std::cerr << e.what() << std::endl;
ctl->report_issue(IssueType::ERR,
L("Could not arrange model objects! "
"Some geometries may be invalid."),
L("Exception occurred"));
}
// Restore previous max value
if(pind) {
pind->max(pmax);
pind->update(0, m_arranging.load() ? L("Arranging done.") :
L("Arranging canceled."));
pind->on_cancel(/*remove cancel function*/);
}
m_arranging.store(false);
}
}

View file

@ -7,7 +7,9 @@
#include <atomic>
#include <iostream>
#include "ProgressIndicator.hpp"
#include "GUI/ProgressIndicator.hpp"
#include <PrintConfig.hpp>
namespace Slic3r {
@ -18,6 +20,21 @@ class PrintConfig;
class ProgressStatusBar;
class DynamicPrintConfig;
/// A Progress indicator object smart pointer
using ProgresIndicatorPtr = std::shared_ptr<ProgressIndicator>;
using FilePath = std::string;
using FilePathList = std::vector<FilePath>;
/// Common runtime issue types
enum class IssueType {
INFO,
WARN,
WARN_Q, // Warning with a question to continue
ERR,
FATAL
};
/**
* @brief A boilerplate class for creating application logic. It should provide
* features as issue reporting and progress indication, etc...
@ -30,34 +47,12 @@ class DynamicPrintConfig;
* UI toolkit dependencies. We can implement it with any UI framework or make it
* a cli client.
*/
class AppControllerBoilerplate {
class AppControllerBase {
public:
/// A Progress indicator object smart pointer
using ProgresIndicatorPtr = std::shared_ptr<ProgressIndicator>;
using Ptr = std::shared_ptr<AppControllerBase>;
private:
class PriData; // Some structure to store progress indication data
// Pimpl data for thread safe progress indication features
std::unique_ptr<PriData> pri_data_;
public:
AppControllerBoilerplate();
~AppControllerBoilerplate();
using Path = std::string;
using PathList = std::vector<Path>;
/// Common runtime issue types
enum class IssueType {
INFO,
WARN,
WARN_Q, // Warning with a question to continue
ERR,
FATAL
};
inline virtual ~AppControllerBase() {}
/**
* @brief Query some paths from the user.
@ -65,25 +60,30 @@ public:
* It should display a file chooser dialog in case of a UI application.
* @param title Title of a possible query dialog.
* @param extensions Recognized file extensions.
* @return Returns a list of paths choosed by the user.
* @return Returns a list of paths chosen by the user.
*/
PathList query_destination_paths(
virtual FilePathList query_destination_paths(
const std::string& title,
const std::string& extensions) const;
const std::string& extensions,
const std::string& functionid = "",
const std::string& hint = "") const = 0;
/**
* @brief Same as query_destination_paths but works for directories only.
*/
PathList query_destination_dirs(
const std::string& title) const;
virtual FilePathList query_destination_dirs(
const std::string& title,
const std::string& functionid = "",
const std::string& hint = "") const = 0;
/**
* @brief Same as query_destination_paths but returns only one path.
*/
Path query_destination_path(
virtual FilePath query_destination_path(
const std::string& title,
const std::string& extensions,
const std::string& hint = "") const;
const std::string& functionid = "",
const std::string& hint = "") const = 0;
/**
* @brief Report an issue to the user be it fatal or recoverable.
@ -95,12 +95,9 @@ public:
* @param brief A very brief description. Can be used for message dialog
* title.
*/
bool report_issue(IssueType issuetype,
const std::string& description,
const std::string& brief);
bool report_issue(IssueType issuetype,
const std::string& description);
virtual bool report_issue(IssueType issuetype,
const std::string& description,
const std::string& brief) = 0;
/**
* @brief Return the global progress indicator for the current controller.
@ -108,9 +105,9 @@ public:
*
* Only one thread should use the global indicator at a time.
*/
ProgresIndicatorPtr global_progress_indicator();
virtual ProgresIndicatorPtr global_progress_indicator() = 0;
void global_progress_indicator(ProgresIndicatorPtr gpri);
virtual void global_progress_indicator(ProgresIndicatorPtr gpri) = 0;
/**
* @brief A predicate telling the caller whether it is the thread that
@ -120,7 +117,7 @@ public:
* @return Return true for the same caller thread that created this
* object and false for every other.
*/
bool is_main_thread() const;
virtual bool is_main_thread() const = 0;
/**
* @brief The frontend supports asynch execution.
@ -136,11 +133,9 @@ public:
* @return true if a job or method can be executed asynchronously, false
* otherwise.
*/
bool supports_asynch() const;
virtual bool supports_asynch() const = 0;
void process_events();
protected:
virtual void process_events() = 0;
/**
* @brief Create a new progress indicator and return a smart pointer to it.
@ -149,29 +144,194 @@ protected:
* @param firstmsg The message for the first subtask to be displayed.
* @return Smart pointer to the created object.
*/
ProgresIndicatorPtr create_progress_indicator(
virtual ProgresIndicatorPtr create_progress_indicator(
unsigned statenum,
const std::string& title,
const std::string& firstmsg) const;
const std::string& firstmsg = "") const = 0;
};
ProgresIndicatorPtr create_progress_indicator(
/**
* @brief Implementation of AppControllerBase for the GUI app
*/
class AppControllerGui: public AppControllerBase {
private:
class PriData; // Some structure to store progress indication data
// Pimpl data for thread safe progress indication features
std::unique_ptr<PriData> m_pri_data;
public:
AppControllerGui();
virtual ~AppControllerGui();
virtual FilePathList query_destination_paths(
const std::string& title,
const std::string& extensions,
const std::string& functionid,
const std::string& hint) const override;
virtual FilePathList query_destination_dirs(
const std::string& /*title*/,
const std::string& /*functionid*/,
const std::string& /*hint*/) const override { return {}; }
virtual FilePath query_destination_path(
const std::string& title,
const std::string& extensions,
const std::string& functionid,
const std::string& hint) const override;
virtual bool report_issue(IssueType issuetype,
const std::string& description,
const std::string& brief = std::string()) override;
virtual ProgresIndicatorPtr global_progress_indicator() override;
virtual void global_progress_indicator(ProgresIndicatorPtr gpri) override;
virtual bool is_main_thread() const override;
virtual bool supports_asynch() const override;
virtual void process_events() override;
virtual ProgresIndicatorPtr create_progress_indicator(
unsigned statenum,
const std::string& title) const;
const std::string& title,
const std::string& firstmsg) const override;
protected:
// This is a global progress indicator placeholder. In the Slic3r UI it can
// contain the progress indicator on the statusbar.
ProgresIndicatorPtr global_progressind_;
ProgresIndicatorPtr m_global_progressind;
};
class AppControllerCli: public AppControllerBase {
class CliProgress : public ProgressIndicator {
std::string m_msg, m_title;
public:
virtual void message(const std::string& msg) override {
m_msg = msg;
}
virtual void title(const std::string& title) override {
m_title = title;
}
};
public:
AppControllerCli() {
std::cout << "Cli AppController ready..." << std::endl;
m_global_progressind = std::make_shared<CliProgress>();
}
virtual ~AppControllerCli() {}
virtual FilePathList query_destination_paths(
const std::string& /*title*/,
const std::string& /*extensions*/,
const std::string& /*functionid*/,
const std::string& /*hint*/) const override { return {}; }
virtual FilePathList query_destination_dirs(
const std::string& /*title*/,
const std::string& /*functionid*/,
const std::string& /*hint*/) const override { return {}; }
virtual FilePath query_destination_path(
const std::string& /*title*/,
const std::string& /*extensions*/,
const std::string& /*functionid*/,
const std::string& /*hint*/) const override { return "out.zip"; }
virtual bool report_issue(IssueType /*issuetype*/,
const std::string& description,
const std::string& brief) override {
std::cerr << brief << ": " << description << std::endl;
return true;
}
virtual ProgresIndicatorPtr global_progress_indicator() override {
return m_global_progressind;
}
virtual void global_progress_indicator(ProgresIndicatorPtr) override {}
virtual bool is_main_thread() const override { return true; }
virtual bool supports_asynch() const override { return false; }
virtual void process_events() override {}
virtual ProgresIndicatorPtr create_progress_indicator(
unsigned /*statenum*/,
const std::string& /*title*/,
const std::string& /*firstmsg*/) const override {
return std::make_shared<CliProgress>();
}
protected:
// This is a global progress indicator placeholder. In the Slic3r UI it can
// contain the progress indicator on the statusbar.
ProgresIndicatorPtr m_global_progressind;
};
class Zipper {
struct Impl;
std::unique_ptr<Impl> m_impl;
public:
Zipper(const std::string& zipfilepath);
~Zipper();
void next_entry(const std::string& fname);
std::string get_name() const;
std::ostream& stream();
void close();
};
/**
* @brief Implementation of the printing logic.
*/
class PrintController: public AppControllerBoilerplate {
Print *print_ = nullptr;
class PrintController {
Print *m_print = nullptr;
std::function<void()> m_rempools;
protected:
// Data structure with the png export input data
struct PngExportData {
std::string zippath; // output zip file
unsigned long width_px = 1440; // resolution - rows
unsigned long height_px = 2560; // resolution columns
double width_mm = 68.0, height_mm = 120.0; // dimensions in mm
double exp_time_first_s = 35.0; // first exposure time
double exp_time_s = 8.0; // global exposure time
double corr_x = 1.0; // offsetting in x
double corr_y = 1.0; // offsetting in y
double corr_z = 1.0; // offsetting in y
};
// Should display a dialog with the input fields for printing to png
PngExportData query_png_export_data(const DynamicPrintConfig&);
// The previous export data, to pre-populate the dialog
PngExportData m_prev_expdata;
void slice(ProgresIndicatorPtr pri);
public:
// Must be public for perl to use it
explicit inline PrintController(Print *print): print_(print) {}
explicit inline PrintController(Print *print): m_print(print) {}
PrintController(const PrintController&) = delete;
PrintController(PrintController&&) = delete;
@ -182,9 +342,15 @@ public:
return PrintController::Ptr( new PrintController(print) );
}
//FIXME Vojtech: Merging error
void slice() {}
void slice_to_png() {}
/**
* @brief Slice the loaded print scene.
*/
void slice();
/**
* @brief Slice the print into zipped png files.
*/
void slice_to_png();
const PrintConfig& config() const;
};
@ -192,9 +358,10 @@ public:
/**
* @brief Top level controller.
*/
class AppController: public AppControllerBoilerplate {
Model *model_ = nullptr;
class AppController {
Model *m_model = nullptr;
PrintController::Ptr printctl;
std::atomic<bool> m_arranging;
public:
/**
@ -211,7 +378,7 @@ public:
* @param model A raw pointer to the model object. This can be used from
* perl.
*/
void set_model(Model *model) { model_ = model; }
void set_model(Model *model) { m_model = model; }
/**
* @brief Set the print object from perl.
@ -237,8 +404,7 @@ public:
* @param gauge_id The ID of the gague widget of the status bar.
* @param statusbar_id The ID of the status bar.
*/
void set_global_progress_indicator(unsigned gauge_id,
unsigned statusbar_id);
void set_global_progress_indicator(ProgressStatusBar *prs);
void arrange_model();
};

View file

@ -1,9 +1,14 @@
#include "AppController.hpp"
#include <wx/stdstream.h>
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
#include <thread>
#include <future>
#include <slic3r/GUI/GUI.hpp>
#include <slic3r/GUI/ProgressStatusBar.hpp>
#include <wx/app.h>
#include <wx/filedlg.h>
@ -20,40 +25,43 @@
namespace Slic3r {
bool AppControllerBoilerplate::supports_asynch() const
bool AppControllerGui::supports_asynch() const
{
return true;
}
void AppControllerBoilerplate::process_events()
void AppControllerGui::process_events()
{
wxSafeYield();
wxYieldIfNeeded();
}
AppControllerBoilerplate::PathList
AppControllerBoilerplate::query_destination_paths(
FilePathList AppControllerGui::query_destination_paths(
const std::string &title,
const std::string &extensions) const
const std::string &extensions,
const std::string &/*functionid*/,
const std::string& hint) const
{
wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
dlg.SetWildcard(extensions);
dlg.ShowModal();
dlg.SetFilename(hint);
wxArrayString paths;
dlg.GetPaths(paths);
FilePathList ret;
PathList ret(paths.size(), "");
for(auto& p : paths) ret.push_back(p.ToStdString());
if(dlg.ShowModal() == wxID_OK) {
wxArrayString paths;
dlg.GetPaths(paths);
for(auto& p : paths) ret.push_back(p.ToStdString());
}
return ret;
}
AppControllerBoilerplate::Path
AppControllerBoilerplate::query_destination_path(
FilePath AppControllerGui::query_destination_path(
const std::string &title,
const std::string &extensions,
const std::string &/*functionid*/,
const std::string& hint) const
{
wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
@ -61,16 +69,16 @@ AppControllerBoilerplate::query_destination_path(
dlg.SetFilename(hint);
Path ret;
FilePath ret;
if(dlg.ShowModal() == wxID_OK) {
ret = Path(dlg.GetPath());
ret = FilePath(dlg.GetPath());
}
return ret;
}
bool AppControllerBoilerplate::report_issue(IssueType issuetype,
bool AppControllerGui::report_issue(IssueType issuetype,
const std::string &description,
const std::string &brief)
{
@ -88,14 +96,52 @@ bool AppControllerBoilerplate::report_issue(IssueType issuetype,
return ret != wxCANCEL;
}
bool AppControllerBoilerplate::report_issue(
AppControllerBoilerplate::IssueType issuetype,
const std::string &description)
wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent);
struct Zipper::Impl {
wxFileName fpath;
wxFFileOutputStream zipfile;
wxZipOutputStream zipstream;
wxStdOutputStream pngstream;
Impl(const std::string& zipfile_path):
fpath(zipfile_path),
zipfile(zipfile_path),
zipstream(zipfile),
pngstream(zipstream)
{
if(!zipfile.IsOk())
throw std::runtime_error(L("Cannot create zip file."));
}
};
Zipper::Zipper(const std::string &zipfilepath)
{
return report_issue(issuetype, description, std::string());
m_impl.reset(new Impl(zipfilepath));
}
wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent);
Zipper::~Zipper() {}
void Zipper::next_entry(const std::string &fname)
{
m_impl->zipstream.PutNextEntry(fname);
}
std::string Zipper::get_name() const
{
return m_impl->fpath.GetName().ToStdString();
}
std::ostream &Zipper::stream()
{
return m_impl->pngstream;
}
void Zipper::close()
{
m_impl->zipstream.Close();
m_impl->zipfile.Close();
}
namespace {
@ -106,49 +152,52 @@ namespace {
class GuiProgressIndicator:
public ProgressIndicator, public wxEvtHandler {
wxProgressDialog gauge_;
wxProgressDialog m_gauge;
using Base = ProgressIndicator;
wxString message_;
int range_; wxString title_;
bool is_asynch_ = false;
wxString m_message;
int m_range; wxString m_title;
bool m_is_asynch = false;
const int id_ = wxWindow::NewControlId();
const int m_id = wxWindow::NewControlId();
// status update handler
void _state( wxCommandEvent& evt) {
unsigned st = evt.GetInt();
message_ = evt.GetString();
m_message = evt.GetString();
_state(st);
}
// Status update implementation
void _state( unsigned st) {
if(!gauge_.IsShown()) gauge_.ShowModal();
if(!m_gauge.IsShown()) m_gauge.ShowModal();
Base::state(st);
gauge_.Update(static_cast<int>(st), message_);
if(!m_gauge.Update(static_cast<int>(st), m_message)) {
cancel();
}
}
public:
/// Setting whether it will be used from the UI thread or some worker thread
inline void asynch(bool is) { is_asynch_ = is; }
inline void asynch(bool is) { m_is_asynch = is; }
/// Get the mode of parallel operation.
inline bool asynch() const { return is_asynch_; }
inline bool asynch() const { return m_is_asynch; }
inline GuiProgressIndicator(int range, const wxString& title,
const wxString& firstmsg) :
gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(),
wxPD_APP_MODAL | wxPD_AUTO_HIDE),
message_(firstmsg),
range_(range), title_(title)
m_gauge(title, firstmsg, range, wxTheApp->GetTopWindow(),
wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT),
m_message(firstmsg),
m_range(range), m_title(title)
{
Base::max(static_cast<float>(range));
Base::states(static_cast<unsigned>(range));
Bind(PROGRESS_STATUS_UPDATE_EVENT,
&GuiProgressIndicator::_state,
this, id_);
this, m_id);
}
virtual void state(float val) override {
@ -157,33 +206,32 @@ public:
void state(unsigned st) {
// send status update event
if(is_asynch_) {
auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_);
if(m_is_asynch) {
auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, m_id);
evt->SetInt(st);
evt->SetString(message_);
evt->SetString(m_message);
wxQueueEvent(this, evt);
} else _state(st);
}
virtual void message(const std::string & msg) override {
message_ = _(msg);
m_message = _(msg);
}
virtual void messageFmt(const std::string& fmt, ...) {
va_list arglist;
va_start(arglist, fmt);
message_ = wxString::Format(_(fmt), arglist);
m_message = wxString::Format(_(fmt), arglist);
va_end(arglist);
}
virtual void title(const std::string & title) override {
title_ = _(title);
m_title = _(title);
}
};
}
AppControllerBoilerplate::ProgresIndicatorPtr
AppControllerBoilerplate::create_progress_indicator(
ProgresIndicatorPtr AppControllerGui::create_progress_indicator(
unsigned statenum,
const std::string& title,
const std::string& firstmsg) const
@ -198,40 +246,23 @@ AppControllerBoilerplate::create_progress_indicator(
return pri;
}
AppControllerBoilerplate::ProgresIndicatorPtr
AppControllerBoilerplate::create_progress_indicator(
unsigned statenum, const std::string &title) const
{
return create_progress_indicator(statenum, title, std::string());
}
namespace {
// A wrapper progress indicator class around the statusbar created in perl.
class Wrapper: public ProgressIndicator, public wxEvtHandler {
wxGauge *gauge_;
wxStatusBar *stbar_;
ProgressStatusBar *m_sbar;
using Base = ProgressIndicator;
wxString message_;
AppControllerBoilerplate& ctl_;
wxString m_message;
AppControllerBase& m_ctl;
void showProgress(bool show = true) {
gauge_->Show(show);
m_sbar->show_progress(show);
}
void _state(unsigned st) {
if( st <= ProgressIndicator::max() ) {
Base::state(st);
if(!gauge_->IsShown()) showProgress(true);
stbar_->SetStatusText(message_);
if(static_cast<long>(st) == gauge_->GetRange()) {
gauge_->SetValue(0);
showProgress(false);
} else {
gauge_->SetValue(static_cast<int>(st));
}
m_sbar->set_status_text(m_message);
m_sbar->set_progress(st);
}
}
@ -244,12 +275,12 @@ class Wrapper: public ProgressIndicator, public wxEvtHandler {
public:
inline Wrapper(wxGauge *gauge, wxStatusBar *stbar,
AppControllerBoilerplate& ctl):
gauge_(gauge), stbar_(stbar), ctl_(ctl)
inline Wrapper(ProgressStatusBar *sbar,
AppControllerBase& ctl):
m_sbar(sbar), m_ctl(ctl)
{
Base::max(static_cast<float>(gauge->GetRange()));
Base::states(static_cast<unsigned>(gauge->GetRange()));
Base::max(static_cast<float>(m_sbar->get_range()));
Base::states(static_cast<unsigned>(m_sbar->get_range()));
Bind(PROGRESS_STATUS_UPDATE_EVENT,
&Wrapper::_state,
@ -262,13 +293,13 @@ public:
virtual void max(float val) override {
if(val > 1.0) {
gauge_->SetRange(static_cast<int>(val));
m_sbar->set_range(static_cast<int>(val));
ProgressIndicator::max(val);
}
}
void state(unsigned st) {
if(!ctl_.is_main_thread()) {
if(!m_ctl.is_main_thread()) {
auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_);
evt->SetInt(st);
wxQueueEvent(this, evt);
@ -278,30 +309,32 @@ public:
}
virtual void message(const std::string & msg) override {
message_ = _(msg);
m_message = _(msg);
}
virtual void message_fmt(const std::string& fmt, ...) override {
va_list arglist;
va_start(arglist, fmt);
message_ = wxString::Format(_(fmt), arglist);
m_message = wxString::Format(_(fmt), arglist);
va_end(arglist);
}
virtual void title(const std::string & /*title*/) override {}
virtual void on_cancel(CancelFn fn) override {
m_sbar->set_cancel_callback(fn);
Base::on_cancel(fn);
}
};
}
void AppController::set_global_progress_indicator(
unsigned gid,
unsigned sid)
void AppController::set_global_progress_indicator(ProgressStatusBar *prsb)
{
wxGauge* gauge = dynamic_cast<wxGauge*>(wxWindow::FindWindowById(gid));
wxStatusBar* sb = dynamic_cast<wxStatusBar*>(wxWindow::FindWindowById(sid));
if(gauge && sb) {
global_progressind_ = std::make_shared<Wrapper>(gauge, sb, *this);
if(prsb) {
auto ctl = GUI::get_appctl();
ctl->global_progress_indicator(std::make_shared<Wrapper>(prsb, *ctl));
}
}
}

View file

@ -196,7 +196,11 @@ const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f };
GLVolume::GLVolume(float r, float g, float b, float a)
: m_offset(Vec3d::Zero())
#if ENABLE_MODELINSTANCE_3D_ROTATION
, m_rotation(Vec3d::Zero())
#else
, m_rotation(0.0)
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
, m_scaling_factor(1.0)
, m_world_matrix(Transform3f::Identity())
, m_world_matrix_dirty(true)
@ -255,7 +259,24 @@ void GLVolume::set_render_color()
set_render_color(color, 4);
}
double GLVolume::get_rotation()
#if ENABLE_MODELINSTANCE_3D_ROTATION
const Vec3d& GLVolume::get_rotation() const
{
return m_rotation;
}
void GLVolume::set_rotation(const Vec3d& rotation)
{
if (m_rotation != rotation)
{
m_rotation = rotation;
m_world_matrix_dirty = true;
m_transformed_bounding_box_dirty = true;
m_transformed_convex_hull_bounding_box_dirty = true;
}
}
#else
double GLVolume::get_rotation() const
{
return m_rotation;
}
@ -270,6 +291,7 @@ void GLVolume::set_rotation(double rotation)
m_transformed_convex_hull_bounding_box_dirty = true;
}
}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
const Vec3d& GLVolume::get_offset() const
{
@ -327,7 +349,13 @@ const Transform3f& GLVolume::world_matrix() const
{
m_world_matrix = Transform3f::Identity();
m_world_matrix.translate(m_offset.cast<float>());
#if ENABLE_MODELINSTANCE_3D_ROTATION
m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation(2), Vec3f::UnitZ()));
m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation(1), Vec3f::UnitY()));
m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation(0), Vec3f::UnitX()));
#else
m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation, Vec3f::UnitZ()));
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
m_world_matrix.scale((float)m_scaling_factor);
m_world_matrix_dirty = false;
}
@ -403,7 +431,13 @@ void GLVolume::render() const
::glCullFace(GL_BACK);
::glPushMatrix();
::glTranslated(m_offset(0), m_offset(1), m_offset(2));
#if ENABLE_MODELINSTANCE_3D_ROTATION
::glRotated(m_rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0);
::glRotated(m_rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0);
::glRotated(m_rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0);
#else
::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor);
if (this->indexed_vertex_array.indexed())
this->indexed_vertex_array.render(this->tverts_range, this->qverts_range);
@ -529,7 +563,13 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c
::glPushMatrix();
::glTranslated(m_offset(0), m_offset(1), m_offset(2));
#if ENABLE_MODELINSTANCE_3D_ROTATION
::glRotated(m_rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0);
::glRotated(m_rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0);
::glRotated(m_rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0);
#else
::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor);
if (n_triangles > 0)
@ -574,7 +614,13 @@ void GLVolume::render_legacy() const
::glPushMatrix();
::glTranslated(m_offset(0), m_offset(1), m_offset(2));
#if ENABLE_MODELINSTANCE_3D_ROTATION
::glRotated(m_rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0);
::glRotated(m_rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0);
::glRotated(m_rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0);
#else
::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor);
if (n_triangles > 0)
@ -591,7 +637,7 @@ double GLVolume::layer_height_texture_z_to_row_id() const
return (this->layer_height_texture.get() == nullptr) ? 0.0 : double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * this->layer_height_texture_data.print_object->model_object()->bounding_box().max(2));
}
void GLVolume::generate_layer_height_texture(PrintObject *print_object, bool force)
void GLVolume::generate_layer_height_texture(const PrintObject *print_object, bool force)
{
LayersTexture *tex = this->layer_height_texture.get();
if (tex == nullptr)
@ -599,7 +645,7 @@ void GLVolume::generate_layer_height_texture(PrintObject *print_object, bool for
return;
// Always try to update the layer height profile.
bool update = print_object->update_layer_height_profile(print_object->model_object()->layer_height_profile) || force;
bool update = print_object->update_layer_height_profile(const_cast<ModelObject*>(print_object->model_object())->layer_height_profile) || force;
// Update if the layer height profile was changed, or when the texture is not valid.
if (! update && ! tex->data.empty() && tex->cells > 0)
// Texture is valid, don't update.
@ -698,7 +744,11 @@ std::vector<int> GLVolumeCollection::load_object(
#else
v.set_offset(Vec3d(instance->offset(0), instance->offset(1), 0.0));
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
v.set_rotation(instance->get_rotation());
#else
v.set_rotation(instance->rotation);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
v.set_scaling_factor(instance->scaling_factor);
}
}
@ -1687,7 +1737,7 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity,
if (extrusion_entity_collection != nullptr)
extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume);
else {
CONFESS("Unexpected extrusion_entity type in to_verts()");
throw std::runtime_error("Unexpected extrusion_entity type in to_verts()");
}
}
}
@ -2067,12 +2117,30 @@ void _3DScene::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, vo
void _3DScene::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback)
{
#if !ENABLE_MODELINSTANCE_3D_ROTATION
s_canvas_mgr.register_on_gizmo_rotate_callback(canvas, callback);
#endif // !ENABLE_MODELINSTANCE_3D_ROTATION
}
void _3DScene::register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback)
{
#if ENABLE_MODELINSTANCE_3D_ROTATION
s_canvas_mgr.register_on_gizmo_rotate_3D_callback(canvas, callback);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
}
void _3DScene::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback)
{
#if !ENABLE_MODELINSTANCE_3D_ROTATION
s_canvas_mgr.register_on_gizmo_flatten_callback(canvas, callback);
#endif // !ENABLE_MODELINSTANCE_3D_ROTATION
}
void _3DScene::register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback)
{
#if ENABLE_MODELINSTANCE_3D_ROTATION
s_canvas_mgr.register_on_gizmo_flatten_3D_callback(canvas, callback);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
}
void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback)

View file

@ -225,7 +225,7 @@ class GLVolume {
// ID of the shader used to render with the layer height texture
unsigned int shader_id;
// The print object to update when generating the layer height texture
PrintObject* print_object;
const PrintObject* print_object;
float z_cursor_relative;
float edit_band_width;
@ -256,8 +256,13 @@ public:
private:
// Offset of the volume to be rendered.
Vec3d m_offset;
#if ENABLE_MODELINSTANCE_3D_ROTATION
// Rotation around three axes of the volume to be rendered.
Vec3d m_rotation;
#else
// Rotation around Z axis of the volume to be rendered.
double m_rotation;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
// Scale factor of the volume to be rendered.
double m_scaling_factor;
// World matrix of the volume to be rendered.
@ -327,8 +332,13 @@ public:
// Sets render color in dependence of current state
void set_render_color();
double get_rotation();
#if ENABLE_MODELINSTANCE_3D_ROTATION
const Vec3d& get_rotation() const;
void set_rotation(const Vec3d& rotation);
#else
double get_rotation() const;
void set_rotation(double rotation);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
const Vec3d& get_offset() const;
void set_offset(const Vec3d& offset);
@ -382,9 +392,9 @@ public:
(void*)(layer_height_texture->data.data() + layer_height_texture->width * layer_height_texture->height * 4);
}
double layer_height_texture_z_to_row_id() const;
void generate_layer_height_texture(PrintObject *print_object, bool force);
void generate_layer_height_texture(const PrintObject *print_object, bool force);
void set_layer_height_texture_data(unsigned int texture_id, unsigned int shader_id, PrintObject* print_object, float z_cursor_relative, float edit_band_width)
void set_layer_height_texture_data(unsigned int texture_id, unsigned int shader_id, const PrintObject* print_object, float z_cursor_relative, float edit_band_width)
{
layer_height_texture_data.texture_id = texture_id;
layer_height_texture_data.shader_id = shader_id;
@ -558,7 +568,9 @@ public:
static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback);
static void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback);
static void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback);
static void register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback);
static void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback);
static void register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback);
static void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback);
static void register_action_add_callback(wxGLCanvas* canvas, void* callback);

View file

@ -0,0 +1,167 @@
#include "BackgroundSlicingProcess.hpp"
#include "GUI.hpp"
#include <wx/event.h>
#include <wx/panel.h>
#include <wx/stdpaths.h>
// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx.
#include "../../libslic3r/Print.hpp"
#include "../../libslic3r/Utils.hpp"
#include "../../libslic3r/GCode/PostProcessor.hpp"
//#undef NDEBUG
#include <cassert>
#include <stdexcept>
#include <boost/format.hpp>
#include <boost/nowide/cstdio.hpp>
namespace Slic3r {
namespace GUI {
extern wxPanel *g_wxPlater;
};
BackgroundSlicingProcess::BackgroundSlicingProcess()
{
m_temp_output_path = wxStandardPaths::Get().GetTempDir().utf8_str().data();
m_temp_output_path += (boost::format(".%1%.gcode") % get_current_pid()).str();
}
BackgroundSlicingProcess::~BackgroundSlicingProcess()
{
this->stop();
this->join_background_thread();
boost::nowide::remove(m_temp_output_path.c_str());
}
void BackgroundSlicingProcess::thread_proc()
{
std::unique_lock<std::mutex> lck(m_mutex);
// Let the caller know we are ready to run the background processing task.
m_state = STATE_IDLE;
lck.unlock();
m_condition.notify_one();
for (;;) {
assert(m_state == STATE_IDLE || m_state == STATE_CANCELED || m_state == STATE_FINISHED);
// Wait until a new task is ready to be executed, or this thread should be finished.
lck.lock();
m_condition.wait(lck, [this](){ return m_state == STATE_STARTED || m_state == STATE_EXIT; });
if (m_state == STATE_EXIT)
// Exiting this thread.
break;
// Process the background slicing task.
m_state = STATE_RUNNING;
lck.unlock();
std::string error;
try {
assert(m_print != nullptr);
m_print->process();
if (! m_print->canceled()) {
wxQueueEvent(GUI::g_wxPlater, new wxCommandEvent(m_event_sliced_id));
m_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
if (! m_print->canceled() && ! m_output_path.empty()) {
if (copy_file(m_temp_output_path, m_output_path) != 0)
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
m_print->set_status(95, "Running post-processing scripts");
run_post_process_scripts(m_output_path, m_print->config());
}
}
} catch (CanceledException &ex) {
// Canceled, this is all right.
assert(m_print->canceled());
} catch (std::exception &ex) {
error = ex.what();
} catch (...) {
error = "Unknown C++ exception.";
}
lck.lock();
m_state = m_print->canceled() ? STATE_CANCELED : STATE_FINISHED;
wxCommandEvent evt(m_event_finished_id);
evt.SetString(error);
evt.SetInt(m_print->canceled() ? -1 : (error.empty() ? 1 : 0));
wxQueueEvent(GUI::g_wxPlater, evt.Clone());
m_print->restart();
lck.unlock();
// Let the UI thread wake up if it is waiting for the background task to finish.
m_condition.notify_one();
// Let the UI thread see the result.
}
m_state = STATE_EXITED;
lck.unlock();
// End of the background processing thread. The UI thread should join m_thread now.
}
void BackgroundSlicingProcess::join_background_thread()
{
std::unique_lock<std::mutex> lck(m_mutex);
if (m_state == STATE_INITIAL) {
// Worker thread has not been started yet.
assert(! m_thread.joinable());
} else {
assert(m_state == STATE_IDLE);
assert(m_thread.joinable());
// Notify the worker thread to exit.
m_state = STATE_EXIT;
lck.unlock();
m_condition.notify_one();
// Wait until the worker thread exits.
m_thread.join();
}
}
bool BackgroundSlicingProcess::start()
{
std::unique_lock<std::mutex> lck(m_mutex);
if (m_state == STATE_INITIAL) {
// The worker thread is not running yet. Start it.
assert(! m_thread.joinable());
m_thread = std::thread([this]{this->thread_proc();});
// Wait until the worker thread is ready to execute the background processing task.
m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; });
}
assert(m_state == STATE_IDLE || this->running());
if (this->running())
// The background processing thread is already running.
return false;
if (! this->idle())
throw std::runtime_error("Cannot start a background task, the worker thread is not idle.");
m_state = STATE_STARTED;
lck.unlock();
m_condition.notify_one();
return true;
}
bool BackgroundSlicingProcess::stop()
{
std::unique_lock<std::mutex> lck(m_mutex);
if (m_state == STATE_INITIAL) {
this->m_output_path.clear();
return false;
}
assert(this->running());
if (m_state == STATE_STARTED || m_state == STATE_RUNNING) {
m_print->cancel();
// Wait until the background processing stops by being canceled.
m_condition.wait(lck, [this](){ return m_state == STATE_CANCELED; });
// In the "Canceled" state. Reset the state to "Idle".
m_state = STATE_IDLE;
} else if (m_state == STATE_FINISHED || m_state == STATE_CANCELED) {
// In the "Finished" or "Canceled" state. Reset the state to "Idle".
m_state = STATE_IDLE;
}
this->m_output_path.clear();
return true;
}
// Apply config over the print. Returns false, if the new config values caused any of the already
// processed steps to be invalidated, therefore the task will need to be restarted.
bool BackgroundSlicingProcess::apply_config(const DynamicPrintConfig &config)
{
this->stop();
bool invalidated = m_print->apply_config(config);
return invalidated;
}
}; // namespace Slic3r

View file

@ -0,0 +1,91 @@
#ifndef slic3r_GUI_BackgroundSlicingProcess_hpp_
#define slic3r_GUI_BackgroundSlicingProcess_hpp_
#include <string>
#include <condition_variable>
#include <mutex>
#include <thread>
namespace Slic3r {
class DynamicPrintConfig;
class GCodePreviewData;
class Print;
// Support for the GUI background processing (Slicing and G-code generation).
// As of now this class is not declared in Slic3r::GUI due to the Perl bindings limits.
class BackgroundSlicingProcess
{
public:
BackgroundSlicingProcess();
// Stop the background processing and finalize the bacgkround processing thread, remove temp files.
~BackgroundSlicingProcess();
void set_print(Print *print) { m_print = print; }
void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; }
// The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished
// and the background processing will transition into G-code export.
// The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed.
void set_sliced_event(int event_id) { m_event_sliced_id = event_id; }
// The following wxCommandEvent will be sent to the UI thread / Platter window, when the G-code export is finished.
// The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed.
void set_finished_event(int event_id) { m_event_finished_id = event_id; }
// Set the output path of the G-code.
void set_output_path(const std::string &path) { m_output_path = path; }
// Start the background processing. Returns false if the background processing was already running.
bool start();
// Cancel the background processing. Returns false if the background processing was not running.
// A stopped background processing may be restarted with start().
bool stop();
// Apply config over the print. Returns false, if the new config values caused any of the already
// processed steps to be invalidated, therefore the task will need to be restarted.
bool apply_config(const DynamicPrintConfig &config);
enum State {
// m_thread is not running yet, or it did not reach the STATE_IDLE yet (it does not wait on the condition yet).
STATE_INITIAL = 0,
// m_thread is waiting for the task to execute.
STATE_IDLE,
STATE_STARTED,
// m_thread is executing a task.
STATE_RUNNING,
// m_thread finished executing a task, and it is waiting until the UI thread picks up the results.
STATE_FINISHED,
// m_thread finished executing a task, the task has been canceled by the UI thread, therefore the UI thread will not be notified.
STATE_CANCELED,
// m_thread exited the loop and it is going to finish. The UI thread should join on m_thread.
STATE_EXIT,
STATE_EXITED,
};
State state() const { return m_state; }
bool idle() const { return m_state == STATE_IDLE; }
bool running() const { return m_state == STATE_STARTED || m_state == STATE_RUNNING || m_state == STATE_FINISHED || m_state == STATE_CANCELED; }
private:
void thread_proc();
void join_background_thread();
Print *m_print = nullptr;
// Data structure, to which the G-code export writes its annotations.
GCodePreviewData *m_gcode_preview_data = nullptr;
std::string m_temp_output_path;
std::string m_output_path;
// Thread, on which the background processing is executed. The thread will always be present
// and ready to execute the slicing process.
std::thread m_thread;
// Mutex and condition variable to synchronize m_thread with the UI thread.
std::mutex m_mutex;
std::condition_variable m_condition;
State m_state = STATE_INITIAL;
// wxWidgets command ID to be sent to the platter to inform that the slicing is finished, and the G-code export will continue.
int m_event_sliced_id = 0;
// wxWidgets command ID to be sent to the platter to inform that the task finished.
int m_event_finished_id = 0;
};
}; // namespace Slic3r
#endif /* slic3r_GUI_BackgroundSlicingProcess_hpp_ */

View file

@ -9,7 +9,6 @@
#include "../../slic3r/GUI/GLGizmo.hpp"
#include "../../libslic3r/ClipperUtils.hpp"
#include "../../libslic3r/PrintConfig.hpp"
#include "../../libslic3r/Print.hpp"
#include "../../libslic3r/GCode/PreviewData.hpp"
#include <GL/glew.h>
@ -21,6 +20,9 @@
#include <wx/image.h>
#include <wx/settings.h>
// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx.
#include "../../libslic3r/Print.hpp"
#include <tbb/parallel_for.h>
#include <tbb/spin_mutex.h>
@ -1031,7 +1033,7 @@ void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object,
// Get a maximum layer height value.
// FIXME This is a duplicate code of Slicing.cpp.
double layer_height_max = DBL_MAX;
const PrintConfig& print_config = print_object.print()->config;
const PrintConfig& print_config = print_object.print()->config();
const std::vector<double>& nozzle_diameters = dynamic_cast<const ConfigOptionFloats*>(print_config.option("nozzle_diameter"))->values;
const std::vector<double>& layer_heights_min = dynamic_cast<const ConfigOptionFloats*>(print_config.option("min_layer_height"))->values;
const std::vector<double>& layer_heights_max = dynamic_cast<const ConfigOptionFloats*>(print_config.option("max_layer_height"))->values;
@ -1046,7 +1048,7 @@ void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object,
layer_height_max *= 1.12;
double max_z = unscale<double>(print_object.size(2));
double layer_height = dynamic_cast<const ConfigOptionFloat*>(print_object.config.option("layer_height"))->value;
double layer_height = dynamic_cast<const ConfigOptionFloat*>(print_object.config().option("layer_height"))->value;
float l = bar_rect.get_left();
float w = bar_rect.get_right() - l;
float b = bar_rect.get_bottom();
@ -1095,6 +1097,9 @@ GLCanvas3D::Mouse::Drag::Drag()
GLCanvas3D::Mouse::Mouse()
: dragging(false)
, position(DBL_MAX, DBL_MAX)
#if ENABLE_GIZMOS_RESET
, ignore_up_event(false)
#endif // ENABLE_GIZMOS_RESET
{
}
@ -1181,9 +1186,11 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent)
return false;
}
#if !ENABLE_MODELINSTANCE_3D_ROTATION
// temporary disable x and y grabbers
gizmo->disable_grabber(0);
gizmo->disable_grabber(1);
#endif // !ENABLE_MODELINSTANCE_3D_ROTATION
m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo));
@ -1360,6 +1367,18 @@ void GLCanvas3D::Gizmos::update(const Linef3& mouse_ray, const Point* mouse_pos)
curr->update(mouse_ray, mouse_pos);
}
#if ENABLE_GIZMOS_RESET
void GLCanvas3D::Gizmos::process_double_click()
{
if (!m_enabled)
return;
GLGizmoBase* curr = _get_current();
if (curr != nullptr)
curr->process_double_click();
}
#endif // ENABLE_GIZMOS_RESET
GLCanvas3D::Gizmos::EType GLCanvas3D::Gizmos::get_current_type() const
{
return m_current;
@ -1432,6 +1451,35 @@ void GLCanvas3D::Gizmos::set_scale(float scale)
reinterpret_cast<GLGizmoScale3D*>(it->second)->set_scale(scale);
}
#if ENABLE_MODELINSTANCE_3D_ROTATION
Vec3d GLCanvas3D::Gizmos::get_rotation() const
{
if (!m_enabled)
return Vec3d::Zero();
GizmosMap::const_iterator it = m_gizmos.find(Rotate);
return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoRotate3D*>(it->second)->get_rotation() : Vec3d::Zero();
}
void GLCanvas3D::Gizmos::set_rotation(const Vec3d& rotation)
{
if (!m_enabled)
return;
GizmosMap::const_iterator it = m_gizmos.find(Rotate);
if (it != m_gizmos.end())
reinterpret_cast<GLGizmoRotate3D*>(it->second)->set_rotation(rotation);
}
Vec3d GLCanvas3D::Gizmos::get_flattening_rotation() const
{
if (!m_enabled)
return Vec3d::Zero();
GizmosMap::const_iterator it = m_gizmos.find(Flatten);
return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoFlatten*>(it->second)->get_flattening_rotation() : Vec3d::Zero();
}
#else
float GLCanvas3D::Gizmos::get_angle_z() const
{
if (!m_enabled)
@ -1459,6 +1507,7 @@ Vec3d GLCanvas3D::Gizmos::get_flattening_normal() const
GizmosMap::const_iterator it = m_gizmos.find(Flatten);
return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoFlatten*>(it->second)->get_flattening_normal() : Vec3d::Zero();
}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object)
{
@ -1619,7 +1668,7 @@ bool GLCanvas3D::WarningTexture::generate(const std::string& msg)
wxCoord w, h;
memDC.GetTextExtent(msg, &w, &h);
int pow_of_two_size = next_highest_power_of_2((int)std::max(w, h));
int pow_of_two_size = next_highest_power_of_2(std::max<unsigned int>(w, h));
m_original_width = (int)w;
m_original_height = (int)h;
@ -2428,7 +2477,11 @@ void GLCanvas3D::update_gizmos_data()
m_gizmos.set_position(Vec3d(model_instance->offset(0), model_instance->offset(1), 0.0));
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
m_gizmos.set_scale(model_instance->scaling_factor);
#if ENABLE_MODELINSTANCE_3D_ROTATION
m_gizmos.set_rotation(model_instance->get_rotation());
#else
m_gizmos.set_angle_z(model_instance->rotation);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
m_gizmos.set_flattening_data(model_object);
m_gizmos.set_model_object_ptr(model_object);
}
@ -2438,7 +2491,11 @@ void GLCanvas3D::update_gizmos_data()
{
m_gizmos.set_position(Vec3d::Zero());
m_gizmos.set_scale(1.0f);
#if ENABLE_MODELINSTANCE_3D_ROTATION
m_gizmos.set_rotation(Vec3d::Zero());
#else
m_gizmos.set_angle_z(0.0f);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
m_gizmos.set_flattening_data(nullptr);
}
}
@ -2626,11 +2683,11 @@ void GLCanvas3D::reload_scene(bool force)
float a = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_rotation_angle"))->value;
float depth = m_print->get_wipe_tower_depth();
if (!m_print->state.is_done(psWipeTower))
if (!m_print->is_step_done(psWipeTower))
depth = (900.f/w) * (float)(extruders_count - 1) ;
m_volumes.load_wipe_tower_preview(1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !m_print->state.is_done(psWipeTower),
m_print->config.nozzle_diameter.values[0] * 1.25f * 4.5f);
m_volumes.load_wipe_tower_preview(1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !m_print->is_step_done(psWipeTower),
m_print->config().nozzle_diameter.values[0] * 1.25f * 4.5f);
}
}
@ -2710,7 +2767,7 @@ void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors)
_load_print_toolpaths();
_load_wipe_tower_toolpaths(str_tool_colors);
for (const PrintObject* object : m_print->objects)
for (const PrintObject* object : m_print->objects())
{
if (object != nullptr)
_load_print_object_toolpaths(*object, str_tool_colors);
@ -2822,6 +2879,19 @@ void GLCanvas3D::register_on_gizmo_scale_uniformly_callback(void* callback)
m_on_gizmo_scale_uniformly_callback.register_callback(callback);
}
#if ENABLE_MODELINSTANCE_3D_ROTATION
void GLCanvas3D::register_on_gizmo_rotate_3D_callback(void* callback)
{
if (callback != nullptr)
m_on_gizmo_rotate_3D_callback.register_callback(callback);
}
void GLCanvas3D::register_on_gizmo_flatten_3D_callback(void* callback)
{
if (callback != nullptr)
m_on_gizmo_flatten_3D_callback.register_callback(callback);
}
#else
void GLCanvas3D::register_on_gizmo_rotate_callback(void* callback)
{
if (callback != nullptr)
@ -2833,6 +2903,7 @@ void GLCanvas3D::register_on_gizmo_flatten_callback(void* callback)
if (callback != nullptr)
m_on_gizmo_flatten_callback.register_callback(callback);
}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
void GLCanvas3D::register_on_update_geometry_info_callback(void* callback)
{
@ -3108,6 +3179,41 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_toolbar_action_running = true;
m_toolbar.do_action((unsigned int)toolbar_contains_mouse);
}
#if ENABLE_GIZMOS_RESET
else if (evt.LeftDClick() && m_gizmos.grabber_contains_mouse())
{
#if ENABLE_GIZMOS_RESET
m_mouse.ignore_up_event = true;
#endif // ENABLE_GIZMOS_RESET
m_gizmos.process_double_click();
switch (m_gizmos.get_current_type())
{
case Gizmos::Scale:
{
m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale());
update_scale_values();
m_dirty = true;
break;
}
case Gizmos::Rotate:
{
#if ENABLE_MODELINSTANCE_3D_ROTATION
const Vec3d& rotation = m_gizmos.get_rotation();
m_on_gizmo_rotate_3D_callback.call(rotation(0), rotation(1), rotation(2));
#else
m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z());
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
update_rotation_values();
m_dirty = true;
break;
}
default:
{
break;
}
}
}
#endif // ENABLE_GIZMOS_RESET
else if (evt.LeftDown() || evt.RightDown())
{
// If user pressed left or right button we first check whether this happened
@ -3126,7 +3232,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (evt.LeftDown())
{
// A volume is selected and the mouse is inside the reset button.
m_print->get_object(layer_editing_object_idx)->reset_layer_height_profile();
// The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself,
// therefore it is safe to call it while the background processing is running.
const_cast<PrintObject*>(m_print->get_object(layer_editing_object_idx))->reset_layer_height_profile();
// Index 2 means no editing, just wait for mouse up event.
m_layers_editing.state = LayersEditing::Completed;
@ -3155,6 +3263,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id(selected_object_idx);
if (m_gizmos.get_current_type() == Gizmos::Flatten) {
#if ENABLE_MODELINSTANCE_3D_ROTATION
// Rotate the object so the normal points downward:
const Vec3d& rotation = m_gizmos.get_flattening_rotation();
m_on_gizmo_flatten_3D_callback.call(rotation(0), rotation(1), rotation(2));
#else
// Rotate the object so the normal points downward:
Vec3d normal = m_gizmos.get_flattening_normal();
if (normal(0) != 0.0 || normal(1) != 0.0 || normal(2) != 0.0) {
@ -3162,6 +3275,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
float angle = acos(clamp(-1.0, 1.0, -normal(2)));
m_on_gizmo_flatten_callback.call(angle, (float)axis(0), (float)axis(1), (float)axis(2));
}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
}
m_dirty = true;
@ -3348,6 +3462,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
case Gizmos::Rotate:
{
#if ENABLE_MODELINSTANCE_3D_ROTATION
// Apply new temporary rotation
Vec3d rotation = m_gizmos.get_rotation();
for (GLVolume* v : volumes)
{
v->set_rotation(rotation);
}
update_rotation_value(rotation);
#else
// Apply new temporary angle_z
float angle_z = m_gizmos.get_angle_z();
for (GLVolume* v : volumes)
@ -3355,6 +3478,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
v->set_rotation((double)angle_z);
}
update_rotation_value((double)angle_z, Z);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
break;
}
default:
@ -3463,12 +3587,20 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled())
{
// deselect and propagate event through callback
#if ENABLE_GIZMOS_RESET
if (!m_mouse.ignore_up_event && m_picking_enabled && !m_toolbar_action_running)
#else
if (m_picking_enabled && !m_toolbar_action_running)
#endif // ENABLE_GIZMOS_RESET
{
deselect_volumes();
_on_select(-1, -1);
update_gizmos_data();
}
#if ENABLE_GIZMOS_RESET
else if (m_mouse.ignore_up_event)
m_mouse.ignore_up_event = false;
#endif // ENABLE_GIZMOS_RESET
}
else if (evt.LeftUp() && m_gizmos.is_dragging())
{
@ -3502,14 +3634,19 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
case Gizmos::Rotate:
{
#if ENABLE_MODELINSTANCE_3D_ROTATION
const Vec3d& rotation = m_gizmos.get_rotation();
m_on_gizmo_rotate_3D_callback.call(rotation(0), rotation(1), rotation(2));
#else
m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z());
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
break;
}
default:
break;
}
m_gizmos.stop_dragging();
Slic3r::GUI::update_settings_value();
update_settings_value();
}
m_mouse.drag.move_volume_idx = -1;
@ -3949,8 +4086,13 @@ void GLCanvas3D::_deregister_callbacks()
m_on_wipe_tower_moved_callback.deregister_callback();
m_on_enable_action_buttons_callback.deregister_callback();
m_on_gizmo_scale_uniformly_callback.deregister_callback();
#if ENABLE_MODELINSTANCE_3D_ROTATION
m_on_gizmo_rotate_3D_callback.deregister_callback();
m_on_gizmo_flatten_3D_callback.deregister_callback();
#else
m_on_gizmo_rotate_callback.deregister_callback();
m_on_gizmo_flatten_callback.deregister_callback();
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
m_on_update_geometry_info_callback.deregister_callback();
m_action_add_callback.deregister_callback();
@ -3977,7 +4119,7 @@ void GLCanvas3D::_mark_volumes_for_layer_height() const
int shader_id = m_layers_editing.get_shader_program_id();
if (is_layers_editing_enabled() && (shader_id != -1) && vol->selected &&
vol->has_layer_height_texture() && (object_id < (int)m_print->objects.size()))
vol->has_layer_height_texture() && (object_id < (int)m_print->objects().size()))
{
vol->set_layer_height_texture_data(m_layers_editing.get_z_texture_id(), shader_id,
m_print->get_object(object_id), _get_layers_editing_cursor_z_relative(), m_layers_editing.band_width);
@ -4215,7 +4357,7 @@ void GLCanvas3D::_render_layer_editing_overlay() const
// If the active object was not allocated at the Print, go away.This should only be a momentary case between an object addition / deletion
// and an update by Platter::async_apply_config.
int object_idx = int(volume->select_group_id / 1000000);
if ((int)m_print->objects.size() < object_idx)
if ((int)m_print->objects().size() < object_idx)
return;
const PrintObject* print_object = m_print->get_object(object_idx);
@ -4302,7 +4444,7 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt)
if (m_print == nullptr)
return;
PrintObject* selected_obj = m_print->get_object(object_idx_selected);
const PrintObject* selected_obj = m_print->get_object(object_idx_selected);
if (selected_obj == nullptr)
return;
@ -4317,14 +4459,15 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt)
// Mark the volume as modified, so Print will pick its layer height profile ? Where to mark it ?
// Start a timer to refresh the print ? schedule_background_process() ?
// The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself.
selected_obj->adjust_layer_height_profile(m_layers_editing.last_z, m_layers_editing.strength, m_layers_editing.band_width, m_layers_editing.last_action);
// The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself,
// therefore it is safe to call it while the background processing is running.
const_cast<PrintObject*>(selected_obj)->adjust_layer_height_profile(m_layers_editing.last_z, m_layers_editing.strength, m_layers_editing.band_width, m_layers_editing.last_action);
// searches the id of the first volume of the selected object
int volume_idx = 0;
for (int i = 0; i < object_idx_selected; ++i)
{
PrintObject* obj = m_print->get_object(i);
const PrintObject* obj = m_print->get_object(i);
if (obj != nullptr)
{
for (int j = 0; j < (int)obj->region_volumes.size(); ++j)
@ -4395,7 +4538,7 @@ int GLCanvas3D::_get_first_selected_object_id() const
{
if (m_print != nullptr)
{
int objects_count = (int)m_print->objects.size();
int objects_count = (int)m_print->objects().size();
for (const GLVolume* vol : m_volumes.volumes)
{
@ -4434,36 +4577,36 @@ void GLCanvas3D::_load_print_toolpaths()
if (m_print == nullptr)
return;
if (!m_print->state.is_done(psSkirt) || !m_print->state.is_done(psBrim))
if (!m_print->is_step_done(psSkirt) || !m_print->is_step_done(psBrim))
return;
if (!m_print->has_skirt() && (m_print->config.brim_width.value == 0))
if (!m_print->has_skirt() && (m_print->config().brim_width.value == 0))
return;
const float color[] = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish
// number of skirt layers
size_t total_layer_count = 0;
for (const PrintObject* print_object : m_print->objects)
for (const PrintObject* print_object : m_print->objects())
{
total_layer_count = std::max(total_layer_count, print_object->total_layer_count());
}
size_t skirt_height = m_print->has_infinite_skirt() ? total_layer_count : std::min<size_t>(m_print->config.skirt_height.value, total_layer_count);
if ((skirt_height == 0) && (m_print->config.brim_width.value > 0))
size_t skirt_height = m_print->has_infinite_skirt() ? total_layer_count : std::min<size_t>(m_print->config().skirt_height.value, total_layer_count);
if ((skirt_height == 0) && (m_print->config().brim_width.value > 0))
skirt_height = 1;
// get first skirt_height layers (maybe this should be moved to a PrintObject method?)
const PrintObject* object0 = m_print->objects.front();
const PrintObject* object0 = m_print->objects().front();
std::vector<float> print_zs;
print_zs.reserve(skirt_height * 2);
for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++i)
for (size_t i = 0; i < std::min(skirt_height, object0->layers().size()); ++i)
{
print_zs.push_back(float(object0->layers[i]->print_z));
print_zs.push_back(float(object0->layers()[i]->print_z));
}
//FIXME why there are support layers?
for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++i)
for (size_t i = 0; i < std::min(skirt_height, object0->support_layers().size()); ++i)
{
print_zs.push_back(float(object0->support_layers[i]->print_z));
print_zs.push_back(float(object0->support_layers()[i]->print_z));
}
sort_remove_duplicates(print_zs);
if (print_zs.size() > skirt_height)
@ -4476,9 +4619,9 @@ void GLCanvas3D::_load_print_toolpaths()
volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size());
volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size());
if (i == 0)
_3DScene::extrusionentity_to_verts(m_print->brim, print_zs[i], Point(0, 0), volume);
_3DScene::extrusionentity_to_verts(m_print->brim(), print_zs[i], Point(0, 0), volume);
_3DScene::extrusionentity_to_verts(m_print->skirt, print_zs[i], Point(0, 0), volume);
_3DScene::extrusionentity_to_verts(m_print->skirt(), print_zs[i], Point(0, 0), volume);
}
volume.bounding_box = volume.indexed_vertex_array.bounding_box();
volume.indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
@ -4517,20 +4660,20 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
}
} ctxt;
ctxt.shifted_copies = &print_object._shifted_copies;
ctxt.shifted_copies = &print_object.copies();
// order layers by print_z
ctxt.layers.reserve(print_object.layers.size() + print_object.support_layers.size());
for (const Layer *layer : print_object.layers)
ctxt.layers.reserve(print_object.layers().size() + print_object.support_layers().size());
for (const Layer *layer : print_object.layers())
ctxt.layers.push_back(layer);
for (const Layer *layer : print_object.support_layers)
for (const Layer *layer : print_object.support_layers())
ctxt.layers.push_back(layer);
std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; });
// Maximum size of an allocation block: 32MB / sizeof(float)
ctxt.has_perimeters = print_object.state.is_done(posPerimeters);
ctxt.has_infill = print_object.state.is_done(posInfill);
ctxt.has_support = print_object.state.is_done(posSupportMaterial);
ctxt.has_perimeters = print_object.is_step_done(posPerimeters);
ctxt.has_infill = print_object.is_step_done(posInfill);
ctxt.has_support = print_object.is_step_done(posSupportMaterial);
ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start";
@ -4570,10 +4713,10 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
}
}
for (const Point &copy : *ctxt.shifted_copies) {
for (const LayerRegion *layerm : layer->regions) {
for (const LayerRegion *layerm : layer->regions()) {
if (ctxt.has_perimeters)
_3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
*vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]);
*vols[ctxt.volume_idx(layerm->region()->config().perimeter_extruder.value, 0)]);
if (ctxt.has_infill) {
for (const ExtrusionEntity *ee : layerm->fills.entities) {
// fill represents infill extrusions of a single island.
@ -4582,8 +4725,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
_3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
*vols[ctxt.volume_idx(
is_solid_infill(fill->entities.front()->role()) ?
layerm->region()->config.solid_infill_extruder :
layerm->region()->config.infill_extruder,
layerm->region()->config().solid_infill_extruder :
layerm->region()->config().infill_extruder,
1)]);
}
}
@ -4595,8 +4738,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
_3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy,
*vols[ctxt.volume_idx(
(extrusion_entity->role() == erSupportMaterial) ?
support_layer->object()->config.support_material_extruder :
support_layer->object()->config.support_material_interface_extruder,
support_layer->object()->config().support_material_extruder :
support_layer->object()->config().support_material_interface_extruder,
2)]);
}
}
@ -4640,10 +4783,10 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors)
{
if ((m_print == nullptr) || m_print->m_wipe_tower_tool_changes.empty())
if ((m_print == nullptr) || m_print->wipe_tower_data().tool_changes.empty())
return;
if (!m_print->state.is_done(psWipeTower))
if (!m_print->is_step_done(psWipeTower))
return;
std::vector<float> tool_colors = _parse_colors(str_tool_colors);
@ -4671,9 +4814,10 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
}
const std::vector<WipeTower::ToolChangeResult>& tool_change(size_t idx) {
const auto &tool_changes = print->wipe_tower_data().tool_changes;
return priming.empty() ?
((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) :
((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]);
((idx == tool_changes.size()) ? final : tool_changes[idx]) :
((idx == 0) ? priming : (idx == tool_changes.size() + 1) ? final : tool_changes[idx - 1]);
}
std::vector<WipeTower::ToolChangeResult> priming;
std::vector<WipeTower::ToolChangeResult> final;
@ -4681,18 +4825,18 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
ctxt.print = m_print;
ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
if (m_print->m_wipe_tower_priming && m_print->config.single_extruder_multi_material_priming)
ctxt.priming.emplace_back(*m_print->m_wipe_tower_priming.get());
if (m_print->m_wipe_tower_final_purge)
ctxt.final.emplace_back(*m_print->m_wipe_tower_final_purge.get());
if (m_print->wipe_tower_data().priming && m_print->config().single_extruder_multi_material_priming)
ctxt.priming.emplace_back(*m_print->wipe_tower_data().priming.get());
if (m_print->wipe_tower_data().final_purge)
ctxt.final.emplace_back(*m_print->wipe_tower_data().final_purge.get());
ctxt.wipe_tower_angle = ctxt.print->config.wipe_tower_rotation_angle.value/180.f * PI;
ctxt.wipe_tower_pos = WipeTower::xy(ctxt.print->config.wipe_tower_x.value, ctxt.print->config.wipe_tower_y.value);
ctxt.wipe_tower_angle = ctxt.print->config().wipe_tower_rotation_angle.value/180.f * PI;
ctxt.wipe_tower_pos = WipeTower::xy(ctxt.print->config().wipe_tower_x.value, ctxt.print->config().wipe_tower_y.value);
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
//FIXME Improve the heuristics for a grain size.
size_t n_items = m_print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1);
size_t n_items = m_print->wipe_tower_data().tool_changes.size() + (ctxt.priming.empty() ? 0 : 1);
size_t grain_size = std::max(n_items / 128, size_t(1));
tbb::spin_mutex new_volume_mutex;
auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
@ -5297,15 +5441,15 @@ void GLCanvas3D::_load_shells()
size_t initial_volumes_count = m_volumes.volumes.size();
m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Shell, 0, (unsigned int)initial_volumes_count);
if (m_print->objects.empty())
if (m_print->objects().empty())
// nothing to render, return
return;
// adds objects' volumes
unsigned int object_id = 0;
for (PrintObject* obj : m_print->objects)
for (const PrintObject* obj : m_print->objects())
{
ModelObject* model_obj = obj->model_object();
const ModelObject* model_obj = obj->model_object();
std::vector<int> instance_ids(model_obj->instances.size());
for (int i = 0; i < (int)model_obj->instances.size(); ++i)
@ -5319,15 +5463,15 @@ void GLCanvas3D::_load_shells()
}
// adds wipe tower's volume
double max_z = m_print->objects[0]->model_object()->get_model()->bounding_box().max(2);
const PrintConfig& config = m_print->config;
double max_z = m_print->objects()[0]->model_object()->get_model()->bounding_box().max(2);
const PrintConfig& config = m_print->config();
unsigned int extruders_count = config.nozzle_diameter.size();
if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) {
float depth = m_print->get_wipe_tower_depth();
if (!m_print->state.is_done(psWipeTower))
if (!m_print->is_step_done(psWipeTower))
depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1) ;
m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
m_use_VBOs && m_initialized, !m_print->state.is_done(psWipeTower), m_print->config.nozzle_diameter.values[0] * 1.25f * 4.5f);
m_use_VBOs && m_initialized, !m_print->is_step_done(psWipeTower), m_print->config().nozzle_diameter.values[0] * 1.25f * 4.5f);
}
}

View file

@ -315,6 +315,9 @@ class GLCanvas3D
bool dragging;
Vec2d position;
Drag drag;
#if ENABLE_GIZMOS_RESET
bool ignore_up_event;
#endif // ENABLE_GIZMOS_RESET
Mouse();
@ -367,7 +370,9 @@ class GLCanvas3D
bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const;
bool grabber_contains_mouse() const;
void update(const Linef3& mouse_ray, const Point* mouse_pos = nullptr);
#if ENABLE_GIZMOS_RESET
void process_double_click();
#endif // ENABLE_GIZMOS_RESET
Rect get_reset_rect_viewport(const GLCanvas3D& canvas) const;
EType get_current_type() const;
@ -383,11 +388,20 @@ class GLCanvas3D
float get_scale() const;
void set_scale(float scale);
#if ENABLE_MODELINSTANCE_3D_ROTATION
Vec3d get_rotation() const;
void set_rotation(const Vec3d& rotation);
#else
float get_angle_z() const;
void set_angle_z(float angle_z);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
void set_flattening_data(const ModelObject* model_object);
#if ENABLE_MODELINSTANCE_3D_ROTATION
Vec3d get_flattening_rotation() const;
#else
Vec3d get_flattening_normal() const;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
void set_flattening_data(const ModelObject* model_object);
void set_model_object_ptr(const ModelObject* model_object);
void clicked_on_object(const Vec2d& mouse_position);
@ -508,8 +522,13 @@ class GLCanvas3D
PerlCallback m_on_wipe_tower_moved_callback;
PerlCallback m_on_enable_action_buttons_callback;
PerlCallback m_on_gizmo_scale_uniformly_callback;
#if ENABLE_MODELINSTANCE_3D_ROTATION
PerlCallback m_on_gizmo_rotate_3D_callback;
PerlCallback m_on_gizmo_flatten_3D_callback;
#else
PerlCallback m_on_gizmo_rotate_callback;
PerlCallback m_on_gizmo_flatten_callback;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
PerlCallback m_on_update_geometry_info_callback;
PerlCallback m_action_add_callback;
@ -635,8 +654,13 @@ public:
void register_on_wipe_tower_moved_callback(void* callback);
void register_on_enable_action_buttons_callback(void* callback);
void register_on_gizmo_scale_uniformly_callback(void* callback);
#if ENABLE_MODELINSTANCE_3D_ROTATION
void register_on_gizmo_rotate_3D_callback(void* callback);
void register_on_gizmo_flatten_3D_callback(void* callback);
#else
void register_on_gizmo_rotate_callback(void* callback);
void register_on_gizmo_flatten_callback(void* callback);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
void register_on_update_geometry_info_callback(void* callback);
void register_action_add_callback(void* callback);

View file

@ -699,6 +699,21 @@ void GLCanvas3DManager::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* c
it->second->register_on_gizmo_scale_uniformly_callback(callback);
}
#if ENABLE_MODELINSTANCE_3D_ROTATION
void GLCanvas3DManager::register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_on_gizmo_rotate_3D_callback(callback);
}
void GLCanvas3DManager::register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_on_gizmo_flatten_3D_callback(callback);
}
#else
void GLCanvas3DManager::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
@ -712,6 +727,7 @@ void GLCanvas3DManager::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, v
if (it != m_canvases.end())
it->second->register_on_gizmo_flatten_callback(callback);
}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
void GLCanvas3DManager::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback)
{

View file

@ -163,8 +163,13 @@ public:
void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback);
void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback);
void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback);
#if ENABLE_MODELINSTANCE_3D_ROTATION
void register_on_gizmo_rotate_3D_callback(wxGLCanvas* canvas, void* callback);
void register_on_gizmo_flatten_3D_callback(wxGLCanvas* canvas, void* callback);
#else
void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback);
void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback);
void register_action_add_callback(wxGLCanvas* canvas, void* callback);

View file

@ -695,6 +695,7 @@ void GLGizmoRotate3D::on_render(const BoundingBoxf3& box) const
}
const float GLGizmoScale3D::Offset = 5.0f;
const Vec3d GLGizmoScale3D::OffsetVec = (double)GLGizmoScale3D::Offset * Vec3d::Ones();
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent)
: GLGizmoBase(parent)
@ -744,7 +745,7 @@ void GLGizmoScale3D::on_start_dragging(const BoundingBoxf3& box)
{
m_starting_drag_position = m_grabbers[m_hover_id].center;
m_show_starting_box = true;
m_starting_box = box;
m_starting_box = BoundingBoxf3(box.min - OffsetVec, box.max + OffsetVec);
}
}
@ -760,6 +761,20 @@ void GLGizmoScale3D::on_update(const Linef3& mouse_ray, const Point* mouse_pos)
do_scale_uniform(mouse_ray);
}
#if ENABLE_GIZMOS_RESET
void GLGizmoScale3D::on_process_double_click()
{
if ((m_hover_id == 0) || (m_hover_id == 1))
m_scale(0) = 1.0;
else if ((m_hover_id == 2) || (m_hover_id == 3))
m_scale(1) = 1.0;
else if ((m_hover_id == 4) || (m_hover_id == 5))
m_scale(2) = 1.0;
else if (m_hover_id >= 6)
m_scale = Vec3d::Ones();
}
#endif // ENABLE_GIZMOS_RESET
void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
{
if (m_grabbers[0].dragging || m_grabbers[1].dragging)
@ -778,9 +793,7 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
::glEnable(GL_DEPTH_TEST);
Vec3d offset_vec = (double)Offset * Vec3d::Ones();
m_box = BoundingBoxf3(box.min - offset_vec, box.max + offset_vec);
m_box = BoundingBoxf3(box.min - OffsetVec, box.max + OffsetVec);
const Vec3d& center = m_box.center();
// x axis
@ -1240,15 +1253,28 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const
::glColor4f(0.9f, 0.9f, 0.9f, 0.5f);
#if ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
for (const InstanceData& inst : m_instances) {
Vec3d position = inst.position + dragged_offset;
#else
for (Vec3d offset : m_instances_positions) {
offset += dragged_offset;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#else
for (Vec2d offset : m_instances_positions) {
offset += to_2d(dragged_offset);
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
::glPushMatrix();
#if ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
::glTranslated(position(0), position(1), position(2));
::glRotated(inst.rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0);
::glRotated(inst.rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0);
::glRotated(inst.rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0);
::glScaled(inst.scaling_factor, inst.scaling_factor, inst.scaling_factor);
#else
::glTranslated(offset(0), offset(1), offset(2));
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#else
::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f);
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
@ -1271,13 +1297,25 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const
{
::glColor3f(1.0f, 1.0f, picking_color_component(i));
#if ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
for (const InstanceData& inst : m_instances) {
#else
for (const Vec3d& offset : m_instances_positions) {
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#else
for (const Vec2d& offset : m_instances_positions) {
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
::glPushMatrix();
#if ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
::glTranslated(inst.position(0), inst.position(1), inst.position(2));
::glRotated(inst.rotation(2) * 180.0 / (double)PI, 0.0, 0.0, 1.0);
::glRotated(inst.rotation(1) * 180.0 / (double)PI, 0.0, 1.0, 0.0);
::glRotated(inst.rotation(0) * 180.0 / (double)PI, 1.0, 0.0, 0.0);
::glScaled(inst.scaling_factor, inst.scaling_factor, inst.scaling_factor);
#else
::glTranslated(offset(0), offset(1), offset(2));
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#else
::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f);
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
@ -1296,10 +1334,18 @@ void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
// ...and save the updated positions of the object instances:
if (m_model_object && !m_model_object->instances.empty()) {
#if ENABLE_MODELINSTANCE_3D_ROTATION
m_instances.clear();
#else
m_instances_positions.clear();
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
for (const auto* instance : m_model_object->instances)
#if ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
m_instances.emplace_back(instance->get_offset(), instance->get_rotation(), instance->scaling_factor);
#else
m_instances_positions.emplace_back(instance->get_offset());
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#else
m_instances_positions.emplace_back(instance->offset);
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
@ -1315,8 +1361,10 @@ void GLGizmoFlatten::update_planes()
for (const ModelVolume* vol : m_model_object->volumes)
ch.merge(vol->get_convex_hull());
ch = ch.convex_hull_3d();
#if !ENABLE_MODELINSTANCE_3D_ROTATION
ch.scale(m_model_object->instances.front()->scaling_factor);
ch.rotate_z(m_model_object->instances.front()->rotation);
#endif // !ENABLE_MODELINSTANCE_3D_ROTATION
m_planes.clear();
@ -1361,8 +1409,8 @@ void GLGizmoFlatten::update_planes()
// if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected anyway):
if (m_planes.back().vertices.size() == 3 &&
(m_planes.back().vertices[0] - m_planes.back().vertices[1]).norm() < 1.f
|| (m_planes.back().vertices[0] - m_planes.back().vertices[2]).norm() < 1.f)
m_planes.pop_back();
|| (m_planes.back().vertices[0] - m_planes.back().vertices[2]).norm() < 1.f)
m_planes.pop_back();
}
// Now we'll go through all the polygons, transform the points into xy plane to process them:
@ -1465,8 +1513,10 @@ void GLGizmoFlatten::update_planes()
m_source_data.bounding_boxes.clear();
for (const auto& vol : m_model_object->volumes)
m_source_data.bounding_boxes.push_back(vol->get_convex_hull().bounding_box());
#if !ENABLE_MODELINSTANCE_3D_ROTATION
m_source_data.scaling_factor = m_model_object->instances.front()->scaling_factor;
m_source_data.rotation = m_model_object->instances.front()->rotation;
#endif // !ENABLE_MODELINSTANCE_3D_ROTATION
const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex();
m_source_data.mesh_first_point = Vec3d((double)first_vertex[0], (double)first_vertex[1], (double)first_vertex[2]);
}
@ -1478,10 +1528,14 @@ bool GLGizmoFlatten::is_plane_update_necessary() const
if (m_state != On || !m_model_object || m_model_object->instances.empty())
return false;
#if ENABLE_MODELINSTANCE_3D_ROTATION
if (m_model_object->volumes.size() != m_source_data.bounding_boxes.size())
#else
if (m_model_object->volumes.size() != m_source_data.bounding_boxes.size()
|| m_model_object->instances.front()->scaling_factor != m_source_data.scaling_factor
|| m_model_object->instances.front()->rotation != m_source_data.rotation)
return true;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
return true;
// now compare the bounding boxes:
for (unsigned int i=0; i<m_model_object->volumes.size(); ++i)
@ -1496,11 +1550,22 @@ bool GLGizmoFlatten::is_plane_update_necessary() const
return false;
}
#if ENABLE_MODELINSTANCE_3D_ROTATION
Vec3d GLGizmoFlatten::get_flattening_rotation() const
{
// calculates the rotations in model space
Eigen::Quaterniond q;
Vec3d angles = q.setFromTwoVectors(m_normal, -Vec3d::UnitZ()).toRotationMatrix().eulerAngles(2, 1, 0);
m_normal = Vec3d::Zero();
return Vec3d(angles(2), angles(1), angles(0));
}
#else
Vec3d GLGizmoFlatten::get_flattening_normal() const {
Vec3d normal = m_model_object->instances.front()->world_matrix(true).matrix().block(0, 0, 3, 3).inverse() * m_normal;
m_normal = Vec3d::Zero();
return normal.normalized();
}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION

View file

@ -94,6 +94,10 @@ public:
void update(const Linef3& mouse_ray, const Point* mouse_pos);
#if ENABLE_GIZMOS_RESET
void process_double_click() { on_process_double_click(); }
#endif // ENABLE_GIZMOS_RESET
void render(const BoundingBoxf3& box) const { on_render(box); }
void render_for_picking(const BoundingBoxf3& box) const { on_render_for_picking(box); }
@ -106,6 +110,9 @@ protected:
virtual void on_start_dragging(const BoundingBoxf3& box) {}
virtual void on_stop_dragging() {}
virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos) = 0;
#if ENABLE_GIZMOS_RESET
virtual void on_process_double_click() {}
#endif // ENABLE_GIZMOS_RESET
virtual void on_render(const BoundingBoxf3& box) const = 0;
virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0;
@ -155,6 +162,9 @@ protected:
virtual bool on_init();
virtual void on_start_dragging(const BoundingBoxf3& box);
virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos);
#if ENABLE_GIZMOS_RESET
virtual void on_process_double_click() { m_angle = 0.0; }
#endif // ENABLE_GIZMOS_RESET
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
@ -178,6 +188,10 @@ class GLGizmoRotate3D : public GLGizmoBase
public:
explicit GLGizmoRotate3D(GLCanvas3D& parent);
#if ENABLE_MODELINSTANCE_3D_ROTATION
Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); }
void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); }
#else
double get_angle_x() const { return m_gizmos[X].get_angle(); }
void set_angle_x(double angle) { m_gizmos[X].set_angle(angle); }
@ -186,6 +200,7 @@ public:
double get_angle_z() const { return m_gizmos[Z].get_angle(); }
void set_angle_z(double angle) { m_gizmos[Z].set_angle(angle); }
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
protected:
virtual bool on_init();
@ -222,6 +237,13 @@ protected:
g.update(mouse_ray, mouse_pos);
}
}
#if ENABLE_GIZMOS_RESET
virtual void on_process_double_click()
{
if (m_hover_id != -1)
m_gizmos[m_hover_id].process_double_click();
}
#endif // ENABLE_GIZMOS_RESET
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const
{
@ -265,6 +287,9 @@ protected:
virtual void on_start_dragging(const BoundingBoxf3& box);
virtual void on_stop_dragging() { m_show_starting_box = false; }
virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos);
#if ENABLE_GIZMOS_RESET
virtual void on_process_double_click();
#endif // ENABLE_GIZMOS_RESET
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
@ -320,8 +345,10 @@ private:
};
struct SourceDataSummary {
std::vector<BoundingBoxf3> bounding_boxes; // bounding boxes of convex hulls of individual volumes
#if !ENABLE_MODELINSTANCE_3D_ROTATION
float scaling_factor;
float rotation;
#endif // !ENABLE_MODELINSTANCE_3D_ROTATION
Vec3d mesh_first_point;
};
@ -330,7 +357,19 @@ private:
std::vector<PlaneData> m_planes;
#if ENABLE_MODELINSTANCE_3D_OFFSET
#if ENABLE_MODELINSTANCE_3D_ROTATION
struct InstanceData
{
Vec3d position;
Vec3d rotation;
double scaling_factor;
InstanceData(const Vec3d& position, const Vec3d& rotation, double scaling_factor) : position(position), rotation(rotation), scaling_factor(scaling_factor) {}
};
std::vector<InstanceData> m_instances;
#else
Pointf3s m_instances_positions;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
#else
std::vector<Vec2d> m_instances_positions;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
@ -344,7 +383,11 @@ public:
explicit GLGizmoFlatten(GLCanvas3D& parent);
void set_flattening_data(const ModelObject* model_object);
#if ENABLE_MODELINSTANCE_3D_ROTATION
Vec3d get_flattening_rotation() const;
#else
Vec3d get_flattening_normal() const;
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
protected:
virtual bool on_init();

View file

@ -2,7 +2,7 @@
#define slic3r_GLToolbar_hpp_
#include "../../slic3r/GUI/GLTexture.hpp"
#include "../../libslic3r/Utils.hpp"
#include "../../callback.hpp"
#include <string>
#include <vector>

View file

@ -1,4 +1,5 @@
#include "GUI.hpp"
#include "../AppController.hpp"
#include "WipeTowerDialog.hpp"
#include <assert.h>
@ -46,9 +47,12 @@
#include "Tab.hpp"
#include "TabIface.hpp"
#include "GUI_Preview.hpp"
#include "GUI_PreviewIface.hpp"
#include "AboutDialog.hpp"
#include "AppConfig.hpp"
#include "ConfigSnapshotDialog.hpp"
#include "ProgressStatusBar.hpp"
#include "Utils.hpp"
#include "MsgDialog.hpp"
#include "ConfigWizard.hpp"
@ -66,6 +70,8 @@
#include "Model.hpp"
#include "LambdaObjectDialog.hpp"
#include "../../libslic3r/Print.hpp"
namespace Slic3r { namespace GUI {
#if __APPLE__
@ -113,7 +119,9 @@ void break_to_debugger()
// Passing the wxWidgets GUI classes instantiated by the Perl part to C++.
wxApp *g_wxApp = nullptr;
wxFrame *g_wxMainFrame = nullptr;
ProgressStatusBar *g_progress_status_bar = nullptr;
wxNotebook *g_wxTabPanel = nullptr;
wxPanel *g_wxPlater = nullptr;
AppConfig *g_AppConfig = nullptr;
PresetBundle *g_PresetBundle= nullptr;
PresetUpdater *g_PresetUpdater = nullptr;
@ -143,6 +151,8 @@ wxStaticBitmap *g_manifold_warning_icon = nullptr;
bool g_show_print_info = false;
bool g_show_manifold_warning_icon = false;
PreviewIface* g_preview = nullptr;
static void init_label_colours()
{
auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
@ -199,11 +209,23 @@ void set_main_frame(wxFrame *main_frame)
wxFrame* get_main_frame() { return g_wxMainFrame; }
void set_progress_status_bar(ProgressStatusBar *prsb)
{
g_progress_status_bar = prsb;
}
ProgressStatusBar* get_progress_status_bar() { return g_progress_status_bar; }
void set_tab_panel(wxNotebook *tab_panel)
{
g_wxTabPanel = tab_panel;
}
void set_plater(wxPanel *plater)
{
g_wxPlater = plater;
}
void set_app_config(AppConfig *app_config)
{
g_AppConfig = app_config;
@ -607,13 +629,13 @@ void open_preferences_dialog(int event_preferences)
dlg->ShowModal();
}
void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed)
void create_preset_tabs(int event_value_change, int event_presets_changed)
{
update_label_colours_from_appconfig();
add_created_tab(new TabPrint (g_wxTabPanel, no_controller), event_value_change, event_presets_changed);
add_created_tab(new TabFilament (g_wxTabPanel, no_controller), event_value_change, event_presets_changed);
add_created_tab(new TabSLAMaterial (g_wxTabPanel, no_controller), event_value_change, event_presets_changed);
add_created_tab(new TabPrinter (g_wxTabPanel, no_controller), event_value_change, event_presets_changed);
add_created_tab(new TabPrint (g_wxTabPanel), event_value_change, event_presets_changed);
add_created_tab(new TabFilament (g_wxTabPanel), event_value_change, event_presets_changed);
add_created_tab(new TabSLAMaterial (g_wxTabPanel), event_value_change, event_presets_changed);
add_created_tab(new TabPrinter (g_wxTabPanel), event_value_change, event_presets_changed);
}
std::vector<PresetTab> preset_tabs = {
@ -648,6 +670,17 @@ TabIface* get_preset_tab_iface(char *name)
return new TabIface(nullptr);
}
PreviewIface* create_preview_iface(wxNotebook* parent, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data)
{
if (g_preview == nullptr)
{
Preview* panel = new Preview(parent, config, print, gcode_preview_data);
g_preview = new PreviewIface(panel);
}
return g_preview;
}
// opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element)
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/)
{
@ -809,6 +842,18 @@ void warning_catcher(wxWindow* parent, const wxString& message){
msg.ShowModal();
}
// Assign a Lambda to the print object to emit a wxWidgets Command with the provided ID
// to deliver a progress status message.
void set_print_callback_event(Print *print, int id)
{
print->set_status_callback([id](int percent, const std::string &message){
wxCommandEvent event(id);
event.SetInt(percent);
event.SetString(message);
wxQueueEvent(g_wxMainFrame, event.Clone());
});
}
wxApp* get_app(){
return g_wxApp;
}
@ -947,6 +992,12 @@ wxString from_u8(const std::string &str)
return wxString::FromUTF8(str.c_str());
}
std::string into_u8(const wxString &str)
{
auto buffer_utf8 = str.utf8_str();
return std::string(buffer_utf8.data());
}
void set_model_events_from_perl(Model &model,
int event_object_selection_changed,
int event_object_settings_changed,
@ -1355,4 +1406,23 @@ void desktop_open_datadir_folder()
#endif
}
namespace {
AppControllerPtr g_appctl;
}
AppControllerPtr get_appctl()
{
return g_appctl;
}
void set_cli_appctl()
{
g_appctl = std::make_shared<AppControllerCli>();
}
void set_gui_appctl()
{
g_appctl = std::make_shared<AppControllerGui>();
}
} }

View file

@ -4,7 +4,7 @@
#include <string>
#include <vector>
#include "PrintConfig.hpp"
#include "../../libslic3r/Utils.hpp"
#include "../../callback.hpp"
#include "GUI_ObjectParts.hpp"
#include <wx/intl.h>
@ -15,6 +15,7 @@ class wxWindow;
class wxFrame;
class wxMenuBar;
class wxNotebook;
class wxPanel;
class wxComboCtrl;
class wxString;
class wxArrayString;
@ -32,10 +33,18 @@ namespace Slic3r {
class PresetBundle;
class PresetCollection;
class Print;
class ProgressStatusBar;
class AppConfig;
class PresetUpdater;
class DynamicPrintConfig;
class TabIface;
class PreviewIface;
class Print;
class GCodePreviewData;
class AppControllerBase;
using AppControllerPtr = std::shared_ptr<AppControllerBase>;
#define _(s) Slic3r::GUI::I18N::translate((s))
@ -97,7 +106,9 @@ void break_to_debugger();
// Passing the wxWidgets GUI classes instantiated by the Perl part to C++.
void set_wxapp(wxApp *app);
void set_main_frame(wxFrame *main_frame);
void set_progress_status_bar(ProgressStatusBar *prsb);
void set_tab_panel(wxNotebook *tab_panel);
void set_plater(wxPanel *plater);
void set_app_config(AppConfig *app_config);
void set_preset_bundle(PresetBundle *preset_bundle);
void set_preset_updater(PresetUpdater *updater);
@ -117,9 +128,14 @@ AppConfig* get_app_config();
wxApp* get_app();
PresetBundle* get_preset_bundle();
wxFrame* get_main_frame();
ProgressStatusBar* get_progress_status_bar();
wxNotebook * get_tab_panel();
wxNotebook* get_tab_panel();
AppControllerPtr get_appctl();
void set_cli_appctl();
void set_gui_appctl();
const wxColour& get_label_clr_modified();
const wxColour& get_label_clr_sys();
const wxColour& get_label_clr_default();
@ -156,9 +172,11 @@ extern void config_wizard(int run_reason);
extern void open_preferences_dialog(int event_preferences);
// Create a new preset tab (print, filament and printer),
void create_preset_tabs(bool no_controller, int event_value_change, int event_presets_changed);
void create_preset_tabs(int event_value_change, int event_presets_changed);
TabIface* get_preset_tab_iface(char *name);
PreviewIface* create_preview_iface(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data);
// add it at the end of the tab panel.
void add_created_tab(Tab* panel, int event_value_change, int event_presets_changed);
// Change option value in config
@ -172,6 +190,10 @@ void show_error_id(int id, const std::string& message); // For Perl
void show_info(wxWindow* parent, const wxString& message, const wxString& title);
void warning_catcher(wxWindow* parent, const wxString& message);
// Assign a Lambda to the print object to emit a wxWidgets Command with the provided ID
// to deliver a progress status message.
void set_print_callback_event(Print *print, int id);
// load language saved at application config
bool load_language();
// save language at application config
@ -202,6 +224,8 @@ int combochecklist_get_flags(wxComboCtrl* comboCtrl);
wxString L_str(const std::string &str);
// Return wxString from std::string in UTF8
wxString from_u8(const std::string &str);
// Return std::string in UTF8 from wxString
std::string into_u8(const wxString &str);
void set_model_events_from_perl(Model &model,
int event_object_selection_changed,

View file

@ -387,13 +387,10 @@ void update_after_moving()
if (volume_id < 0)
return;
Vec3d m = m_move_options;
Vec3d l = m_last_coords;
auto d = Vec3d(m(0) - l(0), m(1) - l(1), m(2) - l(2));
auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
auto d = m_move_options - m_last_coords;
auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
volume->mesh.translate(d(0), d(1), d(2));
m_last_coords = m;
m_last_coords = m_move_options;
m_parts_changed = true;
parts_changed(m_selected_object_id);
@ -1805,11 +1802,15 @@ void update_scale_values(double scaling_factor)
void update_rotation_values()
{
#if ENABLE_MODELINSTANCE_3D_ROTATION
update_rotation_value((*m_objects)[m_selected_object_id]->instances.front()->get_rotation());
#else
auto og = get_optgroup(ogFrequentlyObjectSettings);
auto instance = (*m_objects)[m_selected_object_id]->instances.front();
og->set_value("rotation_x", 0);
og->set_value("rotation_y", 0);
og->set_value("rotation_z", int(Geometry::rad2deg(instance->rotation)));
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
}
void update_rotation_value(double angle, Axis axis)
@ -1836,9 +1837,19 @@ void update_rotation_value(double angle, Axis axis)
}
}
og->set_value(axis_str, int(Geometry::rad2deg(angle)));
og->set_value(axis_str, round_nearest(int(Geometry::rad2deg(angle)), 0));
}
#if ENABLE_MODELINSTANCE_3D_ROTATION
void update_rotation_value(const Vec3d& rotation)
{
auto og = get_optgroup(ogFrequentlyObjectSettings);
og->set_value("rotation_x", int(round_nearest(Geometry::rad2deg(rotation(0)), 0)));
og->set_value("rotation_y", int(round_nearest(Geometry::rad2deg(rotation(1)), 0)));
og->set_value("rotation_z", int(round_nearest(Geometry::rad2deg(rotation(2)), 0)));
}
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
void set_uniform_scaling(const bool uniform_scale)
{
g_is_uniform_scale = uniform_scale;

View file

@ -124,6 +124,9 @@ void update_scale_values(double scaling_factor);
void update_rotation_values();
// update rotation value after "gizmos"
void update_rotation_value(double angle, Axis axis);
#if ENABLE_MODELINSTANCE_3D_ROTATION
void update_rotation_value(const Vec3d& rotation);
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
void set_uniform_scaling(const bool uniform_scale);
void on_begin_drag(wxDataViewEvent &event);

View file

@ -0,0 +1,489 @@
#include "../../libslic3r/libslic3r.h"
#include "GUI_Preview.hpp"
#include "GUI.hpp"
#include "AppConfig.hpp"
#include "3DScene.hpp"
#include "../../libslic3r/GCode/PreviewData.hpp"
#include "PresetBundle.hpp"
#include <wx/notebook.h>
#include <wx/glcanvas.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/choice.h>
#include <wx/combo.h>
#include <wx/checkbox.h>
// this include must follow the wxWidgets ones or it won't compile on Windows -> see http://trac.wxwidgets.org/ticket/2421
#include "../../libslic3r/Print.hpp"
namespace Slic3r {
namespace GUI {
Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data)
: m_canvas(nullptr)
, m_double_slider_sizer(nullptr)
, m_label_view_type(nullptr)
, m_choice_view_type(nullptr)
, m_label_show_features(nullptr)
, m_combochecklist_features(nullptr)
, m_checkbox_travel(nullptr)
, m_checkbox_retractions(nullptr)
, m_checkbox_unretractions(nullptr)
, m_checkbox_shells(nullptr)
, m_config(config)
, m_print(print)
, m_gcode_preview_data(gcode_preview_data)
, m_number_extruders(1)
, m_preferred_color_mode("feature")
, m_loaded(false)
, m_enabled(false)
, m_force_sliders_full_range(false)
{
if (init(notebook, config, print, gcode_preview_data))
{
notebook->AddPage(this, _(L("Preview")));
show_hide_ui_elements("none");
load_print();
}
}
bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data)
{
if ((notebook == nullptr) || (config == nullptr) || (print == nullptr) || (gcode_preview_data == nullptr))
return false;
// creates this panel add append it to the given notebook as a new page
if (!Create(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize))
return false;
int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0 };
int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER;
const AppConfig* app_config = GUI::get_app_config();
bool enable_multisample = (app_config != nullptr) && (app_config->get("use_legacy_opengl") != "1") && (wxVersion >= 30003);
// if multisample is not enabled or supported by the graphic card, remove it from the attributes list
bool can_multisample = enable_multisample && wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample");
// bool can_multisample = enable_multisample && wxGLCanvas::IsDisplaySupported(attribList); // <<< Alternative method: but IsDisplaySupported() seems not to work
if (!can_multisample)
attribList[4] = 0;
m_canvas = new wxGLCanvas(this, wxID_ANY, attribList);
if (m_canvas == nullptr)
return false;
_3DScene::add_canvas(m_canvas);
_3DScene::allow_multisample(m_canvas, can_multisample);
_3DScene::enable_shader(m_canvas, true);
_3DScene::set_config(m_canvas, m_config);
_3DScene::set_print(m_canvas, m_print);
_3DScene::enable_legend_texture(m_canvas, true);
_3DScene::enable_dynamic_background(m_canvas, true);
m_double_slider_sizer = new wxBoxSizer(wxHORIZONTAL);
create_double_slider(this, m_double_slider_sizer, m_canvas);
m_label_view_type = new wxStaticText(this, wxID_ANY, _(L("View")));
m_choice_view_type = new wxChoice(this, wxID_ANY);
m_choice_view_type->Append(_(L("Feature type")));
m_choice_view_type->Append(_(L("Height")));
m_choice_view_type->Append(_(L("Width")));
m_choice_view_type->Append(_(L("Speed")));
m_choice_view_type->Append(_(L("Volumetric flow rate")));
m_choice_view_type->Append(_(L("Tool")));
m_choice_view_type->SetSelection(0);
m_label_show_features = new wxStaticText(this, wxID_ANY, _(L("Show")));
m_combochecklist_features = new wxComboCtrl();
m_combochecklist_features->Create(this, wxID_ANY, _(L("Feature types")), wxDefaultPosition, wxSize(200, -1), wxCB_READONLY);
std::string feature_text = GUI::into_u8(_(L("Feature types")));
std::string feature_items = GUI::into_u8(
_(L("Perimeter")) + "|" +
_(L("External perimeter")) + "|" +
_(L("Overhang perimeter")) + "|" +
_(L("Internal infill")) + "|" +
_(L("Solid infill")) + "|" +
_(L("Top solid infill")) + "|" +
_(L("Bridge infill")) + "|" +
_(L("Gap fill")) + "|" +
_(L("Skirt")) + "|" +
_(L("Support material")) + "|" +
_(L("Support material interface")) + "|" +
_(L("Wipe tower")) + "|" +
_(L("Custom"))
);
Slic3r::GUI::create_combochecklist(m_combochecklist_features, feature_text, feature_items, true);
m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel")));
m_checkbox_retractions = new wxCheckBox(this, wxID_ANY, _(L("Retractions")));
m_checkbox_unretractions = new wxCheckBox(this, wxID_ANY, _(L("Unretractions")));
m_checkbox_shells = new wxCheckBox(this, wxID_ANY, _(L("Shells")));
wxBoxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL);
top_sizer->Add(m_canvas, 1, wxALL | wxEXPAND, 0);
top_sizer->Add(m_double_slider_sizer, 0, wxEXPAND, 0);
wxBoxSizer* bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
bottom_sizer->Add(m_label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5);
bottom_sizer->Add(m_choice_view_type, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
bottom_sizer->AddSpacer(10);
bottom_sizer->Add(m_label_show_features, 0, wxALIGN_CENTER_VERTICAL, 5);
bottom_sizer->Add(m_combochecklist_features, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
bottom_sizer->AddSpacer(20);
bottom_sizer->Add(m_checkbox_travel, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
bottom_sizer->AddSpacer(10);
bottom_sizer->Add(m_checkbox_retractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
bottom_sizer->AddSpacer(10);
bottom_sizer->Add(m_checkbox_unretractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
bottom_sizer->AddSpacer(10);
bottom_sizer->Add(m_checkbox_shells, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(top_sizer, 1, wxALL | wxEXPAND, 0);
main_sizer->Add(bottom_sizer, 0, wxALL | wxEXPAND, 0);
SetSizer(main_sizer);
SetMinSize(GetSize());
GetSizer()->SetSizeHints(this);
bind_event_handlers();
// sets colors for gcode preview extrusion roles
std::vector<std::string> extrusion_roles_colors = {
"Perimeter", "FFFF66",
"External perimeter", "FFA500",
"Overhang perimeter", "0000FF",
"Internal infill", "B1302A",
"Solid infill", "D732D7",
"Top solid infill", "FF1A1A",
"Bridge infill", "9999FF",
"Gap fill", "FFFFFF",
"Skirt", "845321",
"Support material", "00FF00",
"Support material interface", "008000",
"Wipe tower", "B3E3AB",
"Custom", "28CC94"
};
m_gcode_preview_data->set_extrusion_paths_colors(extrusion_roles_colors);
return true;
}
Preview::~Preview()
{
unbind_event_handlers();
if (m_canvas != nullptr)
{
_3DScene::remove_canvas(m_canvas);
delete m_canvas;
}
}
void Preview::register_on_viewport_changed_callback(void* callback)
{
if ((m_canvas != nullptr) && (callback != nullptr))
_3DScene::register_on_viewport_changed_callback(m_canvas, callback);
}
void Preview::set_number_extruders(unsigned int number_extruders)
{
if (m_number_extruders != number_extruders)
{
m_number_extruders = number_extruders;
int type = 0; // color by a feature type
if (number_extruders > 1)
{
int tool_idx = m_choice_view_type->FindString(_(L("Tool")));
int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type
m_choice_view_type->SetSelection(type);
if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types))
m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type;
m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature";
}
}
}
void Preview::reset_gcode_preview_data()
{
m_gcode_preview_data->reset();
_3DScene::reset_legend_texture();
}
void Preview::set_canvas_as_dirty()
{
if (m_canvas != nullptr)
_3DScene::set_as_dirty(m_canvas);
}
void Preview::set_enabled(bool enabled)
{
m_enabled = enabled;
}
void Preview::set_bed_shape(const Pointfs& shape)
{
if (m_canvas != nullptr)
_3DScene::set_bed_shape(m_canvas, shape);
}
void Preview::select_view(const std::string& direction)
{
if (m_canvas != nullptr)
_3DScene::select_view(m_canvas, direction);
}
void Preview::set_viewport_from_scene(wxGLCanvas* canvas)
{
if ((m_canvas != nullptr) && (canvas != nullptr))
_3DScene::set_viewport_from_scene(m_canvas, canvas);
}
void Preview::set_viewport_into_scene(wxGLCanvas* canvas)
{
if ((m_canvas != nullptr) && (canvas != nullptr))
_3DScene::set_viewport_from_scene(canvas, m_canvas);
}
void Preview::set_drop_target(wxDropTarget* target)
{
if (target != nullptr)
SetDropTarget(target);
}
void Preview::load_print()
{
if (m_loaded)
return;
// we require that there's at least one object and the posSlice step
// is performed on all of them(this ensures that _shifted_copies was
// populated and we know the number of layers)
unsigned int n_layers = 0;
if (m_print->is_step_done(posSlice))
{
std::set<float> zs;
for (const PrintObject* print_object : m_print->objects())
{
const LayerPtrs& layers = print_object->layers();
const SupportLayerPtrs& support_layers = print_object->support_layers();
for (const Layer* layer : layers)
{
zs.insert(layer->print_z);
}
for (const SupportLayer* layer : support_layers)
{
zs.insert(layer->print_z);
}
}
n_layers = (unsigned int)zs.size();
}
if (n_layers == 0)
{
reset_sliders();
_3DScene::reset_legend_texture();
if (m_canvas)
m_canvas->Refresh();
return;
}
if (m_preferred_color_mode == "tool_or_feature")
{
// It is left to Slic3r to decide whether the print shall be colored by the tool or by the feature.
// Color by feature if it is a single extruder print.
unsigned int number_extruders = (unsigned int)m_print->extruders().size();
int tool_idx = m_choice_view_type->FindString(_(L("Tool")));
int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type
m_choice_view_type->SetSelection(type);
if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types))
m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type;
// If the->SetSelection changed the following line, revert it to "decide yourself".
m_preferred_color_mode = "tool_or_feature";
}
// Collect colors per extruder.
std::vector<std::string> colors;
if (!m_gcode_preview_data->empty() || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool))
{
const ConfigOptionStrings* extruders_opt = dynamic_cast<const ConfigOptionStrings*>(m_config->option("extruder_colour"));
const ConfigOptionStrings* filamemts_opt = dynamic_cast<const ConfigOptionStrings*>(m_config->option("filament_colour"));
unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size());
unsigned char rgb[3];
for (unsigned int i = 0; i < colors_count; ++i)
{
std::string color = m_config->opt_string("extruder_colour", i);
if (!PresetBundle::parse_color(color, rgb))
{
color = m_config->opt_string("filament_colour", i);
if (!PresetBundle::parse_color(color, rgb))
color = "#FFFFFF";
}
colors.push_back(color);
}
}
if (IsShown() && (m_canvas != nullptr))
{
// used to set the sliders to the extremes of the current zs range
m_force_sliders_full_range = false;
if (m_gcode_preview_data->empty())
{
// load skirt and brim
_3DScene::load_preview(m_canvas, colors);
show_hide_ui_elements("simple");
}
else
{
m_force_sliders_full_range = (_3DScene::get_volumes_count(m_canvas) == 0);
_3DScene::load_gcode_preview(m_canvas, m_gcode_preview_data, colors);
show_hide_ui_elements("full");
// recalculates zs and update sliders accordingly
n_layers = (unsigned int)_3DScene::get_current_print_zs(m_canvas, true).size();
if (n_layers == 0)
{
// all layers filtered out
reset_sliders();
m_canvas->Refresh();
}
}
if (n_layers > 0)
update_sliders();
m_loaded = true;
}
}
void Preview::reload_print(bool force)
{
_3DScene::reset_volumes(m_canvas);
m_loaded = false;
if (!IsShown() && !force)
return;
load_print();
}
void Preview::refresh_print()
{
m_loaded = false;
if (!IsShown())
return;
load_print();
}
void Preview::bind_event_handlers()
{
this->Bind(wxEVT_SIZE, &Preview::on_size, this);
m_choice_view_type->Bind(wxEVT_CHOICE, &Preview::on_choice_view_type, this);
m_combochecklist_features->Bind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this);
m_checkbox_travel->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this);
m_checkbox_retractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this);
m_checkbox_unretractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this);
m_checkbox_shells->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this);
}
void Preview::unbind_event_handlers()
{
this->Unbind(wxEVT_SIZE, &Preview::on_size, this);
m_choice_view_type->Unbind(wxEVT_CHOICE, &Preview::on_choice_view_type, this);
m_combochecklist_features->Unbind(wxEVT_CHECKLISTBOX, &Preview::on_combochecklist_features, this);
m_checkbox_travel->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_travel, this);
m_checkbox_retractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this);
m_checkbox_unretractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this);
m_checkbox_shells->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this);
}
void Preview::show_hide_ui_elements(const std::string& what)
{
bool enable = (what == "full");
m_label_show_features->Enable(enable);
m_combochecklist_features->Enable(enable);
m_checkbox_travel->Enable(enable);
m_checkbox_retractions->Enable(enable);
m_checkbox_unretractions->Enable(enable);
m_checkbox_shells->Enable(enable);
enable = (what != "none");
m_label_view_type->Enable(enable);
m_choice_view_type->Enable(enable);
}
void Preview::reset_sliders()
{
m_enabled = false;
reset_double_slider();
m_double_slider_sizer->Hide((size_t)0);
}
void Preview::update_sliders()
{
m_enabled = true;
update_double_slider(m_force_sliders_full_range);
m_double_slider_sizer->Show((size_t)0);
Layout();
}
void Preview::on_size(wxSizeEvent& evt)
{
evt.Skip();
Refresh();
}
void Preview::on_choice_view_type(wxCommandEvent& evt)
{
m_preferred_color_mode = (m_choice_view_type->GetStringSelection() == L("Tool")) ? "tool" : "feature";
int selection = m_choice_view_type->GetCurrentSelection();
if ((0 <= selection) && (selection < (int)GCodePreviewData::Extrusion::Num_View_Types))
m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)selection;
reload_print();
}
void Preview::on_combochecklist_features(wxCommandEvent& evt)
{
int flags = Slic3r::GUI::combochecklist_get_flags(m_combochecklist_features);
m_gcode_preview_data->extrusion.role_flags = (unsigned int)flags;
refresh_print();
}
void Preview::on_checkbox_travel(wxCommandEvent& evt)
{
m_gcode_preview_data->travel.is_visible = m_checkbox_travel->IsChecked();
refresh_print();
}
void Preview::on_checkbox_retractions(wxCommandEvent& evt)
{
m_gcode_preview_data->retraction.is_visible = m_checkbox_retractions->IsChecked();
refresh_print();
}
void Preview::on_checkbox_unretractions(wxCommandEvent& evt)
{
m_gcode_preview_data->unretraction.is_visible = m_checkbox_unretractions->IsChecked();
refresh_print();
}
void Preview::on_checkbox_shells(wxCommandEvent& evt)
{
m_gcode_preview_data->shell.is_visible = m_checkbox_shells->IsChecked();
refresh_print();
}
} // namespace GUI
} // namespace Slic3r

View file

@ -0,0 +1,91 @@
#ifndef slic3r_GUI_Preview_hpp_
#define slic3r_GUI_Preview_hpp_
#include <wx/panel.h>
#include "../../libslic3r/Point.hpp"
#include <string>
class wxNotebook;
class wxGLCanvas;
class wxBoxSizer;
class wxStaticText;
class wxChoice;
class wxComboCtrl;
class wxCheckBox;
namespace Slic3r {
class DynamicPrintConfig;
class Print;
class GCodePreviewData;
namespace GUI {
class Preview : public wxPanel
{
wxGLCanvas* m_canvas;
wxBoxSizer* m_double_slider_sizer;
wxStaticText* m_label_view_type;
wxChoice* m_choice_view_type;
wxStaticText* m_label_show_features;
wxComboCtrl* m_combochecklist_features;
wxCheckBox* m_checkbox_travel;
wxCheckBox* m_checkbox_retractions;
wxCheckBox* m_checkbox_unretractions;
wxCheckBox* m_checkbox_shells;
DynamicPrintConfig* m_config;
Print* m_print;
GCodePreviewData* m_gcode_preview_data;
unsigned int m_number_extruders;
std::string m_preferred_color_mode;
bool m_loaded;
bool m_enabled;
bool m_force_sliders_full_range;
public:
Preview(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data);
virtual ~Preview();
void register_on_viewport_changed_callback(void* callback);
void set_number_extruders(unsigned int number_extruders);
void reset_gcode_preview_data();
void set_canvas_as_dirty();
void set_enabled(bool enabled);
void set_bed_shape(const Pointfs& shape);
void select_view(const std::string& direction);
void set_viewport_from_scene(wxGLCanvas* canvas);
void set_viewport_into_scene(wxGLCanvas* canvas);
void set_drop_target(wxDropTarget* target);
void load_print();
void reload_print(bool force = false);
void refresh_print();
private:
bool init(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data);
void bind_event_handlers();
void unbind_event_handlers();
void show_hide_ui_elements(const std::string& what);
void reset_sliders();
void update_sliders();
void on_size(wxSizeEvent& evt);
void on_choice_view_type(wxCommandEvent& evt);
void on_combochecklist_features(wxCommandEvent& evt);
void on_checkbox_travel(wxCommandEvent& evt);
void on_checkbox_retractions(wxCommandEvent& evt);
void on_checkbox_unretractions(wxCommandEvent& evt);
void on_checkbox_shells(wxCommandEvent& evt);
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GUI_Preview_hpp_

View file

@ -0,0 +1,62 @@
#include "../../libslic3r/libslic3r.h"
#include "GUI_PreviewIface.hpp"
#include "GUI_Preview.hpp"
namespace Slic3r {
void PreviewIface::register_on_viewport_changed_callback(void* callback)
{
m_preview->register_on_viewport_changed_callback(callback);
}
void PreviewIface::set_number_extruders(unsigned int number_extruders)
{
m_preview->set_number_extruders(number_extruders);
}
void PreviewIface::reset_gcode_preview_data()
{
m_preview->reset_gcode_preview_data();
}
void PreviewIface::reload_print(bool force)
{
m_preview->reload_print(force);
}
void PreviewIface::set_canvas_as_dirty()
{
m_preview->set_canvas_as_dirty();
}
void PreviewIface::set_enabled(bool enabled)
{
m_preview->set_enabled(enabled);
}
void PreviewIface::set_bed_shape(const Pointfs& shape)
{
m_preview->set_bed_shape(shape);
}
void PreviewIface::select_view(const std::string& direction)
{
m_preview->select_view(direction);
}
void PreviewIface::set_viewport_from_scene(wxGLCanvas* canvas)
{
m_preview->set_viewport_from_scene(canvas);
}
void PreviewIface::set_viewport_into_scene(wxGLCanvas* canvas)
{
m_preview->set_viewport_into_scene(canvas);
}
void PreviewIface::set_drop_target(wxDropTarget* target)
{
m_preview->set_drop_target(target);
}
} // namespace Slic3r

View file

@ -0,0 +1,37 @@
#ifndef slic3r_GUI_PreviewIface_hpp_
#define slic3r_GUI_PreviewIface_hpp_
#include "../../libslic3r/Point.hpp"
class wxGLCanvas;
class wxDropTarget;
namespace Slic3r {
namespace GUI {
class Preview;
} // namespace GUI
class PreviewIface
{
GUI::Preview* m_preview;
public:
explicit PreviewIface(GUI::Preview* preview) : m_preview(preview) {}
void register_on_viewport_changed_callback(void* callback);
void set_number_extruders(unsigned int number_extruders);
void reset_gcode_preview_data();
void reload_print(bool force = false);
void set_canvas_as_dirty();
void set_enabled(bool enabled);
void set_bed_shape(const Pointfs& shape);
void select_view(const std::string& direction);
void set_viewport_from_scene(wxGLCanvas* canvas);
void set_viewport_into_scene(wxGLCanvas* canvas);
void set_drop_target(wxDropTarget* target);
};
} // namespace Slic3r
#endif // slic3r_GUI_PreviewIface_hpp_

View file

@ -69,12 +69,12 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
field->m_on_change = [this](std::string opt_id, boost::any value){
//! This function will be called from Field.
//! Call OptionGroup._on_change(...)
if (!this->m_disabled)
if (!m_disabled)
this->on_change_OG(opt_id, value);
};
field->m_on_kill_focus = [this](){
//! This function will be called from Field.
if (!this->m_disabled)
if (!m_disabled)
this->on_kill_focus();
};
field->m_parent = parent();
@ -82,7 +82,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
//! Label to change background color, when option is modified
field->m_Label = label;
field->m_back_to_initial_value = [this](std::string opt_id){
if (!this->m_disabled)
if (!m_disabled)
this->back_to_initial_value(opt_id);
};
field->m_back_to_sys_value = [this](std::string opt_id){

View file

@ -71,14 +71,6 @@ void PreferencesDialog::build()
option = Option (def, "preset_update");
m_optgroup->append_single_option_line(option);
def.label = L("Disable USB/serial connection");
def.type = coBool;
def.tooltip = L("Disable communication with the printer over a serial / USB cable. "
"This simplifies the user interface in case the printer is never attached to the computer.");
def.default_value = new ConfigOptionBool{ app_config->get("no_controller")[0] == '1' }; // 1;
option = Option (def,"no_controller");
m_optgroup->append_single_option_line(option);
def.label = L("Suppress \" - default - \" presets");
def.type = coBool;
def.tooltip = L("Suppress \" - default - \" presets in the Print / Filament / Printer "
@ -118,8 +110,7 @@ void PreferencesDialog::build()
void PreferencesDialog::accept()
{
if (m_values.find("no_controller") != m_values.end()||
m_values.find("no_defaults") != m_values.end()||
if (m_values.find("no_defaults") != m_values.end()||
m_values.find("use_legacy_opengl")!= m_values.end()) {
warning_catcher(this, _(L("You need to restart Slic3r to make the changes effective.")));
}

View file

@ -691,7 +691,7 @@ size_t PresetCollection::first_visible_idx() const
for (; idx < this->m_presets.size(); ++ idx)
if (m_presets[idx].is_visible)
break;
if (idx == this->m_presets.size())
if (idx == m_presets.size())
idx = 0;
return idx;
}

View file

@ -14,31 +14,31 @@ public:
using CancelFn = std::function<void(void)>; // Cancel function signature.
private:
float state_ = .0f, max_ = 1.f, step_;
CancelFn cancelfunc_ = [](){};
float m_state = .0f, m_max = 1.f, m_step;
CancelFn m_cancelfunc = [](){};
public:
inline virtual ~ProgressIndicator() {}
/// Get the maximum of the progress range.
float max() const { return max_; }
float max() const { return m_max; }
/// Get the current progress state
float state() const { return state_; }
float state() const { return m_state; }
/// Set the maximum of the progress range
virtual void max(float maxval) { max_ = maxval; }
virtual void max(float maxval) { m_max = maxval; }
/// Set the current state of the progress.
virtual void state(float val) { state_ = val; }
virtual void state(float val) { m_state = val; }
/**
* @brief Number of states int the progress. Can be used instead of giving a
* maximum value.
*/
virtual void states(unsigned statenum) {
step_ = max_ / statenum;
m_step = m_max / statenum;
}
/// Message shown on the next status update.
@ -51,13 +51,13 @@ public:
virtual void message_fmt(const std::string& fmt, ...);
/// Set up a cancel callback for the operation if feasible.
virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; }
virtual void on_cancel(CancelFn func = CancelFn()) { m_cancelfunc = func; }
/**
* Explicitly shut down the progress indicator and call the associated
* callback.
*/
virtual void cancel() { cancelfunc_(); }
virtual void cancel() { m_cancelfunc(); }
/// Convenience function to call message and status update in one function.
void update(float st, const std::string& msg) {

View file

@ -14,116 +14,117 @@ namespace Slic3r {
ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id):
self(new wxStatusBar(parent ? parent : GUI::get_main_frame(),
id == -1? wxID_ANY : id)),
timer_(new wxTimer(self)),
prog_ (new wxGauge(self,
m_timer(new wxTimer(self)),
m_prog (new wxGauge(self,
wxGA_HORIZONTAL,
100,
wxDefaultPosition,
wxDefaultSize)),
cancelbutton_(new wxButton(self,
m_cancelbutton(new wxButton(self,
-1,
"Cancel",
wxDefaultPosition,
wxDefaultSize))
{
prog_->Hide();
cancelbutton_->Hide();
m_prog->Hide();
m_cancelbutton->Hide();
self->SetFieldsCount(3);
int w[] = {-1, 150, 155};
self->SetStatusWidths(3, w);
self->Bind(wxEVT_TIMER, [this](const wxTimerEvent&) {
if (prog_->IsShown()) timer_->Stop();
if(is_busy()) prog_->Pulse();
if (m_prog->IsShown()) m_timer->Stop();
if(is_busy()) m_prog->Pulse();
});
self->Bind(wxEVT_SIZE, [this](wxSizeEvent& event){
wxRect rect;
self->GetFieldRect(1, rect);
auto offset = 0;
cancelbutton_->Move(rect.GetX() + offset, rect.GetY() + offset);
cancelbutton_->SetSize(rect.GetWidth() - offset, rect.GetHeight());
m_cancelbutton->Move(rect.GetX() + offset, rect.GetY() + offset);
m_cancelbutton->SetSize(rect.GetWidth() - offset, rect.GetHeight());
self->GetFieldRect(2, rect);
prog_->Move(rect.GetX() + offset, rect.GetY() + offset);
prog_->SetSize(rect.GetWidth() - offset, rect.GetHeight());
m_prog->Move(rect.GetX() + offset, rect.GetY() + offset);
m_prog->SetSize(rect.GetWidth() - offset, rect.GetHeight());
event.Skip();
});
cancelbutton_->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) {
if(cancel_cb_) cancel_cb_();
cancelbutton_->Hide();
m_cancelbutton->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) {
if(m_cancel_cb) m_cancel_cb();
m_perl_cancel_callback.call();
m_cancelbutton->Hide();
});
}
ProgressStatusBar::~ProgressStatusBar() {
if(timer_->IsRunning()) timer_->Stop();
if(m_timer->IsRunning()) m_timer->Stop();
}
int ProgressStatusBar::get_progress() const
{
return prog_->GetValue();
return m_prog->GetValue();
}
void ProgressStatusBar::set_progress(int val)
{
if(!prog_->IsShown()) show_progress(true);
if(!m_prog->IsShown()) show_progress(true);
if(val == prog_->GetRange()) {
prog_->SetValue(0);
if(val == m_prog->GetRange()) {
m_prog->SetValue(0);
show_progress(false);
} else {
prog_->SetValue(val);
m_prog->SetValue(val);
}
}
int ProgressStatusBar::get_range() const
{
return prog_->GetRange();
return m_prog->GetRange();
}
void ProgressStatusBar::set_range(int val)
{
if(val != prog_->GetRange()) {
prog_->SetRange(val);
if(val != m_prog->GetRange()) {
m_prog->SetRange(val);
}
}
void ProgressStatusBar::show_progress(bool show)
{
prog_->Show(show);
prog_->Pulse();
m_prog->Show(show);
m_prog->Pulse();
}
void ProgressStatusBar::start_busy(int rate)
{
busy_ = true;
m_busy = true;
show_progress(true);
if (!timer_->IsRunning()) {
timer_->Start(rate);
if (!m_timer->IsRunning()) {
m_timer->Start(rate);
}
}
void ProgressStatusBar::stop_busy()
{
timer_->Stop();
m_timer->Stop();
show_progress(false);
prog_->SetValue(0);
busy_ = false;
m_prog->SetValue(0);
m_busy = false;
}
void ProgressStatusBar::set_cancel_callback(ProgressStatusBar::CancelFn ccb) {
cancel_cb_ = ccb;
if(ccb) cancelbutton_->Show();
else cancelbutton_->Hide();
m_cancel_cb = ccb;
if(ccb) m_cancelbutton->Show();
else m_cancelbutton->Hide();
}
void ProgressStatusBar::run(int rate)
{
if(!timer_->IsRunning()) {
timer_->Start(rate);
if(!m_timer->IsRunning()) {
m_timer->Start(rate);
}
}
@ -133,19 +134,19 @@ void ProgressStatusBar::embed(wxFrame *frame)
mf->SetStatusBar(self);
}
void ProgressStatusBar::set_status_text(const std::string& txt)
void ProgressStatusBar::set_status_text(const wxString& txt)
{
self->SetStatusText(txt);
self->SetStatusText(wxString::FromUTF8(txt.c_str()));
}
void ProgressStatusBar::show_cancel_button()
{
cancelbutton_->Show();
m_cancelbutton->Show();
}
void ProgressStatusBar::hide_cancel_button()
{
cancelbutton_->Hide();
m_cancelbutton->Hide();
}
}

View file

@ -4,6 +4,8 @@
#include <memory>
#include <functional>
#include "../../callback.hpp"
class wxTimer;
class wxGauge;
class wxButton;
@ -11,6 +13,7 @@ class wxTimerEvent;
class wxStatusBar;
class wxWindow;
class wxFrame;
class wxString;
namespace Slic3r {
@ -21,9 +24,9 @@ namespace Slic3r {
*/
class ProgressStatusBar {
wxStatusBar *self; // we cheat! It should be the base class but: perl!
wxTimer *timer_;
wxGauge *prog_;
wxButton *cancelbutton_;
wxTimer *m_timer;
wxGauge *m_prog;
wxButton *m_cancelbutton;
public:
/// Cancel callback function type
@ -39,20 +42,21 @@ public:
void show_progress(bool);
void start_busy(int = 100);
void stop_busy();
inline bool is_busy() const { return busy_; }
inline bool is_busy() const { return m_busy; }
void set_cancel_callback(CancelFn = CancelFn());
inline void remove_cancel_callback() { set_cancel_callback(); }
void run(int rate);
void embed(wxFrame *frame = nullptr);
void set_status_text(const std::string& txt);
void set_status_text(const wxString& txt);
// Temporary methods to satisfy Perl side
void show_cancel_button();
void hide_cancel_button();
PerlCallback m_perl_cancel_callback;
private:
bool busy_ = false;
CancelFn cancel_cb_;
bool m_busy = false;
CancelFn m_cancel_cb;
};
namespace GUI {

View file

@ -1531,6 +1531,7 @@ void TabPrinter::build_fff()
};
#if 0
if (!m_no_controller)
{
optgroup = page->new_optgroup(_(L("USB/Serial connection")));
@ -1575,6 +1576,7 @@ void TabPrinter::build_fff()
line.append_widget(serial_test);
optgroup->append_line(line);
}
#endif
optgroup = page->new_optgroup(_(L("Printer Host upload")));
@ -1748,8 +1750,10 @@ void TabPrinter::build_fff()
build_extruder_pages();
#if 0
if (!m_no_controller)
update_serial_ports();
#endif
}
void TabPrinter::build_sla()

View file

@ -170,7 +170,6 @@ protected:
std::vector<PageShp> m_pages;
bool m_disable_tree_sel_changed_event;
bool m_show_incompatible_presets;
bool m_no_controller;
std::vector<std::string> m_reload_dependent_tabs = {};
enum OptStatus { osSystemValue = 1, osInitValue = 2 };
@ -199,8 +198,8 @@ public:
public:
Tab() {}
Tab(wxNotebook* parent, const wxString& title, const char* name, bool no_controller) :
m_parent(parent), m_title(title), m_name(name), m_no_controller(no_controller) {
Tab(wxNotebook* parent, const wxString& title, const char* name) :
m_parent(parent), m_title(title), m_name(name) {
Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL, name);
get_tabs_list().push_back(this);
}
@ -282,8 +281,8 @@ class TabPrint : public Tab
{
public:
TabPrint() {}
TabPrint(wxNotebook* parent, bool no_controller) :
Tab(parent, _(L("Print Settings")), "print", no_controller) {}
TabPrint(wxNotebook* parent) :
Tab(parent, _(L("Print Settings")), "print") {}
~TabPrint(){}
ogStaticText* m_recommended_thin_wall_thickness_description_line;
@ -302,8 +301,8 @@ class TabFilament : public Tab
ogStaticText* m_cooling_description_line;
public:
TabFilament() {}
TabFilament(wxNotebook* parent, bool no_controller) :
Tab(parent, _(L("Filament Settings")), "filament", no_controller) {}
TabFilament(wxNotebook* parent) :
Tab(parent, _(L("Filament Settings")), "filament") {}
~TabFilament(){}
void build() override;
@ -335,7 +334,7 @@ public:
PrinterTechnology m_printer_technology = ptFFF;
TabPrinter() {}
TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {}
TabPrinter(wxNotebook* parent) : Tab(parent, _(L("Printer Settings")), "printer") {}
~TabPrinter(){}
void build() override;
@ -357,8 +356,8 @@ class TabSLAMaterial : public Tab
{
public:
TabSLAMaterial() {}
TabSLAMaterial(wxNotebook* parent, bool no_controller) :
Tab(parent, _(L("SLA Material Settings")), "sla_material", no_controller) {}
TabSLAMaterial(wxNotebook* parent) :
Tab(parent, _(L("SLA Material Settings")), "sla_material") {}
~TabSLAMaterial(){}
void build() override;

View file

@ -209,6 +209,41 @@ SV* to_SV(TriangleMesh* THIS);
// Return a pointer to the associated wxWidgets object instance given by classname.
extern void* wxPli_sv_2_object( pTHX_ SV* scalar, const char* classname );
inline void confess_at(const char *file, int line, const char *func, const char *pat, ...)
{
#ifdef SLIC3RXS
va_list args;
SV *error_sv = newSVpvf("Error in function %s at %s:%d: ", func,
file, line);
va_start(args, pat);
sv_vcatpvf(error_sv, pat, &args);
va_end(args);
sv_catpvn(error_sv, "\n\t", 2);
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs( sv_2mortal(error_sv) );
PUTBACK;
call_pv("Carp::confess", G_DISCARD);
FREETMPS;
LEAVE;
#endif
}
#ifndef CONFESS
/* 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
/* End implementation of CONFESS("foo"): */
#endif /* CONFESS */
using namespace Slic3r;
#endif