1
0
mirror of https://github.com/FULU-Foundation/OrcaSlicer-bambulab.git synced 2026-08-02 11:35:51 +00:00

Merged with dev

This commit is contained in:
bubnikv
2018-09-17 15:12:13 +02:00
parent d934b63424
commit fe3b92870f
90 changed files with 3310 additions and 1748 deletions
+4 -2
View File
@@ -1225,8 +1225,10 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
return true;
}
Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) const
{
assert(std::abs(2 * offset) < m_resolution);
typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType;
// 0) Prepare a binary grid.
size_t cell_rows = m_rows + 2;
@@ -1237,7 +1239,7 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
cell_inside[r * cell_cols + c] = cell_inside_or_crossing(r - 1, c - 1);
// Fill in empty cells, which have a left / right neighbor filled.
// Fill in empty cells, which have the top / bottom neighbor filled.
{
if (fill_holes) {
std::vector<char> cell_inside2(cell_inside);
for (int r = 1; r + 1 < int(cell_rows); ++ r) {
for (int c = 1; c + 1 < int(cell_cols); ++ c) {
+1 -1
View File
@@ -58,7 +58,7 @@ public:
const size_t cols() const { return m_cols; }
// For supports: Contours enclosing the rasterized edges.
Polygons contours_simplified(coord_t offset) const;
Polygons contours_simplified(coord_t offset, bool fill_holes) const;
protected:
struct Cell {
+4 -5
View File
@@ -61,12 +61,11 @@ ExPolygonCollection::rotate(double angle, const Point &center)
}
template <class T>
bool
ExPolygonCollection::contains(const T &item) const
bool ExPolygonCollection::contains(const T &item) const
{
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
if (it->contains(item)) return true;
}
for (const ExPolygon &poly : this->expolygons)
if (poly.contains(item))
return true;
return false;
}
template bool ExPolygonCollection::contains<Point>(const Point &item) const;
+24 -16
View File
@@ -91,6 +91,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
virtual double min_mm3_per_mm() const = 0;
virtual Polyline as_polyline() const = 0;
virtual void collect_polylines(Polylines &dst) const = 0;
virtual Polylines as_polylines() const { Polylines dst; this->collect_polylines(dst); return dst; }
virtual double length() const = 0;
virtual double total_volume() const = 0;
};
@@ -123,8 +125,11 @@ public:
ExtrusionPath* clone() const { return new ExtrusionPath (*this); }
void reverse() { this->polyline.reverse(); }
Point first_point() const { return this->polyline.points.front(); }
Point last_point() const { return this->polyline.points.back(); }
Point first_point() const override { return this->polyline.points.front(); }
Point last_point() const override { return this->polyline.points.back(); }
size_t size() const { return this->polyline.size(); }
bool empty() const { return this->polyline.empty(); }
bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); }
// Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection.
// Currently not used.
void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
@@ -133,8 +138,8 @@ public:
void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
void clip_end(double distance);
void simplify(double tolerance);
virtual double length() const;
virtual ExtrusionRole role() const { return m_role; }
double length() const override;
ExtrusionRole role() const override { return m_role; }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -149,7 +154,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const { return this->mm3_per_mm; }
Polyline as_polyline() const { return this->polyline; }
virtual double total_volume() const { return mm3_per_mm * unscale<double>(length()); }
void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); }
double total_volume() const override { return mm3_per_mm * unscale<double>(length()); }
private:
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
@@ -178,10 +184,10 @@ public:
bool can_reverse() const { return true; }
ExtrusionMultiPath* clone() const { return new ExtrusionMultiPath(*this); }
void reverse();
Point first_point() const { return this->paths.front().polyline.points.front(); }
Point last_point() const { return this->paths.back().polyline.points.back(); }
virtual double length() const;
virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
Point first_point() const override { return this->paths.front().polyline.points.front(); }
Point last_point() const override { return this->paths.back().polyline.points.back(); }
double length() const override;
ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -196,7 +202,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const;
Polyline as_polyline() const;
virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
};
// Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
@@ -218,18 +225,18 @@ public:
bool make_clockwise();
bool make_counter_clockwise();
void reverse();
Point first_point() const { return this->paths.front().polyline.points.front(); }
Point last_point() const { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
Point first_point() const override { return this->paths.front().polyline.points.front(); }
Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
Polygon polygon() const;
virtual double length() const;
double length() const override;
bool split_at_vertex(const Point &point);
void split_at(const Point &point, bool prefer_non_overhang);
void clip_end(double distance, ExtrusionPaths* paths) const;
// Test, whether the point is extruded by a bridging flow.
// This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
bool has_overhang_point(const Point &point) const;
virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
ExtrusionLoopRole loop_role() const { return m_loop_role; }
ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
ExtrusionLoopRole loop_role() const { return m_loop_role; }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -244,7 +251,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const;
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
private:
ExtrusionLoopRole m_loop_role;
+11 -5
View File
@@ -24,7 +24,7 @@ public:
explicit operator ExtrusionPaths() const;
bool is_collection() const { return true; };
virtual ExtrusionRole role() const {
ExtrusionRole role() const override {
ExtrusionRole out = erNone;
for (const ExtrusionEntity *ee : entities) {
ExtrusionRole er = ee->role();
@@ -71,11 +71,11 @@ public:
Point last_point() const { return this->entities.back()->last_point(); }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
// Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill.
virtual void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const;
void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override;
Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const
{ Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; }
Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
@@ -84,14 +84,20 @@ public:
void flatten(ExtrusionEntityCollection* retval) const;
ExtrusionEntityCollection flatten() const;
double min_mm3_per_mm() const;
virtual double total_volume() const {double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
// Following methods shall never be called on an ExtrusionEntityCollection.
Polyline as_polyline() const {
CONFESS("Calling as_polyline() on a ExtrusionEntityCollection");
return Polyline();
};
virtual double length() const {
void collect_polylines(Polylines &dst) const override {
for (ExtrusionEntity* extrusion_entity : this->entities)
extrusion_entity->collect_polylines(dst);
}
double length() const override {
CONFESS("Calling length() on a ExtrusionEntityCollection");
return 0.;
}
+2 -2
View File
@@ -86,8 +86,8 @@ void FillHoneycomb::_fill_surface_single(
Polylines paths;
{
Polylines p;
for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
p.push_back((Polyline)(*it));
for (Polygon &poly : polygons)
p.emplace_back(poly.points);
paths = intersection_pl(p, to_polygons(expolygon));
}
+6 -3
View File
@@ -115,7 +115,8 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
// 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),
false);
// bridge_flow_ratio
0.f);
}
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
@@ -127,7 +128,8 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig
(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)),
false);
// bridge_flow_ratio
0.f);
}
Flow support_material_interface_flow(const PrintObject *object, float layer_height)
@@ -139,7 +141,8 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig
// 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),
false);
// bridge_flow_ratio
0.f);
}
}
+17 -3
View File
@@ -71,6 +71,7 @@ const char* VOLUME_TYPE = "volume";
const char* NAME_KEY = "name";
const char* MODIFIER_KEY = "modifier";
const char* VOLUME_TYPE_KEY = "volume_type";
const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
const char* VALID_OBJECT_TYPES[] =
@@ -1252,9 +1253,13 @@ namespace Slic3r {
// we extract from the given matrix only the values currently used
// translation
#if ENABLE_MODELINSTANCE_3D_OFFSET
Vec3d offset(transform(0, 3), transform(1, 3), transform(2, 3));
#else
double offset_x = transform(0, 3);
double offset_y = transform(1, 3);
double offset_z = transform(2, 3);
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
// scale
double sx = ::sqrt(sqr(transform(0, 0)) + sqr(transform(1, 0)) + sqr(transform(2, 0)));
@@ -1287,8 +1292,12 @@ namespace Slic3r {
double angle_z = (rotation.axis() == Vec3d::UnitZ()) ? rotation.angle() : -rotation.angle();
#if ENABLE_MODELINSTANCE_3D_OFFSET
instance.set_offset(offset);
#else
instance.offset(0) = offset_x;
instance.offset(1) = offset_y;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
instance.scaling_factor = sx;
instance.rotation = angle_z;
}
@@ -1434,7 +1443,9 @@ namespace Slic3r {
if (metadata.key == NAME_KEY)
volume->name = metadata.value;
else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
volume->modifier = true;
volume->set_type(ModelVolume::PARAMETER_MODIFIER);
else if (metadata.key == VOLUME_TYPE_KEY)
volume->set_type(ModelVolume::type_from_string(metadata.value));
else
volume->config.set_deserialize(metadata.key, metadata.value);
}
@@ -1949,9 +1960,12 @@ namespace Slic3r {
if (!volume->name.empty())
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
// stores volume's modifier field
if (volume->modifier)
// stores volume's modifier field (legacy, to support old slicers)
if (volume->is_modifier())
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
// stores volume's type (overrides the modifier field above)
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<
VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
// stores volume's config data
for (const std::string& key : volume->config.keys())
+60 -4
View File
@@ -29,7 +29,12 @@
// VERSION NUMBERS
// 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
// 2 : Added z component of offset.
const unsigned int VERSION_AMF = 2;
#else
const unsigned int VERSION_AMF = 1;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version";
const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config";
@@ -119,19 +124,31 @@ struct AMFParserContext
NODE_TYPE_INSTANCE, // amf/constellation/instance
NODE_TYPE_DELTAX, // amf/constellation/instance/deltax
NODE_TYPE_DELTAY, // amf/constellation/instance/deltay
#if ENABLE_MODELINSTANCE_3D_OFFSET
NODE_TYPE_DELTAZ, // amf/constellation/instance/deltaz
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
NODE_TYPE_RZ, // amf/constellation/instance/rz
NODE_TYPE_SCALE, // amf/constellation/instance/scale
NODE_TYPE_METADATA, // anywhere under amf/*/metadata
};
struct Instance {
#if ENABLE_MODELINSTANCE_3D_OFFSET
Instance() : deltax_set(false), deltay_set(false), deltaz_set(false), rz_set(false), scale_set(false) {}
#else
Instance() : deltax_set(false), deltay_set(false), rz_set(false), scale_set(false) {}
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
// Shift in the X axis.
float deltax;
bool deltax_set;
// Shift in the Y axis.
float deltay;
bool deltay_set;
#if ENABLE_MODELINSTANCE_3D_OFFSET
// Shift in the Z axis.
float deltaz;
bool deltaz_set;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
// Rotation around the Z axis.
float rz;
bool rz_set;
@@ -254,6 +271,10 @@ void AMFParserContext::startElement(const char *name, const char **atts)
node_type_new = NODE_TYPE_DELTAX;
else if (strcmp(name, "deltay") == 0)
node_type_new = NODE_TYPE_DELTAY;
#if ENABLE_MODELINSTANCE_3D_OFFSET
else if (strcmp(name, "deltaz") == 0)
node_type_new = NODE_TYPE_DELTAZ;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
else if (strcmp(name, "rz") == 0)
node_type_new = NODE_TYPE_RZ;
else if (strcmp(name, "scale") == 0)
@@ -314,7 +335,15 @@ void AMFParserContext::characters(const XML_Char *s, int len)
{
switch (m_path.size()) {
case 4:
#if ENABLE_MODELINSTANCE_3D_OFFSET
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 ||
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)
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
m_value[0].append(s, len);
break;
case 6:
@@ -354,6 +383,14 @@ void AMFParserContext::endElement(const char * /* name */)
m_instance->deltay_set = true;
m_value[0].clear();
break;
#if ENABLE_MODELINSTANCE_3D_OFFSET
case NODE_TYPE_DELTAZ:
assert(m_instance);
m_instance->deltaz = float(atof(m_value[0].c_str()));
m_instance->deltaz_set = true;
m_value[0].clear();
break;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
case NODE_TYPE_RZ:
assert(m_instance);
m_instance->rz = float(atof(m_value[0].c_str()));
@@ -458,9 +495,14 @@ void AMFParserContext::endElement(const char * /* name */)
p = end + 1;
}
m_object->layer_height_profile_valid = true;
} else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume && strcmp(opt_key, "modifier") == 0) {
// Is this volume a modifier volume?
m_volume->modifier = atoi(m_value[1].c_str()) == 1;
} else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
if (strcmp(opt_key, "modifier") == 0) {
// Is this volume a modifier volume?
// "modifier" flag comes first in the XML file, so it may be later overwritten by the "type" flag.
m_volume->set_type((atoi(m_value[1].c_str()) == 1) ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
} else if (strcmp(opt_key, "volume_type") == 0) {
m_volume->set_type(ModelVolume::type_from_string(m_value[1]));
}
}
} else if (m_path.size() == 3) {
if (m_path[1] == NODE_TYPE_MATERIAL) {
@@ -498,8 +540,12 @@ void AMFParserContext::endDocument()
for (const Instance &instance : object.second.instances)
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));
#else
mi->offset(0) = instance.deltax;
mi->offset(1) = instance.deltay;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
mi->rotation = instance.rz_set ? instance.rz : 0.f;
mi->scaling_factor = instance.scale_set ? instance.scale : 1.f;
}
@@ -781,8 +827,9 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
if (!volume->name.empty())
stream << " <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
if (volume->modifier)
if (volume->is_modifier())
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
stream << " <triangle>\n";
for (int j = 0; j < 3; ++j)
@@ -800,12 +847,21 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
" <instance objectid=\"" PRINTF_ZU "\">\n"
" <deltax>%lf</deltax>\n"
" <deltay>%lf</deltay>\n"
#if ENABLE_MODELINSTANCE_3D_OFFSET
" <deltaz>%lf</deltaz>\n"
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
" <rz>%lf</rz>\n"
" <scale>%lf</scale>\n"
" </instance>\n",
object_id,
#if ENABLE_MODELINSTANCE_3D_OFFSET
instance->get_offset(X),
instance->get_offset(Y),
instance->get_offset(Z),
#else
instance->offset(0),
instance->offset(1),
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
instance->rotation,
instance->scaling_factor);
//FIXME missing instance->scaling_factor
+15 -3
View File
@@ -166,7 +166,11 @@ bool load_prus(const char *path, Model *model)
float trafo[3][4] = { 0 };
double instance_rotation = 0.;
double instance_scaling_factor = 1.f;
Vec2d instance_offset(0., 0.);
#if ENABLE_MODELINSTANCE_3D_OFFSET
Vec3d instance_offset = Vec3d::Zero();
#else
Vec2d instance_offset(0., 0.);
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
bool trafo_set = false;
unsigned int group_id = (unsigned int)-1;
unsigned int extruder_id = (unsigned int)-1;
@@ -207,8 +211,12 @@ bool load_prus(const char *path, Model *model)
for (size_t c = 0; c < 3; ++ c)
trafo[r][c] += mat_trafo(r, c);
}
#if ENABLE_MODELINSTANCE_3D_OFFSET
instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2]));
#else
instance_offset(0) = position[0] - zero[0];
instance_offset(1) = position[1] - zero[1];
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
trafo[2][3] = position[2] / instance_scaling_factor;
trafo_set = true;
}
@@ -360,8 +368,12 @@ bool load_prus(const char *path, Model *model)
ModelInstance *instance = model_object->add_instance();
instance->rotation = instance_rotation;
instance->scaling_factor = instance_scaling_factor;
instance->offset = instance_offset;
++ num_models;
#if ENABLE_MODELINSTANCE_3D_OFFSET
instance->set_offset(instance_offset);
#else
instance->offset = instance_offset;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
++num_models;
if (group_id != (size_t)-1)
group_to_model_object[group_id] = model_object;
} else {
+6 -5
View File
@@ -277,7 +277,6 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco
}
std::string WipeTowerIntegration::prime(GCode &gcodegen)
{
assert(m_layer_idx == 0);
@@ -1000,8 +999,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
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();
double extruded_volume = extruder.extruded_volume();
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.m_print_statistics.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
@@ -1014,8 +1013,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write_format(file, "; filament cost = %.1lf\n", filament_cost);
}
}
print.m_print_statistics.total_used_filament = print.m_print_statistics.total_used_filament + used_filament;
print.m_print_statistics.total_extruded_volume = print.m_print_statistics.total_extruded_volume + extruded_volume;
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.m_print_statistics.total_cost);
_write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str());
+1
View File
@@ -98,6 +98,7 @@ public:
void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; }
std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer);
std::string finalize(GCode &gcodegen);
std::vector<float> used_filament_length() const;
private:
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
+6
View File
@@ -154,6 +154,12 @@ public:
// the wipe tower has been completely covered by the tool change extrusions,
// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
virtual bool layer_finished() const = 0;
// Returns used filament length per extruder:
virtual std::vector<float> get_used_filament() const = 0;
// Returns total number of toolchanges:
virtual int get_number_of_toolchanges() const = 0;
};
}; // namespace Slic3r
+35 -12
View File
@@ -111,9 +111,10 @@ public:
const WipeTower::xy start_pos_rotated() const { return m_start_pos; }
const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); }
float elapsed_time() const { return m_elapsed_time; }
float get_and_reset_used_filament_length() { float temp = m_used_filament_length; m_used_filament_length = 0.f; return temp; }
// Extrude with an explicitely provided amount of extrusion.
Writer& extrude_explicit(float x, float y, float e, float f = 0.f)
Writer& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false)
{
if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate))
// Neither extrusion nor a travel move.
@@ -122,6 +123,8 @@ public:
float dx = x - m_current_pos.x;
float dy = y - m_current_pos.y;
double len = sqrt(dx*dx+dy*dy);
if (record_length)
m_used_filament_length += e;
// Now do the "internal rotation" with respect to the wipe tower center
@@ -162,8 +165,8 @@ public:
return *this;
}
Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f)
{ return extrude_explicit(dest.x, dest.y, e, f); }
Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f, bool record_length = false)
{ return extrude_explicit(dest.x, dest.y, e, f, record_length); }
// Travel to a new XY position. f=0 means use the current value.
Writer& travel(float x, float y, float f = 0.f)
@@ -177,7 +180,7 @@ public:
{
float dx = x - m_current_pos.x;
float dy = y - m_current_pos.y;
return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f);
return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true);
}
Writer& extrude(const WipeTower::xy &dest, const float f = 0.f)
@@ -259,8 +262,8 @@ public:
// extrude quickly amount e to x2 with feed f.
Writer& ram(float x1, float x2, float dy, float e0, float e, float f)
{
extrude_explicit(x1, m_current_pos.y + dy, e0, f);
extrude_explicit(x2, m_current_pos.y, e);
extrude_explicit(x1, m_current_pos.y + dy, e0, f, true);
extrude_explicit(x2, m_current_pos.y, e, 0.f, true);
return *this;
}
@@ -404,6 +407,7 @@ private:
float m_last_fan_speed = 0.f;
int current_temp = -1;
const float m_default_analyzer_line_width;
float m_used_filament_length = 0.f;
std::string set_format_X(float x)
{
@@ -525,6 +529,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
++ m_num_tool_changes;
}
m_old_temperature = -1; // If the priming is turned off in config, the temperature changing commands will not actually appear
// in the output gcode - we should not remember emitting them (we will output them twice in the worst case)
// Reset the extruder current to a normal value.
writer.set_extruder_trimpot(550)
.feedrate(6000)
@@ -537,6 +544,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
// so that tool_change() will know to extrude the wipe tower brim:
m_print_brim = true;
// Ask our writer about how much material was consumed:
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
ToolChangeResult result;
result.priming = true;
result.print_z = this->m_z_pos;
@@ -606,10 +616,10 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
toolchange_Load(writer, cleaning_box);
writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
++ m_num_tool_changes;
} else
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
++ m_num_tool_changes;
m_depth_traversed += wipe_area;
if (last_change_in_layer) {// draw perimeter line
@@ -632,6 +642,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
";------------------\n"
"\n\n");
// Ask our writer about how much material was consumed:
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
ToolChangeResult result;
result.priming = false;
result.print_z = this->m_z_pos;
@@ -683,6 +696,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
m_print_brim = false; // Mark the brim as extruded
// Ask our writer about how much material was consumed:
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
ToolChangeResult result;
result.priming = false;
result.print_z = this->m_z_pos;
@@ -804,8 +820,9 @@ void WipeTowerPrusaMM::toolchange_Unload(
.load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
.travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
.resume_preview();
if (new_temperature != 0 && new_temperature != m_old_temperature ) { // Set the extruder temperature, but don't wait.
if (new_temperature != 0 && (new_temperature != m_old_temperature || m_is_first_layer) ) { // Set the extruder temperature, but don't wait.
// If the required temperature is the same as last time, don't emit the M104 again (if user adjusted the value, it would be reset)
// However, always change temperatures on the first layer (this is to avoid issues with priming lines turned off).
writer.set_extruder_temp(new_temperature, false);
m_old_temperature = new_temperature;
}
@@ -849,6 +866,9 @@ void WipeTowerPrusaMM::toolchange_Change(
const unsigned int new_tool,
material_type new_material)
{
// Ask the writer about how much of the old filament we consumed:
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
// Speed override for the material. Go slow for flex and soluble materials.
int speed_override;
switch (new_material) {
@@ -911,7 +931,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
const float& xl = cleaning_box.ld.x;
const float& xr = cleaning_box.rd.x;
// Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
// the ordered volume, even if it means violating the box. This can later be removed and simply
// wipe until the end of the assigned area.
@@ -926,7 +945,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
m_left_to_right = !m_left_to_right;
}
// now the wiping itself:
for (int i = 0; true; ++i) {
if (i!=0) {
@@ -935,7 +953,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
else if (wipe_speed < 2210.f) wipe_speed = 4200.f;
else wipe_speed = std::min(4800.f, wipe_speed + 50.f);
}
float traversed_x = writer.x();
if (m_left_to_right)
writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
@@ -1050,6 +1068,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
// Ask our writer about how much material was consumed:
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
ToolChangeResult result;
result.priming = false;
result.print_z = this->m_z_pos;
@@ -1166,6 +1187,8 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
m_layer_info = m_plan.begin();
m_current_tool = (unsigned int)(-2); // we don't know which extruder to start with - we'll set it according to the first toolchange
for (auto& used : m_used_filament_length) // reset used filament stats
used = 0.f;
std::vector<WipeTower::ToolChangeResult> layer_result;
for (auto layer : m_plan)
+9 -1
View File
@@ -46,7 +46,7 @@ public:
WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging,
const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
m_wipe_tower_pos(x, y),
m_wipe_tower_pos(x, y),
m_wipe_tower_width(width),
m_wipe_tower_rotation_angle(rotation_angle),
m_y_shift(0.f),
@@ -94,6 +94,8 @@ public:
m_filpar[idx].ramming_step_multiplicator /= 100;
while (stream >> speed)
m_filpar[idx].ramming_speed.push_back(speed);
m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
}
@@ -172,6 +174,9 @@ public:
return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
}
virtual std::vector<float> get_used_filament() const override { return m_used_filament_length; }
virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; }
private:
WipeTowerPrusaMM();
@@ -331,6 +336,9 @@ private:
std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
// Stores information about used filament length per extruder:
std::vector<float> m_used_filament_length;
// Returns gcode for wipe tower brim
// sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
+1 -2
View File
@@ -15,8 +15,7 @@
namespace Slic3r {
Flow
LayerRegion::flow(FlowRole role, bool bridge, double width) const
Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const
{
return m_region->flow(
role,
+90 -13
View File
@@ -242,10 +242,19 @@ void Model::center_instances_around_point(const Vec2d &point)
for (size_t i = 0; i < o->instances.size(); ++ i)
bb.merge(o->instance_bounding_box(i, false));
#if ENABLE_MODELINSTANCE_3D_OFFSET
Vec2d shift2 = point - to_2d(bb.center());
Vec3d shift3 = Vec3d(shift2(0), shift2(1), 0.0);
#else
Vec2d shift = point - 0.5 * to_2d(bb.size()) - to_2d(bb.min);
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
for (ModelObject *o : this->objects) {
for (ModelInstance *i : o->instances)
#if ENABLE_MODELINSTANCE_3D_OFFSET
i->set_offset(i->get_offset() + shift3);
#else
i->offset += shift;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
o->invalidate_bounding_box();
}
}
@@ -311,8 +320,13 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
size_t idx = 0;
for (ModelObject *o : this->objects) {
for (ModelInstance *i : o->instances) {
#if ENABLE_MODELINSTANCE_3D_OFFSET
Vec2d offset_xy = positions[idx] - instance_centers[idx];
i->set_offset(Vec3d(offset_xy(0), offset_xy(1), i->get_offset(Z)));
#else
i->offset = positions[idx] - instance_centers[idx];
++ idx;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
++idx;
}
o->invalidate_bounding_box();
}
@@ -336,7 +350,11 @@ void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
for (const ModelInstance *i : instances) {
for (const Vec2d &pos : positions) {
ModelInstance *instance = o->add_instance(*i);
#if ENABLE_MODELINSTANCE_3D_OFFSET
instance->set_offset(instance->get_offset() + Vec3d(pos(0), pos(1), 0.0));
#else
instance->offset += pos;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
}
}
o->invalidate_bounding_box();
@@ -366,13 +384,21 @@ void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
ModelObject* object = this->objects.front();
object->clear_instances();
#if ENABLE_MODELINSTANCE_3D_OFFSET
Vec3d ext_size = object->bounding_box().size() + dist * Vec3d::Ones();
#else
Vec3d size = object->bounding_box().size();
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
for (size_t x_copy = 1; x_copy <= x; ++x_copy) {
for (size_t y_copy = 1; y_copy <= y; ++y_copy) {
ModelInstance* instance = object->add_instance();
instance->offset(0) = (size(0) + dist) * (x_copy-1);
instance->offset(1) = (size(1) + dist) * (y_copy-1);
#if ENABLE_MODELINSTANCE_3D_OFFSET
instance->set_offset(Vec3d(ext_size(0) * (double)(x_copy - 1), ext_size(1) * (double)(y_copy - 1), 0.0));
#else
instance->offset(0) = (size(0) + dist) * (x_copy - 1);
instance->offset(1) = (size(1) + dist) * (y_copy - 1);
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
}
}
}
@@ -601,7 +627,8 @@ const BoundingBoxf3& ModelObject::bounding_box() const
if (! m_bounding_box_valid) {
BoundingBoxf3 raw_bbox;
for (const ModelVolume *v : this->volumes)
if (! v->modifier)
if (v->is_model_part())
// mesh.bounding_box() returns a cached value.
raw_bbox.merge(v->mesh.bounding_box());
BoundingBoxf3 bb;
for (const ModelInstance *i : this->instances)
@@ -632,7 +659,7 @@ TriangleMesh ModelObject::raw_mesh() const
{
TriangleMesh mesh;
for (const ModelVolume *v : this->volumes)
if (! v->modifier)
if (v->is_model_part())
mesh.merge(v->mesh);
return mesh;
}
@@ -643,7 +670,7 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const
{
BoundingBoxf3 bb;
for (const ModelVolume *v : this->volumes)
if (! v->modifier) {
if (v->is_model_part()) {
if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances");
bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true));
}
@@ -655,7 +682,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
{
BoundingBoxf3 bb;
for (ModelVolume *v : this->volumes)
if (! v->modifier)
if (v->is_model_part())
bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate));
return bb;
}
@@ -666,7 +693,7 @@ void ModelObject::center_around_origin()
// center this object around the origin
BoundingBoxf3 bb;
for (ModelVolume *v : this->volumes)
if (! v->modifier)
if (v->is_model_part())
bb.merge(v->mesh.bounding_box());
// Shift is the vector from the center of the bottom face of the bounding box to the origin
@@ -676,12 +703,21 @@ void ModelObject::center_around_origin()
this->translate(shift);
this->origin_translation += shift;
#if ENABLE_MODELINSTANCE_3D_OFFSET
// set z to zero, translation in z has already been done within the mesh
shift(2) = 0.0;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
if (!this->instances.empty()) {
for (ModelInstance *i : this->instances) {
// apply rotation and scaling to vector as well before translating instance,
// in order to leave final position unaltered
#if ENABLE_MODELINSTANCE_3D_OFFSET
i->set_offset(i->get_offset() + i->transform_vector(-shift, true));
#else
Vec3d i_shift = i->world_matrix(true) * shift;
i->offset -= to_2d(i_shift);
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
}
this->invalidate_bounding_box();
}
@@ -763,7 +799,7 @@ size_t ModelObject::facets_count() const
{
size_t num = 0;
for (const ModelVolume *v : this->volumes)
if (! v->modifier)
if (v->is_model_part())
num += v->mesh.stl.stats.number_of_facets;
return num;
}
@@ -771,7 +807,7 @@ size_t ModelObject::facets_count() const
bool ModelObject::needed_repair() const
{
for (const ModelVolume *v : this->volumes)
if (! v->modifier && v->mesh.needed_repair())
if (v->is_model_part() && v->mesh.needed_repair())
return true;
return false;
}
@@ -787,7 +823,7 @@ void ModelObject::cut(coordf_t z, Model* model) const
lower->input_file = "";
for (ModelVolume *volume : this->volumes) {
if (volume->modifier) {
if (! volume->is_model_part()) {
// don't cut modifiers
upper->add_volume(*volume);
lower->add_volume(*volume);
@@ -839,7 +875,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
ModelVolume* new_volume = new_object->add_volume(*mesh);
new_volume->name = volume->name;
new_volume->config = volume->config;
new_volume->modifier = volume->modifier;
new_volume->set_type(volume->type());
new_volume->material_id(volume->material_id());
new_objects->push_back(new_object);
@@ -854,7 +890,7 @@ void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_
{
for (const ModelVolume* vol : this->volumes)
{
if (!vol->modifier)
if (vol->is_model_part())
{
for (ModelInstance* inst : this->instances)
{
@@ -951,6 +987,38 @@ const TriangleMesh& ModelVolume::get_convex_hull() const
return m_convex_hull;
}
ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
{
// Legacy support
if (s == "1")
return PARAMETER_MODIFIER;
// New type (supporting the support enforcers & blockers)
if (s == "ModelPart")
return MODEL_PART;
if (s == "ParameterModifier")
return PARAMETER_MODIFIER;
if (s == "SupportEnforcer")
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)
{
switch (t) {
case MODEL_PART: return "ModelPart";
case PARAMETER_MODIFIER: return "ParameterModifier";
case SUPPORT_ENFORCER: return "SupportEnforcer";
case SUPPORT_BLOCKER: return "SupportBlocker";
default:
assert(false);
return "ModelPart";
}
}
// Split this volume, append the result to the object owning this volume.
// Return the number of volumes created from this one.
// This is useful to assign different materials to different volumes of an object.
@@ -1005,8 +1073,13 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
}
// Translate the bounding box.
if (! dont_translate) {
#if ENABLE_MODELINSTANCE_3D_OFFSET
bbox.min += this->m_offset;
bbox.max += this->m_offset;
#else
Eigen::Map<Vec2d>(bbox.min.data()) += this->offset;
Eigen::Map<Vec2d>(bbox.max.data()) += this->offset;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
}
}
return bbox;
@@ -1033,7 +1106,11 @@ Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, b
Transform3d m = Transform3d::Identity();
if (!dont_translate)
#if ENABLE_MODELINSTANCE_3D_OFFSET
m.translate(m_offset);
#else
m.translate(Vec3d(offset(0), offset(1), 0.0));
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
if (!dont_rotate)
m.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ()));
+55 -14
View File
@@ -166,15 +166,27 @@ public:
// Configuration parameters specific to an object model geometry or a modifier volume,
// overriding the global Slic3r settings and the ModelObject settings.
DynamicPrintConfig config;
// Is it an object to be printed, or a modifier volume?
bool modifier;
enum Type {
MODEL_TYPE_INVALID = -1,
MODEL_PART = 0,
PARAMETER_MODIFIER,
SUPPORT_ENFORCER,
SUPPORT_BLOCKER,
};
// A parent object owning this modifier volume.
ModelObject* get_object() const { return this->object; };
ModelObject* get_object() const { return this->object; };
Type type() const { return m_type; }
void set_type(const Type t) { m_type = t; }
bool is_model_part() const { return m_type == MODEL_PART; }
bool is_modifier() const { return m_type == PARAMETER_MODIFIER; }
bool is_support_enforcer() const { return m_type == SUPPORT_ENFORCER; }
bool is_support_blocker() const { return m_type == SUPPORT_BLOCKER; }
t_model_material_id material_id() const { return this->_material_id; }
void material_id(t_model_material_id material_id);
ModelMaterial* material() const;
void set_material(t_model_material_id material_id, const ModelMaterial &material);
void material_id(t_model_material_id material_id);
ModelMaterial* material() const;
void set_material(t_model_material_id material_id, const ModelMaterial &material);
// Split this volume, append the result to the object owning this volume.
// Return the number of volumes created from this one.
// This is useful to assign different materials to different volumes of an object.
@@ -185,24 +197,30 @@ public:
void calculate_convex_hull();
const TriangleMesh& get_convex_hull() const;
// Helpers for loading / storing into AMF / 3MF files.
static Type type_from_string(const std::string &s);
static std::string type_to_string(const Type t);
private:
// Parent object owning this ModelVolume.
ModelObject* object;
t_model_material_id _material_id;
ModelObject* object;
// Is it an object to be printed, or a modifier volume?
Type m_type;
t_model_material_id _material_id;
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object)
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object)
{
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), modifier(false), object(object) {}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {}
ModelVolume(ModelObject *object, const ModelVolume &other) :
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), modifier(other.modifier), object(object)
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object)
{
this->material_id(other.material_id());
}
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object)
name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object)
{
this->material_id(other.material_id());
if (mesh.stl.stats.number_of_facets > 1)
@@ -225,15 +243,32 @@ public:
friend class ModelObject;
#if ENABLE_MODELINSTANCE_3D_OFFSET
private:
Vec3d m_offset; // in unscaled coordinates
public:
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
double rotation; // Rotation around the Z axis, in radians around mesh center point
double scaling_factor;
#if !ENABLE_MODELINSTANCE_3D_OFFSET
Vec2d offset; // in unscaled coordinates
#endif // !ENABLE_MODELINSTANCE_3D_OFFSET
// flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state())
EPrintVolumeState print_volume_state;
ModelObject* get_object() const { return this->object; }
#if ENABLE_MODELINSTANCE_3D_OFFSET
const Vec3d& get_offset() const { return m_offset; }
double get_offset(Axis axis) const { return m_offset(axis); }
void set_offset(const Vec3d& offset) { m_offset = offset; }
void set_offset(Axis axis, double offset) { m_offset(axis) = offset; }
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
// 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.
@@ -253,9 +288,15 @@ private:
// Parent object, owning this instance.
ModelObject* object;
#if ENABLE_MODELINSTANCE_3D_OFFSET
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) {}
#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) :
rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object), print_volume_state(PVS_Inside) {}
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
};
+60 -23
View File
@@ -130,6 +130,7 @@ objfunc(const PointImpl& bincenter,
double norm, // A norming factor for physical dimensions
// a spatial index to quickly get neighbors of the candidate item
const SpatIndex& spatindex,
const SpatIndex& smalls_spatindex,
const ItemGroup& remaining
)
{
@@ -161,7 +162,7 @@ objfunc(const PointImpl& bincenter,
// Will hold the resulting score
double score = 0;
if(isBig(item.area())) {
if(isBig(item.area()) || spatindex.empty()) {
// This branch is for the bigger items..
auto minc = ibb.minCorner(); // bottom left corner
@@ -183,6 +184,8 @@ objfunc(const PointImpl& bincenter,
// The smalles distance from the arranged pile center:
auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
auto bindist = pl::distance(ibb.center(), bincenter) / norm;
dist = 0.8*dist + 0.2*bindist;
// Density is the pack density: how big is the arranged pile
double density = 0;
@@ -207,14 +210,20 @@ objfunc(const PointImpl& bincenter,
// candidate to be aligned with only one item.
auto alignment_score = 1.0;
density = (fullbb.width()*fullbb.height()) / (norm*norm);
density = std::sqrt((fullbb.width() / norm )*
(fullbb.height() / norm));
auto querybb = item.boundingBox();
// Query the spatial index for the neighbors
std::vector<SpatElement> result;
result.reserve(spatindex.size());
spatindex.query(bgi::intersects(querybb),
std::back_inserter(result));
if(isBig(item.area())) {
spatindex.query(bgi::intersects(querybb),
std::back_inserter(result));
} else {
smalls_spatindex.query(bgi::intersects(querybb),
std::back_inserter(result));
}
for(auto& e : result) { // now get the score for the best alignment
auto idx = e.second;
@@ -235,12 +244,8 @@ objfunc(const PointImpl& bincenter,
if(result.empty())
score = 0.5 * dist + 0.5 * density;
else
score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score;
score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score;
}
} else if( !isBig(item.area()) && spatindex.empty()) {
auto bindist = pl::distance(ibb.center(), bincenter) / norm;
// Bindist is surprisingly enough...
score = bindist;
} else {
// Here there are the small items that should be placed around the
// already processed bigger items.
@@ -291,6 +296,7 @@ protected:
PConfig pconf_; // Placement configuration
double bin_area_;
SpatIndex rtree_;
SpatIndex smallsrtree_;
double norm_;
Pile merged_pile_;
Box pilebb_;
@@ -299,7 +305,8 @@ protected:
public:
_ArrBase(const TBin& bin, Distance dist,
std::function<void(unsigned)> progressind):
std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcond):
pck_(bin, dist), bin_area_(sl::area(bin)),
norm_(std::sqrt(sl::area(bin)))
{
@@ -317,6 +324,7 @@ public:
pilebb_ = sl::boundingBox(merged_pile);
rtree_.clear();
smallsrtree_.clear();
// We will treat big items (compared to the print bed) differently
auto isBig = [this](double a) {
@@ -326,10 +334,12 @@ public:
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});
}
};
pck_.progressIndicator(progressind);
pck_.stopCondition(stopcond);
}
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
@@ -343,8 +353,9 @@ class AutoArranger<Box>: public _ArrBase<Box> {
public:
AutoArranger(const Box& bin, Distance dist,
std::function<void(unsigned)> progressind):
_ArrBase<Box>(bin, dist, progressind)
std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcond):
_ArrBase<Box>(bin, dist, progressind, stopcond)
{
pconf_.object_function = [this, bin] (const Item &item) {
@@ -357,6 +368,7 @@ public:
bin_area_,
norm_,
rtree_,
smallsrtree_,
remaining_);
double score = std::get<0>(result);
@@ -380,8 +392,9 @@ class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
public:
AutoArranger(const lnCircle& bin, Distance dist,
std::function<void(unsigned)> progressind):
_ArrBase<lnCircle>(bin, dist, progressind) {
std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcond):
_ArrBase<lnCircle>(bin, dist, progressind, stopcond) {
pconf_.object_function = [this, &bin] (const Item &item) {
@@ -393,6 +406,7 @@ public:
bin_area_,
norm_,
rtree_,
smallsrtree_,
remaining_);
double score = std::get<0>(result);
@@ -421,8 +435,9 @@ template<>
class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
public:
AutoArranger(const PolygonImpl& bin, Distance dist,
std::function<void(unsigned)> progressind):
_ArrBase<PolygonImpl>(bin, dist, progressind)
std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcond):
_ArrBase<PolygonImpl>(bin, dist, progressind, stopcond)
{
pconf_.object_function = [this, &bin] (const Item &item) {
@@ -435,6 +450,7 @@ public:
bin_area_,
norm_,
rtree_,
smallsrtree_,
remaining_);
double score = std::get<0>(result);
@@ -449,8 +465,9 @@ template<> // Specialization with no bin
class AutoArranger<bool>: public _ArrBase<Box> {
public:
AutoArranger(Distance dist, std::function<void(unsigned)> progressind):
_ArrBase<Box>(Box(0, 0), dist, progressind)
AutoArranger(Distance dist, std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcond):
_ArrBase<Box>(Box(0, 0), dist, progressind, stopcond)
{
this->pconf_.object_function = [this] (const Item &item) {
@@ -462,6 +479,7 @@ public:
0,
norm_,
rtree_,
smallsrtree_,
remaining_);
return std::get<0>(result);
};
@@ -511,8 +529,13 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
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)
#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);
}
@@ -649,11 +672,19 @@ void applyResult(
// 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);
#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
inst_ptr->rotation = rot;
#if ENABLE_MODELINSTANCE_3D_OFFSET
inst_ptr->set_offset(foff);
#else
inst_ptr->offset = foff;
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
}
}
@@ -680,12 +711,16 @@ void applyResult(
* remaining items which do not fit onto the print area next to the print
* bed or leave them untouched (let the user arrange them by hand or remove
* them).
* \param progressind Progress indicator callback called when an object gets
* packed. The unsigned argument is the number of items remaining to pack.
* \param stopcondition A predicate returning true if abort is needed.
*/
bool arrange(Model &model, coordf_t min_obj_distance,
const Slic3r::Polyline& bed,
BedShapeHint bedhint,
bool first_bin_only,
std::function<void(unsigned)> progressind)
std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcondition)
{
using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
@@ -710,6 +745,8 @@ bool arrange(Model &model, coordf_t min_obj_distance,
BoundingBox bbb(bed);
auto& cfn = stopcondition;
auto binbb = Box({
static_cast<libnest2d::Coord>(bbb.min(0)),
static_cast<libnest2d::Coord>(bbb.min(1))
@@ -723,7 +760,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
case BedShapeType::BOX: {
// Create the arranger for the box shaped bed
AutoArranger<Box> arrange(binbb, min_obj_distance, progressind);
AutoArranger<Box> arrange(binbb, min_obj_distance, progressind, cfn);
// Arrange and return the items with their respective indices within the
// input sequence.
@@ -735,7 +772,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
auto c = bedhint.shape.circ;
auto cc = lnCircle(c);
AutoArranger<lnCircle> arrange(cc, min_obj_distance, progressind);
AutoArranger<lnCircle> arrange(cc, min_obj_distance, progressind, cfn);
result = arrange(shapes.begin(), shapes.end());
break;
}
@@ -747,7 +784,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
P irrbed = sl::create<PolygonImpl>(std::move(ctour));
AutoArranger<P> arrange(irrbed, min_obj_distance, progressind);
AutoArranger<P> arrange(irrbed, min_obj_distance, progressind, cfn);
// Arrange and return the items with their respective indices within the
// input sequence.
@@ -756,7 +793,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
}
};
if(result.empty()) return false;
if(result.empty() || stopcondition()) return false;
if(first_bin_only) {
applyResult(result.front(), 0, shapemap);
+3 -1
View File
@@ -35,8 +35,10 @@ public:
Point first_point() const;
virtual Point last_point() const = 0;
virtual Lines lines() const = 0;
size_t size() const { return points.size(); }
bool empty() const { return points.empty(); }
double length() const;
bool is_valid() const { return this->points.size() >= 2; }
bool is_valid() const { return this->points.size() >= 2; }
int find_point(const Point &point) const;
bool has_boundary_point(const Point &point) const;
+1
View File
@@ -22,6 +22,7 @@ typedef Point Vector;
// Vector types with a fixed point coordinate base type.
typedef Eigen::Matrix<coord_t, 2, 1, Eigen::DontAlign> Vec2crd;
typedef Eigen::Matrix<coord_t, 3, 1, Eigen::DontAlign> Vec3crd;
typedef Eigen::Matrix<int, 3, 1, Eigen::DontAlign> Vec3i;
typedef Eigen::Matrix<int64_t, 2, 1, Eigen::DontAlign> Vec2i64;
typedef Eigen::Matrix<int64_t, 3, 1, Eigen::DontAlign> Vec3i64;
+6
View File
@@ -103,6 +103,12 @@ inline void polygons_rotate(Polygons &polys, double angle)
p.rotate(cos_angle, sin_angle);
}
inline void polygons_reverse(Polygons &polys)
{
for (Polygon &p : polys)
p.reverse();
}
inline Points to_points(const Polygon &poly)
{
return poly.points;
+6 -8
View File
@@ -184,15 +184,13 @@ void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
bool Polyline::is_straight() const
{
/* Check that each segment's direction is equal to the line connecting
first point and last point. (Checking each line against the previous
one would cause the error to accumulate.) */
// Check that each segment's direction is equal to the line connecting
// first point and last point. (Checking each line against the previous
// one would cause the error to accumulate.)
double dir = Line(this->first_point(), this->last_point()).direction();
Lines lines = this->lines();
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
if (!line->parallel_to(dir)) return false;
}
for (const auto &line: this->lines())
if (! line.parallel_to(dir))
return false;
return true;
}
+2
View File
@@ -21,6 +21,8 @@ public:
Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {}
Polyline(std::initializer_list<Point> list) : MultiPoint(list) {}
explicit Polyline(const Point &p1, const Point &p2) { points.reserve(2); points.emplace_back(p1); points.emplace_back(p2); }
explicit Polyline(const Points &points) : MultiPoint(points) {}
explicit Polyline(Points &&points) : MultiPoint(std::move(points)) {}
Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; }
static Polyline new_scale(const std::vector<Vec2d> &points) {
+10 -3
View File
@@ -392,9 +392,12 @@ void Print::add_model_object(ModelObject* model_object, int idx)
//FIXME lock mutex!
this->invalidate_all_steps();
for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
size_t volume_id = 0;
for (const ModelVolume *volume : model_object->volumes) {
if (! volume->is_model_part() && ! volume->is_modifier())
continue;
// Get the config applied to this volume.
PrintRegionConfig config = this->_region_config_from_model_volume(*model_object->volumes[volume_id]);
PrintRegionConfig config = this->_region_config_from_model_volume(*volume);
// Find an existing print region with the same config.
size_t region_id = size_t(-1);
for (size_t i = 0; i < m_regions.size(); ++ i)
@@ -409,6 +412,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
}
// Assign volume to a region.
object->add_region_volume(region_id, volume_id);
++ volume_id;
}
// Apply config to print object.
@@ -893,7 +897,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
ModelVolume *volume = model_object->volumes[volume_id];
//FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
if (! volume->material_id().empty() && ! volume->config.has("extruder"))
if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder"))
volume->config.opt<ConfigOptionInt>("extruder", true)->value = int(volume_id + 1);
}
}
@@ -1295,6 +1299,9 @@ void Print::_make_wipe_tower()
}
m_wipe_tower_data.final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(
wipe_tower.tool_change((unsigned int)-1, false));
m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
}
std::string Print::output_filename() const
+17 -1
View File
@@ -135,7 +135,10 @@ public:
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;
// 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:
@@ -252,6 +255,10 @@ public:
// 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:
void make_perimeters();
void prepare_infill();
@@ -297,6 +304,7 @@ private:
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
@@ -309,6 +317,8 @@ struct WipeTowerData
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;
@@ -318,6 +328,8 @@ struct WipeTowerData
priming.reset(nullptr);
tool_changes.clear();
final_purge.reset(nullptr);
used_filament.clear();
number_of_toolchanges = -1;
depth = 0.f;
}
};
@@ -331,6 +343,8 @@ struct PrintStatistics
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() {
@@ -340,6 +354,8 @@ struct PrintStatistics
total_extruded_volume = 0.;
total_cost = 0.;
total_weight = 0.;
total_wipe_tower_cost = 0.;
total_wipe_tower_filament = 0.;
filament_stats.clear();
}
};
@@ -463,7 +479,7 @@ private:
// 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() { if (m_canceled) throw CanceledException(); }
void throw_if_canceled() const { if (m_canceled) throw CanceledException(); }
void _make_skirt();
void _make_brim();
+19 -12
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);
@@ -1717,6 +1716,14 @@ void PrintConfigDef::init_fff_params()
def->cli = "support-material!";
def->default_value = new ConfigOptionBool(false);
def = this->add("support_material_auto", coBool);
def->label = L("Auto generated supports");
def->category = L("Support material");
def->tooltip = L("If checked, supports will be generated automatically based on the overhang threshold value."\
" If unchecked, supports will be generated inside the \"Support Enforcer\" volumes only.");
def->cli = "support-material-auto!";
def->default_value = new ConfigOptionBool(true);
def = this->add("support_material_xy_spacing", coFloatOrPercent);
def->label = L("XY separation between an object and its support");
def->category = L("Support material");
@@ -1755,7 +1762,7 @@ void PrintConfigDef::init_fff_params()
"for the first object layer.");
def->sidetext = L("mm");
def->cli = "support-material-contact-distance=f";
def->min = 0;
// def->min = 0;
def->enum_values.push_back("0");
def->enum_values.push_back("0.2");
def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str());
@@ -1981,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);
+11
View File
@@ -345,6 +345,7 @@ public:
ConfigOptionFloatOrPercent extrusion_width;
ConfigOptionFloatOrPercent first_layer_height;
ConfigOptionBool infill_only_where_needed;
// Force the generation of solid shells between adjacent materials/volumes.
ConfigOptionBool interface_shells;
ConfigOptionFloat layer_height;
ConfigOptionInt raft_layers;
@@ -352,6 +353,9 @@ public:
// ConfigOptionFloat seam_preferred_direction;
// ConfigOptionFloat seam_preferred_direction_jitter;
ConfigOptionBool support_material;
// Automatic supports (generated based on support_material_threshold).
ConfigOptionBool support_material_auto;
// Direction of the support pattern (in XY plane).
ConfigOptionFloat support_material_angle;
ConfigOptionBool support_material_buildplate_only;
ConfigOptionFloat support_material_contact_distance;
@@ -361,12 +365,15 @@ public:
ConfigOptionBool support_material_interface_contact_loops;
ConfigOptionInt support_material_interface_extruder;
ConfigOptionInt support_material_interface_layers;
// Spacing between interface lines (the hatching distance). Set zero to get a solid interface.
ConfigOptionFloat support_material_interface_spacing;
ConfigOptionFloatOrPercent support_material_interface_speed;
ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
// Spacing between support material lines (the hatching distance).
ConfigOptionFloat support_material_spacing;
ConfigOptionFloat support_material_speed;
ConfigOptionBool support_material_synchronize_layers;
// Overhang angle threshold.
ConfigOptionInt support_material_threshold;
ConfigOptionBool support_material_with_sheath;
ConfigOptionFloatOrPercent support_material_xy_spacing;
@@ -389,6 +396,7 @@ protected:
// OPT_PTR(seam_preferred_direction);
// OPT_PTR(seam_preferred_direction_jitter);
OPT_PTR(support_material);
OPT_PTR(support_material_auto);
OPT_PTR(support_material_angle);
OPT_PTR(support_material_buildplate_only);
OPT_PTR(support_material_contact_distance);
@@ -436,10 +444,12 @@ public:
ConfigOptionInt infill_every_layers;
ConfigOptionFloatOrPercent infill_overlap;
ConfigOptionFloat infill_speed;
// Detect bridging perimeters
ConfigOptionBool overhangs;
ConfigOptionInt perimeter_extruder;
ConfigOptionFloatOrPercent perimeter_extrusion_width;
ConfigOptionFloat perimeter_speed;
// Total number of perimeters.
ConfigOptionInt perimeters;
ConfigOptionFloatOrPercent small_perimeter_speed;
ConfigOptionFloat solid_infill_below_area;
@@ -447,6 +457,7 @@ public:
ConfigOptionFloatOrPercent solid_infill_extrusion_width;
ConfigOptionInt solid_infill_every_layers;
ConfigOptionFloatOrPercent solid_infill_speed;
// Detect thin walls.
ConfigOptionBool thin_walls;
ConfigOptionFloatOrPercent top_infill_extrusion_width;
ConfigOptionInt top_solid_layers;
+70 -27
View File
@@ -112,8 +112,18 @@ bool PrintObject::reload_model_instances()
Points copies;
copies.reserve(m_model_object->instances.size());
for (const ModelInstance *mi : m_model_object->instances)
{
#if ENABLE_MODELINSTANCE_3D_OFFSET
if (mi->is_printable())
{
const Vec3d& offset = mi->get_offset();
copies.emplace_back(Point::new_scale(offset(0), offset(1)));
}
#else
if (mi->is_printable())
copies.emplace_back(Point::new_scale(mi->offset(0), mi->offset(1)));
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
}
return this->set_copies(copies);
}
@@ -490,6 +500,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
steps.emplace_back(posSlice);
} else if (
opt_key == "support_material"
|| opt_key == "support_material_auto"
|| opt_key == "support_material_angle"
|| opt_key == "support_material_buildplate_only"
|| opt_key == "support_material_enforce_layers"
@@ -1560,34 +1571,66 @@ end:
std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
{
std::vector<ExPolygons> layers;
std::vector<const ModelVolume*> volumes;
if (region_id < this->region_volumes.size()) {
std::vector<int> &volumes = this->region_volumes[region_id];
if (! volumes.empty()) {
// Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh;
for (int volume_id : volumes) {
ModelVolume *volume = this->model_object()->volumes[volume_id];
if (volume->modifier == modifier)
mesh.merge(volume->mesh);
m_print->throw_if_canceled();
}
if (mesh.stl.stats.number_of_facets > 0) {
// transform mesh
// we ignore the per-instance transformations currently and only
// consider the first one
this->model_object()->instances.front()->transform_mesh(&mesh, true);
// align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), - float(this->model_object()->bounding_box().min(2)));
// perform actual slicing
TriangleMeshSlicer mslicer;
Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
mslicer.init(&mesh, callback);
mslicer.slice(z, &layers, callback);
m_print->throw_if_canceled();
}
for (int volume_id : this->region_volumes[region_id]) {
const ModelVolume *volume = this->model_object()->volumes[volume_id];
if (modifier ? volume->is_modifier() : volume->is_model_part())
volumes.emplace_back(volume);
}
}
return this->_slice_volumes(z, volumes);
}
std::vector<ExPolygons> PrintObject::slice_support_enforcers() const
{
std::vector<const ModelVolume*> volumes;
for (const ModelVolume *volume : this->model_object()->volumes)
if (volume->is_support_enforcer())
volumes.emplace_back(volume);
std::vector<float> zs;
zs.reserve(this->layers().size());
for (const Layer *l : this->layers())
zs.emplace_back(l->slice_z);
return this->_slice_volumes(zs, volumes);
}
std::vector<ExPolygons> PrintObject::slice_support_blockers() const
{
std::vector<const ModelVolume*> volumes;
for (const ModelVolume *volume : this->model_object()->volumes)
if (volume->is_support_blocker())
volumes.emplace_back(volume);
std::vector<float> zs;
zs.reserve(this->layers().size());
for (const Layer *l : this->layers())
zs.emplace_back(l->slice_z);
return this->_slice_volumes(zs, volumes);
}
std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
{
std::vector<ExPolygons> layers;
if (! volumes.empty()) {
// Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh;
for (const ModelVolume *v : volumes)
mesh.merge(v->mesh);
if (mesh.stl.stats.number_of_facets > 0) {
// transform mesh
// we ignore the per-instance transformations currently and only
// consider the first one
this->model_object()->instances.front()->transform_mesh(&mesh, true);
// align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), - float(this->model_object()->bounding_box().min(2)));
// perform actual slicing
TriangleMeshSlicer mslicer;
const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
mslicer.init(&mesh, callback);
mslicer.slice(z, &layers, callback);
m_print->throw_if_canceled();
}
}
return layers;
+5
View File
@@ -56,4 +56,9 @@ coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
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(m_config.bridge_flow_ratio.value);
}
}
+3 -3
View File
@@ -224,9 +224,9 @@ std::vector<coordf_t> layer_height_profile_adaptive(
// 1) Initialize the SlicingAdaptive class with the object meshes.
SlicingAdaptive as;
as.set_slicing_parameters(slicing_params);
for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it)
if (! (*it)->modifier)
as.add_mesh(&(*it)->mesh);
for (const ModelVolume *volume : volumes)
if (volume->is_model_part())
as.add_mesh(&volume->mesh);
as.prepare();
// 2) Generate layers using the algorithm of @platsch
File diff suppressed because it is too large Load Diff
+16
View File
@@ -12,6 +12,7 @@ class PrintConfig;
class PrintObjectConfig;
// how much we extend support around the actual contact area
//FIXME this should be dependent on the nozzle diameter!
#define SUPPORT_MATERIAL_MARGIN 1.5
// This class manages raft and supports for a single PrintObject.
@@ -71,6 +72,21 @@ public:
overhang_polygons = nullptr;
}
void reset() {
layer_type = sltUnknown;
print_z = 0.;
bottom_z = 0.;
height = 0.;
idx_object_layer_above = size_t(-1);
idx_object_layer_below = size_t(-1);
bridging = false;
polygons.clear();
delete contact_polygons;
contact_polygons = nullptr;
delete overhang_polygons;
overhang_polygons = nullptr;
}
bool operator==(const MyLayer &layer2) const {
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
+5
View File
@@ -37,6 +37,11 @@ public:
void clear() { surfaces.clear(); }
bool empty() const { return surfaces.empty(); }
bool has(SurfaceType type) const {
for (const Surface &surface : this->surfaces)
if (surface.surface_type == type) return true;
return false;
}
void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
+12
View File
@@ -0,0 +1,12 @@
#ifndef _technologies_h_
#define _technologies_h_
// 1.42.0 techs
#define ENABLE_1_42_0 1
// Add z coordinate to model instances' offset
#define ENABLE_MODELINSTANCE_3D_OFFSET (1 && ENABLE_1_42_0)
#endif // _technologies_h_
+337 -135
View File
@@ -21,16 +21,20 @@
#include <Eigen/Dense>
// for SLIC3R_DEBUG_SLICE_PROCESSING
#include "libslic3r.h"
#if 0
#define DEBUG
#define _DEBUG
#undef NDEBUG
#define SLIC3R_DEBUG
// #define SLIC3R_TRIANGLEMESH_DEBUG
#endif
#include <assert.h>
#ifdef SLIC3R_DEBUG
// #define SLIC3R_TRIANGLEMESH_DEBUG
#if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING)
#include "SVG.hpp"
#endif
@@ -156,7 +160,6 @@ void TriangleMesh::repair()
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
}
float TriangleMesh::volume()
{
if (this->stl.stats.volume == -1)
@@ -320,7 +323,7 @@ bool TriangleMesh::has_multiple_patches() const
facet_visited[facet_idx] = true;
for (int j = 0; j < 3; ++ j) {
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
if (! facet_visited[neighbor_idx])
if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
facet_queue[facet_queue_cnt ++] = neighbor_idx;
}
}
@@ -363,7 +366,7 @@ size_t TriangleMesh::number_of_patches() const
facet_visited[facet_idx] = true;
for (int j = 0; j < 3; ++ j) {
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
if (! facet_visited[neighbor_idx])
if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
facet_queue[facet_queue_cnt ++] = neighbor_idx;
}
}
@@ -623,6 +626,20 @@ void TriangleMesh::require_shared_vertices()
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices";
stl_generate_shared_vertices(&(this->stl));
}
#ifdef _DEBUG
// Verify validity of neighborship data.
for (int facet_idx = 0; facet_idx < stl.stats.number_of_facets; ++facet_idx) {
const stl_neighbors &nbr = stl.neighbors_start[facet_idx];
const int *vertices = stl.v_indices[facet_idx].vertex;
for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) {
int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx];
if (nbr_face != -1) {
assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3]);
assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]);
}
}
}
#endif /* _DEBUG */
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
}
@@ -699,13 +716,13 @@ void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type
}
}
// Assign an edge index to the 1st face.
this->facets_edges[edge_i.face * 3 + std::abs(edge_i.face_edge) - 1] = num_edges;
this->facets_edges[edge_i.face * 3 + std::abs(edge_i.face_edge) - 1] = num_edges;
if (found) {
EdgeToFace &edge_j = edges_map[j];
this->facets_edges[edge_j.face * 3 + std::abs(edge_j.face_edge) - 1] = num_edges;
// Mark the edge as connected.
edge_j.face = -1;
}
this->facets_edges[edge_j.face * 3 + std::abs(edge_j.face_edge) - 1] = num_edges;
// Mark the edge as connected.
edge_j.face = -1;
}
++ num_edges;
if ((i & 0x0ffff) == 0)
throw_on_cancel();
@@ -781,13 +798,30 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
{
static int iRun = 0;
for (size_t i = 0; i < z.size(); ++ i) {
Polygons &polygons = (*layers)[i];
SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), union_ex(polygons, true));
Polygons &polygons = (*layers)[i];
ExPolygons expolygons = union_ex(polygons, true);
SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), expolygons);
{
BoundingBox bbox;
for (const IntersectionLine &l : lines[i]) {
bbox.merge(l.a);
bbox.merge(l.b);
}
SVG svg(debug_out_path("slice_loops_%d_%d.svg", iRun, i).c_str(), bbox);
svg.draw(expolygons);
for (const IntersectionLine &l : lines[i])
svg.draw(l, "red", 0);
svg.draw_outline(expolygons, "black", "blue", 0);
svg.Close();
}
#if 0
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
for (Polygon &poly : polygons) {
for (size_t i = 1; i < poly.points.size(); ++ i)
assert(poly.points[i-1] != poly.points[i]);
assert(poly.points.front() != poly.points.back());
}
#endif
}
++ iRun;
}
@@ -803,46 +837,51 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2)));
const float max_z = fmaxf(facet.vertex[0](2), fmaxf(facet.vertex[1](2), facet.vertex[2](2)));
#ifdef SLIC3R_DEBUG
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0](2),
facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1](2),
facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2](2));
printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
#endif
#endif /* SLIC3R_TRIANGLEMESH_DEBUG */
// find layer extents
std::vector<float>::const_iterator min_layer, max_layer;
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
#ifdef SLIC3R_DEBUG
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
#endif
#endif /* SLIC3R_TRIANGLEMESH_DEBUG */
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
std::vector<float>::size_type layer_idx = it - z.begin();
IntersectionLine il;
if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il)) {
if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il) == TriangleMeshSlicer::Slicing) {
boost::lock_guard<boost::mutex> l(*lines_mutex);
if (il.edge_type == feHorizontal) {
// Insert all three edges of the face.
// Insert all marked edges of the face. The marked edges do not share an edge with another horizontal face
// (they may not have a nighbor, or their neighbor is vertical)
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
const bool reverse = this->mesh->stl.facet_start[facet_idx].normal(2) < 0;
for (int j = 0; j < 3; ++ j) {
int a_id = vertices[j % 3];
int b_id = vertices[(j+1) % 3];
if (reverse)
std::swap(a_id, b_id);
const stl_vertex &a = this->v_scaled_shared[a_id];
const stl_vertex &b = this->v_scaled_shared[b_id];
il.a(0) = a(0);
il.a(1) = a(1);
il.b(0) = b(0);
il.b(1) = b(1);
il.a_id = a_id;
il.b_id = b_id;
(*lines)[layer_idx].emplace_back(il);
}
for (int j = 0; j < 3; ++ j)
if (il.flags & ((IntersectionLine::EDGE0_NO_NEIGHBOR | IntersectionLine::EDGE0_FOLD) << j)) {
int a_id = vertices[j % 3];
int b_id = vertices[(j+1) % 3];
if (reverse)
std::swap(a_id, b_id);
const stl_vertex &a = this->v_scaled_shared[a_id];
const stl_vertex &b = this->v_scaled_shared[b_id];
il.a(0) = a(0);
il.a(1) = a(1);
il.b(0) = b(0);
il.b(1) = b(1);
il.a_id = a_id;
il.b_id = b_id;
assert(il.a != il.b);
// This edge will not be used as a seed for loop extraction if it was added due to a fold of two overlapping horizontal faces.
il.set_no_seed((IntersectionLine::EDGE0_FOLD << j) != 0);
(*lines)[layer_idx].emplace_back(il);
}
} else
(*lines)[layer_idx].emplace_back(il);
}
@@ -861,7 +900,7 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygo
[&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]);
printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]);
#endif
throw_on_cancel();
this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]);
@@ -871,23 +910,22 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygo
}
// Return true, if the facet has been sliced and line_out has been filled.
bool TriangleMeshSlicer::slice_facet(
TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
float slice_z, const stl_facet &facet, const int facet_idx,
const float min_z, const float max_z,
IntersectionLine *line_out) const
{
IntersectionPoint points[3];
size_t num_points = 0;
size_t points_on_layer[3];
size_t num_points_on_layer = 0;
size_t point_on_layer = size_t(-1);
// Reorder vertices so that the first one is the one with lowest Z.
// This is needed to get all intersection lines in a consistent order
// (external on the right of the line)
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
int i = (facet.vertex[1](2) == min_z) ? 1 : ((facet.vertex[2](2) == min_z) ? 2 : 0);
for (int j = i; j - i < 3; ++ j) { // loop through facet edges
for (int j = i; j - i < 3; ++j ) { // loop through facet edges
int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)];
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
int a_id = vertices[j % 3];
int b_id = vertices[(j+1) % 3];
const stl_vertex &a = this->v_scaled_shared[a_id];
@@ -900,116 +938,279 @@ bool TriangleMeshSlicer::slice_facet(
const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
const stl_vertex &v2 = this->v_scaled_shared[vertices[2]];
bool swap = false;
const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal;
// We may ignore this edge for slicing purposes, but we may still use it for object cutting.
FacetSliceType result = Slicing;
const stl_neighbors &nbr = this->mesh->stl.neighbors_start[facet_idx];
if (min_z == max_z) {
// All three vertices are aligned with slice_z.
line_out->edge_type = feHorizontal;
if (this->mesh->stl.facet_start[facet_idx].normal(2) < 0) {
// Mark neighbor edges, which do not have a neighbor.
uint32_t edges = 0;
for (int nbr_idx = 0; nbr_idx != 3; ++ nbr_idx) {
// If the neighbor with an edge starting with a vertex idx (nbr_idx - 2) shares no
// opposite face, add it to the edges to process when slicing.
if (nbr.neighbor[nbr_idx] == -1) {
// Mark this edge to be added to the slice.
edges |= (IntersectionLine::EDGE0_NO_NEIGHBOR << nbr_idx);
}
#if 1
else if (normal(2) > 0) {
// Produce edges for opposite faced overlapping horizontal faces aka folds.
// This method often produces connecting lines (noise) at the cutting plane.
// Produce the edges for the top facing face of the pair of top / bottom facing faces.
// Index of a neighbor face.
const int nbr_face = nbr.neighbor[nbr_idx];
const int *nbr_vertices = this->mesh->stl.v_indices[nbr_face].vertex;
int idx_vertex_opposite = nbr_vertices[nbr.which_vertex_not[nbr_idx]];
const stl_vertex &c2 = this->v_scaled_shared[idx_vertex_opposite];
if (c2(2) == slice_z) {
// Edge shared by facet_idx and nbr_face.
int a_id = vertices[nbr_idx];
int b_id = vertices[(nbr_idx + 1) % 3];
int c1_id = vertices[(nbr_idx + 2) % 3];
const stl_vertex &a = this->v_scaled_shared[a_id];
const stl_vertex &b = this->v_scaled_shared[b_id];
const stl_vertex &c1 = this->v_scaled_shared[c1_id];
// Verify that the two neighbor faces share a common edge.
assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id);
assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id);
double n1 = (double(c1(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c1(1)) - double(a(1))) * (double(b(0)) - double(a(0)));
double n2 = (double(c2(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c2(1)) - double(a(1))) * (double(b(0)) - double(a(0)));
if (n1 * n2 > 0)
// The two faces overlap. This indicates an invalid mesh geometry (non-manifold),
// but these are the real world objects, and leaving out these edges leads to missing contours.
edges |= (IntersectionLine::EDGE0_FOLD << nbr_idx);
}
}
#endif
}
// Use some edges of this triangle for slicing only if at least one of its edge does not have an opposite face.
result = (edges == 0) ? Cutting : Slicing;
line_out->flags |= edges;
if (normal(2) < 0) {
// If normal points downwards this is a bottom horizontal facet so we reverse its point order.
swap = true;
}
} else if (v0(2) < slice_z || v1(2) < slice_z || v2(2) < slice_z) {
// Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
line_out->edge_type = feTop;
swap = true;
} else {
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
line_out->edge_type = feBottom;
// Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
int nbr_idx = j % 3;
int nbr_face = nbr.neighbor[nbr_idx];
// Is the third vertex below the cutting plane?
bool third_below = v0(2) < slice_z || v1(2) < slice_z || v2(2) < slice_z;
// Is this a concave corner?
if (nbr_face == -1) {
#ifdef _DEBUG
printf("Face has no neighbor!\n");
#endif
} else {
assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id);
assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id);
int idx_vertex_opposite = this->mesh->stl.v_indices[nbr_face].vertex[nbr.which_vertex_not[nbr_idx]];
const stl_vertex &c = this->v_scaled_shared[idx_vertex_opposite];
if (c(2) == slice_z) {
double normal_nbr = (double(c(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c(1)) - double(a(1))) * (double(b(0)) - double(a(0)));
#if 0
if ((normal_nbr < 0) == third_below) {
printf("Flipped normal?\n");
}
#endif
result =
// A vertical face shares edge with a horizontal face. Verify, whether the shared edge makes a convex or concave corner.
// Unfortunately too often there are flipped normals, which brake our assumption. Let's rather return every edge,
// and leth the code downstream hopefully handle it.
#if 1
// Ignore concave corners for slicing.
// This method has the unfortunate property, that folds in a horizontal plane create concave corners,
// leading to broken contours, if these concave corners are not replaced by edges of the folds, see above.
((normal_nbr < 0) == third_below) ? Cutting : Slicing;
#else
// Use concave corners for slicing. This leads to the test 01_trianglemesh.t "slicing a top tangent plane includes its area" failing,
// and rightly so.
Slicing;
#endif
} else {
// For a pair of faces touching exactly at the cutting plane, ignore one of them. An arbitrary rule is to ignore the face with a higher index.
result = (facet_idx < nbr_face) ? Slicing : Cutting;
}
}
if (third_below) {
line_out->edge_type = feTop;
swap = true;
} else
line_out->edge_type = feBottom;
}
line_out->a = to_2d(swap ? b : a).cast<coord_t>();
line_out->b = to_2d(swap ? a : b).cast<coord_t>();
line_out->a_id = swap ? b_id : a_id;
line_out->b_id = swap ? a_id : b_id;
return true;
assert(line_out->a != line_out->b);
return result;
}
if (a(2) == slice_z) {
// Only point a alings with the cutting plane.
points_on_layer[num_points_on_layer ++] = num_points;
IntersectionPoint &point = points[num_points ++];
point(0) = a(0);
point(1) = a(1);
point.point_id = a_id;
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
point_on_layer = num_points;
IntersectionPoint &point = points[num_points ++];
point(0) = a(0);
point(1) = a(1);
point.point_id = a_id;
}
} else if (b(2) == slice_z) {
// Only point b alings with the cutting plane.
points_on_layer[num_points_on_layer ++] = num_points;
IntersectionPoint &point = points[num_points ++];
point(0) = b(0);
point(1) = b(1);
point.point_id = b_id;
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
point_on_layer = num_points;
IntersectionPoint &point = points[num_points ++];
point(0) = b(0);
point(1) = b(1);
point.point_id = b_id;
}
} else if ((a(2) < slice_z && b(2) > slice_z) || (b(2) < slice_z && a(2) > slice_z)) {
// A general case. The face edge intersects the cutting plane. Calculate the intersection point.
IntersectionPoint &point = points[num_points ++];
point(0) = b(0) + (a(0) - b(0)) * (slice_z - b(2)) / (a(2) - b(2));
point(1) = b(1) + (a(1) - b(1)) * (slice_z - b(2)) / (a(2) - b(2));
point.edge_id = edge_id;
assert(a_id != b_id);
// Sort the edge to give a consistent answer.
const stl_vertex *pa = &a;
const stl_vertex *pb = &b;
if (a_id > b_id) {
std::swap(a_id, b_id);
std::swap(pa, pb);
}
IntersectionPoint &point = points[num_points];
double t = (double(slice_z) - double((*pb)(2))) / (double((*pa)(2)) - double((*pb)(2)));
if (t <= 0.) {
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
point(0) = (*pa)(0);
point(1) = (*pa)(1);
point_on_layer = num_points ++;
point.point_id = a_id;
}
} else if (t >= 1.) {
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
point(0) = (*pb)(0);
point(1) = (*pb)(1);
point_on_layer = num_points ++;
point.point_id = b_id;
}
} else {
point(0) = coord_t(floor(double((*pb)(0)) + (double((*pa)(0)) - double((*pb)(0))) * t + 0.5));
point(1) = coord_t(floor(double((*pb)(1)) + (double((*pa)(1)) - double((*pb)(1))) * t + 0.5));
point.edge_id = edge_id;
++ num_points;
}
}
}
// We can't have only one point on layer because each vertex gets detected
// twice (once for each edge), and we can't have three points on layer,
// because we assume this code is not getting called for horizontal facets.
assert(num_points_on_layer == 0 || num_points_on_layer == 2);
if (num_points_on_layer > 0) {
assert(points[points_on_layer[0]].point_id == points[points_on_layer[1]].point_id);
assert(num_points == 2 || num_points == 3);
if (num_points < 3)
// This triangle touches the cutting plane with a single vertex. Ignore it.
return false;
// Erase one of the duplicate points.
-- num_points;
for (int i = points_on_layer[1]; i < num_points; ++ i)
points[i] = points[i + 1];
}
// Facets must intersect each plane 0 or 2 times.
assert(num_points == 0 || num_points == 2);
// Facets must intersect each plane 0 or 2 times, or it may touch the plane at a single vertex only.
assert(num_points < 3);
if (num_points == 2) {
line_out->edge_type = feNone;
line_out->edge_type = feGeneral;
line_out->a = (Point)points[1];
line_out->b = (Point)points[0];
line_out->a_id = points[1].point_id;
line_out->b_id = points[0].point_id;
line_out->edge_a_id = points[1].edge_id;
line_out->edge_b_id = points[0].edge_id;
return true;
// Not a zero lenght edge.
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
//assert(line_out->a != line_out->b);
// The plane cuts at least one edge in a general position.
assert(line_out->a_id == -1 || line_out->b_id == -1);
assert(line_out->edge_a_id != -1 || line_out->edge_b_id != -1);
// General slicing position, use the segment for both slicing and object cutting.
#if 0
if (line_out->a_id != -1 && line_out->b_id != -1) {
// Solving a degenerate case, where both the intersections snapped to an edge.
// Correctly classify the face as below or above based on the position of the 3rd point.
int i = vertices[0];
if (i == line_out->a_id || i == line_out->b_id)
i = vertices[1];
if (i == line_out->a_id || i == line_out->b_id)
i = vertices[2];
assert(i != line_out->a_id && i != line_out->b_id);
line_out->edge_type = (this->v_scaled_shared[i].z < slice_z) ? feTop : feBottom;
}
#endif
return Slicing;
}
return NoSlice;
}
//FIXME Should this go away? For valid meshes the function slice_facet() returns Slicing
// and sets edges of vertical triangles to produce only a single edge per pair of neighbor faces.
// So the following code makes only sense now to handle degenerate meshes with more than two faces
// sharing a single edge.
static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
{
std::vector<IntersectionLine*> by_vertex_pair;
by_vertex_pair.reserve(lines.size());
for (IntersectionLine& line : lines)
if (line.edge_type != feGeneral && line.a_id != -1)
// This is a face edge. Check whether there is its neighbor stored in lines.
by_vertex_pair.emplace_back(&line);
auto edges_lower_sorted = [](const IntersectionLine *l1, const IntersectionLine *l2) {
// Sort vertices of l1, l2 lexicographically
int l1a = l1->a_id;
int l1b = l1->b_id;
int l2a = l2->a_id;
int l2b = l2->b_id;
if (l1a > l1b)
std::swap(l1a, l1b);
if (l2a > l2b)
std::swap(l2a, l2b);
// Lexicographical "lower" operator on lexicographically sorted vertices should bring equal edges together when sored.
return l1a < l2a || (l1a == l2a && l1b < l2b);
};
std::sort(by_vertex_pair.begin(), by_vertex_pair.end(), edges_lower_sorted);
for (auto line = by_vertex_pair.begin(); line != by_vertex_pair.end(); ++ line) {
IntersectionLine &l1 = **line;
if (! l1.skip()) {
// Iterate as long as line and line2 edges share the same end points.
for (auto line2 = line + 1; line2 != by_vertex_pair.end() && ! edges_lower_sorted(*line, *line2); ++ line2) {
// Lines must share the end points.
assert(! edges_lower_sorted(*line, *line2));
assert(! edges_lower_sorted(*line2, *line));
IntersectionLine &l2 = **line2;
if (l2.skip())
continue;
if (l1.a_id == l2.a_id) {
assert(l1.b_id == l2.b_id);
l2.set_skip();
// If they are both oriented upwards or downwards (like a 'V'),
// then we can remove both edges from this layer since it won't
// affect the sliced shape.
// If one of them is oriented upwards and the other is oriented
// downwards, let's only keep one of them (it doesn't matter which
// one since all 'top' lines were reversed at slicing).
if (l1.edge_type == l2.edge_type) {
l1.set_skip();
break;
}
} else {
assert(l1.a_id == l2.b_id && l1.b_id == l2.a_id);
// If this edge joins two horizontal facets, remove both of them.
if (l1.edge_type == feHorizontal && l2.edge_type == feHorizontal) {
l1.set_skip();
l2.set_skip();
break;
}
}
}
}
}
return false;
}
void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
{
// Remove tangent edges.
//FIXME This is O(n^2) in rare cases when many faces intersect the cutting plane.
for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++ line)
if (! line->skip && line->edge_type != feNone) {
// This line is af facet edge. There may be a duplicate line with the same end vertices.
// If the line is is an edge connecting two facets, find another facet edge
// having the same endpoints but in reverse order.
for (IntersectionLines::iterator line2 = line + 1; line2 != lines.end(); ++ line2)
if (! line2->skip && line2->edge_type != feNone) {
// Are these facets adjacent? (sharing a common edge on this layer)
if (line->a_id == line2->a_id && line->b_id == line2->b_id) {
line2->skip = true;
/* if they are both oriented upwards or downwards (like a 'V')
then we can remove both edges from this layer since it won't
affect the sliced shape */
/* if one of them is oriented upwards and the other is oriented
downwards, let's only keep one of them (it doesn't matter which
one since all 'top' lines were reversed at slicing) */
if (line->edge_type == line2->edge_type) {
line->skip = true;
break;
}
} else if (line->a_id == line2->b_id && line->b_id == line2->a_id) {
/* if this edge joins two horizontal facets, remove both of them */
if (line->edge_type == feHorizontal && line2->edge_type == feHorizontal) {
line->skip = true;
line2->skip = true;
break;
}
}
}
}
#if 0
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
//#ifdef _DEBUG
for (const Line &l : lines)
assert(l.a != l.b);
#endif /* _DEBUG */
remove_tangent_edges(lines);
struct OpenPolyline {
OpenPolyline() {};
@@ -1032,7 +1233,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
by_edge_a_id.reserve(lines.size());
by_a_id.reserve(lines.size());
for (IntersectionLine &line : lines) {
if (! line.skip) {
if (! line.skip()) {
if (line.edge_a_id != -1)
by_edge_a_id.emplace_back(&line);
if (line.a_id != -1)
@@ -1049,13 +1250,14 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
// take first spare line and start a new loop
IntersectionLine *first_line = nullptr;
for (; it_line_seed != lines.end(); ++ it_line_seed)
if (! it_line_seed->skip) {
if (it_line_seed->is_seed_candidate()) {
//if (! it_line_seed->skip()) {
first_line = &(*it_line_seed ++);
break;
}
if (first_line == nullptr)
break;
first_line->skip = true;
first_line->set_skip();
Points loop_pts;
loop_pts.emplace_back(first_line->a);
IntersectionLine *last_line = first_line;
@@ -1076,7 +1278,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
if (it_begin != by_edge_a_id.end()) {
auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
if (! (*it_line)->skip) {
if (! (*it_line)->skip()) {
next_line = *it_line;
break;
}
@@ -1088,7 +1290,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
if (it_begin != by_a_id.end()) {
auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
if (! (*it_line)->skip) {
if (! (*it_line)->skip()) {
next_line = *it_line;
break;
}
@@ -1119,7 +1321,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
*/
loop_pts.emplace_back(next_line->a);
last_line = next_line;
next_line->skip = true;
next_line->set_skip();
}
}
}
@@ -1186,12 +1388,12 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
}
}
}
if (next_start == nullptr) {
// The current loop could not be closed. Unmark the segment.
opl.consumed = false;
break;
}
// Attach this polyline to the end of the initial polyline.
if (next_start == nullptr) {
// The current loop could not be closed. Unmark the segment.
opl.consumed = false;
break;
}
// Attach this polyline to the end of the initial polyline.
if (next_start->start) {
auto it = next_start->polyline->points.begin();
std::copy(++ it, next_start->polyline->points.end(), back_inserter(opl.points));
@@ -1211,8 +1413,8 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) ||
(ip1.point_id != -1 && ip1.point_id == ip2.point_id)) {
// The current loop is complete. Add it to the output.
/*assert(opl.points.front().point_id == opl.points.back().point_id);
assert(opl.points.front().edge_id == opl.points.back().edge_id);*/
//assert(opl.points.front().point_id == opl.points.back().point_id);
//assert(opl.points.front().edge_id == opl.points.back().edge_id);
// Remove the duplicate last point.
opl.points.pop_back();
if (opl.points.size() >= 3) {
@@ -1227,9 +1429,9 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
loops->emplace_back(std::move(opl.points));
}
opl.points.clear();
break;
break;
}
// Continue with the current loop.
// Continue with the current loop.
}
}
}
@@ -1277,15 +1479,15 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &l
if (slice_idx == -1)
// Ignore this hole.
continue;
assert(current_contour_area < std::numeric_limits<double>::max() && current_contour_area >= -hole->area());
(*slices)[slice_idx].holes.emplace_back(std::move(*hole));
assert(current_contour_area < std::numeric_limits<double>::max() && current_contour_area >= -hole->area());
(*slices)[slice_idx].holes.emplace_back(std::move(*hole));
}
#if 0
// If the input mesh is not valid, the holes may intersect with the external contour.
// Rather subtract them from the outer contour.
Polygons poly;
for (auto it_slice = slices->begin(); it_slice != slices->end(); ++ it_slice) {
for (auto it_slice = slices->begin(); it_slice != slices->end(); ++ it_slice) {
if (it_slice->holes.empty()) {
poly.emplace_back(std::move(it_slice->contour));
} else {
@@ -1295,7 +1497,7 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &l
it->reverse();
polygons_append(poly, diff(contours, it_slice->holes));
}
}
}
// If the input mesh is not valid, the input contours may intersect.
*slices = union_ex(poly);
#endif
@@ -1412,7 +1614,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
// intersect facet with cutting plane
IntersectionLine line;
if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line)) {
if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line) != TriangleMeshSlicer::NoSlice) {
// Save intersection lines for generating correct triangulations.
if (line.edge_type == feTop) {
lower_lines.emplace_back(line);
+31 -6
View File
@@ -83,7 +83,7 @@ private:
enum FacetEdgeType {
// A general case, the cutting plane intersect a face at two different edges.
feNone,
feGeneral,
// Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
feTop,
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
@@ -117,6 +117,14 @@ public:
class IntersectionLine : public Line
{
public:
IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feGeneral), flags(0) {}
bool skip() const { return (this->flags & SKIP) != 0; }
void set_skip() { this->flags |= SKIP; }
bool is_seed_candidate() const { return (this->flags & NO_SEED) == 0 && ! this->skip(); }
void set_no_seed(bool set) { if (set) this->flags |= NO_SEED; else this->flags &= ~NO_SEED; }
// Inherits Point a, b
// For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
// Vertex indices of the line end points.
@@ -125,11 +133,23 @@ public:
// Source mesh edges of the line end points.
int edge_a_id;
int edge_b_id;
// feNone, feTop, feBottom, feHorizontal
// feGeneral, feTop, feBottom, feHorizontal
FacetEdgeType edge_type;
// Used by TriangleMeshSlicer::make_loops() to skip duplicate edges.
bool skip;
IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feNone), skip(false) {};
// Used by TriangleMeshSlicer::slice() to skip duplicate edges.
enum {
// Triangle edge added, because it has no neighbor.
EDGE0_NO_NEIGHBOR = 0x001,
EDGE1_NO_NEIGHBOR = 0x002,
EDGE2_NO_NEIGHBOR = 0x004,
// Triangle edge added, because it makes a fold with another horizontal edge.
EDGE0_FOLD = 0x010,
EDGE1_FOLD = 0x020,
EDGE2_FOLD = 0x040,
// The edge cannot be a seed of a greedy loop extraction (folds are not safe to become seeds).
NO_SEED = 0x100,
SKIP = 0x200,
};
uint32_t flags;
};
typedef std::vector<IntersectionLine> IntersectionLines;
typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
@@ -144,7 +164,12 @@ public:
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;
bool slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
enum FacetSliceType {
NoSlice = 0,
Slicing = 1,
Cutting = 2
};
FacetSliceType slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
const float min_z, const float max_z, IntersectionLine *line_out) const;
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
+1
View File
@@ -9,6 +9,7 @@ namespace Slic3r {
extern void set_logging_level(unsigned int level);
extern void trace(unsigned int level, const char *message);
extern void disable_multi_threading();
// Set a path with GUI resource files.
void set_var_dir(const std::string &path);
+2
View File
@@ -13,6 +13,8 @@
#include <vector>
#include <boost/thread.hpp>
#include "Technologies.hpp"
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
#define SLIC3R_VERSION "1.41.0"
#define SLIC3R_BUILD "UNKNOWN"
+10
View File
@@ -25,6 +25,8 @@
#include <boost/nowide/convert.hpp>
#include <boost/nowide/cstdio.hpp>
#include <tbb/task_scheduler_init.h>
namespace Slic3r {
static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error;
@@ -83,6 +85,14 @@ void trace(unsigned int level, const char *message)
(::boost::log::keywords::severity = severity)) << message;
}
void disable_multi_threading()
{
// Disable parallelization so the Shiny profiler works
static tbb::task_scheduler_init *tbb_init = nullptr;
if (tbb_init == nullptr)
tbb_init = new tbb::task_scheduler_init(1);
}
static std::string g_var_dir;
void set_var_dir(const std::string &dir)