Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_3dconnexion

This commit is contained in:
Enrico Turri 2019-09-27 14:52:30 +02:00
commit 4b3eab0ed9
53 changed files with 1055 additions and 365 deletions

View file

@ -89,33 +89,34 @@ struct stl_neighbors {
}; };
struct stl_stats { struct stl_stats {
stl_stats() { this->reset(); } stl_stats() { memset(&header, 0, 81); }
void reset() { memset(this, 0, sizeof(stl_stats)); this->volume = -1.0; } char header[81];
char header[81]; stl_type type = (stl_type)0;
stl_type type; uint32_t number_of_facets = 0;
uint32_t number_of_facets; stl_vertex max = stl_vertex::Zero();
stl_vertex max; stl_vertex min = stl_vertex::Zero();
stl_vertex min; stl_vertex size = stl_vertex::Zero();
stl_vertex size; float bounding_diameter = 0.f;
float bounding_diameter; float shortest_edge = 0.f;
float shortest_edge; float volume = -1.f;
float volume; int connected_edges = 0;
int connected_edges; int connected_facets_1_edge = 0;
int connected_facets_1_edge; int connected_facets_2_edge = 0;
int connected_facets_2_edge; int connected_facets_3_edge = 0;
int connected_facets_3_edge; int facets_w_1_bad_edge = 0;
int facets_w_1_bad_edge; int facets_w_2_bad_edge = 0;
int facets_w_2_bad_edge; int facets_w_3_bad_edge = 0;
int facets_w_3_bad_edge; int original_num_facets = 0;
int original_num_facets; int edges_fixed = 0;
int edges_fixed; int degenerate_facets = 0;
int degenerate_facets; int facets_removed = 0;
int facets_removed; int facets_added = 0;
int facets_added; int facets_reversed = 0;
int facets_reversed; int backwards_edges = 0;
int backwards_edges; int normals_fixed = 0;
int normals_fixed; int number_of_parts = 0;
int number_of_parts;
void clear() { *this = stl_stats(); }
}; };
struct stl_file { struct stl_file {
@ -124,7 +125,7 @@ struct stl_file {
void clear() { void clear() {
this->facet_start.clear(); this->facet_start.clear();
this->neighbors_start.clear(); this->neighbors_start.clear();
this->stats.reset(); this->stats.clear();
} }
size_t memsize() const { size_t memsize() const {

View file

@ -15,4 +15,4 @@
#undef clipper_hpp #undef clipper_hpp
#undef use_xyz #undef use_xyz
#endif clipper_z_hpp #endif // clipper_z_hpp

View file

@ -100,7 +100,7 @@ add_library(libslic3r STATIC
Geometry.cpp Geometry.cpp
Geometry.hpp Geometry.hpp
Int128.hpp Int128.hpp
# KdTree.hpp KDTreeIndirect.hpp
Layer.cpp Layer.cpp
Layer.hpp Layer.hpp
LayerRegion.cpp LayerRegion.cpp
@ -142,6 +142,8 @@ add_library(libslic3r STATIC
PrintObject.cpp PrintObject.cpp
PrintRegion.cpp PrintRegion.cpp
Semver.cpp Semver.cpp
ShortestPath.cpp
ShortestPath.hpp
SLAPrint.cpp SLAPrint.cpp
SLAPrint.hpp SLAPrint.hpp
SLA/SLAAutoSupports.hpp SLA/SLAAutoSupports.hpp

View file

@ -1,5 +1,6 @@
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Geometry.hpp" #include "Geometry.hpp"
#include "ShortestPath.hpp"
// #define CLIPPER_UTILS_DEBUG // #define CLIPPER_UTILS_DEBUG
@ -671,21 +672,19 @@ void traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval)
// collect ordering points // collect ordering points
Points ordering_points; Points ordering_points;
ordering_points.reserve(nodes.size()); ordering_points.reserve(nodes.size());
for (ClipperLib::PolyNodes::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { for (ClipperLib::PolyNode *pn : nodes)
Point p((*it)->Contour.front().X, (*it)->Contour.front().Y); ordering_points.emplace_back(Point(pn->Contour.front().X, pn->Contour.front().Y));
ordering_points.emplace_back(p);
}
// perform the ordering // perform the ordering
ClipperLib::PolyNodes ordered_nodes; ClipperLib::PolyNodes ordered_nodes = chain_clipper_polynodes(ordering_points, nodes);
Slic3r::Geometry::chained_path_items(ordering_points, nodes, ordered_nodes);
// push results recursively // push results recursively
for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) { for (ClipperLib::PolyNode *pn : ordered_nodes) {
// traverse the next depth // traverse the next depth
traverse_pt((*it)->Childs, retval); traverse_pt(pn->Childs, retval);
retval->emplace_back(ClipperPath_to_Slic3rPolygon((*it)->Contour)); retval->emplace_back(ClipperPath_to_Slic3rPolygon(pn->Contour));
if ((*it)->IsHole()) retval->back().reverse(); // ccw if (pn->IsHole())
retval->back().reverse(); // ccw
} }
} }

View file

@ -249,7 +249,7 @@ ConfigOption* ConfigOptionDef::create_default_option() const
// Special case: For a DynamicConfig, convert a templated enum to a generic enum. // Special case: For a DynamicConfig, convert a templated enum to a generic enum.
new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt()) : new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt()) :
this->default_value->clone(); this->default_value->clone();
return this->create_empty_option(); return this->create_empty_option();
} }
// Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread! // Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread!

View file

@ -353,7 +353,7 @@ public:
bool apply_override(const ConfigOption *rhs) override { bool apply_override(const ConfigOption *rhs) override {
if (this->nullable()) if (this->nullable())
throw std::runtime_error("Cannot override a nullable ConfigOption."); throw std::runtime_error("Cannot override a nullable ConfigOption.");
if (rhs->type() != this->type()) if (rhs->type() != this->type())
throw std::runtime_error("ConfigOptionVector.apply_override() applied to different types."); throw std::runtime_error("ConfigOptionVector.apply_override() applied to different types.");
auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs); auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
if (! rhs->nullable()) { if (! rhs->nullable()) {
@ -461,7 +461,7 @@ public:
for (const double &v : this->values) { for (const double &v : this->values) {
if (&v != &this->values.front()) if (&v != &this->values.front())
ss << ","; ss << ",";
serialize_single_value(ss, v); serialize_single_value(ss, v);
} }
return ss.str(); return ss.str();
} }
@ -607,7 +607,7 @@ public:
for (const int &v : this->values) { for (const int &v : this->values) {
if (&v != &this->values.front()) if (&v != &this->values.front())
ss << ","; ss << ",";
serialize_single_value(ss, v); serialize_single_value(ss, v);
} }
return ss.str(); return ss.str();
} }

View file

@ -5,6 +5,8 @@
#include "Polygon.hpp" #include "Polygon.hpp"
#include "Polyline.hpp" #include "Polyline.hpp"
#include <assert.h>
namespace Slic3r { namespace Slic3r {
class ExPolygonCollection; class ExPolygonCollection;

View file

@ -16,7 +16,6 @@ ExtrusionEntityCollection& ExtrusionEntityCollection::operator=(const ExtrusionE
this->entities = other.entities; this->entities = other.entities;
for (size_t i = 0; i < this->entities.size(); ++i) for (size_t i = 0; i < this->entities.size(); ++i)
this->entities[i] = this->entities[i]->clone(); this->entities[i] = this->entities[i]->clone();
this->orig_indices = other.orig_indices;
this->no_sort = other.no_sort; this->no_sort = other.no_sort;
return *this; return *this;
} }
@ -24,7 +23,6 @@ ExtrusionEntityCollection& ExtrusionEntityCollection::operator=(const ExtrusionE
void ExtrusionEntityCollection::swap(ExtrusionEntityCollection &c) void ExtrusionEntityCollection::swap(ExtrusionEntityCollection &c)
{ {
std::swap(this->entities, c.entities); std::swap(this->entities, c.entities);
std::swap(this->orig_indices, c.orig_indices);
std::swap(this->no_sort, c.no_sort); std::swap(this->no_sort, c.no_sort);
} }
@ -82,10 +80,10 @@ ExtrusionEntityCollection ExtrusionEntityCollection::chained_path(bool no_revers
return coll; return coll;
} }
void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector<size_t>* orig_indices) const void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const
{ {
if (this->entities.empty()) return; if (this->entities.empty()) return;
this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role, orig_indices); this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role);
} }
ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point start_near, bool no_reverse, ExtrusionRole role) const ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point start_near, bool no_reverse, ExtrusionRole role) const
@ -95,7 +93,7 @@ ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point sta
return coll; return coll;
} }
void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector<size_t>* orig_indices) const void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role) const
{ {
if (this->no_sort) { if (this->no_sort) {
*retval = *this; *retval = *this;
@ -103,7 +101,6 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
} }
retval->entities.reserve(this->entities.size()); retval->entities.reserve(this->entities.size());
retval->orig_indices.reserve(this->entities.size());
// if we're asked to return the original indices, build a map // if we're asked to return the original indices, build a map
std::map<ExtrusionEntity*,size_t> indices_map; std::map<ExtrusionEntity*,size_t> indices_map;
@ -122,8 +119,8 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
ExtrusionEntity *entity = entity_src->clone(); ExtrusionEntity *entity = entity_src->clone();
my_paths.push_back(entity); my_paths.push_back(entity);
if (orig_indices != nullptr) // if (orig_indices != nullptr)
indices_map[entity] = &entity_src - &this->entities.front(); // indices_map[entity] = &entity_src - &this->entities.front();
} }
Points endpoints; Points endpoints;
@ -142,8 +139,8 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
if (start_index % 2 && !no_reverse && entity->can_reverse()) if (start_index % 2 && !no_reverse && entity->can_reverse())
entity->reverse(); entity->reverse();
retval->entities.push_back(my_paths.at(path_index)); retval->entities.push_back(my_paths.at(path_index));
if (orig_indices != nullptr) // if (orig_indices != nullptr)
orig_indices->push_back(indices_map[entity]); // orig_indices->push_back(indices_map[entity]);
my_paths.erase(my_paths.begin() + path_index); my_paths.erase(my_paths.begin() + path_index);
endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2); endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2);
start_near = retval->entities.back()->last_point(); start_near = retval->entities.back()->last_point();

View file

@ -14,15 +14,14 @@ public:
ExtrusionEntity* clone_move() override { return new ExtrusionEntityCollection(std::move(*this)); } ExtrusionEntity* clone_move() override { return new ExtrusionEntityCollection(std::move(*this)); }
ExtrusionEntitiesPtr entities; // we own these entities ExtrusionEntitiesPtr entities; // we own these entities
std::vector<size_t> orig_indices; // handy for XS
bool no_sort; bool no_sort;
ExtrusionEntityCollection(): no_sort(false) {}; ExtrusionEntityCollection(): no_sort(false) {};
ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : orig_indices(other.orig_indices), no_sort(other.no_sort) { this->append(other.entities); } ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : no_sort(other.no_sort) { this->append(other.entities); }
ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), orig_indices(std::move(other.orig_indices)), no_sort(other.no_sort) {} ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), no_sort(other.no_sort) {}
explicit ExtrusionEntityCollection(const ExtrusionPaths &paths); explicit ExtrusionEntityCollection(const ExtrusionPaths &paths);
ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other); ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other);
ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other) ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other)
{ this->entities = std::move(other.entities); this->orig_indices = std::move(other.orig_indices); this->no_sort = other.no_sort; return *this; } { this->entities = std::move(other.entities); this->no_sort = other.no_sort; return *this; }
~ExtrusionEntityCollection() { clear(); } ~ExtrusionEntityCollection() { clear(); }
explicit operator ExtrusionPaths() const; explicit operator ExtrusionPaths() const;
@ -67,9 +66,9 @@ public:
void replace(size_t i, const ExtrusionEntity &entity); void replace(size_t i, const ExtrusionEntity &entity);
void remove(size_t i); void remove(size_t i);
ExtrusionEntityCollection chained_path(bool no_reverse = false, ExtrusionRole role = erMixed) const; ExtrusionEntityCollection chained_path(bool no_reverse = false, ExtrusionRole role = erMixed) const;
void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed, std::vector<size_t>* orig_indices = nullptr) const; void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const;
ExtrusionEntityCollection chained_path_from(Point start_near, bool no_reverse = false, ExtrusionRole role = erMixed) const; ExtrusionEntityCollection chained_path_from(Point start_near, bool no_reverse = false, ExtrusionRole role = erMixed) const;
void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed, std::vector<size_t>* orig_indices = nullptr) const; void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed) const;
void reverse(); void reverse();
Point first_point() const { return this->entities.front()->first_point(); } Point first_point() const { return this->entities.front()->first_point(); }
Point last_point() const { return this->entities.back()->last_point(); } Point last_point() const { return this->entities.back()->last_point(); }

View file

@ -15,40 +15,39 @@ namespace Slic3r {
struct SurfaceFillParams struct SurfaceFillParams
{ {
SurfaceFillParams() : flow(0.f, 0.f, 0.f, false) { memset(this, 0, sizeof(*this)); }
// Zero based extruder ID. // Zero based extruder ID.
unsigned int extruder; unsigned int extruder = 0;
// Infill pattern, adjusted for the density etc. // Infill pattern, adjusted for the density etc.
InfillPattern pattern; InfillPattern pattern = InfillPattern(0);
// FillBase // FillBase
// in unscaled coordinates // in unscaled coordinates
coordf_t spacing; coordf_t spacing = 0.;
// infill / perimeter overlap, in unscaled coordinates // infill / perimeter overlap, in unscaled coordinates
coordf_t overlap; coordf_t overlap = 0.;
// Angle as provided by the region config, in radians. // Angle as provided by the region config, in radians.
float angle; float angle = 0.f;
// Non-negative for a bridge. // Non-negative for a bridge.
float bridge_angle; float bridge_angle = 0.f;
// FillParams // FillParams
float density; float density = 0.f;
// Don't connect the fill lines around the inner perimeter. // Don't connect the fill lines around the inner perimeter.
bool dont_connect; bool dont_connect = false;
// Don't adjust spacing to fill the space evenly. // Don't adjust spacing to fill the space evenly.
bool dont_adjust; bool dont_adjust = false;
// width, height of extrusion, nozzle diameter, is bridge // width, height of extrusion, nozzle diameter, is bridge
// For the output, for fill generator. // For the output, for fill generator.
Flow flow; Flow flow = Flow(0.f, 0.f, 0.f, false);
// For the output // For the output
ExtrusionRole extrusion_role; ExtrusionRole extrusion_role = ExtrusionRole(0);
// Various print settings? // Various print settings?
// Index of this entry in a linear vector. // Index of this entry in a linear vector.
size_t idx; size_t idx = 0;
bool operator<(const SurfaceFillParams &rhs) const { bool operator<(const SurfaceFillParams &rhs) const {

View file

@ -1,5 +1,5 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../PolylineCollection.hpp" #include "../ShortestPath.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include "Fill3DHoneycomb.hpp" #include "Fill3DHoneycomb.hpp"
@ -175,27 +175,24 @@ void Fill3DHoneycomb::_fill_surface_single(
std::swap(expolygon_off, expolygons_off.front()); std::swap(expolygon_off, expolygons_off.front());
} }
} }
Polylines chained = PolylineCollection::chained_path_from(
std::move(polylines),
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
bool first = true; bool first = true;
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
if (! first) { if (! first) {
// Try to connect the lines. // Try to connect the lines.
Points &pts_end = polylines_out.back().points; Points &pts_end = polylines_out.back().points;
const Point &first_point = it_polyline->points.front(); const Point &first_point = polyline.points.front();
const Point &last_point = pts_end.back(); const Point &last_point = pts_end.back();
// TODO: we should also check that both points are on a fill_boundary to avoid // TODO: we should also check that both points are on a fill_boundary to avoid
// connecting paths on the boundaries of internal regions // connecting paths on the boundaries of internal regions
if ((last_point - first_point).cast<double>().norm() <= 1.5 * distance && if ((last_point - first_point).cast<double>().norm() <= 1.5 * distance &&
expolygon_off.contains(Line(last_point, first_point))) { expolygon_off.contains(Line(last_point, first_point))) {
// Append the polyline. // Append the polyline.
pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end()); pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
continue; continue;
} }
} }
// The lines cannot be connected. // The lines cannot be connected.
polylines_out.emplace_back(std::move(*it_polyline)); polylines_out.emplace_back(std::move(polyline));
first = false; first = false;
} }
} }

View file

@ -1,5 +1,5 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../PolylineCollection.hpp" #include "../ShortestPath.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
@ -166,11 +166,8 @@ void FillGyroid::_fill_surface_single(
std::swap(expolygon_off, expolygons_off.front()); std::swap(expolygon_off, expolygons_off.front());
} }
} }
Polylines chained = PolylineCollection::chained_path_from(
std::move(polylines),
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
bool first = true; bool first = true;
for (Polyline &polyline : chained) { for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
if (! first) { if (! first) {
// Try to connect the lines. // Try to connect the lines.
Points &pts_end = polylines_out.back().points; Points &pts_end = polylines_out.back().points;

View file

@ -1,5 +1,5 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../PolylineCollection.hpp" #include "../ShortestPath.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include "FillHoneycomb.hpp" #include "FillHoneycomb.hpp"
@ -93,22 +93,20 @@ void FillHoneycomb::_fill_surface_single(
// connect paths // connect paths
if (! paths.empty()) { // prevent calling leftmost_point() on empty collections if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
Polylines chained = PolylineCollection::chained_path_from( Polylines chained = chain_infill_polylines(std::move(paths));
std::move(paths),
PolylineCollection::leftmost_point(paths), false);
assert(paths.empty()); assert(paths.empty());
paths.clear(); paths.clear();
for (Polylines::iterator it_path = chained.begin(); it_path != chained.end(); ++ it_path) { for (Polyline &path : chained) {
if (! paths.empty()) { if (! paths.empty()) {
// distance between first point of this path and last point of last path // distance between first point of this path and last point of last path
double distance = (it_path->first_point() - paths.back().last_point()).cast<double>().norm(); double distance = (path.first_point() - paths.back().last_point()).cast<double>().norm();
if (distance <= m.hex_width) { if (distance <= m.hex_width) {
paths.back().points.insert(paths.back().points.end(), it_path->points.begin(), it_path->points.end()); paths.back().points.insert(paths.back().points.end(), path.points.begin(), path.points.end());
continue; continue;
} }
} }
// Don't connect the paths. // Don't connect the paths.
paths.push_back(*it_path); paths.push_back(std::move(path));
} }
} }

View file

@ -1,6 +1,6 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp" #include "../ExPolygon.hpp"
#include "../PolylineCollection.hpp" #include "../ShortestPath.hpp"
#include "../Surface.hpp" #include "../Surface.hpp"
#include "FillRectilinear.hpp" #include "FillRectilinear.hpp"
@ -92,15 +92,12 @@ void FillRectilinear::_fill_surface_single(
std::swap(expolygon_off, expolygons_off.front()); std::swap(expolygon_off, expolygons_off.front());
} }
} }
Polylines chained = PolylineCollection::chained_path_from(
std::move(polylines),
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
bool first = true; bool first = true;
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { for (Polyline &polyline : chain_infill_polylines(std::move(polylines))) {
if (! first) { if (! first) {
// Try to connect the lines. // Try to connect the lines.
Points &pts_end = polylines_out.back().points; Points &pts_end = polylines_out.back().points;
const Point &first_point = it_polyline->points.front(); const Point &first_point = polyline.points.front();
const Point &last_point = pts_end.back(); const Point &last_point = pts_end.back();
// Distance in X, Y. // Distance in X, Y.
const Vector distance = last_point - first_point; const Vector distance = last_point - first_point;
@ -109,12 +106,12 @@ void FillRectilinear::_fill_surface_single(
if (this->_can_connect(std::abs(distance(0)), std::abs(distance(1))) && if (this->_can_connect(std::abs(distance(0)), std::abs(distance(1))) &&
expolygon_off.contains(Line(last_point, first_point))) { expolygon_off.contains(Line(last_point, first_point))) {
// Append the polyline. // Append the polyline.
pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end()); pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
continue; continue;
} }
} }
// The lines cannot be connected. // The lines cannot be connected.
polylines_out.emplace_back(std::move(*it_polyline)); polylines_out.emplace_back(std::move(polyline));
first = false; first = false;
} }
} }

View file

@ -979,7 +979,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << layer_height_profile.front(); stream << layer_height_profile.front();
for (size_t i = 1; i < layer_height_profile.size(); ++i) for (size_t i = 1; i < layer_height_profile.size(); ++i)
stream << ";" << layer_height_profile[i]; stream << ";" << layer_height_profile[i];
stream << "\n </metadata>\n"; stream << "\n </metadata>\n";
} }
// Export layer height ranges including the layer range specific config overrides. // Export layer height ranges including the layer range specific config overrides.

View file

@ -246,7 +246,7 @@ static void extract_model_from_archive(
sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) { sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
// Normal was mangled. Maybe denormals or "not a number" were stored? // Normal was mangled. Maybe denormals or "not a number" were stored?
// Just reset the normal and silently ignore it. // Just reset the normal and silently ignore it.
memset(&facet.normal, 0, sizeof(facet.normal)); facet.normal = stl_normal::Zero();
} }
facets.emplace_back(facet); facets.emplace_back(facet);
} }
@ -278,7 +278,7 @@ static void extract_model_from_archive(
instance->set_rotation(instance_rotation); instance->set_rotation(instance_rotation);
instance->set_scaling_factor(instance_scaling_factor); instance->set_scaling_factor(instance_scaling_factor);
instance->set_offset(instance_offset); instance->set_offset(instance_offset);
if (group_id != (size_t)-1) if (group_id != (unsigned int)(-1))
group_to_model_object[group_id] = model_object; group_to_model_object[group_id] = model_object;
} else { } else {
// This is not the 1st mesh of a group. Add it to the ModelObject. // This is not the 1st mesh of a group. Add it to the ModelObject.

View file

@ -6,6 +6,7 @@
#include "Geometry.hpp" #include "Geometry.hpp"
#include "GCode/PrintExtents.hpp" #include "GCode/PrintExtents.hpp"
#include "GCode/WipeTower.hpp" #include "GCode/WipeTower.hpp"
#include "ShortestPath.hpp"
#include "Utils.hpp" #include "Utils.hpp"
#include <algorithm> #include <algorithm>
@ -659,7 +660,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
if (print->is_step_done(psGCodeExport) && boost::filesystem::exists(boost::filesystem::path(path))) if (print->is_step_done(psGCodeExport) && boost::filesystem::exists(boost::filesystem::path(path)))
return; return;
print->set_started(psGCodeExport); print->set_started(psGCodeExport);
BOOST_LOG_TRIVIAL(info) << "Exporting G-code..." << log_memory_info(); BOOST_LOG_TRIVIAL(info) << "Exporting G-code..." << log_memory_info();
@ -1160,7 +1161,7 @@ void GCode::_do_export(Print &print, FILE *file)
for (const LayerToPrint &ltp : layers_to_print) { for (const LayerToPrint &ltp : layers_to_print) {
std::vector<LayerToPrint> lrs; std::vector<LayerToPrint> lrs;
lrs.emplace_back(std::move(ltp)); lrs.emplace_back(std::move(ltp));
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), &copy - object.copies().data()); this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, &copy - object.copies().data());
print.throw_if_canceled(); print.throw_if_canceled();
} }
#ifdef HAS_PRESSURE_EQUALIZER #ifdef HAS_PRESSURE_EQUALIZER
@ -1174,12 +1175,8 @@ void GCode::_do_export(Print &print, FILE *file)
} }
} }
} else { } else {
// Order objects using a nearest neighbor search. // Order object instances using a nearest neighbor search.
std::vector<size_t> object_indices; std::vector<std::pair<size_t, size_t>> print_object_instances_ordering = chain_print_object_instances(print);
Points object_reference_points;
for (PrintObject *object : print.objects())
object_reference_points.push_back(object->copies().front());
Slic3r::Geometry::chained_path(object_reference_points, object_indices);
// Sort layers by Z. // Sort layers by Z.
// All extrusion moves with the same top layer height are extruded uninterrupted. // All extrusion moves with the same top layer height are extruded uninterrupted.
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print); std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
@ -1218,7 +1215,7 @@ void GCode::_do_export(Print &print, FILE *file)
const LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first); const LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
if (m_wipe_tower && layer_tools.has_wipe_tower) if (m_wipe_tower && layer_tools.has_wipe_tower)
m_wipe_tower->next_layer(); m_wipe_tower->next_layer();
this->process_layer(file, print, layer.second, layer_tools, size_t(-1)); this->process_layer(file, print, layer.second, layer_tools, &print_object_instances_ordering, size_t(-1));
print.throw_if_canceled(); print.throw_if_canceled();
} }
#ifdef HAS_PRESSURE_EQUALIZER #ifdef HAS_PRESSURE_EQUALIZER
@ -1415,7 +1412,7 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
// Skip the rest of the line. // Skip the rest of the line.
for (; *ptr != 0 && *ptr != '\r' && *ptr != '\n'; ++ ptr); for (; *ptr != 0 && *ptr != '\r' && *ptr != '\n'; ++ ptr);
// Skip the end of line indicators. // Skip the end of line indicators.
for (; *ptr == '\r' || *ptr == '\n'; ++ ptr); for (; *ptr == '\r' || *ptr == '\n'; ++ ptr);
} }
return temp_set_by_gcode; return temp_set_by_gcode;
} }
@ -1529,8 +1526,54 @@ inline std::vector<GCode::ObjectByExtruder::Island>& object_islands_by_extruder(
return islands; return islands;
} }
std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
std::vector<GCode::ObjectByExtruder> &objects_by_extruder,
const std::vector<LayerToPrint> &layers,
// Ordering must be defined for normal (non-sequential print).
const std::vector<std::pair<size_t, size_t>> *ordering,
// For sequential print, the instance of the object to be printing has to be defined.
const size_t single_object_instance_idx)
{
std::vector<InstanceToPrint> out;
if (ordering == nullptr) {
// Sequential print, single object is being printed.
for (ObjectByExtruder &object_by_extruder : objects_by_extruder) {
const size_t layer_id = &object_by_extruder - objects_by_extruder.data();
const PrintObject *print_object = layers[layer_id].object();
if (print_object)
out.emplace_back(object_by_extruder, layer_id, *print_object, single_object_instance_idx);
}
} else {
// Create mapping from PrintObject* to ObjectByExtruder*.
std::vector<std::pair<const PrintObject*, ObjectByExtruder*>> sorted;
sorted.reserve(objects_by_extruder.size());
for (ObjectByExtruder &object_by_extruder : objects_by_extruder) {
const size_t layer_id = &object_by_extruder - objects_by_extruder.data();
const PrintObject *print_object = layers[layer_id].object();
if (print_object)
sorted.emplace_back(print_object, &object_by_extruder);
}
std::sort(sorted.begin(), sorted.end());
if (! sorted.empty()) {
const Print &print = *sorted.front().first->print();
out.reserve(sorted.size());
for (const std::pair<size_t, size_t> &instance_id : *ordering) {
const PrintObject &print_object = *print.objects()[instance_id.first];
std::pair<const PrintObject*, ObjectByExtruder*> key(&print_object, nullptr);
auto it = std::lower_bound(sorted.begin(), sorted.end(), key);
if (it != sorted.end() && it->first == &print_object)
// ObjectByExtruder for this PrintObject was found.
out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance_id.second);
}
}
}
return out;
}
// In sequential mode, process_layer is called once per each object and its copy, // In sequential mode, process_layer is called once per each object and its copy,
// therefore layers will contain a single entry and single_object_idx will point to the copy of the object. // therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object.
// In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated. // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated.
// For multi-material prints, this routine minimizes extruder switches by gathering extruder specific extrusion paths // For multi-material prints, this routine minimizes extruder switches by gathering extruder specific extrusion paths
// and performing the extruder specific extrusions together. // and performing the extruder specific extrusions together.
@ -1541,14 +1584,16 @@ void GCode::process_layer(
// Set of object & print layers of the same PrintObject and with the same print_z. // Set of object & print layers of the same PrintObject and with the same print_z.
const std::vector<LayerToPrint> &layers, const std::vector<LayerToPrint> &layers,
const LayerTools &layer_tools, const LayerTools &layer_tools,
// Pairs of PrintObject index and its instance index.
const std::vector<std::pair<size_t, size_t>> *ordering,
// If set to size_t(-1), then print all copies of all objects. // If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object. // Otherwise print a single copy of a single object.
const size_t single_object_idx) const size_t single_object_instance_idx)
{ {
assert(! layers.empty()); assert(! layers.empty());
// assert(! layer_tools.extruders.empty()); // assert(! layer_tools.extruders.empty());
// Either printing all copies of all objects, or just a single copy of a single object. // Either printing all copies of all objects, or just a single copy of a single object.
assert(single_object_idx == size_t(-1) || layers.size() == 1); assert(single_object_instance_idx == size_t(-1) || layers.size() == 1);
if (layer_tools.extruders.empty()) if (layer_tools.extruders.empty())
// Nothing to extrude. // Nothing to extrude.
@ -1762,6 +1807,17 @@ void GCode::process_layer(
layer_surface_bboxes.reserve(n_slices); layer_surface_bboxes.reserve(n_slices);
for (const ExPolygon &expoly : layer.slices.expolygons) for (const ExPolygon &expoly : layer.slices.expolygons)
layer_surface_bboxes.push_back(get_extents(expoly.contour)); layer_surface_bboxes.push_back(get_extents(expoly.contour));
// Traverse the slices in an increasing order of bounding box size, so that the islands inside another islands are tested first,
// so we can just test a point inside ExPolygon::contour and we may skip testing the holes.
std::vector<size_t> slices_test_order;
slices_test_order.reserve(n_slices);
for (size_t i = 0; i < n_slices; ++ i)
slices_test_order.emplace_back(i);
std::sort(slices_test_order.begin(), slices_test_order.end(), [&layer_surface_bboxes](int i, int j) {
const Vec2d s1 = layer_surface_bboxes[i].size().cast<double>();
const Vec2d s2 = layer_surface_bboxes[j].size().cast<double>();
return s1.x() * s1.y() < s2.x() * s2.y();
});
auto point_inside_surface = [&layer, &layer_surface_bboxes](const size_t i, const Point &point) { auto point_inside_surface = [&layer, &layer_surface_bboxes](const size_t i, const Point &point) {
const BoundingBox &bbox = layer_surface_bboxes[i]; const BoundingBox &bbox = layer_surface_bboxes[i];
return point(0) >= bbox.min(0) && point(0) < bbox.max(0) && return point(0) >= bbox.min(0) && point(0) < bbox.max(0) &&
@ -1809,16 +1865,19 @@ void GCode::process_layer(
extruder, extruder,
&layer_to_print - layers.data(), &layer_to_print - layers.data(),
layers.size(), n_slices+1); layers.size(), n_slices+1);
for (size_t i = 0; i <= n_slices; ++i) for (size_t i = 0; i <= n_slices; ++ i) {
bool last = i == n_slices;
size_t island_idx = last ? n_slices : slices_test_order[i];
if (// fill->first_point does not fit inside any slice if (// fill->first_point does not fit inside any slice
i == n_slices || last ||
// fill->first_point fits inside ith slice // fill->first_point fits inside ith slice
point_inside_surface(i, fill->first_point())) { point_inside_surface(island_idx, fill->first_point())) {
if (islands[i].by_region.empty()) if (islands[island_idx].by_region.empty())
islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region()); islands[island_idx].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region());
islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size()); islands[island_idx].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size());
break; break;
} }
}
} }
} }
} }
@ -1883,62 +1942,49 @@ void GCode::process_layer(
if (objects_by_extruder_it == by_extruder.end()) if (objects_by_extruder_it == by_extruder.end())
continue; continue;
std::vector<InstanceToPrint> instances_to_print = sort_print_object_instances(objects_by_extruder_it->second, layers, ordering, single_object_instance_idx);
// We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature): // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
bool is_anything_overridden = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden(); bool is_anything_overridden = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden();
for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) { for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) {
if (is_anything_overridden && print_wipe_extrusions == 0) if (is_anything_overridden && print_wipe_extrusions == 0)
gcode+="; PURGING FINISHED\n"; gcode+="; PURGING FINISHED\n";
for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) { for (InstanceToPrint &instance_to_print : instances_to_print) {
const size_t layer_id = &object_by_extruder - objects_by_extruder_it->second.data(); m_config.apply(instance_to_print.print_object.config(), true);
const PrintObject *print_object = layers[layer_id].object(); m_layer = layers[instance_to_print.layer_id].layer();
if (print_object == nullptr)
// This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
continue;
m_config.apply(print_object->config(), true);
m_layer = layers[layer_id].layer();
if (m_config.avoid_crossing_perimeters) if (m_config.avoid_crossing_perimeters)
m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true)); m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
Points copies;
if (single_object_idx == size_t(-1))
copies = print_object->copies();
else
copies.push_back(print_object->copies()[single_object_idx]);
// Sort the copies by the closest point starting with the current print position.
unsigned int copy_id = 0; if (this->config().gcode_label_objects)
for (const Point &copy : copies) { gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n";
if (this->config().gcode_label_objects) // When starting a new object, use the external motion planner for the first travel move.
gcode += std::string("; printing object ") + print_object->model_object()->name + " id:" + std::to_string(layer_id) + " copy " + std::to_string(copy_id) + "\n"; const Point &offset = instance_to_print.print_object.copies()[instance_to_print.instance_id];
// When starting a new object, use the external motion planner for the first travel move. std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
std::pair<const PrintObject*, Point> this_object_copy(print_object, copy); if (m_last_obj_copy != this_object_copy)
if (m_last_obj_copy != this_object_copy) m_avoid_crossing_perimeters.use_external_mp_once = true;
m_avoid_crossing_perimeters.use_external_mp_once = true; m_last_obj_copy = this_object_copy;
m_last_obj_copy = this_object_copy; this->set_origin(unscale(offset));
this->set_origin(unscale(copy)); if (instance_to_print.object_by_extruder.support != nullptr && !print_wipe_extrusions) {
if (object_by_extruder.support != nullptr && !print_wipe_extrusions) { m_layer = layers[instance_to_print.layer_id].support_layer;
m_layer = layers[layer_id].support_layer; gcode += this->extrude_support(
gcode += this->extrude_support( // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
// support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths. instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, false, instance_to_print.object_by_extruder.support_extrusion_role));
object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role)); m_layer = layers[instance_to_print.layer_id].layer();
m_layer = layers[layer_id].layer();
}
for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
if (print.config().infill_first) {
gcode += this->extrude_infill(print, by_region_specific);
gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
} else {
gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
gcode += this->extrude_infill(print,by_region_specific);
}
}
if (this->config().gcode_label_objects)
gcode += std::string("; stop printing object ") + print_object->model_object()->name + " id:" + std::to_string(layer_id) + " copy " + std::to_string(copy_id) + "\n";
++ copy_id;
} }
for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) {
const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(instance_to_print.instance_id, extruder_id, print_wipe_extrusions) : island.by_region;
if (print.config().infill_first) {
gcode += this->extrude_infill(print, by_region_specific);
gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]);
} else {
gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]);
gcode += this->extrude_infill(print,by_region_specific);
}
}
if (this->config().gcode_label_objects)
gcode += std::string("; stop printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n";
} }
} }
} }
@ -2542,12 +2588,10 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy
std::string gcode; std::string gcode;
for (const ObjectByExtruder::Island::Region &region : by_region) { for (const ObjectByExtruder::Island::Region &region : by_region) {
m_config.apply(print.regions()[&region - &by_region.front()]->config()); m_config.apply(print.regions()[&region - &by_region.front()]->config());
ExtrusionEntityCollection chained = region.infills.chained_path_from(m_last_pos, false); for (ExtrusionEntity *fill : region.infills.chained_path_from(m_last_pos, false).entities) {
for (ExtrusionEntity *fill : chained.entities) {
auto *eec = dynamic_cast<ExtrusionEntityCollection*>(fill); auto *eec = dynamic_cast<ExtrusionEntityCollection*>(fill);
if (eec) { if (eec) {
ExtrusionEntityCollection chained2 = eec->chained_path_from(m_last_pos, false); for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos, false).entities)
for (ExtrusionEntity *ee : chained2.entities)
gcode += this->extrude_entity(*ee, "infill"); gcode += this->extrude_entity(*ee, "infill");
} else } else
gcode += this->extrude_entity(*fill, "infill"); gcode += this->extrude_entity(*fill, "infill");

View file

@ -202,7 +202,7 @@ protected:
const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; } const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; }
coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; } coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; }
}; };
static std::vector<GCode::LayerToPrint> collect_layers_to_print(const PrintObject &object); static std::vector<LayerToPrint> collect_layers_to_print(const PrintObject &object);
static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print); static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print);
void process_layer( void process_layer(
// Write into the output file. // Write into the output file.
@ -210,7 +210,9 @@ protected:
const Print &print, const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z. // Set of object & print layers of the same PrintObject and with the same print_z.
const std::vector<LayerToPrint> &layers, const std::vector<LayerToPrint> &layers,
const LayerTools &layer_tools, const LayerTools &layer_tools,
// Pairs of PrintObject index and its instance index.
const std::vector<std::pair<size_t, size_t>> *ordering,
// If set to size_t(-1), then print all copies of all objects. // If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object. // Otherwise print a single copy of a single object.
const size_t single_object_idx = size_t(-1)); const size_t single_object_idx = size_t(-1));
@ -258,6 +260,25 @@ protected:
std::vector<Island> islands; std::vector<Island> islands;
}; };
struct InstanceToPrint
{
InstanceToPrint(ObjectByExtruder &object_by_extruder, size_t layer_id, const PrintObject &print_object, size_t instance_id) :
object_by_extruder(object_by_extruder), layer_id(layer_id), print_object(print_object), instance_id(instance_id) {}
ObjectByExtruder &object_by_extruder;
const size_t layer_id;
const PrintObject &print_object;
// Instance idx of the copy of a print object.
const size_t instance_id;
};
std::vector<InstanceToPrint> sort_print_object_instances(
std::vector<ObjectByExtruder> &objects_by_extruder,
const std::vector<LayerToPrint> &layers,
// Ordering must be defined for normal (non-sequential print).
const std::vector<std::pair<size_t, size_t>> *ordering,
// For sequential print, the instance of the object to be printing has to be defined.
const size_t single_object_instance_idx);
std::string extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid); std::string extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid);
std::string extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region); std::string extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region);

View file

@ -308,7 +308,7 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
LayerTools lt_new(0.5f * (lt.print_z + lt_object.print_z)); LayerTools lt_new(0.5f * (lt.print_z + lt_object.print_z));
// Find the 1st layer above lt_new. // Find the 1st layer above lt_new.
for (j = i + 1; j < m_layer_tools.size() && m_layer_tools[j].print_z < lt_new.print_z - EPSILON; ++ j); for (j = i + 1; j < m_layer_tools.size() && m_layer_tools[j].print_z < lt_new.print_z - EPSILON; ++ j);
if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) { if (std::abs(m_layer_tools[j].print_z - lt_new.print_z) < EPSILON) {
m_layer_tools[j].has_wipe_tower = true; m_layer_tools[j].has_wipe_tower = true;
} else { } else {
LayerTools &lt_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new); LayerTools &lt_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new);

View file

@ -698,7 +698,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool last_in_lay
writer.append(std::string("; material : " + (m_current_tool < m_filpar.size() ? m_filpar[m_current_tool].material : "(NONE)") + " -> " + m_filpar[tool].material + "\n").c_str()) writer.append(std::string("; material : " + (m_current_tool < m_filpar.size() ? m_filpar[m_current_tool].material : "(NONE)") + " -> " + m_filpar[tool].material + "\n").c_str())
.append(";--------------------\n"); .append(";--------------------\n");
writer.speed_override_backup(); writer.speed_override_backup();
writer.speed_override(100); writer.speed_override(100);
Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed); Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed);
@ -748,7 +748,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool last_in_lay
if (m_current_tool < m_used_filament_length.size()) if (m_current_tool < m_used_filament_length.size())
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
ToolChangeResult result; ToolChangeResult result;
result.priming = false; result.priming = false;
result.initial_tool = int(old_tool); result.initial_tool = int(old_tool);
result.new_tool = int(m_current_tool); result.new_tool = int(m_current_tool);
@ -806,7 +806,7 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of
if (m_current_tool < m_used_filament_length.size()) if (m_current_tool < m_used_filament_length.size())
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
ToolChangeResult result; ToolChangeResult result;
result.priming = false; result.priming = false;
result.initial_tool = int(old_tool); result.initial_tool = int(old_tool);
result.new_tool = int(m_current_tool); result.new_tool = int(m_current_tool);
@ -1163,7 +1163,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y() + 0.5f * step); writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y() + 0.5f * step);
writer.extrude(box.ld.x() + m_perimeter_width / 2.f, writer.y()); writer.extrude(box.ld.x() + m_perimeter_width / 2.f, writer.y());
} }
writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle
} }
else { // Extrude a sparse infill to support the material to be printed above. else { // Extrude a sparse infill to support the material to be printed above.
const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width); const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width);
@ -1196,7 +1196,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
if (m_current_tool < m_used_filament_length.size()) if (m_current_tool < m_used_filament_length.size())
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
ToolChangeResult result; ToolChangeResult result;
result.priming = false; result.priming = false;
result.initial_tool = int(old_tool); result.initial_tool = int(old_tool);
result.new_tool = int(m_current_tool); result.new_tool = int(m_current_tool);

View file

@ -309,49 +309,7 @@ convex_hull(const Polygons &polygons)
return convex_hull(std::move(pp)); return convex_hull(std::move(pp));
} }
/* accepts an arrayref of points and returns a list of indices bool directions_parallel(double angle1, double angle2, double max_diff)
according to a nearest-neighbor walk */
void
chained_path(const Points &points, std::vector<Points::size_type> &retval, Point start_near)
{
PointConstPtrs my_points;
std::map<const Point*,Points::size_type> indices;
my_points.reserve(points.size());
for (Points::const_iterator it = points.begin(); it != points.end(); ++it) {
my_points.push_back(&*it);
indices[&*it] = it - points.begin();
}
retval.reserve(points.size());
while (!my_points.empty()) {
Points::size_type idx = start_near.nearest_point_index(my_points);
start_near = *my_points[idx];
retval.push_back(indices[ my_points[idx] ]);
my_points.erase(my_points.begin() + idx);
}
}
void
chained_path(const Points &points, std::vector<Points::size_type> &retval)
{
if (points.empty()) return; // can't call front() on empty vector
chained_path(points, retval, points.front());
}
/* retval and items must be different containers */
template<class T>
void
chained_path_items(Points &points, T &items, T &retval)
{
std::vector<Points::size_type> indices;
chained_path(points, indices);
for (std::vector<Points::size_type>::const_iterator it = indices.begin(); it != indices.end(); ++it)
retval.push_back(items[*it]);
}
template void chained_path_items(Points &points, ClipperLib::PolyNodes &items, ClipperLib::PolyNodes &retval);
bool
directions_parallel(double angle1, double angle2, double max_diff)
{ {
double diff = fabs(angle1 - angle2); double diff = fabs(angle1 - angle2);
max_diff += EPSILON; max_diff += EPSILON;
@ -359,8 +317,7 @@ directions_parallel(double angle1, double angle2, double max_diff)
} }
template<class T> template<class T>
bool bool contains(const std::vector<T> &vector, const Point &point)
contains(const std::vector<T> &vector, const Point &point)
{ {
for (typename std::vector<T>::const_iterator it = vector.begin(); it != vector.end(); ++it) { for (typename std::vector<T>::const_iterator it = vector.begin(); it != vector.end(); ++it) {
if (it->contains(point)) return true; if (it->contains(point)) return true;
@ -369,16 +326,14 @@ contains(const std::vector<T> &vector, const Point &point)
} }
template bool contains(const ExPolygons &vector, const Point &point); template bool contains(const ExPolygons &vector, const Point &point);
double double rad2deg_dir(double angle)
rad2deg_dir(double angle)
{ {
angle = (angle < PI) ? (-angle + PI/2.0) : (angle + PI/2.0); angle = (angle < PI) ? (-angle + PI/2.0) : (angle + PI/2.0);
if (angle < 0) angle += PI; if (angle < 0) angle += PI;
return rad2deg(angle); return rad2deg(angle);
} }
void void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval)
simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval)
{ {
Polygons pp; Polygons pp;
for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) { for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) {
@ -391,8 +346,7 @@ simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval)
*retval = Slic3r::simplify_polygons(pp); *retval = Slic3r::simplify_polygons(pp);
} }
double double linint(double value, double oldmin, double oldmax, double newmin, double newmax)
linint(double value, double oldmin, double oldmax, double newmin, double newmax)
{ {
return (value - oldmin) * (newmax - newmin) / (oldmax - oldmin) + newmin; return (value - oldmin) * (newmax - newmin) / (oldmax - oldmin) + newmin;
} }

View file

@ -138,9 +138,6 @@ Pointf3s convex_hull(Pointf3s points);
Polygon convex_hull(Points points); Polygon convex_hull(Points points);
Polygon convex_hull(const Polygons &polygons); Polygon convex_hull(const Polygons &polygons);
void chained_path(const Points &points, std::vector<Points::size_type> &retval, Point start_near);
void chained_path(const Points &points, std::vector<Points::size_type> &retval);
template<class T> void chained_path_items(Points &points, T &items, T &retval);
bool directions_parallel(double angle1, double angle2, double max_diff = 0); bool directions_parallel(double angle1, double angle2, double max_diff = 0);
template<class T> bool contains(const std::vector<T> &vector, const Point &point); template<class T> bool contains(const std::vector<T> &vector, const Point &point);
template<typename T> T rad2deg(T angle) { return T(180.0) * angle / T(PI); } template<typename T> T rad2deg(T angle) { return T(180.0) * angle / T(PI); }

View file

@ -0,0 +1,231 @@
// KD tree built upon external data set, referencing the external data by integer indices.
#ifndef slic3r_KDTreeIndirect_hpp_
#define slic3r_KDTreeIndirect_hpp_
#include <algorithm>
#include <limits>
#include <vector>
#include "Utils.hpp" // for next_highest_power_of_2()
namespace Slic3r {
// KD tree for N-dimensional closest point search.
template<size_t ANumDimensions, typename ACoordType, typename ACoordinateFn>
class KDTreeIndirect
{
public:
static constexpr size_t NumDimensions = ANumDimensions;
using CoordinateFn = ACoordinateFn;
using CoordType = ACoordType;
// Following could be static constexpr size_t, but that would not link in C++11
enum : size_t {
npos = size_t(-1)
};
KDTreeIndirect(CoordinateFn coordinate) : coordinate(coordinate) {}
KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t> indices) : coordinate(coordinate) { this->build(std::move(indices)); }
KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t> &&indices) : coordinate(coordinate) { this->build(std::move(indices)); }
KDTreeIndirect(CoordinateFn coordinate, size_t num_indices) : coordinate(coordinate) { this->build(num_indices); }
KDTreeIndirect(KDTreeIndirect &&rhs) : m_nodes(std::move(rhs.m_nodes)), coordinate(std::move(rhs.coordinate)) {}
KDTreeIndirect& operator=(KDTreeIndirect &&rhs) { m_nodes = std::move(rhs.m_nodes); coordinate = std::move(rhs.coordinate); return *this; }
void clear() { m_nodes.clear(); }
void build(size_t num_indices)
{
std::vector<size_t> indices;
indices.reserve(num_indices);
for (size_t i = 0; i < num_indices; ++ i)
indices.emplace_back(i);
this->build(std::move(indices));
}
void build(std::vector<size_t> &&indices)
{
if (indices.empty())
clear();
else {
// Allocate a next highest power of 2 nodes, because the incomplete binary tree will not have the leaves filled strictly from the left.
m_nodes.assign(next_highest_power_of_2(indices.size() + 1), npos);
build_recursive(indices, 0, 0, 0, (int)(indices.size() - 1));
}
indices.clear();
}
enum class VisitorReturnMask : unsigned int
{
CONTINUE_LEFT = 1,
CONTINUE_RIGHT = 2,
STOP = 4,
};
template<typename CoordType>
unsigned int descent_mask(const CoordType &point_coord, const CoordType &search_radius, size_t idx, size_t dimension) const
{
CoordType dist = point_coord - this->coordinate(idx, dimension);
return (dist * dist < search_radius + CoordType(EPSILON)) ?
((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) :
(dist < CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT);
}
// Visitor is supposed to return a bit mask of VisitorReturnMask.
template<typename Visitor>
void visit(Visitor &visitor) const
{
visit_recursive(0, 0, visitor);
}
CoordinateFn coordinate;
private:
// Build a balanced tree by splitting the input sequence by an axis aligned plane at a dimension.
void build_recursive(std::vector<size_t> &input, size_t node, int dimension, int left, int right)
{
if (left > right)
return;
assert(node < m_nodes.size());
if (left == right) {
// Insert a node into the balanced tree.
m_nodes[node] = input[left];
return;
}
// Partition the input sequence to two equal halves.
int center = (left + right) >> 1;
partition_input(input, dimension, left, right, center);
// Insert a node into the tree.
m_nodes[node] = input[center];
// Partition the left and right subtrees.
size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension;
build_recursive(input, (node << 1) + 1, next_dimension, left, center - 1);
build_recursive(input, (node << 1) + 2, next_dimension, center + 1, right);
}
// Partition the input m_nodes <left, right> at k using QuickSelect method.
// https://en.wikipedia.org/wiki/Quickselect
void partition_input(std::vector<size_t> &input, int dimension, int left, int right, int k) const
{
while (left < right) {
// Guess the k'th element.
// Pick the pivot as a median of first, center and last value.
// Sort first, center and last values.
int center = (left + right) >> 1;
auto left_value = this->coordinate(input[left], dimension);
auto center_value = this->coordinate(input[center], dimension);
auto right_value = this->coordinate(input[right], dimension);
if (center_value < left_value) {
std::swap(input[left], input[center]);
std::swap(left_value, center_value);
}
if (right_value < left_value) {
std::swap(input[left], input[right]);
std::swap(left_value, right_value);
}
if (right_value < center_value) {
std::swap(input[center], input[right]);
// No need to do that, result is not used.
// std::swap(center_value, right_value);
}
// Only two or three values are left and those are sorted already.
if (left + 3 > right)
break;
// left and right items are already at their correct positions.
// input[left].point[dimension] <= input[center].point[dimension] <= input[right].point[dimension]
// Move the pivot to the (right - 1) position.
std::swap(input[center], input[right - 1]);
// Pivot value.
double pivot = this->coordinate(input[right - 1], dimension);
// Partition the set based on the pivot.
int i = left;
int j = right - 1;
for (;;) {
// Skip left points that are already at correct positions.
// Search will certainly stop at position (right - 1), which stores the pivot.
while (this->coordinate(input[++ i], dimension) < pivot) ;
// Skip right points that are already at correct positions.
while (this->coordinate(input[-- j], dimension) > pivot && i < j) ;
if (i >= j)
break;
std::swap(input[i], input[j]);
}
// Restore pivot to the center of the sequence.
std::swap(input[i], input[right]);
// Which side the kth element is in?
if (k < i)
right = i - 1;
else if (k == i)
// Sequence is partitioned, kth element is at its place.
break;
else
left = i + 1;
}
}
template<typename Visitor>
void visit_recursive(size_t node, size_t dimension, Visitor &visitor) const
{
assert(! m_nodes.empty());
if (node >= m_nodes.size() || m_nodes[node] == npos)
return;
// Left / right child node index.
size_t left = (node << 1) + 1;
size_t right = left + 1;
unsigned int mask = visitor(m_nodes[node], dimension);
if ((mask & (unsigned int)VisitorReturnMask::STOP) == 0) {
size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension;
if (mask & (unsigned int)VisitorReturnMask::CONTINUE_LEFT)
visit_recursive(left, next_dimension, visitor);
if (mask & (unsigned int)VisitorReturnMask::CONTINUE_RIGHT)
visit_recursive(right, next_dimension, visitor);
}
}
std::vector<size_t> m_nodes;
};
// Find a closest point using Euclidian metrics.
// Returns npos if not found.
template<typename KDTreeIndirectType, typename PointType, typename FilterFn>
size_t find_closest_point(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter)
{
struct Visitor {
using CoordType = typename KDTreeIndirectType::CoordType;
const KDTreeIndirectType &kdtree;
const PointType &point;
const FilterFn filter;
size_t min_idx = KDTreeIndirectType::npos;
CoordType min_dist = std::numeric_limits<CoordType>::max();
Visitor(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter) : kdtree(kdtree), point(point), filter(filter) {}
unsigned int operator()(size_t idx, size_t dimension) {
if (this->filter(idx)) {
auto dist = CoordType(0);
for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++ i) {
CoordType d = point[i] - kdtree.coordinate(idx, i);
dist += d * d;
}
if (dist < min_dist) {
min_dist = dist;
min_idx = idx;
}
}
return kdtree.descent_mask(point[dimension], min_dist, idx, dimension);
}
} visitor(kdtree, point, filter);
kdtree.visit(visitor);
return visitor.min_idx;
}
template<typename KDTreeIndirectType, typename PointType>
size_t find_closest_point(const KDTreeIndirectType& kdtree, const PointType& point)
{
return find_closest_point(kdtree, point, [](size_t) { return true; });
}
} // namespace Slic3r
#endif /* slic3r_KDTreeIndirect_hpp_ */

View file

@ -1,8 +1,8 @@
#include "Layer.hpp" #include "Layer.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Geometry.hpp"
#include "Print.hpp" #include "Print.hpp"
#include "Fill/Fill.hpp" #include "Fill/Fill.hpp"
#include "ShortestPath.hpp"
#include "SVG.hpp" #include "SVG.hpp"
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
@ -57,8 +57,7 @@ void Layer::make_slices()
ordering_points.push_back(ex.contour.first_point()); ordering_points.push_back(ex.contour.first_point());
// sort slices // sort slices
std::vector<Points::size_type> order; std::vector<Points::size_type> order = chain_points(ordering_points);
Slic3r::Geometry::chained_path(ordering_points, order);
// populate slices vector // populate slices vector
for (size_t i : order) for (size_t i : order)

View file

@ -144,7 +144,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
} }
if (! lower_layer_covered->empty()) if (! lower_layer_covered->empty())
voids = diff(voids, *lower_layer_covered); voids = diff(voids, *lower_layer_covered);
fill_boundaries = diff(fill_boundaries, voids); fill_boundaries = diff(fill_boundaries, voids);
} }
} }

View file

@ -1462,7 +1462,7 @@ stl_stats ModelObject::get_object_stl_stats() const
return this->volumes[0]->mesh().stl.stats; return this->volumes[0]->mesh().stl.stats;
stl_stats full_stats; stl_stats full_stats;
memset(&full_stats, 0, sizeof(stl_stats)); full_stats.volume = 0.f;
// fill full_stats from all objet's meshes // fill full_stats from all objet's meshes
for (ModelVolume* volume : this->volumes) for (ModelVolume* volume : this->volumes)

View file

@ -13,21 +13,28 @@ public:
{} {}
~MutablePriorityQueue() { clear(); } ~MutablePriorityQueue() { clear(); }
inline void clear() { m_heap.clear(); } void clear();
inline void reserve(size_t cnt) { m_heap.reserve(cnt); } void reserve(size_t cnt) { m_heap.reserve(cnt); }
inline void push(const T &item); void push(const T &item);
inline void push(T &&item); void push(T &&item);
inline void pop(); void pop();
inline T& top() { return m_heap.front(); } T& top() { return m_heap.front(); }
inline void remove(size_t idx); void remove(size_t idx);
inline void update(size_t idx) { T item = m_heap[idx]; remove(idx); push(item); } void update(size_t idx) { T item = m_heap[idx]; remove(idx); push(item); }
inline size_t size() const { return m_heap.size(); } size_t size() const { return m_heap.size(); }
inline bool empty() const { return m_heap.empty(); } bool empty() const { return m_heap.empty(); }
using iterator = typename std::vector<T>::iterator;
using const_iterator = typename std::vector<T>::const_iterator;
iterator begin() { return m_heap.begin(); }
iterator end() { return m_heap.end(); }
const_iterator cbegin() const { return m_heap.cbegin(); }
const_iterator cend() const { return m_heap.cend(); }
protected: protected:
inline void update_heap_up(size_t top, size_t bottom); void update_heap_up(size_t top, size_t bottom);
inline void update_heap_down(size_t top, size_t bottom); void update_heap_down(size_t top, size_t bottom);
private: private:
std::vector<T> m_heap; std::vector<T> m_heap;
@ -42,6 +49,17 @@ MutablePriorityQueue<T, IndexSetter, LessPredicate> make_mutable_priority_queue(
std::forward<IndexSetter>(index_setter), std::forward<LessPredicate>(less_predicate)); std::forward<IndexSetter>(index_setter), std::forward<LessPredicate>(less_predicate));
} }
template<class T, class LessPredicate, class IndexSetter>
inline void MutablePriorityQueue<T, LessPredicate, IndexSetter>::clear()
{
#ifndef NDEBUG
for (size_t idx = 0; idx < m_heap.size(); ++ idx)
// Mark as removed from the queue.
m_index_setter(m_heap[idx], std::numeric_limits<size_t>::max());
#endif /* NDEBUG */
m_heap.clear();
}
template<class T, class LessPredicate, class IndexSetter> template<class T, class LessPredicate, class IndexSetter>
inline void MutablePriorityQueue<T, LessPredicate, IndexSetter>::push(const T &item) inline void MutablePriorityQueue<T, LessPredicate, IndexSetter>::push(const T &item)
{ {
@ -64,6 +82,10 @@ template<class T, class LessPredicate, class IndexSetter>
inline void MutablePriorityQueue<T, LessPredicate, IndexSetter>::pop() inline void MutablePriorityQueue<T, LessPredicate, IndexSetter>::pop()
{ {
assert(! m_heap.empty()); assert(! m_heap.empty());
#ifndef NDEBUG
// Mark as removed from the queue.
m_index_setter(m_heap.front(), std::numeric_limits<size_t>::max());
#endif /* NDEBUG */
if (m_heap.size() > 1) { if (m_heap.size() > 1) {
m_heap.front() = m_heap.back(); m_heap.front() = m_heap.back();
m_heap.pop_back(); m_heap.pop_back();
@ -77,6 +99,10 @@ template<class T, class LessPredicate, class IndexSetter>
inline void MutablePriorityQueue<T, LessPredicate, IndexSetter>::remove(size_t idx) inline void MutablePriorityQueue<T, LessPredicate, IndexSetter>::remove(size_t idx)
{ {
assert(idx < m_heap.size()); assert(idx < m_heap.size());
#ifndef NDEBUG
// Mark as removed from the queue.
m_index_setter(m_heap[idx], std::numeric_limits<size_t>::max());
#endif /* NDEBUG */
if (idx + 1 == m_heap.size()) { if (idx + 1 == m_heap.size()) {
m_heap.pop_back(); m_heap.pop_back();
return; return;

View file

@ -1,6 +1,8 @@
#include "PerimeterGenerator.hpp" #include "PerimeterGenerator.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "ExtrusionEntityCollection.hpp" #include "ExtrusionEntityCollection.hpp"
#include "ShortestPath.hpp"
#include <cmath> #include <cmath>
#include <cassert> #include <cassert>
@ -86,24 +88,24 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thi
return paths; return paths;
} }
static ExtrusionEntityCollection variable_width(const ThickPolylines& polylines, ExtrusionRole role, Flow flow) static void variable_width(const ThickPolylines& polylines, ExtrusionRole role, Flow flow, std::vector<ExtrusionEntity*> &out)
{ {
// This value determines granularity of adaptive width, as G-code does not allow // This value determines granularity of adaptive width, as G-code does not allow
// variable extrusion within a single move; this value shall only affect the amount // variable extrusion within a single move; this value shall only affect the amount
// of segments, and any pruning shall be performed before we apply this tolerance. // of segments, and any pruning shall be performed before we apply this tolerance.
ExtrusionEntityCollection coll;
const float tolerance = float(scale_(0.05)); const float tolerance = float(scale_(0.05));
for (const ThickPolyline &p : polylines) { for (const ThickPolyline &p : polylines) {
ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance); ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance);
// Append paths to collection. // Append paths to collection.
if (! paths.empty()) { if (! paths.empty()) {
if (paths.front().first_point() == paths.back().last_point()) if (paths.front().first_point() == paths.back().last_point())
coll.append(ExtrusionLoop(std::move(paths))); out.emplace_back(new ExtrusionLoop(std::move(paths)));
else else {
coll.append(std::move(paths)); for (ExtrusionPath &path : paths)
out.emplace_back(new ExtrusionPath(std::move(path)));
}
} }
} }
return coll;
} }
// Hierarchy of perimeters. // Hierarchy of perimeters.
@ -186,43 +188,47 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
paths.push_back(path); paths.push_back(path);
} }
coll.append(ExtrusionLoop(paths, loop_role)); coll.append(ExtrusionLoop(std::move(paths), loop_role));
} }
// Append thin walls to the nearest-neighbor search (only for first iteration) // Append thin walls to the nearest-neighbor search (only for first iteration)
if (! thin_walls.empty()) { if (! thin_walls.empty()) {
ExtrusionEntityCollection tw = variable_width(thin_walls, erExternalPerimeter, perimeter_generator.ext_perimeter_flow); variable_width(thin_walls, erExternalPerimeter, perimeter_generator.ext_perimeter_flow, coll.entities);
coll.append(tw.entities);
thin_walls.clear(); thin_walls.clear();
} }
// Sort entities into a new collection using a nearest-neighbor search, // Traverse children and build the final collection.
// preserving the original indices which are useful for detecting thin walls. Point zero_point(0, 0);
ExtrusionEntityCollection sorted_coll; std::vector<std::pair<size_t, bool>> chain = chain_extrusion_entities(coll.entities, &zero_point);
coll.chained_path(&sorted_coll, false, erMixed, &sorted_coll.orig_indices); ExtrusionEntityCollection out;
for (const std::pair<size_t, bool> &idx : chain) {
// traverse children and build the final collection assert(coll.entities[idx.first] != nullptr);
ExtrusionEntityCollection entities; if (idx.first >= loops.size()) {
for (const size_t &idx : sorted_coll.orig_indices) { // This is a thin wall.
if (idx >= loops.size()) { out.entities.reserve(out.entities.size() + 1);
// This is a thin wall. Let's get it from the sorted collection as it might have been reversed. out.entities.emplace_back(coll.entities[idx.first]);
entities.append(std::move(*sorted_coll.entities[&idx - &sorted_coll.orig_indices.front()])); coll.entities[idx.first] = nullptr;
if (idx.second)
out.entities.back()->reverse();
} else { } else {
const PerimeterGeneratorLoop &loop = loops[idx]; const PerimeterGeneratorLoop &loop = loops[idx.first];
ExtrusionLoop eloop = *dynamic_cast<ExtrusionLoop*>(coll.entities[idx]); assert(thin_walls.empty());
ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls); ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls);
out.entities.reserve(out.entities.size() + children.entities.size() + 1);
ExtrusionLoop *eloop = static_cast<ExtrusionLoop*>(coll.entities[idx.first]);
coll.entities[idx.first] = nullptr;
if (loop.is_contour) { if (loop.is_contour) {
eloop.make_counter_clockwise(); eloop->make_counter_clockwise();
entities.append(std::move(children.entities)); out.append(std::move(children.entities));
entities.append(std::move(eloop)); out.entities.emplace_back(eloop);
} else { } else {
eloop.make_clockwise(); eloop->make_clockwise();
entities.append(std::move(eloop)); out.entities.emplace_back(eloop);
entities.append(std::move(children.entities)); out.append(std::move(children.entities));
} }
} }
} }
return entities; return out;
} }
void PerimeterGenerator::process() void PerimeterGenerator::process()
@ -445,8 +451,8 @@ void PerimeterGenerator::process()
for (const ExPolygon &ex : gaps_ex) for (const ExPolygon &ex : gaps_ex)
ex.medial_axis(max, min, &polylines); ex.medial_axis(max, min, &polylines);
if (! polylines.empty()) { if (! polylines.empty()) {
ExtrusionEntityCollection gap_fill = variable_width(polylines, erGapFill, this->solid_infill_flow); ExtrusionEntityCollection gap_fill;
this->gap_fill->append(gap_fill.entities); variable_width(polylines, erGapFill, this->solid_infill_flow, gap_fill.entities);
/* Make sure we don't infill narrow parts that are already gap-filled /* Make sure we don't infill narrow parts that are already gap-filled
(we only consider this surface's gaps to reduce the diff() complexity). (we only consider this surface's gaps to reduce the diff() complexity).
Growing actual extrusions ensures that gaps not filled by medial axis Growing actual extrusions ensures that gaps not filled by medial axis
@ -456,7 +462,8 @@ void PerimeterGenerator::process()
//FIXME Vojtech: This grows by a rounded extrusion width, not by line spacing, //FIXME Vojtech: This grows by a rounded extrusion width, not by line spacing,
// therefore it may cover the area, but no the volume. // therefore it may cover the area, but no the volume.
last = diff_ex(to_polygons(last), gap_fill.polygons_covered_by_width(10.f)); last = diff_ex(to_polygons(last), gap_fill.polygons_covered_by_width(10.f));
} this->gap_fill->append(std::move(gap_fill.entities));
}
} }
// create one more offset to be used as boundary for fill // create one more offset to be used as boundary for fill

View file

@ -23,12 +23,6 @@ Polyline::operator Line() const
return Line(this->points.front(), this->points.back()); return Line(this->points.front(), this->points.back());
} }
Point
Polyline::last_point() const
{
return this->points.back();
}
Point Point
Polyline::leftmost_point() const Polyline::leftmost_point() const
{ {

View file

@ -62,7 +62,8 @@ public:
operator Polylines() const; operator Polylines() const;
operator Line() const; operator Line() const;
Point last_point() const; Point last_point() const override { return this->points.back(); }
Point leftmost_point() const; Point leftmost_point() const;
virtual Lines lines() const; virtual Lines lines() const;
void clip_end(double distance); void clip_end(double distance);

View file

@ -7,6 +7,7 @@
#include "Flow.hpp" #include "Flow.hpp"
#include "Geometry.hpp" #include "Geometry.hpp"
#include "I18N.hpp" #include "I18N.hpp"
#include "ShortestPath.hpp"
#include "SupportMaterial.hpp" #include "SupportMaterial.hpp"
#include "GCode.hpp" #include "GCode.hpp"
#include "GCode/WipeTower.hpp" #include "GCode/WipeTower.hpp"
@ -252,7 +253,7 @@ bool Print::is_step_done(PrintObjectStep step) const
{ {
if (m_objects.empty()) if (m_objects.empty())
return false; return false;
tbb::mutex::scoped_lock lock(this->state_mutex()); tbb::mutex::scoped_lock lock(this->state_mutex());
for (const PrintObject *object : m_objects) for (const PrintObject *object : m_objects)
if (! object->is_step_done_unguarded(step)) if (! object->is_step_done_unguarded(step))
return false; return false;
@ -1824,8 +1825,8 @@ void Print::_make_brim()
[](const std::pair<const ClipperLib_Z::Path*, size_t> &l, const std::pair<const ClipperLib_Z::Path*, size_t> &r) { [](const std::pair<const ClipperLib_Z::Path*, size_t> &l, const std::pair<const ClipperLib_Z::Path*, size_t> &r) {
return l.second < r.second; return l.second < r.second;
}); });
Vec3f last_pt(0.f, 0.f, 0.f);
Point last_pt(0, 0);
for (size_t i = 0; i < loops_trimmed_order.size();) { for (size_t i = 0; i < loops_trimmed_order.size();) {
// Find all pieces that the initial loop was split into. // Find all pieces that the initial loop was split into.
size_t j = i + 1; size_t j = i + 1;
@ -1841,16 +1842,23 @@ void Print::_make_brim()
points.emplace_back(coord_t(pt.X), coord_t(pt.Y)); points.emplace_back(coord_t(pt.X), coord_t(pt.Y));
i = j; i = j;
} else { } else {
//FIXME this is not optimal as the G-code generator will follow the sequence of paths verbatim without respect to minimum travel distance. //FIXME The path chaining here may not be optimal.
ExtrusionEntityCollection this_loop_trimmed;
this_loop_trimmed.entities.reserve(j - i);
for (; i < j; ++ i) { for (; i < j; ++ i) {
m_brim.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()))); this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())));
const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first; const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first;
Points &points = static_cast<ExtrusionPath*>(m_brim.entities.back())->polyline.points; Points &points = static_cast<ExtrusionPath*>(this_loop_trimmed.entities.back())->polyline.points;
points.reserve(path.size()); points.reserve(path.size());
for (const ClipperLib_Z::IntPoint &pt : path) for (const ClipperLib_Z::IntPoint &pt : path)
points.emplace_back(coord_t(pt.X), coord_t(pt.Y)); points.emplace_back(coord_t(pt.X), coord_t(pt.Y));
} }
chain_and_reorder_extrusion_entities(this_loop_trimmed.entities, &last_pt);
m_brim.entities.reserve(m_brim.entities.size() + this_loop_trimmed.entities.size());
append(m_brim.entities, std::move(this_loop_trimmed.entities));
this_loop_trimmed.entities.clear();
} }
last_pt = m_brim.last_point();
} }
} }
} else { } else {

View file

@ -96,6 +96,7 @@ public:
const SupportLayerPtrs& support_layers() const { return m_support_layers; } const SupportLayerPtrs& support_layers() const { return m_support_layers; }
const Transform3d& trafo() const { return m_trafo; } const Transform3d& trafo() const { return m_trafo; }
const Points& copies() const { return m_copies; } const Points& copies() const { return m_copies; }
const Point copy_center(size_t idx) const { return m_copies[idx] + m_copies_shift + Point(this->size.x() / 2, this->size.y() / 2); }
// since the object is aligned to origin, bounding box coincides with size // since the object is aligned to origin, bounding box coincides with size
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); } BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }

View file

@ -268,8 +268,7 @@ public:
std::string text; std::string text;
// Bitmap of flags. // Bitmap of flags.
enum FlagBits { enum FlagBits {
DEFAULT, DEFAULT = 0,
NO_RELOAD_SCENE = 0,
RELOAD_SCENE = 1 << 1, RELOAD_SCENE = 1 << 1,
RELOAD_SLA_SUPPORT_POINTS = 1 << 2, RELOAD_SLA_SUPPORT_POINTS = 1 << 2,
RELOAD_SLA_PREVIEW = 1 << 3, RELOAD_SLA_PREVIEW = 1 << 3,

View file

@ -12,7 +12,6 @@
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <float.h> #include <float.h>
#include <tbb/task_scheduler_init.h>
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
#include <tbb/atomic.h> #include <tbb/atomic.h>
@ -75,13 +74,9 @@ PrintBase::ApplyStatus PrintObject::set_copies(const Points &points)
{ {
// Order copies with a nearest-neighbor search. // Order copies with a nearest-neighbor search.
std::vector<Point> copies; std::vector<Point> copies;
{ copies.reserve(points.size());
std::vector<Points::size_type> ordered_copies; for (const Point &pt : points)
Slic3r::Geometry::chained_path(points, ordered_copies); copies.emplace_back(pt + m_copies_shift);
copies.reserve(ordered_copies.size());
for (size_t point_idx : ordered_copies)
copies.emplace_back(points[point_idx] + m_copies_shift);
}
// Invalidate and set copies. // Invalidate and set copies.
PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED; PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED;
if (copies != m_copies) { if (copies != m_copies) {
@ -1480,7 +1475,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full
if (object_max_z <= 0.f) if (object_max_z <= 0.f)
object_max_z = (float)model_object.raw_bounding_box().size().z(); object_max_z = (float)model_object.raw_bounding_box().size().z();
return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders); return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
} }
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions) // returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)

View file

@ -0,0 +1,394 @@
#if 0
#pragma optimize("", off)
#undef NDEBUG
#undef assert
#endif
#include "clipper.hpp"
#include "ShortestPath.hpp"
#include "KDTreeIndirect.hpp"
#include "MutablePriorityQueue.hpp"
#include "Print.hpp"
#include <cmath>
#include <cassert>
namespace Slic3r {
// Chain perimeters (always closed) and thin fills (closed or open) using a greedy algorithm.
// Solving a Traveling Salesman Problem (TSP) with the modification, that the sites are not always points, but points and segments.
// Solving using a greedy algorithm, where a shortest edge is added to the solution if it does not produce a bifurcation or a cycle.
// Return index and "reversed" flag.
// https://en.wikipedia.org/wiki/Multi-fragment_algorithm
// The algorithm builds a tour for the traveling salesman one edge at a time and thus maintains multiple tour fragments, each of which
// is a simple path in the complete graph of cities. At each stage, the algorithm selects the edge of minimal cost that either creates
// a new fragment, extends one of the existing paths or creates a cycle of length equal to the number of cities.
template<typename PointType, typename SegmentEndPointFunc>
std::vector<std::pair<size_t, bool>> chain_segments(SegmentEndPointFunc end_point_func, size_t num_segments, const PointType *start_near)
{
std::vector<std::pair<size_t, bool>> out;
if (num_segments == 0) {
// Nothing to do.
}
else if (num_segments == 1)
{
// Just sort the end points so that the first point visited is closest to start_near.
out.emplace_back(0, start_near != nullptr &&
(end_point_func(0, true) - *start_near).template cast<double>().squaredNorm() < (end_point_func(0, false) - *start_near).template cast<double>().squaredNorm());
}
else
{
// End points of segments for the KD tree closest point search.
// A single end point is inserted into the search structure for loops, two end points are entered for open paths.
struct EndPoint {
EndPoint(const Vec2d &pos) : pos(pos) {}
Vec2d pos;
// Identifier of the chain, to which this end point belongs. Zero means unassigned.
size_t chain_id = 0;
// Link to the closest currently valid end point.
EndPoint *edge_out = nullptr;
// Distance to the next end point following the link.
// Zero value -> start of the final path.
double distance_out = std::numeric_limits<double>::max();
size_t heap_idx = std::numeric_limits<size_t>::max();
};
std::vector<EndPoint> end_points;
end_points.reserve(num_segments * 2);
for (size_t i = 0; i < num_segments; ++ i) {
end_points.emplace_back(end_point_func(i, true ).template cast<double>());
end_points.emplace_back(end_point_func(i, false).template cast<double>());
}
// Construct the closest point KD tree over end points of segments.
auto coordinate_fn = [&end_points](size_t idx, size_t dimension) -> double { return end_points[idx].pos[dimension]; };
KDTreeIndirect<2, double, decltype(coordinate_fn)> kdtree(coordinate_fn, end_points.size());
// Helper to detect loops in already connected paths.
// Unique chain IDs are assigned to paths. If paths are connected, end points will not have their chain IDs updated, but the chain IDs
// will remember an "equivalent" chain ID, which is the lowest ID of all the IDs in the path, and the lowest ID is equivalent to itself.
class EquivalentChains {
public:
// Zero'th chain ID is invalid.
EquivalentChains(size_t reserve) { m_equivalent_with.reserve(reserve); m_equivalent_with.emplace_back(0); }
// Generate next equivalence class.
size_t next() {
m_equivalent_with.emplace_back(++ m_last_chain_id);
return m_last_chain_id;
}
// Get equivalence class for chain ID.
size_t operator()(size_t chain_id) {
if (chain_id != 0) {
for (size_t last = chain_id;;) {
size_t lower = m_equivalent_with[last];
if (lower == last) {
m_equivalent_with[chain_id] = lower;
chain_id = lower;
break;
}
last = lower;
}
}
return chain_id;
}
size_t merge(size_t chain_id1, size_t chain_id2) {
size_t chain_id = std::min((*this)(chain_id1), (*this)(chain_id2));
m_equivalent_with[chain_id1] = chain_id;
m_equivalent_with[chain_id2] = chain_id;
return chain_id;
}
#ifndef NDEBUG
bool validate()
{
assert(m_last_chain_id >= 0);
assert(m_last_chain_id + 1 == m_equivalent_with.size());
for (size_t i = 0; i < m_equivalent_with.size(); ++ i) {
for (size_t last = i;;) {
size_t lower = m_equivalent_with[last];
assert(lower <= last);
if (lower == last)
break;
last = lower;
}
}
return true;
}
#endif /* NDEBUG */
private:
// Unique chain ID assigned to chains of end points of segments.
size_t m_last_chain_id = 0;
std::vector<size_t> m_equivalent_with;
} equivalent_chain(num_segments);
// Find the first end point closest to start_near.
EndPoint *first_point = nullptr;
size_t first_point_idx = std::numeric_limits<size_t>::max();
if (start_near != nullptr) {
size_t idx = find_closest_point(kdtree, start_near->template cast<double>());
assert(idx < end_points.size());
first_point = &end_points[idx];
first_point->distance_out = 0.;
first_point->chain_id = equivalent_chain.next();
first_point_idx = idx;
}
// Assign the closest point and distance to the end points.
for (EndPoint &end_point : end_points) {
assert(end_point.edge_out == nullptr);
if (&end_point != first_point) {
size_t this_idx = &end_point - &end_points.front();
// Find the closest point to this end_point, which lies on a different extrusion path (filtered by the lambda).
// Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it.
size_t next_idx = find_closest_point(kdtree, end_point.pos,
[this_idx, first_point_idx](size_t idx){ return idx != first_point_idx && (idx ^ this_idx) > 1; });
assert(next_idx < end_points.size());
EndPoint &end_point2 = end_points[next_idx];
end_point.edge_out = &end_point2;
end_point.distance_out = (end_point2.pos - end_point.pos).squaredNorm();
}
}
// Initialize a heap of end points sorted by the lowest distance to the next valid point of a path.
auto queue = make_mutable_priority_queue<EndPoint*>(
[](EndPoint *ep, size_t idx){ ep->heap_idx = idx; },
[](EndPoint *l, EndPoint *r){ return l->distance_out < r->distance_out; });
queue.reserve(end_points.size() * 2 - 1);
for (EndPoint &ep : end_points)
if (first_point != &ep)
queue.push(&ep);
#ifndef NDEBUG
auto validate_graph_and_queue = [&equivalent_chain, &end_points, &queue, first_point]() -> bool {
assert(equivalent_chain.validate());
for (EndPoint &ep : end_points) {
if (ep.heap_idx < queue.size()) {
// End point is on the heap.
assert(*(queue.cbegin() + ep.heap_idx) == &ep);
assert(ep.chain_id == 0);
} else {
// End point is NOT on the heap, therefore it is part of the output path.
assert(ep.heap_idx == std::numeric_limits<size_t>::max());
assert(ep.chain_id != 0);
if (&ep == first_point) {
assert(ep.edge_out == nullptr);
} else {
assert(ep.edge_out != nullptr);
// Detect loops.
for (EndPoint *pt = &ep; pt != nullptr;) {
// Out of queue. It is a final point.
assert(pt->heap_idx == std::numeric_limits<size_t>::max());
EndPoint *pt_other = &end_points[(pt - &end_points.front()) ^ 1];
if (pt_other->heap_idx < queue.size())
// The other side of this segment is undecided yet.
break;
pt = pt_other->edge_out;
}
}
}
}
for (EndPoint *ep : queue)
// Points in the queue are not connected yet.
assert(ep->chain_id == 0);
return true;
};
#endif /* NDEBUG */
// Chain the end points: find (num_segments - 1) shortest links not forming bifurcations or loops.
assert(num_segments >= 2);
for (int iter = int(num_segments) - 2;; -- iter) {
assert(validate_graph_and_queue());
// Take the first end point, for which the link points to the currently closest valid neighbor.
EndPoint &end_point1 = *queue.top();
assert(end_point1.edge_out != nullptr);
// No point on the queue may be connected yet.
assert(end_point1.chain_id == 0);
// Take the closest end point to the first end point,
EndPoint &end_point2 = *end_point1.edge_out;
bool valid = true;
size_t end_point1_other_chain_id = 0;
size_t end_point2_other_chain_id = 0;
if (end_point2.chain_id > 0) {
// The other side is part of the output path. Don't connect to end_point2, update end_point1 and try another one.
valid = false;
} else {
// End points of the opposite ends of the segments.
end_point1_other_chain_id = equivalent_chain(end_points[(&end_point1 - &end_points.front()) ^ 1].chain_id);
end_point2_other_chain_id = equivalent_chain(end_points[(&end_point2 - &end_points.front()) ^ 1].chain_id);
if (end_point1_other_chain_id == end_point2_other_chain_id && end_point1_other_chain_id != 0)
// This edge forms a loop. Update end_point1 and try another one.
valid = false;
}
if (valid) {
// Remove the first and second point from the queue.
queue.pop();
queue.remove(end_point2.heap_idx);
assert(end_point1.edge_out = &end_point2);
end_point2.edge_out = &end_point1;
end_point2.distance_out = end_point1.distance_out;
// Assign chain IDs to the newly connected end points, set equivalent_chain if two chains were merged.
size_t chain_id =
(end_point1_other_chain_id == 0) ?
((end_point2_other_chain_id == 0) ? equivalent_chain.next() : end_point2_other_chain_id) :
((end_point2_other_chain_id == 0) ? end_point1_other_chain_id :
(end_point1_other_chain_id == end_point2_other_chain_id) ?
end_point1_other_chain_id :
equivalent_chain.merge(end_point1_other_chain_id, end_point2_other_chain_id));
end_point1.chain_id = chain_id;
end_point2.chain_id = chain_id;
assert(validate_graph_and_queue());
if (iter == 0) {
// Last iteration. There shall be exactly one or two end points waiting to be connected.
assert(queue.size() == ((first_point == nullptr) ? 2 : 1));
if (first_point == nullptr)
first_point = queue.top();
while (! queue.empty()) {
queue.top()->edge_out = nullptr;
queue.pop();
}
break;
}
} else {
// This edge forms a loop. Update end_point1 and try another one.
++ iter;
end_point1.edge_out = nullptr;
// Update edge_out and distance.
size_t this_idx = &end_point1 - &end_points.front();
// Find the closest point to this end_point, which lies on a different extrusion path (filtered by the filter lambda).
size_t next_idx = find_closest_point(kdtree, end_point1.pos, [&end_points, &equivalent_chain, this_idx](size_t idx) {
assert(end_points[this_idx].edge_out == nullptr);
assert(end_points[this_idx].chain_id == 0);
if ((idx ^ this_idx) <= 1 || end_points[idx].chain_id != 0)
// Points of the same segment shall not be connected,
// cannot connect to an already connected point (ideally those would be removed from the KD tree, but the update is difficult).
return false;
size_t chain1 = equivalent_chain(end_points[this_idx ^ 1].chain_id);
size_t chain2 = equivalent_chain(end_points[idx ^ 1].chain_id);
return chain1 != chain2 || chain1 == 0;
});
assert(next_idx < end_points.size());
end_point1.edge_out = &end_points[next_idx];
end_point1.distance_out = (end_points[next_idx].pos - end_point1.pos).squaredNorm();
// Update position of this end point in the queue based on the distance calculated at the line above.
queue.update(end_point1.heap_idx);
//FIXME Remove the other end point from the KD tree.
// As the KD tree update is expensive, do it only after some larger number of points is removed from the queue.
assert(validate_graph_and_queue());
}
}
assert(queue.empty());
// Now interconnect pairs of segments into a chain.
assert(first_point != nullptr);
do {
assert(out.size() < num_segments);
size_t first_point_id = first_point - &end_points.front();
size_t segment_id = first_point_id >> 1;
EndPoint *second_point = &end_points[first_point_id ^ 1];
out.emplace_back(segment_id, (first_point_id & 1) != 0);
first_point = second_point->edge_out;
} while (first_point != nullptr);
}
assert(out.size() == num_segments);
return out;
}
std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
{
auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); };
std::vector<std::pair<size_t, bool>> out = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, entities.size(), start_near);
for (size_t i = 0; i < entities.size(); ++ i) {
ExtrusionEntity *ee = entities[i];
if (ee->is_loop())
// Ignore reversals for loops, as the start point equals the end point.
out[i].second = false;
// Is can_reverse() respected by the reversals?
assert(entities[i]->can_reverse() || ! out[i].second);
}
return out;
}
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const std::vector<std::pair<size_t, bool>> &chain)
{
assert(entities.size() == chain.size());
std::vector<ExtrusionEntity*> out;
out.reserve(entities.size());
for (const std::pair<size_t, bool> &idx : chain) {
assert(entities[idx.first] != nullptr);
out.emplace_back(entities[idx.first]);
if (idx.second)
out.back()->reverse();
}
entities.swap(out);
}
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
{
reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near));
}
std::vector<size_t> chain_points(const Points &points, Point *start_near)
{
auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };
std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, points.size(), start_near);
std::vector<size_t> out;
out.reserve(ordered.size());
for (auto &segment_and_reversal : ordered)
out.emplace_back(segment_and_reversal.first);
return out;
}
Polylines chain_infill_polylines(Polylines &&polylines)
{
auto segment_end_point = [&polylines](size_t idx, bool first_point) -> const Point& { return first_point ? polylines[idx].first_point() : polylines[idx].last_point(); };
std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, polylines.size(), nullptr);
Polylines out;
out.reserve(polylines.size());
for (auto &segment_and_reversal : ordered) {
out.emplace_back(std::move(polylines[segment_and_reversal.first]));
if (segment_and_reversal.second)
out.back().reverse();
}
return out;
}
template<class T> static inline T chain_path_items(const Points &points, const T &items)
{
auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };
std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, points.size(), nullptr);
T out;
out.reserve(items.size());
for (auto &segment_and_reversal : ordered)
out.emplace_back(items[segment_and_reversal.first]);
return out;
}
ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const ClipperLib::PolyNodes &items)
{
return chain_path_items(points, items);
}
std::vector<std::pair<size_t, size_t>> chain_print_object_instances(const Print &print)
{
// Order objects using a nearest neighbor search.
Points object_reference_points;
std::vector<std::pair<size_t, size_t>> instances;
for (size_t i = 0; i < print.objects().size(); ++ i) {
const PrintObject &object = *print.objects()[i];
for (size_t j = 0; j < object.copies().size(); ++ j) {
object_reference_points.emplace_back(object.copy_center(j));
instances.emplace_back(i, j);
}
}
auto segment_end_point = [&object_reference_points](size_t idx, bool /* first_point */) -> const Point& { return object_reference_points[idx]; };
std::vector<std::pair<size_t, bool>> ordered = chain_segments<Point, decltype(segment_end_point)>(segment_end_point, instances.size(), nullptr);
std::vector<std::pair<size_t, size_t>> out;
out.reserve(instances.size());
for (auto &segment_and_reversal : ordered)
out.emplace_back(instances[segment_and_reversal.first]);
return out;
}
} // namespace Slic3r

View file

@ -0,0 +1,33 @@
#ifndef slic3r_ShortestPath_hpp_
#define slic3r_ShortestPath_hpp_
#include "libslic3r.h"
#include "ExtrusionEntity.hpp"
#include "Point.hpp"
#include <utility>
#include <vector>
namespace ClipperLib { class PolyNode; }
namespace Slic3r {
std::vector<size_t> chain_points(const Points &points, Point *start_near = nullptr);
std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const std::vector<std::pair<size_t, bool>> &chain);
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
Polylines chain_infill_polylines(Polylines &&src);
std::vector<ClipperLib::PolyNode*> chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items);
// Chain instances of print objects by an approximate shortest path.
// Returns pairs of PrintObject idx and instance of that PrintObject.
class Print;
std::vector<std::pair<size_t, size_t>> chain_print_object_instances(const Print &print);
} // namespace Slic3r
#endif /* slic3r_ShortestPath_hpp_ */

View file

@ -783,7 +783,7 @@ namespace SupportMaterialInternal {
for (const ExtrusionPath &ep : loop.paths) for (const ExtrusionPath &ep : loop.paths)
if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty())
return ep.size() >= (ep.is_closed() ? 3 : 2); return ep.size() >= (ep.is_closed() ? 3 : 2);
return false; return false;
} }
static bool has_bridging_perimeters(const ExtrusionEntityCollection &perimeters) static bool has_bridging_perimeters(const ExtrusionEntityCollection &perimeters)
{ {
@ -2125,7 +2125,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
} }
// $layer->slices contains the full shape of layer, thus including // $layer->slices contains the full shape of layer, thus including
// perimeter's width. $support contains the full shape of support // perimeter's width. $support contains the full shape of support
// material, thus including the width of its foremost extrusion. // material, thus including the width of its foremost extrusion.
// We leave a gap equal to a full extrusion width. // We leave a gap equal to a full extrusion width.
support_layer.polygons = diff(support_layer.polygons, polygons_trimming); support_layer.polygons = diff(support_layer.polygons, polygons_trimming);
} }
@ -2934,20 +2934,13 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Prepare fillers. // Prepare fillers.
SupportMaterialPattern support_pattern = m_object_config->support_material_pattern; SupportMaterialPattern support_pattern = m_object_config->support_material_pattern;
bool with_sheath = m_object_config->support_material_with_sheath; bool with_sheath = m_object_config->support_material_with_sheath;
InfillPattern infill_pattern; InfillPattern infill_pattern = (support_pattern == smpHoneycomb ? ipHoneycomb : ipRectilinear);
std::vector<float> angles; std::vector<float> angles;
angles.push_back(base_angle); angles.push_back(base_angle);
switch (support_pattern) {
case smpRectilinearGrid: if (support_pattern == smpRectilinearGrid)
angles.push_back(interface_angle); angles.push_back(interface_angle);
// fall through
case smpRectilinear:
infill_pattern = ipRectilinear;
break;
case smpHoneycomb:
infill_pattern = ipHoneycomb;
break;
}
BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.))); BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.)));
// const coordf_t link_max_length_factor = 3.; // const coordf_t link_max_length_factor = 3.;
@ -3217,7 +3210,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
density = 0.5f; density = 0.5f;
flow = m_first_layer_flow; flow = m_first_layer_flow;
// use the proper spacing for first layer as we don't need to align // use the proper spacing for first layer as we don't need to align
// its pattern to the other layers // its pattern to the other layers
//FIXME When paralellizing, each thread shall have its own copy of the fillers. //FIXME When paralellizing, each thread shall have its own copy of the fillers.
filler->spacing = flow.spacing(); filler->spacing = flow.spacing();
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));

View file

@ -342,7 +342,7 @@ static void copy_config_dir_single_level(const boost::filesystem::path &path_src
! boost::filesystem::create_directory(path_dst)) ! boost::filesystem::create_directory(path_dst))
throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + path_dst.string()); throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + path_dst.string());
for (auto &dir_entry : boost::filesystem::directory_iterator(path_src)) for (auto &dir_entry : boost::filesystem::directory_iterator(path_src))
if (Slic3r::is_ini_file(dir_entry)) if (Slic3r::is_ini_file(dir_entry))
boost::filesystem::copy_file(dir_entry.path(), path_dst / dir_entry.path().filename(), boost::filesystem::copy_option::overwrite_if_exists); boost::filesystem::copy_file(dir_entry.path(), path_dst / dir_entry.path().filename(), boost::filesystem::copy_option::overwrite_if_exists);
} }
@ -351,7 +351,7 @@ static void delete_existing_ini_files(const boost::filesystem::path &path)
{ {
if (! boost::filesystem::is_directory(path)) if (! boost::filesystem::is_directory(path))
return; return;
for (auto &dir_entry : boost::filesystem::directory_iterator(path)) for (auto &dir_entry : boost::filesystem::directory_iterator(path))
if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini"))
boost::filesystem::remove(dir_entry.path()); boost::filesystem::remove(dir_entry.path());
} }
@ -378,7 +378,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot:
sprintf(name, "filament_%u", i); sprintf(name, "filament_%u", i);
if (! app_config.has("presets", name)) if (! app_config.has("presets", name))
break; break;
snapshot.filaments.emplace_back(app_config.get("presets", name)); snapshot.filaments.emplace_back(app_config.get("presets", name));
} }
// Vendor specific config bundles and installed printers. // Vendor specific config bundles and installed printers.
for (const std::pair<std::string, std::map<std::string, std::set<std::string>>> &vendor : app_config.vendors()) { for (const std::pair<std::string, std::map<std::string, std::set<std::string>>> &vendor : app_config.vendors()) {
@ -417,7 +417,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot:
// Backup the presets. // Backup the presets.
for (const char *subdir : { "print", "filament", "printer", "vendor" }) for (const char *subdir : { "print", "filament", "printer", "vendor" })
copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir); copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir);
snapshot.save_ini((snapshot_dir / "snapshot.ini").string()); snapshot.save_ini((snapshot_dir / "snapshot.ini").string());
assert(m_snapshots.empty() || m_snapshots.back().time_captured <= snapshot.time_captured); assert(m_snapshots.empty() || m_snapshots.back().time_captured <= snapshot.time_captured);
m_snapshots.emplace_back(std::move(snapshot)); m_snapshots.emplace_back(std::move(snapshot));
return m_snapshots.back(); return m_snapshots.back();

View file

@ -227,9 +227,9 @@ size_t Index::load(const boost::filesystem::path &path)
// End of semver or keyword. // End of semver or keyword.
break; break;
} }
if (*key_end != 0 && *key_end != ' ' && *key_end != '\t' && *key_end != '=') if (*key_end != 0 && *key_end != ' ' && *key_end != '\t' && *key_end != '=')
throw file_parser_error("Invalid keyword or semantic version", path, idx_line); throw file_parser_error("Invalid keyword or semantic version", path, idx_line);
char *value = left_trim(key_end); char *value = left_trim(key_end);
bool key_value_pair = *value == '='; bool key_value_pair = *value == '=';
if (key_value_pair) if (key_value_pair)
value = left_trim(value + 1); value = left_trim(value + 1);
@ -245,11 +245,11 @@ size_t Index::load(const boost::filesystem::path &path)
if (strcmp(key, "min_slic3r_version") == 0 || strcmp(key, "max_slic3r_version") == 0) { if (strcmp(key, "min_slic3r_version") == 0 || strcmp(key, "max_slic3r_version") == 0) {
if (! svalue.empty()) if (! svalue.empty())
semver = Semver::parse(svalue); semver = Semver::parse(svalue);
if (! semver) if (! semver)
throw file_parser_error(std::string(key) + " must referece a valid semantic version", path, idx_line); throw file_parser_error(std::string(key) + " must referece a valid semantic version", path, idx_line);
if (strcmp(key, "min_slic3r_version") == 0) if (strcmp(key, "min_slic3r_version") == 0)
ver.min_slic3r_version = *semver; ver.min_slic3r_version = *semver;
else else
ver.max_slic3r_version = *semver; ver.max_slic3r_version = *semver;
} else { } else {
// Ignore unknown keys, as there may come new keys in the future. // Ignore unknown keys, as there may come new keys in the future.

View file

@ -455,7 +455,7 @@ void BedShapePanel::update_shape()
else if (page_idx == SHAPE_CUSTOM) else if (page_idx == SHAPE_CUSTOM)
m_shape = m_loaded_shape; m_shape = m_loaded_shape;
update_preview(); update_preview();
} }
// Loads an stl file, projects it to the XY plane and calculates a polygon. // Loads an stl file, projects it to the XY plane and calculates a polygon.

View file

@ -931,8 +931,8 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
if (items_count > 1) if (items_count > 1)
m_original_height += (items_count - 1) * scaled_square_contour; m_original_height += (items_count - 1) * scaled_square_contour;
m_width = (int)next_highest_power_of_2((uint32_t)m_original_width); m_width = (int)next_highest_power_of_2((uint32_t)m_original_width);
m_height = (int)next_highest_power_of_2((uint32_t)m_original_height); m_height = (int)next_highest_power_of_2((uint32_t)m_original_height);
// generates bitmap // generates bitmap
wxBitmap bitmap(m_width, m_height); wxBitmap bitmap(m_width, m_height);
@ -1885,7 +1885,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
if (m_reload_delayed) if (m_reload_delayed)
return; return;
bool update_object_list = false; bool update_object_list = false;
if (m_volumes.volumes != glvolumes_new) if (m_volumes.volumes != glvolumes_new)
update_object_list = true; update_object_list = true;
@ -3413,10 +3413,9 @@ void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool foc
m_sidebar_field = focus_on ? opt_key : ""; m_sidebar_field = focus_on ? opt_key : "";
if (!m_sidebar_field.empty()) if (!m_sidebar_field.empty())
{
m_gizmos.reset_all_states(); m_gizmos.reset_all_states();
m_dirty = true;
} m_dirty = true;
} }
void GLCanvas3D::handle_layers_data_focus_event(const t_layer_height_range range, const EditorType type) void GLCanvas3D::handle_layers_data_focus_event(const t_layer_height_range range, const EditorType type)
@ -5096,14 +5095,14 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
for (const GCodePreviewData::Extrusion::Layer &layer : preview_data.extrusion.layers) for (const GCodePreviewData::Extrusion::Layer &layer : preview_data.extrusion.layers)
for (const ExtrusionPath &path : layer.paths) for (const ExtrusionPath &path : layer.paths)
++ num_paths_per_role[size_t(path.role())]; ++ num_paths_per_role[size_t(path.role())];
std::vector<std::vector<float>> roles_values; std::vector<std::vector<float>> roles_values;
roles_values.assign(size_t(erCount), std::vector<float>()); roles_values.assign(size_t(erCount), std::vector<float>());
for (size_t i = 0; i < roles_values.size(); ++ i) for (size_t i = 0; i < roles_values.size(); ++ i)
roles_values[i].reserve(num_paths_per_role[i]); roles_values[i].reserve(num_paths_per_role[i]);
for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
for (const ExtrusionPath& path : layer.paths) for (const ExtrusionPath& path : layer.paths)
roles_values[size_t(path.role())].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path)); roles_values[size_t(path.role())].emplace_back(Helper::path_filter(preview_data.extrusion.view_type, path));
roles_filters.reserve(size_t(erCount)); roles_filters.reserve(size_t(erCount));
size_t num_buffers = 0; size_t num_buffers = 0;
for (std::vector<float> &values : roles_values) { for (std::vector<float> &values : roles_values) {
sort_remove_duplicates(values); sort_remove_duplicates(values);

View file

@ -125,7 +125,7 @@ void config_wizard(int reason)
if (! wxGetApp().check_unsaved_changes()) if (! wxGetApp().check_unsaved_changes())
return; return;
try { try {
ConfigWizard wizard(nullptr, static_cast<ConfigWizard::RunReason>(reason)); ConfigWizard wizard(nullptr, static_cast<ConfigWizard::RunReason>(reason));
wizard.run(wxGetApp().preset_bundle, wxGetApp().preset_updater); wizard.run(wxGetApp().preset_bundle, wxGetApp().preset_updater);
} }

View file

@ -725,7 +725,7 @@ bool GUI_App::load_language(wxString language, bool initial)
#endif #endif
if (initial) if (initial)
message + "\n\nApplication will close."; message + "\n\nApplication will close.";
wxMessageBox(message, "PrusaSlicer - Switching language failed", wxOK | wxICON_ERROR); wxMessageBox(message, "PrusaSlicer - Switching language failed", wxOK | wxICON_ERROR);
if (initial) if (initial)
std::exit(EXIT_FAILURE); std::exit(EXIT_FAILURE);
else else

View file

@ -753,9 +753,9 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol
} }
select_items(items); select_items(items);
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME //#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
selection_changed(); selection_changed();
#endif //no __WXOSX__ //__WXMSW__ //#endif //no __WXOSX__ //__WXMSW__
} }
void ObjectList::paste_objects_into_list(const std::vector<size_t>& object_idxs) void ObjectList::paste_objects_into_list(const std::vector<size_t>& object_idxs)
@ -773,9 +773,9 @@ void ObjectList::paste_objects_into_list(const std::vector<size_t>& object_idxs)
wxGetApp().plater()->changed_objects(object_idxs); wxGetApp().plater()->changed_objects(object_idxs);
select_items(items); select_items(items);
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME //#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
selection_changed(); selection_changed();
#endif //no __WXOSX__ //__WXMSW__ //#endif //no __WXOSX__ //__WXMSW__
} }
#ifdef __WXOSX__ #ifdef __WXOSX__
@ -815,7 +815,9 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
if (col == nullptr) { if (col == nullptr) {
if (wxOSX) if (wxOSX)
UnselectAll(); UnselectAll();
else else if (!evt_context_menu)
// Case, when last item was deleted and under GTK was called wxEVT_DATAVIEW_SELECTION_CHANGED,
// which invoked next list_manipulation(false)
return; return;
} }
@ -1720,9 +1722,9 @@ void ObjectList::load_subobject(ModelVolumeType type)
if (sel_item) if (sel_item)
select_item(sel_item); select_item(sel_item);
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME //#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
selection_changed(); selection_changed();
#endif //no __WXOSX__ //__WXMSW__ //#endif //no __WXOSX__ //__WXMSW__
} }
void ObjectList::load_part( ModelObject* model_object, void ObjectList::load_part( ModelObject* model_object,
@ -1858,9 +1860,9 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
const auto object_item = m_objects_model->GetTopParent(GetSelection()); const auto object_item = m_objects_model->GetTopParent(GetSelection());
select_item(m_objects_model->AddVolumeChild(object_item, name, type, select_item(m_objects_model->AddVolumeChild(object_item, name, type,
new_volume->get_mesh_errors_count()>0)); new_volume->get_mesh_errors_count()>0));
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME //#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
selection_changed(); selection_changed();
#endif //no __WXOSX__ //__WXMSW__ //#endif //no __WXOSX__ //__WXMSW__
} }
void ObjectList::load_shape_object(const std::string& type_name) void ObjectList::load_shape_object(const std::string& type_name)
@ -1899,6 +1901,9 @@ void ObjectList::load_shape_object(const std::string& type_name)
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
new_object->invalidate_bounding_box(); new_object->invalidate_bounding_box();
new_object->center_around_origin();
new_object->ensure_on_bed();
const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb(); const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb();
new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -new_object->origin_translation(2))); new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -new_object->origin_translation(2)));

View file

@ -192,7 +192,7 @@ ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(const wx
else else
panel->SetSizer(optgroup->sizer); panel->SetSizer(optgroup->sizer);
return optgroup; return optgroup;
} }

View file

@ -924,7 +924,7 @@ void MainFrame::load_config_file()
wxString file; wxString file;
if (dlg.ShowModal() == wxID_OK) if (dlg.ShowModal() == wxID_OK)
file = dlg.GetPath(); file = dlg.GetPath();
if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) { if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) {
wxGetApp().app_config->update_config_dir(get_dir_name(file)); wxGetApp().app_config->update_config_dir(get_dir_name(file));
m_last_config = file; m_last_config = file;
} }

View file

@ -133,7 +133,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
m_options_mode.push_back(option_set[0].opt.mode); m_options_mode.push_back(option_set[0].opt.mode);
// if we have a single option with no label, no sidetext just add it directly to sizer // if we have a single option with no label, no sidetext just add it directly to sizer
if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width && if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width &&
option_set.front().opt.label.empty() && option_set.front().opt.label.empty() &&
option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr && option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr &&
line.get_extra_widgets().size() == 0) { line.get_extra_widgets().size() == 0) {

View file

@ -3346,7 +3346,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
this->statusbar()->set_progress(evt.status.percent); this->statusbar()->set_progress(evt.status.percent);
this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8("")); this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8(""));
} }
if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE || PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) { if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE | PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) {
switch (this->printer_technology) { switch (this->printer_technology) {
case ptFFF: case ptFFF:
this->update_fff_scene(); this->update_fff_scene();

View file

@ -279,7 +279,7 @@ std::string PresetBundle::load_system_presets()
errors_cummulative += "\n"; errors_cummulative += "\n";
} }
} }
if (first) { if (first) {
// No config bundle loaded, reset. // No config bundle loaded, reset.
this->reset(false); this->reset(false);
} }

View file

@ -410,7 +410,7 @@ void Selection::set_deserialized(EMode mode, const std::vector<std::pair<size_t,
if (! m_valid) if (! m_valid)
return; return;
m_mode = mode; m_mode = mode;
for (unsigned int i : m_list) for (unsigned int i : m_list)
(*m_volumes)[i]->selected = false; (*m_volumes)[i]->selected = false;
m_list.clear(); m_list.clear();

View file

@ -371,7 +371,7 @@ void WipingPanel::toggle_advanced(bool user_action) {
else else
m_advanced = !advanced_matches_simple(); // if called from constructor, show what is appropriate m_advanced = !advanced_matches_simple(); // if called from constructor, show what is appropriate
(m_advanced ? m_page_advanced : m_page_simple)->Show(); (m_advanced ? m_page_advanced : m_page_simple)->Show();
(!m_advanced ? m_page_advanced : m_page_simple)->Hide(); (!m_advanced ? m_page_advanced : m_page_simple)->Hide();
m_widget_button->SetLabel(m_advanced ? _(L("Show simplified settings")) : _(L("Show advanced settings"))); m_widget_button->SetLabel(m_advanced ? _(L("Show simplified settings")) : _(L("Show advanced settings")));

View file

@ -669,7 +669,7 @@ wxDataViewItem ObjectDataViewModel::Add(const wxString &name,
if (has_errors) if (has_errors)
root->m_bmp = *m_warning_bmp; root->m_bmp = *m_warning_bmp;
m_objects.push_back(root); m_objects.push_back(root);
// notify control // notify control
wxDataViewItem child((void*)root); wxDataViewItem child((void*)root);
wxDataViewItem parent((void*)NULL); wxDataViewItem parent((void*)NULL);
@ -720,7 +720,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
root->SetBitmap(*m_warning_bmp); root->SetBitmap(*m_warning_bmp);
// notify control // notify control
const wxDataViewItem child((void*)node); const wxDataViewItem child((void*)node);
ItemAdded(parent_item, child); ItemAdded(parent_item, child);
root->m_volumes_cnt++; root->m_volumes_cnt++;

View file

@ -3,6 +3,7 @@
%{ %{
#include <xsinit.h> #include <xsinit.h>
#include "libslic3r/Geometry.hpp" #include "libslic3r/Geometry.hpp"
#include "libslic3r/ShortestPath.hpp"
%} %}
@ -49,7 +50,7 @@ std::vector<Points::size_type>
chained_path(points) chained_path(points)
Points points Points points
CODE: CODE:
Slic3r::Geometry::chained_path(points, RETVAL); RETVAL = chain_points(points);
OUTPUT: OUTPUT:
RETVAL RETVAL
@ -58,7 +59,7 @@ chained_path_from(points, start_from)
Points points Points points
Point* start_from Point* start_from
CODE: CODE:
Slic3r::Geometry::chained_path(points, RETVAL, *start_from); RETVAL = chain_points(points, start_from);
OUTPUT: OUTPUT:
RETVAL RETVAL